From c46346de526541f0b1552e814c0d5ec5773caa83 Mon Sep 17 00:00:00 2001 From: Jeroen Oudshoorn Date: Fri, 12 Jan 2024 18:42:47 +0100 Subject: [PATCH] Display update --- pwnagotchi/_version.py | 2 +- pwnagotchi/ui/display.py | 178 ++++- pwnagotchi/ui/hw/__init__.py | 225 +++++- pwnagotchi/ui/hw/dfrobot.py | 43 ++ pwnagotchi/ui/hw/dfrobot_v2.py | 43 ++ pwnagotchi/ui/hw/libs/waveshare/epdconfig.py | 243 +++++++ .../ui/hw/libs/waveshare/v13in3k/epd13in3k.py | 188 +++++ .../ui/hw/libs/waveshare/v1in02/epd1in02.py | 349 +++++++++ .../ui/hw/libs/waveshare/v1in54/epd1in54.py | 260 +++++++ .../libs/waveshare/v1in54_v2/epd1in54_V2.py | 316 ++++++++ .../ui/hw/libs/waveshare/v1in54b/epd1in54b.py | 219 ++++++ .../libs/waveshare/v1in54b_v2/epd1in54b_v2.py | 193 +++++ .../ui/hw/libs/waveshare/v1in54c/epd1in54c.py | 156 ++++ .../ui/hw/libs/waveshare/v1in64g/epd1in64g.py | 241 +++++++ .../hw/libs/waveshare/v2in13_V1/epd2in13.py | 224 ++++++ .../hw/libs/waveshare/v2in13_V1/epd2in13bc.py | 164 +++++ .../waveshare/v2in13_V1/epd2in13bcFAST.py | 359 ++++++++++ .../libs/waveshare/v2in13_V2/epd2in13_V2.py | 314 ++++++++ .../libs/waveshare/v2in13_V3/epd2in13_V3.py | 404 +++++++++++ .../libs/waveshare/v2in13_V4/epd2in13_V4.py | 350 +++++++++ .../libs/waveshare/v2in13b_v3/epd2in13b_V3.py | 161 +++++ .../libs/waveshare/v2in13b_v4/epd2in13b_V4.py | 204 ++++++ .../hw/libs/waveshare/v2in13bc/epd2in13bc.py | 378 ++++++++++ .../ui/hw/libs/waveshare/v2in13d/epd2in13d.py | 358 +++++++++ .../ui/hw/libs/waveshare/v2in23g/epd2in13g.py | 242 +++++++ .../ui/hw/libs/waveshare/v2in36g/epd2in36g.py | 241 +++++++ .../ui/hw/libs/waveshare/v2in66/epd2in66.py | 232 ++++++ .../ui/hw/libs/waveshare/v2in66g/epd2in66g.py | 229 ++++++ .../ui/hw/libs/waveshare/v2in7/epd2in7.py | 520 ++++++++++++++ .../hw/libs/waveshare/v2in7_v2/epd2in7_V2.py | 520 ++++++++++++++ .../ui/hw/libs/waveshare/v2in7b/epd2in7b.py | 272 +++++++ .../libs/waveshare/v2in7b_v2/epd2in7b_V2.py | 194 +++++ .../ui/hw/libs/waveshare/v2in9/epd2in9.py | 201 ++++++ .../hw/libs/waveshare/v2in9_v2/epd2in9V2.py | 530 ++++++++++++++ .../libs/waveshare/v2in9b_v3/epd2in9b_V3.py | 164 +++++ .../libs/waveshare/v2in9b_v4/epd2in9b_V4.py | 387 ++++++++++ .../ui/hw/libs/waveshare/v2in9bc/epd2in9bc.py | 158 ++++ .../ui/hw/libs/waveshare/v2in9d/epd2in9d.py | 303 ++++++++ .../ui/hw/libs/waveshare/v3in0g/epd3in0g.py | 222 ++++++ .../ui/hw/libs/waveshare/v3in52/epd3in52.py | 460 ++++++++++++ .../ui/hw/libs/waveshare/v3in7/epd3in7.py | 458 ++++++++++++ .../ui/hw/libs/waveshare/v4in01f/epd4in01f.py | 238 ++++++ .../ui/hw/libs/waveshare/v4in2/epd4in2.py | 678 ++++++++++++++++++ .../ui/hw/libs/waveshare/v4in26/epd4in26.py | 516 +++++++++++++ .../hw/libs/waveshare/v4in2_v2/epd4in2_V2.py | 528 ++++++++++++++ .../libs/waveshare/v4in2b_v2/epd4in2b_V2.py | 161 +++++ .../ui/hw/libs/waveshare/v4in2bc/epd4in2bc.py | 151 ++++ .../ui/hw/libs/waveshare/v4in37g/epd4in37g.py | 242 +++++++ .../ui/hw/libs/waveshare/v5in65f/epd5in65f.py | 217 ++++++ .../ui/hw/libs/waveshare/v5in83/epd5in83.py | 203 ++++++ .../libs/waveshare/v5in83_v2/epd5in83_V2.py | 176 +++++ .../libs/waveshare/v5in83b_v2/epd5in83b_V2.py | 181 +++++ .../hw/libs/waveshare/v5in83bc/epd5in83bc.py | 203 ++++++ .../ui/hw/libs/waveshare/v7in3f/epd7in3f.py | 249 +++++++ .../ui/hw/libs/waveshare/v7in3g/epd7in3g.py | 243 +++++++ .../ui/hw/libs/waveshare/v7in5/epd7in5.py | 185 +++++ .../hw/libs/waveshare/v7in5_HD/epd7in5_HD.py | 181 +++++ .../hw/libs/waveshare/v7in5_v2/epd7in5_V2.py | 289 ++++++++ .../waveshare/v7in5_v2/epd7in5_V2_fast.py | 280 ++++++++ .../libs/waveshare/v7in5_v2/epd7in5_V2_old.py | 530 ++++++++++++++ .../libs/waveshare/v7in5b_HD/epd7in5b_HD.py | 206 ++++++ .../libs/waveshare/v7in5b_v2/epd7in5b_V2.py | 192 +++++ .../ui/hw/libs/waveshare/v7in5bc/epd7in5bc.py | 204 ++++++ pwnagotchi/ui/hw/spotpear24in.py | 52 ++ pwnagotchi/ui/hw/waveshare13in3k.py | 0 pwnagotchi/ui/hw/waveshare1in02.py | 0 pwnagotchi/ui/hw/waveshare1in44lcd.py | 46 ++ pwnagotchi/ui/hw/waveshare1in54.py | 46 ++ pwnagotchi/ui/hw/waveshare1in54_V2.py | 46 ++ pwnagotchi/ui/hw/waveshare1in54b.py | 46 ++ pwnagotchi/ui/hw/waveshare1in54b_V2.py | 46 ++ pwnagotchi/ui/hw/waveshare1in54c.py | 0 pwnagotchi/ui/hw/waveshare1in64g.py | 0 pwnagotchi/ui/hw/waveshare2in13.py | 90 +++ pwnagotchi/ui/hw/waveshare2in13_V2.py | 69 ++ pwnagotchi/ui/hw/waveshare2in13_V3.py | 48 ++ pwnagotchi/ui/hw/waveshare2in13_V4.py | 46 ++ pwnagotchi/ui/hw/waveshare2in13b_V3.py | 72 ++ pwnagotchi/ui/hw/waveshare2in13b_V4.py | 72 ++ pwnagotchi/ui/hw/waveshare2in13bc.py | 47 ++ pwnagotchi/ui/hw/waveshare2in13d.py | 47 ++ pwnagotchi/ui/hw/waveshare2in23g.py | 0 pwnagotchi/ui/hw/waveshare2in36g.py | 0 pwnagotchi/ui/hw/waveshare2in66.py | 0 pwnagotchi/ui/hw/waveshare2in7.py | 46 ++ pwnagotchi/ui/hw/waveshare2in7_V2.py | 49 ++ pwnagotchi/ui/hw/waveshare2in7b.py | 46 ++ pwnagotchi/ui/hw/waveshare2in7b_V2.py | 46 ++ pwnagotchi/ui/hw/waveshare2in9.py | 47 ++ pwnagotchi/ui/hw/waveshare2in9_V2.py | 47 ++ pwnagotchi/ui/hw/waveshare2in9b_V3.py | 47 ++ pwnagotchi/ui/hw/waveshare2in9b_V4.py | 47 ++ pwnagotchi/ui/hw/waveshare2in9bc.py | 0 pwnagotchi/ui/hw/waveshare2in9d.py | 0 pwnagotchi/ui/hw/waveshare3in0g.py | 0 pwnagotchi/ui/hw/waveshare3in52.py | 0 pwnagotchi/ui/hw/waveshare3in5lcd.py | 53 ++ pwnagotchi/ui/hw/waveshare3in7.py | 0 pwnagotchi/ui/hw/waveshare4in01f.py | 0 pwnagotchi/ui/hw/waveshare4in2.py | 0 pwnagotchi/ui/hw/waveshare4in26.py | 0 pwnagotchi/ui/hw/waveshare4in2_V2.py | 0 pwnagotchi/ui/hw/waveshare4in2b_V2.py | 0 pwnagotchi/ui/hw/waveshare4in2bc.py | 0 pwnagotchi/ui/hw/waveshare4in37g.py | 0 pwnagotchi/ui/hw/waveshare5in65f.py | 0 pwnagotchi/ui/hw/waveshare5in83.py | 0 pwnagotchi/ui/hw/waveshare5in83_V2.py | 0 pwnagotchi/ui/hw/waveshare5in83b_V2.py | 0 pwnagotchi/ui/hw/waveshare5in83bc.py | 0 pwnagotchi/ui/hw/waveshare7in3f.py | 0 pwnagotchi/ui/hw/waveshare7in3g.py | 0 pwnagotchi/ui/hw/waveshare7in5.py | 0 pwnagotchi/ui/hw/waveshare7in5_HD.py | 0 pwnagotchi/ui/hw/waveshare7in5_V2.py | 0 pwnagotchi/ui/hw/waveshare7in5b_HD.py | 0 pwnagotchi/ui/hw/waveshare7in5b_V2.py | 0 pwnagotchi/ui/hw/waveshare7in5bc.py | 0 pwnagotchi/ui/view.py | 2 +- pwnagotchi/ui/web/handler.py | 6 +- pwnagotchi/ui/web/server.py | 10 +- .../ui/web/static/js/jquery-3.7.1.min.js | 2 + .../web/static/js/jquery-qrcode-0.18.0.min.js | 2 + pwnagotchi/ui/web/static/js/kjua-0.9.0.min.js | 2 + pwnagotchi/ui/web/templates/base.html | 2 +- pwnagotchi/ui/web/templates/plugins.html | 48 +- pwnagotchi/ui/web/templates/profile.html | 4 +- requirements.txt | 4 +- 128 files changed, 18153 insertions(+), 88 deletions(-) create mode 100644 pwnagotchi/ui/hw/dfrobot.py create mode 100644 pwnagotchi/ui/hw/dfrobot_v2.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/epdconfig.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v13in3k/epd13in3k.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v1in02/epd1in02.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v1in54/epd1in54.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v1in54_v2/epd1in54_V2.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v1in54b/epd1in54b.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v1in54b_v2/epd1in54b_v2.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v1in54c/epd1in54c.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v1in64g/epd1in64g.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13bc.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13bcFAST.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in13_V2/epd2in13_V2.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in13_V3/epd2in13_V3.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in13_V4/epd2in13_V4.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in13b_v3/epd2in13b_V3.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in13b_v4/epd2in13b_V4.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in13bc/epd2in13bc.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in13d/epd2in13d.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in23g/epd2in13g.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in36g/epd2in36g.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in66/epd2in66.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in66g/epd2in66g.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in7/epd2in7.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in7_v2/epd2in7_V2.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in7b/epd2in7b.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in7b_v2/epd2in7b_V2.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in9/epd2in9.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in9_v2/epd2in9V2.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in9b_v3/epd2in9b_V3.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in9b_v4/epd2in9b_V4.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in9bc/epd2in9bc.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v2in9d/epd2in9d.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v3in0g/epd3in0g.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v3in52/epd3in52.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v3in7/epd3in7.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v4in01f/epd4in01f.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v4in2/epd4in2.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v4in26/epd4in26.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v4in2_v2/epd4in2_V2.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v4in2b_v2/epd4in2b_V2.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v4in2bc/epd4in2bc.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v4in37g/epd4in37g.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v5in65f/epd5in65f.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v5in83/epd5in83.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v5in83_v2/epd5in83_V2.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v5in83b_v2/epd5in83b_V2.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v5in83bc/epd5in83bc.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v7in3f/epd7in3f.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v7in3g/epd7in3g.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v7in5/epd7in5.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v7in5_HD/epd7in5_HD.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2_fast.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2_old.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v7in5b_HD/epd7in5b_HD.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v7in5b_v2/epd7in5b_V2.py create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v7in5bc/epd7in5bc.py create mode 100644 pwnagotchi/ui/hw/spotpear24in.py create mode 100644 pwnagotchi/ui/hw/waveshare13in3k.py create mode 100644 pwnagotchi/ui/hw/waveshare1in02.py create mode 100644 pwnagotchi/ui/hw/waveshare1in44lcd.py create mode 100644 pwnagotchi/ui/hw/waveshare1in54.py create mode 100644 pwnagotchi/ui/hw/waveshare1in54_V2.py create mode 100644 pwnagotchi/ui/hw/waveshare1in54b.py create mode 100644 pwnagotchi/ui/hw/waveshare1in54b_V2.py create mode 100644 pwnagotchi/ui/hw/waveshare1in54c.py create mode 100644 pwnagotchi/ui/hw/waveshare1in64g.py create mode 100644 pwnagotchi/ui/hw/waveshare2in13.py create mode 100644 pwnagotchi/ui/hw/waveshare2in13_V2.py create mode 100644 pwnagotchi/ui/hw/waveshare2in13_V3.py create mode 100644 pwnagotchi/ui/hw/waveshare2in13_V4.py create mode 100644 pwnagotchi/ui/hw/waveshare2in13b_V3.py create mode 100644 pwnagotchi/ui/hw/waveshare2in13b_V4.py create mode 100644 pwnagotchi/ui/hw/waveshare2in13bc.py create mode 100644 pwnagotchi/ui/hw/waveshare2in13d.py create mode 100644 pwnagotchi/ui/hw/waveshare2in23g.py create mode 100644 pwnagotchi/ui/hw/waveshare2in36g.py create mode 100644 pwnagotchi/ui/hw/waveshare2in66.py create mode 100644 pwnagotchi/ui/hw/waveshare2in7.py create mode 100644 pwnagotchi/ui/hw/waveshare2in7_V2.py create mode 100644 pwnagotchi/ui/hw/waveshare2in7b.py create mode 100644 pwnagotchi/ui/hw/waveshare2in7b_V2.py create mode 100644 pwnagotchi/ui/hw/waveshare2in9.py create mode 100644 pwnagotchi/ui/hw/waveshare2in9_V2.py create mode 100644 pwnagotchi/ui/hw/waveshare2in9b_V3.py create mode 100644 pwnagotchi/ui/hw/waveshare2in9b_V4.py create mode 100644 pwnagotchi/ui/hw/waveshare2in9bc.py create mode 100644 pwnagotchi/ui/hw/waveshare2in9d.py create mode 100644 pwnagotchi/ui/hw/waveshare3in0g.py create mode 100644 pwnagotchi/ui/hw/waveshare3in52.py create mode 100644 pwnagotchi/ui/hw/waveshare3in5lcd.py create mode 100644 pwnagotchi/ui/hw/waveshare3in7.py create mode 100644 pwnagotchi/ui/hw/waveshare4in01f.py create mode 100644 pwnagotchi/ui/hw/waveshare4in2.py create mode 100644 pwnagotchi/ui/hw/waveshare4in26.py create mode 100644 pwnagotchi/ui/hw/waveshare4in2_V2.py create mode 100644 pwnagotchi/ui/hw/waveshare4in2b_V2.py create mode 100644 pwnagotchi/ui/hw/waveshare4in2bc.py create mode 100644 pwnagotchi/ui/hw/waveshare4in37g.py create mode 100644 pwnagotchi/ui/hw/waveshare5in65f.py create mode 100644 pwnagotchi/ui/hw/waveshare5in83.py create mode 100644 pwnagotchi/ui/hw/waveshare5in83_V2.py create mode 100644 pwnagotchi/ui/hw/waveshare5in83b_V2.py create mode 100644 pwnagotchi/ui/hw/waveshare5in83bc.py create mode 100644 pwnagotchi/ui/hw/waveshare7in3f.py create mode 100644 pwnagotchi/ui/hw/waveshare7in3g.py create mode 100644 pwnagotchi/ui/hw/waveshare7in5.py create mode 100644 pwnagotchi/ui/hw/waveshare7in5_HD.py create mode 100644 pwnagotchi/ui/hw/waveshare7in5_V2.py create mode 100644 pwnagotchi/ui/hw/waveshare7in5b_HD.py create mode 100644 pwnagotchi/ui/hw/waveshare7in5b_V2.py create mode 100644 pwnagotchi/ui/hw/waveshare7in5bc.py create mode 100644 pwnagotchi/ui/web/static/js/jquery-3.7.1.min.js create mode 100644 pwnagotchi/ui/web/static/js/jquery-qrcode-0.18.0.min.js create mode 100644 pwnagotchi/ui/web/static/js/kjua-0.9.0.min.js diff --git a/pwnagotchi/_version.py b/pwnagotchi/_version.py index 44695857..a147628d 100644 --- a/pwnagotchi/_version.py +++ b/pwnagotchi/_version.py @@ -1 +1 @@ -__version__ = '2.6.3' +__version__ = '2.6.4' diff --git a/pwnagotchi/ui/display.py b/pwnagotchi/ui/display.py index dc65bfe9..bbc7bd32 100644 --- a/pwnagotchi/ui/display.py +++ b/pwnagotchi/ui/display.py @@ -25,11 +25,59 @@ class Display(View): ) self._render_thread_instance.start() - def is_inky(self): - return self._implementation.name == 'inky' + def is_lcdhat(self): + return self._implementation.name == 'lcdhat' - def is_papirus(self): - return self._implementation.name == 'papirus' + def is_waveshare144lcd(self): + return self._implementation.name == 'waveshare144lcd' + + def is_oledhat(self): + return self._implementation.name == 'oledhat' + + def is_waveshare1in02(self): + return self._implementation.name == 'waveshare1in02' + + def is_waveshare1in54(self): + return self._implementation.name == 'waveshare1in54' + + def is_waveshare1in54V2(self): + return self._implementation.name == 'waveshare1in54_v2' + + def is_waveshare1in54b(self): + return self._implementation.name == 'waveshare1in54b' + + def is_waveshare1in54bV22(self): + return self._implementation.name == 'waveshare1in54b_v2' + + def is_waveshare1in54c(self): + return self._implementation.name == 'waveshare1in54c' + + def is_waveshare1in64g(self): + return self._implementation.name == 'waveshare1in64g' + + def is_waveshare2in7(self): + return self._implementation.name == 'waveshare2in7' + + def is_waveshare2in7V2(self): + return self._implementation.name == 'waveshare2in7_v2' + + def is_waveshare2in9(self): + return self._implementation.name == 'waveshare2in9' + + def is_waveshare2in9V2(self): + return self._implementation.name == 'waveshare2in9_v2' + + def is_waveshare2in9bV3(self): + return self._implementation.name == 'waveshare2in9b_v3' + + def is_waveshare2in9bV4(self): + return self._implementation.name == 'waveshare2in9b_v4' + + def is_waveshare2in9bc(self): + return self._implementation.name == 'waveshare2in9bc' + + def is_waveshare2in9d(self): + return self._implementation.name == 'waveshare2in9d' def is_waveshare_v1(self): return self._implementation.name == 'waveshare_1' @@ -43,20 +91,107 @@ class Display(View): def is_waveshare_v4(self): return self._implementation.name == 'waveshare_4' - def is_waveshare27inch(self): - return self._implementation.name == 'waveshare27inch' + def is_waveshare2in13b_v3(self): + return self._implementation.name == 'waveshare2in13b_v3' - def is_waveshare27inchV2(self): - return self._implementation.name == 'waveshare27inchv2' + def is_waveshare2in13b_v4(self): + return self._implementation.name == 'waveshare2in13b_v4' - def is_waveshare29inch(self): - return self._implementation.name == 'waveshare29inch' + def is_waveshare2in13bc(self): + return self._implementation.name == 'waveshare2in13bc' - def is_oledhat(self): - return self._implementation.name == 'oledhat' + def is_waveshare2in13d(self): + return self._implementation.name == 'waveshare2in13d' - def is_lcdhat(self): - return self._implementation.name == 'lcdhat' + def is_waveshare2in23g(self): + return self._implementation.name == 'waveshare2in23g' + + def is_waveshare2in36g(self): + return self._implementation.name == 'waveshare2in36g' + + def is_waveshare2in66(self): + return self._implementation.name == 'waveshare2in66' + + def is_waveshare2in66g(self): + return self._implementation.name == 'waveshare2in66g' + + def is_waveshare3in0g(self): + return self._implementation.name == 'waveshare3in0g' + + def is_waveshare3in7(self): + return self._implementation.name == 'waveshare3in7' + + def is_waveshare3in52(self): + return self._implementation.name == 'waveshare3in52' + + def is_waveshare4in01f(self): + return self._implementation.name == 'waveshare4in01f' + + def is_waveshare4in2(self): + return self._implementation.name == 'waveshare4in2' + + def is_waveshare4in2V2(self): + return self._implementation.name == 'waveshare4in2_v2' + + def is_waveshare4in2bV2(self): + return self._implementation.name == 'waveshare4in2b_v2' + + def is_waveshare4in2bc(self): + return self._implementation.name == 'waveshare4in2bc' + + def is_waveshare4in26(self): + return self._implementation.name == 'waveshare4in26' + + def is_waveshare4in37g(self): + return self._implementation.name == 'waveshare4in37g' + + def is_waveshare5in65f(self): + return self._implementation.name == 'waveshare5in65f' + + def is_waveshare5in83(self): + return self._implementation.name == 'waveshare5in83' + + def is_waveshare5in83V2(self): + return self._implementation.name == 'waveshare5in83_v2' + + def is_waveshare5in83bV2(self): + return self._implementation.name == 'waveshare5in83b_v2' + + def is_waveshare5in83bc(self): + return self._implementation.name == 'waveshare5in83bc' + + def is_waveshare7in3f(self): + return self._implementation.name == 'waveshare7in3f' + + def is_waveshare7in3g(self): + return self._implementation.name == 'waveshare7in3g' + + def is_waveshare7in5(self): + return self._implementation.name == 'waveshare7in5' + + def is_waveshare7in5HD(self): + return self._implementation.name == 'waveshare7in5_HD' + + def is_waveshare7in5V2(self): + return self._implementation.name == 'waveshare7in5_v2' + + def is_waveshare7in5bHD(self): + return self._implementation.name == 'waveshare7in5b_HD' + + def is_waveshare7in5bV2(self): + return self._implementation.name == 'waveshare7in5b_v2' + + def is_waveshare7in5bc(self): + return self._implementation.name == 'waveshare7in5bc' + + def is_waveshare13in3k(self): + return self._implementation.name == 'waveshare13in3k' + + def is_inky(self): + return self._implementation.name == 'inky' + + def is_papirus(self): + return self._implementation.name == 'papirus' def is_dfrobot_v1(self): return self._implementation.name == 'dfrobot_v1' @@ -64,21 +199,6 @@ class Display(View): def is_dfrobot_v2(self): return self._implementation.name == 'dfrobot_v2' - def is_waveshare144lcd(self): - return self._implementation.name == 'waveshare144lcd' - - def is_waveshare154inch(self): - return self._implementation.name == 'waveshare154inch' - - def is_waveshare213d(self): - return self._implementation.name == 'waveshare213d' - - def is_waveshare213bc(self): - return self._implementation.name == 'waveshare213bc' - - def is_waveshare213inb_v4(self): - return self._implementation.name == 'waveshare213inb_v4' - def is_spotpear24inch(self): return self._implementation.name == 'spotpear24inch' diff --git a/pwnagotchi/ui/hw/__init__.py b/pwnagotchi/ui/hw/__init__.py index 8ca0f548..abe7478e 100644 --- a/pwnagotchi/ui/hw/__init__.py +++ b/pwnagotchi/ui/hw/__init__.py @@ -2,22 +2,64 @@ from pwnagotchi.ui.hw.inky import Inky from pwnagotchi.ui.hw.papirus import Papirus from pwnagotchi.ui.hw.oledhat import OledHat from pwnagotchi.ui.hw.lcdhat import LcdHat -from pwnagotchi.ui.hw.dfrobot1 import DFRobotV1 -from pwnagotchi.ui.hw.dfrobot2 import DFRobotV2 -from pwnagotchi.ui.hw.waveshare1 import WaveshareV1 -from pwnagotchi.ui.hw.waveshare2 import WaveshareV2 -from pwnagotchi.ui.hw.waveshare3 import WaveshareV3 -from pwnagotchi.ui.hw.waveshare4 import WaveshareV4 -from pwnagotchi.ui.hw.waveshare27inch import Waveshare27inch -from pwnagotchi.ui.hw.waveshare27inchv2 import Waveshare27inchV2 -from pwnagotchi.ui.hw.waveshare29inch import Waveshare29inch -from pwnagotchi.ui.hw.waveshare144lcd import Waveshare144lcd -from pwnagotchi.ui.hw.waveshare154inch import Waveshare154inch -from pwnagotchi.ui.hw.waveshare213d import Waveshare213d -from pwnagotchi.ui.hw.waveshare213bc import Waveshare213bc -from pwnagotchi.ui.hw.waveshare35lcd import Waveshare35lcd -from pwnagotchi.ui.hw.spotpear24inch import Spotpear24inch +from pwnagotchi.ui.hw.dfrobot import DFRobotV1 +from pwnagotchi.ui.hw.dfrobot_v2 import DFRobotV2 +from pwnagotchi.ui.hw.waveshare2in13 import WaveshareV1 +from pwnagotchi.ui.hw.waveshare2in13_V2 import WaveshareV2 +from pwnagotchi.ui.hw.waveshare2in13_V3 import WaveshareV3 +from pwnagotchi.ui.hw.waveshare2in13_V4 import WaveshareV4 +from pwnagotchi.ui.hw.waveshare2in7 import Waveshare27inch +from pwnagotchi.ui.hw.waveshare2in7_V2 import Waveshare27inchV2 +from pwnagotchi.ui.hw.waveshare2in9 import Waveshare29inch +from pwnagotchi.ui.hw.waveshare2in9_V2 import Waveshare29inchV2 +from pwnagotchi.ui.hw.waveshare1in44lcd import Waveshare144lcd +from pwnagotchi.ui.hw.waveshare1in54b import Waveshare154inchb +from pwnagotchi.ui.hw.waveshare2in13bc import Waveshare213bc +from pwnagotchi.ui.hw.waveshare2in13d import Waveshare213d +from pwnagotchi.ui.hw.waveshare2in13b_V4 import Waveshare213bV4 +from pwnagotchi.ui.hw.waveshare3in5lcd import Waveshare35lcd +from pwnagotchi.ui.hw.spotpear24in import Spotpear24inch from pwnagotchi.ui.hw.displayhatmini import DisplayHatMini +# from pwnagotchi.ui.hw.waveshare1in02 import Waveshare102 +from pwnagotchi.ui.hw.waveshare1in54 import Waveshare154 +from pwnagotchi.ui.hw.waveshare1in54_V2 import Waveshare154V2 +from pwnagotchi.ui.hw.waveshare1in54b_V2 import Waveshare154bV2 +# from pwnagotchi.ui.hw.waveshare1in54c import Waveshare154c +# from pwnagotchi.ui.hw.waveshare1in64g import Waveshare164g +from pwnagotchi.ui.hw.waveshare2in7b import Waveshare27b +from pwnagotchi.ui.hw.waveshare2in7b_V2 import Waveshare27bV2 +from pwnagotchi.ui.hw.waveshare2in9b_V3 import Waveshare29bV3 +from pwnagotchi.ui.hw.waveshare2in9b_V4 import Waveshare29bV4 +# from pwnagotchi.ui.hw.waveshare2in9bc import Waveshare29bc +# from pwnagotchi.ui.hw.waveshare2in9d import Waveshare29d +from pwnagotchi.ui.hw.waveshare2in13b_V3 import Waveshare213bV3 +# from pwnagotchi.ui.hw.waveshare2in23g import Waveshare223g +# from pwnagotchi.ui.hw.waveshare2in36g import Waveshare236g +# from pwnagotchi.ui.hw.waveshare2in66 import Waveshare266 +# from pwnagotchi.ui.hw.waveshare3in0g import Waveshare30g +# from pwnagotchi.ui.hw.waveshare3in7 import Waveshare37 +# from pwnagotchi.ui.hw.waveshare3in52 import Waveshare352 +# from pwnagotchi.ui.hw.waveshare4in01f import Waveshare401f +# from pwnagotchi.ui.hw.waveshare4in2 import Waveshare42inch +# from pwnagotchi.ui.hw.waveshare4in2_V2 import Waveshare42V2 +# from pwnagotchi.ui.hw.waveshare4in2b_V2 import Waveshare42bV2 +# from pwnagotchi.ui.hw.waveshare4in2bc import Waveshare42bc +# from pwnagotchi.ui.hw.waveshare4in26 import Waveshare426 +# from pwnagotchi.ui.hw.waveshare4in37g import Waveshare437g +# from pwnagotchi.ui.hw.waveshare5in65f import Waveshare565f +# from pwnagotchi.ui.hw.waveshare5in83 import Waveshare583 +# from pwnagotchi.ui.hw.waveshare5in83_V2 import Waveshare583V2 +# from pwnagotchi.ui.hw.waveshare5in83b_V2 import Waveshare583bV2 +# from pwnagotchi.ui.hw.waveshare5in83b_V2 import Waveshare583bc +# from pwnagotchi.ui.hw.waveshare7in3f import Waveshare73f +# from pwnagotchi.ui.hw.waveshare7in3g import Waveshare73g +# from pwnagotchi.ui.hw.waveshare7in5 import Waveshare75 +# from pwnagotchi.ui.hw.waveshare7in5_HD import Waveshare75HD +# from pwnagotchi.ui.hw.waveshare7in5_V2 import Waveshare75V2 +# from pwnagotchi.ui.hw.waveshare7in5b_HD import Waveshare75bHD +# from pwnagotchi.ui.hw.waveshare7in5b_V2 import Waveshare75bV2 +# from pwnagotchi.ui.hw.waveshare7in5bc import Waveshare75bc +# from pwnagotchi.ui.hw.waveshare13in3k import Waveshare133k def display_for(config): @@ -28,18 +70,42 @@ def display_for(config): elif config['ui']['display']['type'] == 'papirus': return Papirus(config) - if config['ui']['display']['type'] == 'oledhat': + elif config['ui']['display']['type'] == 'oledhat': return OledHat(config) - if config['ui']['display']['type'] == 'lcdhat': + elif config['ui']['display']['type'] == 'lcdhat': return LcdHat(config) - if config['ui']['display']['type'] == 'dfrobot_1': + elif config['ui']['display']['type'] == 'dfrobot_1': return DFRobotV1(config) - if config['ui']['display']['type'] == 'dfrobot_2': + elif config['ui']['display']['type'] == 'dfrobot_2': return DFRobotV2(config) + elif config['ui']['display']['type'] == 'waveshare1in54': + return Waveshare154(config) + + elif config['ui']['display']['type'] == 'waveshare1in54_v2': + return Waveshare154V2(config) + + elif config['ui']['display']['type'] == 'waveshare1in54b': + return Waveshare154inchb(config) + + elif config['ui']['display']['type'] == 'waveshare1in54b_v2': + return Waveshare154bV2(config) + + elif config['ui']['display']['type'] == 'waveshare2in7b': + return Waveshare27b(config) + + elif config['ui']['display']['type'] == 'waveshare2in7b_v2': + return Waveshare27bV2(config) + + elif config['ui']['display']['type'] == 'waveshare2in9b_v3': + return Waveshare29bV3(config) + + elif config['ui']['display']['type'] == 'waveshare2in9b_v4': + return Waveshare29bV4(config) + elif config['ui']['display']['type'] == 'waveshare_1': return WaveshareV1(config) @@ -52,26 +118,32 @@ def display_for(config): elif config['ui']['display']['type'] == 'waveshare_4': return WaveshareV4(config) - elif config['ui']['display']['type'] == 'waveshare27inch': + elif config['ui']['display']['type'] == 'waveshare2in7': return Waveshare27inch(config) - elif config['ui']['display']['type'] == 'waveshare27inchv2': + elif config['ui']['display']['type'] == 'waveshare2in7_v2': return Waveshare27inchV2(config) - elif config['ui']['display']['type'] == 'waveshare29inch': + elif config['ui']['display']['type'] == 'waveshare2in9': return Waveshare29inch(config) + elif config['ui']['display']['type'] == 'waveshare2in9_v2': + return Waveshare29inchV2(config) + elif config['ui']['display']['type'] == 'waveshare144lcd': return Waveshare144lcd(config) - elif config['ui']['display']['type'] == 'waveshare154inch': - return Waveshare154inch(config) + elif config['ui']['display']['type'] == 'waveshare1in54b': + return Waveshare154inchb(config) - elif config['ui']['display']['type'] == 'waveshare213d': + elif config['ui']['display']['type'] == 'waveshare2in13bc': + return Waveshare213bc(config) + + elif config['ui']['display']['type'] == 'waveshare2in13d': return Waveshare213d(config) - elif config['ui']['display']['type'] == 'waveshare213bc': - return Waveshare213bc(config) + elif config['ui']['display']['type'] == 'waveshare2in13b_v4': + return Waveshare213bV4(config) elif config['ui']['display']['type'] == 'waveshare35lcd': return Waveshare35lcd(config) @@ -81,3 +153,102 @@ def display_for(config): elif config['ui']['display']['type'] == 'displayhatmini': return DisplayHatMini(config) + + elif config['ui']['display']['type'] == 'waveshare1in54c': + return + + elif config['ui']['display']['type'] == 'waveshare1in64g': + return + + elif config['ui']['display']['type'] == 'waveshare1in02': + return + + elif config['ui']['display']['type'] == 'waveshare2in9bc': + return + + elif config['ui']['display']['type'] == 'waveshare2in9d': + return + + elif config['ui']['display']['type'] == 'waveshare2in13b_v3': + return + + elif config['ui']['display']['type'] == 'waveshare2in23g': + return + + elif config['ui']['display']['type'] == 'waveshare2in36g': + return + + elif config['ui']['display']['type'] == 'waveshare2in66': + return + + elif config['ui']['display']['type'] == 'waveshare3in0g': + return + + elif config['ui']['display']['type'] == 'waveshare3in7': + return + + elif config['ui']['display']['type'] == 'waveshare3in52': + return + + elif config['ui']['display']['type'] == 'waveshare4in01f': + return + + elif config['ui']['display']['type'] == 'waveshare4in2': + return + + elif config['ui']['display']['type'] == 'waveshare4in2_v2': + return + + elif config['ui']['display']['type'] == 'waveshare4in2b_v2': + return + + elif config['ui']['display']['type'] == 'waveshare4in2bc': + return + + elif config['ui']['display']['type'] == 'waveshare4in26': + return + + elif config['ui']['display']['type'] == 'waveshare4in37g': + return + + elif config['ui']['display']['type'] == 'waveshare5in65f': + return + + elif config['ui']['display']['type'] == 'waveshare5in83': + return + + elif config['ui']['display']['type'] == 'waveshare5in83_v2': + return + + elif config['ui']['display']['type'] == 'waveshare5in83b_v2': + return + + elif config['ui']['display']['type'] == 'waveshare5in83bc': + return + + elif config['ui']['display']['type'] == 'waveshare7in3f': + return + + elif config['ui']['display']['type'] == 'waveshare7in3g': + return + + elif config['ui']['display']['type'] == 'waveshare7in5': + return + + elif config['ui']['display']['type'] == 'waveshare7in5_HD': + return + + elif config['ui']['display']['type'] == 'waveshare7in5_v2': + return + + elif config['ui']['display']['type'] == 'waveshare7in5b_HD': + return + + elif config['ui']['display']['type'] == 'waveshare7in5b_v2': + return + + elif config['ui']['display']['type'] == 'waveshare7in5bc': + return + + elif config['ui']['display']['type'] == 'waveshare13in3k': + return diff --git a/pwnagotchi/ui/hw/dfrobot.py b/pwnagotchi/ui/hw/dfrobot.py new file mode 100644 index 00000000..732faf8e --- /dev/null +++ b/pwnagotchi/ui/hw/dfrobot.py @@ -0,0 +1,43 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + +class DFRobotV1(DisplayImpl): + def __init__(self, config): + super(DFRobotV1, self).__init__(config, 'dfrobot_1') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 250 + self._layout['height'] = 122 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (185, 0) + self._layout['line1'] = [0, 14, 250, 14] + self._layout['line2'] = [0, 108, 250, 108] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 109) + self._layout['mode'] = (225, 109) + self._layout['status'] = { + 'pos': (125, 20), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + return self._layout + + def initialize(self): + logging.info("initializing dfrobot1 display") + from pwnagotchi.ui.hw.libs.dfrobot.v1.dfrobot import DFRobot + self._display = DFRobot() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear(0xFF) diff --git a/pwnagotchi/ui/hw/dfrobot_v2.py b/pwnagotchi/ui/hw/dfrobot_v2.py new file mode 100644 index 00000000..995afd38 --- /dev/null +++ b/pwnagotchi/ui/hw/dfrobot_v2.py @@ -0,0 +1,43 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + +class DFRobotV2(DisplayImpl): + def __init__(self, config): + super(DFRobotV2, self).__init__(config, 'dfrobot_2') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 250 + self._layout['height'] = 122 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (185, 0) + self._layout['line1'] = [0, 14, 250, 14] + self._layout['line2'] = [0, 108, 250, 108] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 109) + self._layout['mode'] = (225, 109) + self._layout['status'] = { + 'pos': (125, 20), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + return self._layout + + def initialize(self): + logging.info("initializing dfrobot2 display") + from pwnagotchi.ui.hw.libs.dfrobot.v2.dfrobot import DFRobot + self._display = DFRobot() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear(0xFF) diff --git a/pwnagotchi/ui/hw/libs/waveshare/epdconfig.py b/pwnagotchi/ui/hw/libs/waveshare/epdconfig.py new file mode 100644 index 00000000..c469b0f5 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/epdconfig.py @@ -0,0 +1,243 @@ +# /***************************************************************************** +# * | File : epdconfig.py +# * | Author : Waveshare team +# * | Function : Hardware underlying interface +# * | Info : +# *---------------- +# * | This version: V1.2 +# * | Date : 2022-10-29 +# * | Info : +# ****************************************************************************** +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import os +import logging +import sys +import time + +logger = logging.getLogger(__name__) + + +class RaspberryPi: + # Pin definition + RST_PIN = 17 + DC_PIN = 25 + CS_PIN = 8 + BUSY_PIN = 24 + PWR_PIN = 18 + + def __init__(self): + import spidev + import RPi.GPIO + + self.GPIO = RPi.GPIO + self.SPI = spidev.SpiDev() + + def digital_write(self, pin, value): + self.GPIO.output(pin, value) + + def digital_read(self, pin): + return self.GPIO.input(pin) + + def delay_ms(self, delaytime): + time.sleep(delaytime / 1000.0) + + def spi_writebyte(self, data): + self.SPI.writebytes(data) + + def spi_writebyte2(self, data): + self.SPI.writebytes2(data) + + def module_init(self): + self.GPIO.setmode(self.GPIO.BCM) + self.GPIO.setwarnings(False) + self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) + self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) + self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) + self.GPIO.setup(self.PWR_PIN, self.GPIO.OUT) + self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) + + self.GPIO.output(self.PWR_PIN, 1) + + # SPI device, bus = 0, device = 0 + self.SPI.open(0, 0) + self.SPI.max_speed_hz = 4000000 + self.SPI.mode = 0b00 + return 0 + + def module_exit(self): + logger.debug("spi end") + self.SPI.close() + + logger.debug("close 5V, Module enters 0 power consumption ...") + self.GPIO.output(self.RST_PIN, 0) + self.GPIO.output(self.DC_PIN, 0) + self.GPIO.output(self.PWR_PIN, 0) + + self.GPIO.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_PIN, self.PWR_PIN]) + + +class JetsonNano: + # Pin definition + RST_PIN = 17 + DC_PIN = 25 + CS_PIN = 8 + BUSY_PIN = 24 + PWR_PIN = 18 + + def __init__(self): + import ctypes + find_dirs = [ + os.path.dirname(os.path.realpath(__file__)), + '/usr/local/lib', + '/usr/lib', + ] + self.SPI = None + for find_dir in find_dirs: + so_filename = os.path.join(find_dir, 'sysfs_software_spi.so') + if os.path.exists(so_filename): + self.SPI = ctypes.cdll.LoadLibrary(so_filename) + break + if self.SPI is None: + raise RuntimeError('Cannot find sysfs_software_spi.so') + + import Jetson.GPIO + self.GPIO = Jetson.GPIO + + def digital_write(self, pin, value): + self.GPIO.output(pin, value) + + def digital_read(self, pin): + return self.GPIO.input(self.BUSY_PIN) + + def delay_ms(self, delaytime): + time.sleep(delaytime / 1000.0) + + def spi_writebyte(self, data): + self.SPI.SYSFS_software_spi_transfer(data[0]) + + def spi_writebyte2(self, data): + for i in range(len(data)): + self.SPI.SYSFS_software_spi_transfer(data[i]) + + def module_init(self): + self.GPIO.setmode(self.GPIO.BCM) + self.GPIO.setwarnings(False) + self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) + self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) + self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) + self.GPIO.setup(self.PWR_PIN, self.GPIO.OUT) + self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) + + self.GPIO.output(self.PWR_PIN, 1) + + self.SPI.SYSFS_software_spi_begin() + return 0 + + def module_exit(self): + logger.debug("spi end") + self.SPI.SYSFS_software_spi_end() + + logger.debug("close 5V, Module enters 0 power consumption ...") + self.GPIO.output(self.RST_PIN, 0) + self.GPIO.output(self.DC_PIN, 0) + self.GPIO.output(self.PWR_PIN, 0) + + self.GPIO.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_PIN, self.PWR_PIN]) + + +class SunriseX3: + # Pin definition + RST_PIN = 17 + DC_PIN = 25 + CS_PIN = 8 + BUSY_PIN = 24 + PWR_PIN = 18 + Flag = 0 + + def __init__(self): + import spidev + import Hobot.GPIO + + self.GPIO = Hobot.GPIO + self.SPI = spidev.SpiDev() + + def digital_write(self, pin, value): + self.GPIO.output(pin, value) + + def digital_read(self, pin): + return self.GPIO.input(pin) + + def delay_ms(self, delaytime): + time.sleep(delaytime / 1000.0) + + def spi_writebyte(self, data): + self.SPI.writebytes(data) + + def spi_writebyte2(self, data): + # for i in range(len(data)): + # self.SPI.writebytes([data[i]]) + self.SPI.xfer3(data) + + def module_init(self): + if self.Flag == 0: + self.Flag = 1 + self.GPIO.setmode(self.GPIO.BCM) + self.GPIO.setwarnings(False) + self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) + self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) + self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) + self.GPIO.setup(self.PWR_PIN, self.GPIO.OUT) + self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) + + self.GPIO.output(self.PWR_PIN, 1) + + # SPI device, bus = 0, device = 0 + self.SPI.open(2, 0) + self.SPI.max_speed_hz = 4000000 + self.SPI.mode = 0b00 + return 0 + else: + return 0 + + def module_exit(self): + logger.debug("spi end") + self.SPI.close() + + logger.debug("close 5V, Module enters 0 power consumption ...") + self.Flag = 0 + self.GPIO.output(self.RST_PIN, 0) + self.GPIO.output(self.DC_PIN, 0) + self.GPIO.output(self.PWR_PIN, 0) + + self.GPIO.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_PIN], self.PWR_PIN) + + +if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'): + implementation = RaspberryPi() +elif os.path.exists('/sys/bus/platform/drivers/gpio-x3'): + implementation = SunriseX3() +else: + implementation = JetsonNano() + +for func in [x for x in dir(implementation) if not x.startswith('_')]: + setattr(sys.modules[__name__], func, getattr(implementation, func)) + +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v13in3k/epd13in3k.py b/pwnagotchi/ui/hw/libs/waveshare/v13in3k/epd13in3k.py new file mode 100644 index 00000000..b535dd18 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v13in3k/epd13in3k.py @@ -0,0 +1,188 @@ +# ***************************************************************************** +# * | File : epd7in5.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 960 +EPD_HEIGHT = 680 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.SPI.writebytes2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 1): + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(20) + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xF7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + self.ReadBusy() + + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x0C) + self.send_data(0xAE) + self.send_data(0xC7) + self.send_data(0xC3) + self.send_data(0xC0) + self.send_data(0x80) + + self.send_command(0x01) + self.send_data(0xA7) + self.send_data(0x02) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) # X-mode + + self.send_command(0x44) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0xBF) + self.send_data(0x03) + + self.send_command(0x45) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0xA7) + self.send_data(0x02) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x05) + + self.send_command(0x18) # use the internal temperature sensor + self.send_data(0x80) + + self.send_command(0x4E) + self.send_data(0x00) + + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + + # EPD hardware init end + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif imwidth == self.height and imheight == self.width: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.TurnOnDisplay() + + def Clear(self): + buf = [0xFF] * (int(self.width / 8) * self.height) + self.send_command(0x24) + self.send_data2(buf) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1in02/epd1in02.py b/pwnagotchi/ui/hw/libs/waveshare/v1in02/epd1in02.py new file mode 100644 index 00000000..ae93437d --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1in02/epd1in02.py @@ -0,0 +1,349 @@ +# ***************************************************************************** +# * | File : epd1in54.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# ******************************************************************************/ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 80 +EPD_HEIGHT = 128 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # full screen update LUT + + lut_w1 = [ + 0x60, 0x5A, 0x5A, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_b1 = [ + 0x90, 0x5A, 0x5A, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + # partial screen update LUT + lut_w = [ + 0x60, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x80, 0x1f, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_b = [ + 0x90, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x40, 0x1f, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) # module reset + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + busy = not (busy & 0x01) + while (busy): + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + busy = not (busy & 0x01) + epdconfig.delay_ms(800) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x12) + epdconfig.delay_ms(10) + self.ReadBusy() + + def SetFulltReg(self): + self.send_command(0x23) + for count in range(0, 42): + self.send_data(self.lut_w1[count]) + + self.send_command(0x24) + for count in range(0, 42): + self.send_data(self.lut_b1[count]) + + def SetPartReg(self): + self.send_command(0x23) + for count in range(0, 42): + self.send_data(self.lut_w[count]) + + self.send_command(0x24) + for count in range(0, 42): + self.send_data(self.lut_b[count]) + + def Init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0xD2) + self.send_data(0x3F) + + self.send_command(0x00) + self.send_data(0x6F) # from outside + + self.send_command(0x01) # power setting + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + + self.send_command(0x06) # Configuring the charge pump + self.send_data(0x3f) + + self.send_command(0x2A) # Setting XON and the options of LUT + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x30) # Set the clock frequency + self.send_data(0x17) # 50Hz + + self.send_command(0x50) # Set VCOM and data output interval + self.send_data(0x57) + + self.send_command(0x60) # Set The non-overlapping period of Gate and Source. + self.send_data(0x22) + + self.send_command(0x61) # resolution setting + self.send_data(0x50) # source 128 + self.send_data(0x80) + + self.send_command(0x82) # sets VCOM_DC value + self.send_data(0x12) # -1v + + self.send_command(0xe3) # Set POWER SAVING + self.send_data(0x33) + self.SetFulltReg() + self.send_command(0x04) # power on + self.ReadBusy() + # EPD hardware init end + return 0 + + def Partial_Init(self): + self.reset() + + self.send_command(0xD2) + self.send_data(0x3F) + + self.send_command(0x00) + self.send_data(0x6F) # from outside + + self.send_command(0x01) # power setting + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + + self.send_command(0x06) # Configuring the charge pump + self.send_data(0x3f) + + self.send_command(0x2A) # Setting XON and the options of LUT + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x30) # Set the clock frequency + self.send_data(0x17) + + self.send_command(0x50) # Set VCOM and data output interval + self.send_data(0xf2) + + self.send_command(0x60) # Set The non-overlapping period of Gate and Source. + self.send_data(0x22) + + self.send_command(0x82) # Set VCOM_DC value + self.send_data(0x12) # -1v + + self.send_command(0xe3) # Set POWER SAVING + self.send_data(0x33) + + self.SetPartReg() + + self.send_command(0x04) # Set POWER SAVING + self.ReadBusy() + # EPD hardware init end + return 0 + + def getbuffer(self, image): + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (image == None): + return + # Width = (self.width % 8 == 0)? (self.width / 8 ): (self.width / 8 + 1) + if (self.width % 8 == 0): + Width = self.width / 8 + else: + Width = self.width / 8 + 1 + + self.send_command(0x10) + for j in range(0, self.height): + for i in range(0, int(Width)): + self.send_data(0xff) + + self.send_command(0x13) + for j in range(0, self.height): + for i in range(0, int(Width)): + self.send_data(image[i + j * int(Width)]) + self.TurnOnDisplay() + + def Clear(self): + # Width = (self.width % 8 == 0)? (self.width / 8 ): (self.width / 8 + 1) + if (self.width % 8 == 0): + Width = self.width / 8 + else: + Width = self.width / 8 + 1 + + Height = self.height + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, int(Width)): + self.send_data(0x00) + + self.send_command(0x13) + for j in range(0, Height): + for i in range(0, int(Width)): + self.send_data(0xff) + self.TurnOnDisplay() + + def DisplayPartial(self, old_Image, Image): + + # Set partial Windows */ + self.send_command(0x91) # This command makes the display enter partial mode + self.send_command(0x90) # resolution setting + self.send_data(0) # x-start + self.send_data(79) # x-end + + self.send_data(0) + self.send_data(127) # y-end + self.send_data(0x00) + + # Width = (self.width % 8 == 0)? (self.width / 8 ): (self.width / 8 + 1) + if (self.width % 8 == 0): + Width = self.width / 8 + else: + Width = self.width / 8 + 1 + + Height = self.height + # send data + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, int(Width)): + self.send_data(old_Image[i + j * int(Width)]) + + self.send_command(0x13) + for j in range(0, Height): + for i in range(0, int(Width)): + self.send_data(Image[i + j * int(Width)]) + + # Set partial refresh + self.TurnOnDisplay() + + def Sleep(self): + self.send_command(0x50) + self.send_data(0xf7) + self.send_command(0x02) + self.ReadBusy() + self.send_command(0x07) + self.send_data(0xA5) + epdconfig.delay_ms(200) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1in54/epd1in54.py b/pwnagotchi/ui/hw/libs/waveshare/v1in54/epd1in54.py new file mode 100644 index 00000000..d1214857 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1in54/epd1in54.py @@ -0,0 +1,260 @@ +# ***************************************************************************** +# * | File : epd1in54.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V3.1 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# V3.1(2019-06-18): +# 2.remove commands define: +# #define PANEL_SETTING 0x00 +# #define POWER_SETTING 0x01 +# #define POWER_OFF 0x02 +# #define POWER_OFF_SEQUENCE_SETTING 0x03 +# #define POWER_ON 0x04 +# #define POWER_ON_MEASURE 0x05 +# #define BOOSTER_SOFT_START 0x06 +# #define DEEP_SLEEP 0x07 +# #define DATA_START_TRANSMISSION_1 0x10 +# #define DATA_STOP 0x11 +# #define DISPLAY_REFRESH 0x12 +# #define DATA_START_TRANSMISSION_2 0x13 +# #define PLL_CONTROL 0x30 +# #define TEMPERATURE_SENSOR_COMMAND 0x40 +# #define TEMPERATURE_SENSOR_CALIBRATION 0x41 +# #define TEMPERATURE_SENSOR_WRITE 0x42 +# #define TEMPERATURE_SENSOR_READ 0x43 +# #define VCOM_AND_DATA_INTERVAL_SETTING 0x50 +# #define LOW_POWER_DETECTION 0x51 +# #define TCON_SETTING 0x60 +# #define TCON_RESOLUTION 0x61 +# #define SOURCE_AND_GATE_START_SETTING 0x62 +# #define GET_STATUS 0x71 +# #define AUTO_MEASURE_VCOM 0x80 +# #define VCOM_VALUE 0x81 +# #define VCM_DC_SETTING_REGISTER 0x82 +# #define PROGRAM_MODE 0xA0 +# #define ACTIVE_PROGRAM 0xA1 +# #define READ_OTP_DATA 0xA2 +# ----------------------------------------------------------------------------- +# V3.0(2018-11-01): +# # 1.Remove: +# digital_write(self, pin, value) +# digital_read(self, pin) +# delay_ms(self, delaytime) +# set_lut(self, lut) +# self.lut = self.lut_full_update +# * 2.Change: +# display_frame -> TurnOnDisplay +# set_memory_area -> SetWindow +# set_memory_pointer -> SetCursor +# * 3.How to use +# epd = epd1in54.EPD() +# epd.init(epd.lut_full_update) +# image = Image.new('1', (epd1in54.EPD_WIDTH, epd1in54.EPD_HEIGHT), 255) +# ... +# drawing ...... +# ... +# epd.display(getbuffer(image)) +# ******************************************************************************/ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 200 +EPD_HEIGHT = 200 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + lut_full_update = [ + 0x02, 0x02, 0x01, 0x11, 0x12, 0x12, 0x22, 0x22, + 0x66, 0x69, 0x69, 0x59, 0x58, 0x99, 0x99, 0x88, + 0x00, 0x00, 0x00, 0x00, 0xF8, 0xB4, 0x13, 0x51, + 0x35, 0x51, 0x51, 0x19, 0x01, 0x00 + ] + + lut_partial_update = [ + 0x10, 0x18, 0x18, 0x08, 0x18, 0x18, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) # module reset + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0xC4) + self.send_command(0x20) # MASTER_ACTIVATION + self.send_command(0xFF) # TERMINATE_FRAME_READ_WRITE + + self.ReadBusy() + + def SetWindow(self, x_start, y_start, x_end, y_end): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data((x_start >> 3) & 0xFF) + self.send_data((x_end >> 3) & 0xFF) + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(y_start & 0xFF) + self.send_data((y_start >> 8) & 0xFF) + self.send_data(y_end & 0xFF) + self.send_data((y_end >> 8) & 0xFF) + + def SetCursor(self, x, y): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data((x >> 3) & 0xFF) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + # self.ReadBusy() + + def init(self, lut): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # DRIVER_OUTPUT_CONTROL + self.send_data((EPD_HEIGHT - 1) & 0xFF) + self.send_data(((EPD_HEIGHT - 1) >> 8) & 0xFF) + self.send_data(0x00) # GD = 0 SM = 0 TB = 0 + + self.send_command(0x0C) # BOOSTER_SOFT_START_CONTROL + self.send_data(0xD7) + self.send_data(0xD6) + self.send_data(0x9D) + + self.send_command(0x2C) # WRITE_VCOM_REGISTER + self.send_data(0xA8) # VCOM 7C + + self.send_command(0x3A) # SET_DUMMY_LINE_PERIOD + self.send_data(0x1A) # 4 dummy lines per gate + + self.send_command(0x3B) # SET_GATE_TIME + self.send_data(0x08) # 2us per line + + self.send_command(0x11) # DATA_ENTRY_MODE_SETTING + self.send_data(0x03) # X increment Y increment + + # set the look-up table register + self.send_command(0x32) + for i in range(0, len(lut)): + self.send_data(lut[i]) + # EPD hardware init end + return 0 + + def getbuffer(self, image): + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (image == None): + return + + self.SetWindow(0, 0, self.width, self.height) + for j in range(0, self.height): + self.SetCursor(0, j) + self.send_command(0x24) + for i in range(0, int(self.width / 8)): + self.send_data(image[i + j * int(self.width / 8)]) + self.TurnOnDisplay() + + def Clear(self, color=0xFF): + # self.SetWindow(0, 0, self.width - 1, self.height - 1) + # send the color data + self.SetWindow(0, 0, self.width, self.height) + # epdconfig.digital_write(self.dc_pin, 1) + # epdconfig.digital_write(self.cs_pin, 0) + for j in range(0, self.height): + self.SetCursor(0, j) + self.send_command(0x24) + for i in range(0, int(self.width / 8)): + self.send_data(color) + # epdconfig.digital_write(self.cs_pin, 1) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP_MODE + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1in54_v2/epd1in54_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v1in54_v2/epd1in54_V2.py new file mode 100644 index 00000000..e2f42a68 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1in54_v2/epd1in54_V2.py @@ -0,0 +1,316 @@ +# ***************************************************************************** +# * | File : epd1in54_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-10 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 200 +EPD_HEIGHT = 200 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # waveform full refresh + WF_Full_1IN54 = [ + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x8, 0x1, 0x0, 0x8, 0x1, 0x0, 0x2, + 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, + 0x22, 0x17, 0x41, 0x0, 0x32, 0x20 + ] + + # waveform partial refresh(fast) + WF_PARTIAL_1IN54_0 = [ + 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, + 0x02, 0x17, 0x41, 0xB0, 0x32, 0x28, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0xc7) + self.send_command(0x20) # MASTER_ACTIVATION + self.ReadBusy() + + def TurnOnDisplayPart(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0xcF) + self.send_command(0x20) # MASTER_ACTIVATION + self.ReadBusy() + + def lut(self, lut): + self.send_command(0x32) # WRITE_LUT_REGISTER + self.send_data2(lut) + + def set_lut(self, lut): + self.lut(lut) + + self.send_command(0x3f) + self.send_data(lut[153]) + + self.send_command(0x03) + self.send_data(lut[154]) + + self.send_command(0x04) + self.send_data(lut[155]) + self.send_data(lut[156]) + self.send_data(lut[157]) + + self.send_command(0x2c) + self.send_data(lut[158]) + + def SetWindows(self, Xstart, Ystart, Xend, Yend): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + self.send_data((Xstart >> 3) & 0xFF) + self.send_data((Xend >> 3) & 0xFF) + + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(Ystart & 0xFF) + self.send_data((Ystart >> 8) & 0xFF) + self.send_data(Yend & 0xFF) + self.send_data((Yend >> 8) & 0xFF) + + def SetCursor(self, Xstart, Ystart): + self.send_command(0x4E); # SET_RAM_X_ADDRESS_COUNTER + self.send_data(Xstart & 0xFF); + + self.send_command(0x4F); # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(Ystart & 0xFF); + self.send_data((Ystart >> 8) & 0xFF); + + def init(self, isPartial): + if (epdconfig.module_init() != 0): + return -1 + + if (isPartial): + logger.debug("partial refresh") + self.reset() + self.ReadBusy() + + self.set_lut(self.WF_PARTIAL_1IN54_0) + + self.send_command(0x37) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x40) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3c) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x22) + self.send_data(0xc0) + self.send_command(0x20) + self.ReadBusy() + + else: + logger.debug("full refresh") + # EPD hardware init start + self.reset() + + self.ReadBusy() + self.send_command(0x12) # SWRESET (software reset) + self.ReadBusy() + + self.send_command(0x01) # DRIVER_OUTPUT_CONTROL + self.send_data(0xC7) # (EPD_HEIGHT - 1) & 0xFF + self.send_data(0x00) # ((EPD_HEIGHT - 1) >> 8) & 0xFF + self.send_data(0x01) # GD = 0 SM = 0 TB = 0 + + self.send_command(0x11) # data entry mode + self.send_data(0x01) + + self.SetWindows(0, self.height - 1, self.width - 1, 0) # Set Windows + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x01) + + self.send_command(0x18) + self.send_data(0x80) + + self.send_command(0x22) # #Load Temperature and waveform setting. + self.send_data(0XB1) + self.send_command(0x20) + + self.SetCursor(0, self.height - 1) # Set Cursor + + self.ReadBusy() + + self.set_lut(self.WF_Full_1IN54) # Set lut + + def Clear(self, color=0xFF): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x24) + self.send_data2([color] * self.height * linewidth) + + self.TurnOnDisplay() + + def getbuffer(self, image): + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (image == None): + return + + self.send_command(0x24) + self.send_data2(image) + self.TurnOnDisplay() + + def displayPartBaseImage(self, image): + if (image == None): + return + + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + + self.TurnOnDisplay() + + def displayPart(self, image): + if (image == None): + return + + self.send_command(0x24) + self.send_data2(image) + + self.TurnOnDisplayPart() + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP_MODE + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1in54b/epd1in54b.py b/pwnagotchi/ui/hw/libs/waveshare/v1in54b/epd1in54b.py new file mode 100644 index 00000000..6262daf5 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1in54b/epd1in54b.py @@ -0,0 +1,219 @@ +# ***************************************************************************** +# * | File : epd1in54b.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 200 +EPD_HEIGHT = 200 + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + lut_vcom0 = [0x0E, 0x14, 0x01, 0x0A, 0x06, 0x04, 0x0A, 0x0A, 0x0F, 0x03, 0x03, 0x0C, 0x06, 0x0A, 0x00] + lut_w = [0x0E, 0x14, 0x01, 0x0A, 0x46, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x86, 0x0A, 0x04] + lut_b = [0x0E, 0x14, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x06, 0x4A, 0x04] + lut_g1 = [0x8E, 0x94, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x06, 0x0A, 0x04] + lut_g2 = [0x8E, 0x94, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x06, 0x0A, 0x04] + lut_vcom1 = [0x03, 0x1D, 0x01, 0x01, 0x08, 0x23, 0x37, 0x37, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + lut_red0 = [0x83, 0x5D, 0x01, 0x81, 0x48, 0x23, 0x77, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + lut_red1 = [0x03, 0x1D, 0x01, 0x01, 0x08, 0x23, 0x37, 0x37, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) # module reset + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def set_lut_bw(self): + self.send_command(0x20) # vcom + for count in range(0, 15): + self.send_data(self.lut_vcom0[count]) + self.send_command(0x21) # ww -- + for count in range(0, 15): + self.send_data(self.lut_w[count]) + self.send_command(0x22) # bw r + for count in range(0, 15): + self.send_data(self.lut_b[count]) + self.send_command(0x23) # wb w + for count in range(0, 15): + self.send_data(self.lut_g1[count]) + self.send_command(0x24) # bb b + for count in range(0, 15): + self.send_data(self.lut_g2[count]) + + def set_lut_red(self): + self.send_command(0x25) + for count in range(0, 15): + self.send_data(self.lut_vcom1[count]) + self.send_command(0x26) + for count in range(0, 15): + self.send_data(self.lut_red0[count]) + self.send_command(0x27) + for count in range(0, 15): + self.send_data(self.lut_red1[count]) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x07) + self.send_data(0x00) + self.send_data(0x08) + self.send_data(0x00) + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x07) + self.send_data(0x07) + self.send_data(0x07) + self.send_command(0x04) # POWER_ON + + self.ReadBusy() + + self.send_command(0X00) # PANEL_SETTING + self.send_data(0xCF) + self.send_command(0X50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0x17) + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x39) + self.send_command(0x61) # TCON_RESOLUTION set x and y + self.send_data(0xC8) + self.send_data(0x00) + self.send_data(0xC8) + self.send_command(0x82) # VCM_DC_SETTING_REGISTER + self.send_data(0x0E) + + self.set_lut_bw() + self.set_lut_red() + return 0 + + def getbuffer(self, image): + buf = [0xFF] * int(self.width * self.height / 8) + # Set buffer to value of Python Imaging Library image. + # Image must be in mode 1. + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + if imwidth != self.width or imheight != self.height: + raise ValueError('Image must be same dimensions as display \ + ({0}x{1}).' .format(self.width, self.height)) + + pixels = image_monocolor.load() + for y in range(self.height): + for x in range(self.width): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + return buf + + def display(self, blackimage, redimage): + # send black data + if (blackimage != None): + self.send_command(0x10) # DATA_START_TRANSMISSION_1 + for i in range(0, int(self.width * self.height / 8)): + temp = 0x00 + for bit in range(0, 4): + if (blackimage[i] & (0x80 >> bit) != 0): + temp |= 0xC0 >> (bit * 2) + self.send_data(temp) + temp = 0x00 + for bit in range(4, 8): + if (blackimage[i] & (0x80 >> bit) != 0): + temp |= 0xC0 >> ((bit - 4) * 2) + self.send_data(temp) + + # send red data + if (redimage != None): + self.send_command(0x13) # DATA_START_TRANSMISSION_2 + for i in range(0, int(self.width * self.height / 8)): + self.send_data(redimage[i]) + + self.send_command(0x12) # DISPLAY_REFRESH + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) # DATA_START_TRANSMISSION_1 + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_data(0xFF) + + self.send_command(0x13) # DATA_START_TRANSMISSION_2 + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x12) # DISPLAY_REFRESH + self.ReadBusy() + + def sleep(self): + self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0x17) + self.send_command(0x82) # to solve Vcom drop + self.send_data(0x00) + self.send_command(0x01) # power setting + self.send_data(0x02) # gate switch to external + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.ReadBusy() + + self.send_command(0x02) # power off + + epdconfig.module_exit() + +### END OF FILE ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1in54b_v2/epd1in54b_v2.py b/pwnagotchi/ui/hw/libs/waveshare/v1in54b_v2/epd1in54b_v2.py new file mode 100644 index 00000000..9f0d5848 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1in54b_v2/epd1in54b_v2.py @@ -0,0 +1,193 @@ +# ***************************************************************************** +# * | File : epd1in54b.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.1 +# * | Date : 2022-08-10 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 200 +EPD_HEIGHT = 200 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) # module reset + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.ReadBusy() + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x01) # Driver output control + self.send_data(0xC7) + self.send_data(0x00) + self.send_data(0x01) + + self.send_command(0x11) # data entry mode + self.send_data(0x01) + + self.send_command(0x44) # set Ram-X address start/end position + self.send_data(0x00) + self.send_data(0x18) # 0x18-->(24+1)*8=200 + + self.send_command(0x45) # set Ram-Y address start/end position + self.send_data(0xC7) # 0xC7-->(199+1)=200 + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x05) + + self.send_command(0x18) # Read built-in temperature sensor + self.send_data(0x80) + + self.send_command(0x4E) # set RAM x address count to 0 + self.send_data(0x00) + self.send_command(0x4F) # set RAM y address count to 0X199 + self.send_data(0xC7) + self.send_data(0x00) + self.ReadBusy() + return 0 + + def getbuffer(self, image): + buf = [0xFF] * int(self.width * self.height / 8) + # Set buffer to value of Python Imaging Library image. + # Image must be in mode 1. + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + if imwidth != self.width or imheight != self.height: + raise ValueError('Image must be same dimensions as display \ + ({0}x{1}).'.format(self.width, self.height)) + + pixels = image_monocolor.load() + for y in range(self.height): + for x in range(self.width): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + return buf + + def display(self, blackimage, redimage): + + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + buf = [0x00] * self.height * linewidth + + # send black data + if (blackimage != None): + self.send_command(0x24) # DATA_START_TRANSMISSION_1 + self.send_data2(blackimage) + + # send red data + if (redimage != None): + self.send_command(0x26) # DATA_START_TRANSMISSION_2 + for i in range(0, int(self.width * self.height / 8)): + buf[i] = ~redimage[i] + self.send_data2(buf) + + self.send_command(0x22) # DISPLAY_REFRESH + self.send_data(0xF7) + self.send_command(0x20) # DISPLAY_REFRESH + self.ReadBusy() + + def Clear(self): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x24) # DATA_START_TRANSMISSION_1 + self.send_data2([0xff] * int(self.height * linewidth)) + + self.send_command(0x26) # DATA_START_TRANSMISSION_2 + self.send_data2([0x00] * int(self.height * linewidth)) + + self.send_command(0x22) # DISPLAY_REFRESH + self.send_data(0xF7) + self.send_command(0x20) # DISPLAY_REFRESH + self.ReadBusy() + + def sleep(self): + self.send_command(0x10) # enter deep sleep + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1in54c/epd1in54c.py b/pwnagotchi/ui/hw/libs/waveshare/v1in54c/epd1in54c.py new file mode 100644 index 00000000..d07078af --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1in54c/epd1in54c.py @@ -0,0 +1,156 @@ +# ***************************************************************************** +# * | File : epd1in54c.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 152 +EPD_HEIGHT = 152 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(1) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(10) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(200) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x06) # boost soft start + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + self.send_command(0x04) # power on + + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0x0f) # LUT from OTP,160x296 + self.send_data(0x0d) # VCOM to 0V fast + + self.send_command(0x61) # resolution setting + self.send_data(0x98) + self.send_data(0x00) + self.send_data(0x98) + + self.send_command(0x50) + self.send_data(0x77) + + def getbuffer(self, image): + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, blackimage, yellowimage): + self.send_command(0x10) + logger.debug("blackimage") + for i in range(0, int(self.width * self.height / 8)): + self.send_data(blackimage[i]) + self.send_command(0x13) + logger.debug("yellowimage") + for i in range(0, int(self.width * self.height / 8)): + self.send_data(yellowimage[i]) + + self.send_command(0x12) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x12) + self.ReadBusy() + + # after this, call epd.init() to awaken the module + def sleep(self): + self.send_command(0X02) # power off + self.ReadBusy() + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1in64g/epd1in64g.py b/pwnagotchi/ui/hw/libs/waveshare/v1in64g/epd1in64g.py new file mode 100644 index 00000000..5310d847 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1in64g/epd1in64g.py @@ -0,0 +1,241 @@ +# ***************************************************************************** +# * | File : epd1in64g.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1 +# * | Date : 2022-07-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# ******************************************************************************/ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +import PIL +from PIL import Image +import io + +# Display resolution +EPD_WIDTH = 168 +EPD_HEIGHT = 168 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.BLACK = 0x000000 # 00 BGR + self.WHITE = 0xffffff # 01 + self.YELLOW = 0x00ffff # 10 + self.RED = 0x0000ff # 11 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) # module reset + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusyH(self): + logger.debug("e-Paper busy H") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(5) + logger.debug("e-Paper busy H release") + + def ReadBusyL(self): + logger.debug("e-Paper busy L") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy L release") + + def TurnOnDisplay(self): + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0x01) + self.ReadBusyH() + + self.send_command(0x02) # POWER_OFF + self.send_data(0X00) + self.ReadBusyH() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + + self.reset() + + self.send_command(0x66) + self.send_data(0x49) + self.send_data(0x55) + self.send_data(0x13) + self.send_data(0x5D) + + self.send_command(0x66) + self.send_data(0x49) + self.send_data(0x55) + + self.send_command(0xB0) + self.send_data(0x03) + + self.send_command(0x00) + self.send_data(0x4F) + self.send_data(0x6B) + + self.send_command(0x03) + self.send_data(0x00) + + self.send_command(0xF0) + self.send_data(0xF6) + self.send_data(0x0D) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x06) + self.send_data(0xCF) + self.send_data(0xDF) + self.send_data(0x0F) + + self.send_command(0x41) + self.send_data(0x00) + + self.send_command(0x50) + self.send_data(0x30) + + self.send_command(0x60) + self.send_data(0x0C) + self.send_data(0x05) + + self.send_command(0x61) + self.send_data(0xA8) + self.send_data(0x00) + self.send_data(0xA8) + + self.send_command(0x84) + self.send_data(0x01) + return 0 + + def getbuffer(self, image): + # Create a pallette with the 4 colors supported by the panel + pal_image = Image.new("P", (1, 1)) + pal_image.putpalette((0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 0, 0) + (0, 0, 0) * 252) + + # Check if we need to rotate the image + imwidth, imheight = image.size + if (imwidth == self.width and imheight == self.height): + image_temp = image + elif (imwidth == self.height and imheight == self.width): + image_temp = image.rotate(90, expand=True) + else: + logger.warning( + "Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) + + # Convert the soruce image to the 4 colors, dithering if needed + image_4color = image_temp.convert("RGB").quantize(palette=pal_image) + buf_4color = bytearray(image_4color.tobytes('raw')) + + # into a single byte to transfer to the panel + buf = [0x00] * int(self.width * self.height / 4) + idx = 0 + for i in range(0, len(buf_4color), 4): + buf[idx] = (buf_4color[i] << 6) + (buf_4color[i + 1] << 4) + (buf_4color[i + 2] << 2) + buf_4color[i + 3] + idx += 1 + + return buf + + def display(self, image): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x68) + self.send_data(0x01) + + self.send_command(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(image[i + j * Width]) + + self.send_command(0x68) + self.send_data(0x00) + + self.TurnOnDisplay() + + def Clear(self, color=0x55): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x68) + self.send_data(0x01) + + self.send_command(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(color) + + self.send_command(0x68) + self.send_data(0x00) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.send_data(0x00) + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13.py new file mode 100644 index 00000000..394ab177 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13.py @@ -0,0 +1,224 @@ +# ***************************************************************************** +# * | File : epd2in13.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 122 +EPD_HEIGHT = 250 + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + lut_full_update = [ + 0x22, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + lut_partial_update = [ + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.cs_pin, 1) + + def send_command(self, command): + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(100) + + def TurnOnDisplay(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0xC4) + self.send_command(0x20) # MASTER_ACTIVATION + self.send_command(0xFF) # TERMINATE_FRAME_READ_WRITE + + logging.debug("e-Paper busy") + self.ReadBusy() + logging.debug("e-Paper busy release") + + def init(self, lut): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + self.send_command(0x01) # DRIVER_OUTPUT_CONTROL + self.send_data((EPD_HEIGHT - 1) & 0xFF) + self.send_data(((EPD_HEIGHT - 1) >> 8) & 0xFF) + self.send_data(0x00) # GD = 0 SM = 0 TB = 0 + + self.send_command(0x0C) # BOOSTER_SOFT_START_CONTROL + self.send_data(0xD7) + self.send_data(0xD6) + self.send_data(0x9D) + + self.send_command(0x2C) # WRITE_VCOM_REGISTER + self.send_data(0xA8) # VCOM 7C + + self.send_command(0x3A) # SET_DUMMY_LINE_PERIOD + self.send_data(0x1A) # 4 dummy lines per gate + + self.send_command(0x3B) # SET_GATE_TIME + self.send_data(0x08) # 2us per line + + self.send_command(0X3C) # BORDER_WAVEFORM_CONTROL + self.send_data(0x03) + + self.send_command(0X11) # DATA_ENTRY_MODE_SETTING + self.send_data(0x03) # X increment; Y increment + + # WRITE_LUT_REGISTER + self.send_command(0x32) + for count in range(30): + self.send_data(lut[count]) + + return 0 + +## + # @brief: specify the memory area for data R/W + ## + def SetWindows(self, x_start, y_start, x_end, y_end): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + self.send_data((x_start >> 3) & 0xFF) + self.send_data((x_end >> 3) & 0xFF) + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(y_start & 0xFF) + self.send_data((y_start >> 8) & 0xFF) + self.send_data(y_end & 0xFF) + self.send_data((y_end >> 8) & 0xFF) + +## + # @brief: specify the start point for data R/W + ## + def SetCursor(self, x, y): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data((x >> 3) & 0xFF) + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + self.ReadBusy() + + def getbuffer(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + buf = [0xFF] * (linewidth * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + + if(imwidth == self.width and imheight == self.height): + for y in range(imheight): + for x in range(imwidth): + if pixels[x, y] == 0: + # x = imwidth - x + buf[int(x / 8) + y * linewidth] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + # newy = imwidth - newy - 1 + buf[int(newx / 8) + newy*linewidth] &= ~(0x80 >> (y % 8)) + return buf + + + def display(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + self.SetWindows(0, 0, self.width, self.height); + for j in range(0, self.height): + self.SetCursor(0, j); + self.send_command(0x24); + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + self.TurnOnDisplay() + + def Clear(self, color): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + self.SetWindows(0, 0, self.width, self.height); + for j in range(0, self.height): + self.SetCursor(0, j); + self.send_command(0x24); + for i in range(0, linewidth): + self.send_data(color) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) #enter deep sleep + self.send_data(0x01) + epdconfig.delay_ms(100) + + epdconfig.module_exit() + +### END OF FILE ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13bc.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13bc.py new file mode 100644 index 00000000..96bbf063 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13bc.py @@ -0,0 +1,164 @@ +# ***************************************************************************** +# * | File : epd2in13bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +from .. import epdconfig +import RPi.GPIO as GPIO +# import numpy as np + +# Display resolution +EPD_WIDTH = 104 +EPD_HEIGHT = 212 + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, GPIO.HIGH) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, GPIO.LOW) # module reset + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, GPIO.HIGH) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, GPIO.LOW) + epdconfig.digital_write(self.cs_pin, GPIO.LOW) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, GPIO.HIGH) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, GPIO.HIGH) + epdconfig.digital_write(self.cs_pin, GPIO.LOW) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, GPIO.HIGH) + + def ReadBusy(self): + epdconfig.delay_ms(20) + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0x8F) + + self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0xF0) + + self.send_command(0x61) # RESOLUTION_SETTING + self.send_data(self.width & 0xff) + self.send_data(self.height >> 8) + self.send_data(self.height & 0xff) + return 0 + + def getbuffer(self, image): + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + if(imwidth == self.width and imheight == self.height): + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def displayBlack(self, imageblack): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + self.send_command(0x92) + + self.send_command(0x12) # REFRESH + self.ReadBusy() + + def display(self, imageblack, imagecolor): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + self.send_command(0x92) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imagecolor[i]) + self.send_command(0x92) + + self.send_command(0x12) # REFRESH + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x92) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x92) + + self.send_command(0x12) # REFRESH + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0xA5) # check code + +# epdconfig.module_exit() +### END OF FILE ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13bcFAST.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13bcFAST.py new file mode 100644 index 00000000..82ac744c --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13bcFAST.py @@ -0,0 +1,359 @@ +# ***************************************************************************** +# * | File : epd2in13d.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +# ============================================================================= +# THIS FILE HAS BEEN MODIFIED FROM ORIGINAL, IT HAS BEEN MODIFIED TO RUN THE +# THREE COLOR WAVESHARE 2.13IN DISPLAY AT A MUCH, MUCH FASTER RATE THAN NORMAL +# AND IT COULD DAMAGE YOUR DISPLAY. THERE IS NO WARRANTY INCLUDED AND YOU USE +# THIS CODE AT YOUR OWN RISK. WE ARE NOT RESPONSIBLE FOR ANYTHING THAT HAPPENS +# INCLUDING BUT NOT LIMITED TO: DESTRUCTION OF YOUR DISPLAY, PI, HOUSE, CAR, +# SPACE-TIME-CONTINUUM, OR IF THE CODE KILLS YOUR CAT. IF YOU AREN'T WILLING TO +# TAKE THESE RISKS, PLEASE DO NOT USE THIS CODE. +# ============================================================================= + + +import logging +from .. import epdconfig +from PIL import Image +import RPi.GPIO as GPIO + +# Display resolution +EPD_WIDTH = 104 +EPD_HEIGHT = 212 + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + lut_vcomDC = [ + 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ] + + lut_ww = [ + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, + 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bw = [ + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03, + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_wb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x00, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_vcom1 = [ + 0xA0, 0x10, 0x10, 0x00, 0x00, 0x02, + 0x00, 0x10, 0x10, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ] + + lut_ww1 = [ + 0x50, 0x01, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x42, 0x42, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bw1 = [ + 0x50, 0x01, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x42, 0x42, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_wb1 = [ + 0xA0, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x50, 0x42, 0x42, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bb1 = [ + 0xA0, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x50, 0x42, 0x42, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0x71) + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x12) + epdconfig.delay_ms(10) + self.ReadBusy() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + self.send_data(0x03) + + self.send_command(0x06) # boost soft start + self.send_data(0x17) # A + self.send_data(0x17) # B + self.send_data(0x17) # C + + self.send_command(0x04) + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # LUT from OTP,128x296 + self.send_data(0x0d) # VCOM to 0V fast + + self.send_command(0x30) # PLL setting + self.send_data(0x21) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(self.width) + self.send_data((self.height >> 8) & 0xff) + self.send_data(self.height& 0xff) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x28) + return 0 + + def SetFullReg(self): + self.send_command(0x82) + self.send_data(0x00) + self.send_command(0X50) + self.send_data(0x97) +# self.send_command(0x00) # panel setting +# self.send_data(0x9f) # LUT from OTP,128x296 + + def SetPartReg(self): +# self.send_command(0x00) # panel setting +# self.send_data(0xbf) # LUT from OTP,128x296 + self.send_command(0x82) + self.send_data(0x03) + self.send_command(0X50) + self.send_data(0x47) + + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcom1[count]) + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww1[count]) + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw1[count]) + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_wb1[count]) + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_bb1[count]) + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (Image == None): + return + + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + def DisplayPartial(self, image): + if (Image == None): + return + + self.SetPartReg() + self.send_command(0x91) + self.send_command(0x90) + self.send_data(0) + self.send_data(self.width - 1) + + self.send_data(0) + self.send_data(0) + self.send_data(int(self.height / 256)) + self.send_data(self.height % 256 - 1) + self.send_data(0x28) + + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~image[i]) + epdconfig.delay_ms(10) + + self.TurnOnDisplay() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) # power off + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13_V2/epd2in13_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V2/epd2in13_V2.py new file mode 100644 index 00000000..813ca632 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V2/epd2in13_V2.py @@ -0,0 +1,314 @@ +# ***************************************************************************** +# * | File : epd2in13_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 122 +EPD_HEIGHT = 250 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + FULL_UPDATE = 0 + PART_UPDATE = 1 + lut_full_update = [ + 0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, # LUT0: BB: VS 0 ~7 + 0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, # LUT1: BW: VS 0 ~7 + 0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, # LUT2: WB: VS 0 ~7 + 0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, # LUT3: WW: VS 0 ~7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT4: VCOM: VS 0 ~7 + + 0x03, 0x03, 0x00, 0x00, 0x02, # TP0 A~D RP0 + 0x09, 0x09, 0x00, 0x00, 0x02, # TP1 A~D RP1 + 0x03, 0x03, 0x00, 0x00, 0x02, # TP2 A~D RP2 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP3 A~D RP3 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP4 A~D RP4 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP5 A~D RP5 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP6 A~D RP6 + + 0x15, 0x41, 0xA8, 0x32, 0x30, 0x0A, + ] + + lut_partial_update = [ # 20 bytes + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT0: BB: VS 0 ~7 + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT1: BW: VS 0 ~7 + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT2: WB: VS 0 ~7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT3: WW: VS 0 ~7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT4: VCOM: VS 0 ~7 + + 0x0A, 0x00, 0x00, 0x00, 0x00, # TP0 A~D RP0 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP1 A~D RP1 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP2 A~D RP2 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP3 A~D RP3 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP4 A~D RP4 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP5 A~D RP5 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP6 A~D RP6 + + 0x15, 0x41, 0xA8, 0x32, 0x30, 0x0A, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(100) + + def TurnOnDisplay(self): + self.send_command(0x22) + self.send_data(0xC7) + self.send_command(0x20) + self.ReadBusy() + + def TurnOnDisplayPart(self): + self.send_command(0x22) + self.send_data(0x0c) + self.send_command(0x20) + self.ReadBusy() + + def init(self, update): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + if (update == self.FULL_UPDATE): + self.ReadBusy() + self.send_command(0x12) # soft reset + self.ReadBusy() + + self.send_command(0x74) # set analog block control + self.send_data(0x54) + self.send_command(0x7E) # set digital block control + self.send_data(0x3B) + + self.send_command(0x01) # Driver output control + self.send_data(0xF9) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x01) + + self.send_command(0x44) # set Ram-X address start/end position + self.send_data(0x00) + self.send_data(0x0F) # 0x0C-->(15+1)*8=128 + + self.send_command(0x45) # set Ram-Y address start/end position + self.send_data(0xF9) # 0xF9-->(249+1)=250 + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x03) + + self.send_command(0x2C) # VCOM Voltage + self.send_data(0x55) # + + self.send_command(0x03) + self.send_data(self.lut_full_update[70]) + + self.send_command(0x04) # + self.send_data(self.lut_full_update[71]) + self.send_data(self.lut_full_update[72]) + self.send_data(self.lut_full_update[73]) + + self.send_command(0x3A) # Dummy Line + self.send_data(self.lut_full_update[74]) + self.send_command(0x3B) # Gate time + self.send_data(self.lut_full_update[75]) + + self.send_command(0x32) + for count in range(70): + self.send_data(self.lut_full_update[count]) + + self.send_command(0x4E) # set RAM x address count to 0 + self.send_data(0x00) + self.send_command(0x4F) # set RAM y address count to 0X127 + self.send_data(0xF9) + self.send_data(0x00) + self.ReadBusy() + else: + self.send_command(0x2C) # VCOM Voltage + self.send_data(0x26) + + self.ReadBusy() + + self.send_command(0x32) + for count in range(70): + self.send_data(self.lut_partial_update[count]) + + self.send_command(0x37) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x40) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x22) + self.send_data(0xC0) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x01) + return 0 + + def getbuffer(self, image): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + buf = [0xFF] * (linewidth * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + if pixels[x, y] == 0: + x = imwidth - x + buf[int(x / 8) + y * linewidth] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + newy = imwidth - newy - 1 + buf[int(newx / 8) + newy * linewidth] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + self.send_command(0x24) + self.send_data2(image) + self.TurnOnDisplay() + + def displayPartial(self, image): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + buf = [0x00] * self.height * linewidth + for j in range(0, self.height): + for i in range(0, linewidth): + buf[i + j * linewidth] = ~image[i + j * linewidth] + + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(buf) + self.TurnOnDisplayPart() + + def displayPartBaseImage(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + self.TurnOnDisplay() + + def Clear(self, color=0xFF): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + # logger.debug(linewidth) + + buf = [0x00] * self.height * linewidth + for j in range(0, self.height): + for i in range(0, linewidth): + buf[i + j * linewidth] = color + + self.send_command(0x24) + self.send_data2(buf) + + # self.send_command(0x26) + # for j in range(0, self.height): + # for i in range(0, linewidth): + # self.send_data(color) + + self.TurnOnDisplay() + + def sleep(self): + # self.send_command(0x22) #POWER OFF + # self.send_data(0xC3) + # self.send_command(0x20) + + self.send_command(0x10) # enter deep sleep + self.send_data(0x03) + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13_V3/epd2in13_V3.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V3/epd2in13_V3.py new file mode 100644 index 00000000..47f89a9e --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V3/epd2in13_V3.py @@ -0,0 +1,404 @@ +# ***************************************************************************** +# * | File : epd2in13_V3.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.2 +# * | Date : 2022-08-9 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 122 +EPD_HEIGHT = 250 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + lut_partial_update = [ + 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, + 0x22, 0x17, 0x41, 0x00, 0x32, 0x36, + ] + + lut_full_update = [ + 0x80, 0x4A, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x40, 0x4A, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x80, 0x4A, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x40, 0x4A, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xF, 0x0, 0x0, 0xF, 0x0, 0x0, 0x2, + 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, + 0x22, 0x17, 0x41, 0x0, 0x32, 0x36, + ] + + ''' + function :Hardware reset + parameter: + ''' + + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + + ''' + function :send command + parameter: + command : Command register + ''' + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + ''' + function :send data + parameter: + data : Write data + ''' + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + ''' + function :Wait until the busy_pin goes LOW + parameter: + ''' + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(10) + logger.debug("e-Paper busy release") + + ''' + function : Turn On Display + parameter: + ''' + + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Turn On Display Part + parameter: + ''' + + def TurnOnDisplayPart(self): + self.send_command(0x22) # Display Update Control + self.send_data(0x0f) # fast:0x0c, quality:0x0f, 0xcf + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Set lut + parameter: + lut : lut data + ''' + + def Lut(self, lut): + self.send_command(0x32) + for i in range(0, 153): + self.send_data(lut[i]) + self.ReadBusy() + + ''' + function : Send lut data and configuration + parameter: + lut : lut data + ''' + + def SetLut(self, lut): + self.Lut(lut) + self.send_command(0x3f) + self.send_data(lut[153]) + self.send_command(0x03) # gate voltage + self.send_data(lut[154]) + self.send_command(0x04) # source voltage + self.send_data(lut[155]) # VSH + self.send_data(lut[156]) # VSH2 + self.send_data(lut[157]) # VSL + self.send_command(0x2c) # VCOM + self.send_data(lut[158]) + + ''' + function : Setting the display window + parameter: + xstart : X-axis starting position + ystart : Y-axis starting position + xend : End position of X-axis + yend : End position of Y-axis + ''' + + def SetWindow(self, x_start, y_start, x_end, y_end): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data((x_start >> 3) & 0xFF) + self.send_data((x_end >> 3) & 0xFF) + + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(y_start & 0xFF) + self.send_data((y_start >> 8) & 0xFF) + self.send_data(y_end & 0xFF) + self.send_data((y_end >> 8) & 0xFF) + + ''' + function : Set Cursor + parameter: + x : X-axis starting position + y : Y-axis starting position + ''' + + def SetCursor(self, x, y): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data(x & 0xFF) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + + ''' + function : Initialize the e-Paper register + parameter: + ''' + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.ReadBusy() + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x01) # Driver output control + self.send_data(0xf9) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.SetWindow(0, 0, self.width - 1, self.height - 1) + self.SetCursor(0, 0) + + self.send_command(0x3c) + self.send_data(0x05) + + self.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x80) + + self.send_command(0x18) + self.send_data(0x80) + + self.ReadBusy() + + self.SetLut(self.lut_full_update) + return 0 + + ''' + function : Display images + parameter: + image : Image data + ''' + + def getbuffer(self, image): + img = image + imwidth, imheight = img.size + if (imwidth == self.width and imheight == self.height): + img = img.convert('1') + elif (imwidth == self.height and imheight == self.width): + # image has correct dimensions, but needs to be rotated + img = img.rotate(90, expand=True).convert('1') + else: + logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) + # return a blank buffer + return [0x00] * (int(self.width / 8) * self.height) + + buf = bytearray(img.tobytes('raw')) + return buf + + ''' + function : Sends the image buffer in RAM to e-Paper and displays + parameter: + image : Image data + ''' + + def display(self, image): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x24) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + self.TurnOnDisplay() + + ''' + function : Sends the image buffer in RAM to e-Paper and partial refresh + parameter: + image : Image data + ''' + + def displayPartial(self, image): + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(1) + epdconfig.digital_write(self.reset_pin, 1) + + self.SetLut(self.lut_partial_update) + self.send_command(0x37) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x40) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x22) + self.send_data(0xC0) + self.send_command(0x20) + self.ReadBusy() + + self.SetWindow(0, 0, self.width - 1, self.height - 1) + self.SetCursor(0, 0) + + self.send_command(0x24) # WRITE_RAM + # for j in range(0, self.height): + # for i in range(0, linewidth): + # self.send_data(image[i + j * linewidth]) + self.send_data2(image) + self.TurnOnDisplayPart() + + ''' + function : Refresh a base image + parameter: + image : Image data + ''' + + def displayPartBaseImage(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + self.TurnOnDisplay() + + ''' + function : Clear screen + parameter: + ''' + + def Clear(self, color=0xFF): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + # logger.debug(linewidth) + + self.send_command(0x24) + self.send_data2([color] * int(self.height * linewidth)) + self.TurnOnDisplay() + + ''' + function : Enter sleep mode + parameter: + ''' + + def sleep(self): + self.send_command(0x10) # enter deep sleep + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13_V4/epd2in13_V4.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V4/epd2in13_V4.py new file mode 100644 index 00000000..625cd97c --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V4/epd2in13_V4.py @@ -0,0 +1,350 @@ +# ***************************************************************************** +# * | File : epd2in13_V4.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2023-06-25 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 122 +EPD_HEIGHT = 250 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + ''' + function :Hardware reset + parameter: + ''' + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + + ''' + function :send command + parameter: + command : Command register + ''' + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + ''' + function :send data + parameter: + data : Write data + ''' + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + ''' + function :Wait until the busy_pin goes LOW + parameter: + ''' + def ReadBusy(self): + logger.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(10) + logger.debug("e-Paper busy release") + + ''' + function : Turn On Display + parameter: + ''' + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xf7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Turn On Display Fast + parameter: + ''' + def TurnOnDisplay_Fast(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) # fast:0x0c, quality:0x0f, 0xcf + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Turn On Display Part + parameter: + ''' + def TurnOnDisplayPart(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xff) # fast:0x0c, quality:0x0f, 0xcf + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + + ''' + function : Setting the display window + parameter: + xstart : X-axis starting position + ystart : Y-axis starting position + xend : End position of X-axis + yend : End position of Y-axis + ''' + def SetWindow(self, x_start, y_start, x_end, y_end): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data((x_start>>3) & 0xFF) + self.send_data((x_end>>3) & 0xFF) + + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(y_start & 0xFF) + self.send_data((y_start >> 8) & 0xFF) + self.send_data(y_end & 0xFF) + self.send_data((y_end >> 8) & 0xFF) + + ''' + function : Set Cursor + parameter: + x : X-axis starting position + y : Y-axis starting position + ''' + def SetCursor(self, x, y): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data(x & 0xFF) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + + ''' + function : Initialize the e-Paper register + parameter: + ''' + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.ReadBusy() + self.send_command(0x12) #SWRESET + self.ReadBusy() + + self.send_command(0x01) #Driver output control + self.send_data(0xf9) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) #data entry mode + self.send_data(0x03) + + self.SetWindow(0, 0, self.width-1, self.height-1) + self.SetCursor(0, 0) + + self.send_command(0x3c) + self.send_data(0x05) + + self.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x80) + + self.send_command(0x18) + self.send_data(0x80) + + self.ReadBusy() + + return 0 + + ''' + function : Initialize the e-Paper fast register + parameter: + ''' + def init_fast(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x12) #SWRESET + self.ReadBusy() + + self.send_command(0x18) # Read built-in temperature sensor + self.send_command(0x80) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.SetWindow(0, 0, self.width-1, self.height-1) + self.SetCursor(0, 0) + + self.send_command(0x22) # Load temperature value + self.send_data(0xB1) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x1A) # Write to temperature register + self.send_data(0x64) + self.send_data(0x00) + + self.send_command(0x22) # Load temperature value + self.send_data(0x91) + self.send_command(0x20) + self.ReadBusy() + + return 0 + ''' + function : Display images + parameter: + image : Image data + ''' + def getbuffer(self, image): + img = image + imwidth, imheight = img.size + if(imwidth == self.width and imheight == self.height): + img = img.convert('1') + elif(imwidth == self.height and imheight == self.width): + # image has correct dimensions, but needs to be rotated + img = img.rotate(90, expand=True).convert('1') + else: + logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) + # return a blank buffer + return [0x00] * (int(self.width/8) * self.height) + + buf = bytearray(img.tobytes('raw')) + return buf + + ''' + function : Sends the image buffer in RAM to e-Paper and displays + parameter: + image : Image data + ''' + def display(self, image): + self.send_command(0x24) + self.send_data2(image) + self.TurnOnDisplay() + + ''' + function : Sends the image buffer in RAM to e-Paper and fast displays + parameter: + image : Image data + ''' + def display_fast(self, image): + self.send_command(0x24) + self.send_data2(image) + self.TurnOnDisplay_Fast() + ''' + function : Sends the image buffer in RAM to e-Paper and partial refresh + parameter: + image : Image data + ''' + def displayPartial(self, image): + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(1) + epdconfig.digital_write(self.reset_pin, 1) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x01) # Driver output control + self.send_data(0xF9) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.SetWindow(0, 0, self.width - 1, self.height - 1) + self.SetCursor(0, 0) + + self.send_command(0x24) # WRITE_RAM + self.send_data2(image) + self.TurnOnDisplayPart() + + ''' + function : Refresh a base image + parameter: + image : Image data + ''' + def displayPartBaseImage(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + self.TurnOnDisplay() + + ''' + function : Clear screen + parameter: + ''' + def Clear(self, color=0xFF): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + # logger.debug(linewidth) + + self.send_command(0x24) + self.send_data2([color] * int(self.height * linewidth)) + self.TurnOnDisplay() + + ''' + function : Enter sleep mode + parameter: + ''' + def sleep(self): + self.send_command(0x10) #enter deep sleep + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13b_v3/epd2in13b_V3.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13b_v3/epd2in13b_V3.py new file mode 100644 index 00000000..eb07034f --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13b_v3/epd2in13b_V3.py @@ -0,0 +1,161 @@ +# ***************************************************************************** +# * | File : epd2in13bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 104 +EPD_HEIGHT = 212 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0x71) + while epdconfig.digital_read(self.busy_pin) == 0: + self.send_command(0x71) + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if epdconfig.module_init() != 0: + return -1 + + self.reset() + self.send_command(0x04) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0x00) # panel setting + self.send_data(0x0f) # LUT from OTP,128x296 + self.send_data(0x89) # Temperature sensor, boost and other related timing settings + + self.send_command(0x61) # resolution setting + self.send_data(0x68) + self.send_data(0x00) + self.send_data(0xD4) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x77) # WBmode:VBDF 17|D7 VBDW 97 VBDB 57 + # WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7 + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif imwidth == self.height and imheight == self.width: + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imagered[i]) + + self.send_command(0x12) # REFRESH + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x12) # REFRESH + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0xA5) # check code + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +# END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13b_v4/epd2in13b_V4.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13b_v4/epd2in13b_V4.py new file mode 100644 index 00000000..e6509880 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13b_v4/epd2in13b_V4.py @@ -0,0 +1,204 @@ +# ***************************************************************************** +# * | File : epd2in13b_V4.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2022-04-21 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 122 +EPD_HEIGHT = 250 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + + # send 1 byte command + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + # send 1 byte data + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + # judge e-Paper whether is busy + def busy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) != 0): + epdconfig.delay_ms(10) + logger.debug("e-Paper busy release") + + # set the display window + def set_windows(self, xstart, ystart, xend, yend): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + self.send_data((xstart >> 3) & 0xff) + self.send_data((xend >> 3) & 0xff) + + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(ystart & 0xff) + self.send_data((ystart >> 8) & 0xff) + self.send_data(yend & 0xff) + self.send_data((yend >> 8) & 0xff) + + # set the display cursor(origin) + def set_cursor(self, xstart, ystart): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + self.send_data(xstart & 0xff) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(ystart & 0xff) + self.send_data((ystart >> 8) & 0xff) + + # initialize + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.busy() + self.send_command(0x12) # SWRESET + self.busy() + + self.send_command(0x01) # Driver output control + self.send_data(0xf9) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.set_windows(0, 0, self.width - 1, self.height - 1) + self.set_cursor(0, 0) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x05) + + self.send_command(0x18) # Read built-in temperature sensor + self.send_data(0x80) + + self.send_command(0x21) # Display update control + self.send_data(0x80) + self.send_data(0x80) + + self.busy() + + return 0 + + # turn on display + def ondisplay(self): + self.send_command(0x20) + self.busy() + + # image converted to bytearray + def getbuffer(self, image): + img = image + imwidth, imheight = img.size + if (imwidth == self.width and imheight == self.height): + img = img.convert('1') + elif (imwidth == self.height and imheight == self.width): + # image has correct dimensions, but needs to be rotated + img = img.rotate(90, expand=True).convert('1') + else: + logger.warning("Wrong image dimensions: must be " + + str(self.width) + "x" + str(self.height)) + # return a blank buffer + return [0x00] * (int(self.width/8) * self.height) + + buf = bytearray(img.tobytes('raw')) + return buf + + # display image + def display(self, imageblack, imagered): + self.send_command(0x24) + self.send_data2(imageblack) + + self.send_command(0x26) + self.send_data2(imagered) + + self.ondisplay() + + # display white image + def clear(self): + if self.width % 8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + buf = [0xff] * (int(linewidth * self.height)) + + self.send_command(0x24) + self.send_data2(buf) + + self.send_command(0x26) + self.send_data2(buf) + + self.ondisplay() + + # Compatible with older version functions + def Clear(self): + self.clear() + + # sleep + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP + self.send_data(0x01) # check code + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13bc/epd2in13bc.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13bc/epd2in13bc.py new file mode 100644 index 00000000..62043a4f --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13bc/epd2in13bc.py @@ -0,0 +1,378 @@ +# ***************************************************************************** +# * | File : epd2in13bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig +from PIL import Image + +# Display resolution +EPD_WIDTH = 104 +EPD_HEIGHT = 212 + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + + lut_vcomDC = [ + 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ] + + lut_ww = [ + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, + 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bw = [ + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03, + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_wb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_vcom1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ] + + lut_ww1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bw1 = [ + 0x80, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_wb1 = [ + 0x40, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bb1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x12) + epdconfig.delay_ms(10) + self.ReadBusy() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + logging.debug("e-Paper 2.13bc preboot Freeze recovery") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + self.reset() + epdconfig.delay_ms(200) + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + self.send_data(0x03) + epdconfig.delay_ms(200) + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + self.send_command(0x04) # POWER_ON + epdconfig.delay_ms(200) + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) # power off + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + epdconfig.GPIO.output(epdconfig.RST_PIN, 0) + epdconfig.GPIO.output(epdconfig.DC_PIN, 0) + epdconfig.GPIO.output(epdconfig.CS_PIN, 0) + #logging.debug("Reset, powerdown, voltage off done") + logging.debug("e-Paper is not frozen now :)") + + + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + self.send_data(0x03) + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + + self.send_command(0x04) # POWER_ON + logging.debug("e-Paper 2.13bc bootup busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + +# self.send_command(0x00) # PANEL_SETTING +# self.send_data(0x8F) +# self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING +# self.send_data(0xF0) +# self.send_command(0x61) # RESOLUTION_SETTING +# self.send_data(self.width & 0xff) +# self.send_data(self.height >> 8) +# self.send_data(self.height & 0xff) + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # LUT from OTP,128x296 + self.send_data(0x0d) # VCOM to 0V fast + + self.send_command(0x30) # PLL setting + self.send_data(0x3a) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(self.width & 0xff) + self.send_data((self.height >> 8) & 0xff) + self.send_data(self.height& 0xff) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x28) + + #self.Clear() + logging.debug("e-Paper booted") + return 0 + + def SetFullReg(self): + self.send_command(0x82) + self.send_data(0x00) + self.send_command(0X50) + self.send_data(0x97) + + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcomDC[count]) + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww[count]) + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw[count]) + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_wb[count]) + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_bb[count]) + + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + self.send_command(0x92) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imagered[i]) + self.send_command(0x92) + + self.send_command(0x12) # REFRESH + self.ReadBusy() + + def pwndisplay(self, imageblack): + if (Image == None): + return + + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x92) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x92) + + self.send_command(0x12) # REFRESH + self.ReadBusy() + + def pwnclear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0xA5) # check code + + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13d/epd2in13d.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13d/epd2in13d.py new file mode 100644 index 00000000..7a2c6cf2 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13d/epd2in13d.py @@ -0,0 +1,358 @@ +# ***************************************************************************** +# * | File : epd2in13d.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig +from PIL import Image +import RPi.GPIO as GPIO + +# Display resolution +EPD_WIDTH = 104 +EPD_HEIGHT = 212 + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + lut_vcomDC = [ + 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ] + + lut_ww = [ + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, + 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bw = [ + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03, + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_wb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_vcom1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ] + + lut_ww1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bw1 = [ + 0x80, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_wb1 = [ + 0x40, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bb1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0x71) + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x12) + epdconfig.delay_ms(10) + self.ReadBusy() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + self.send_data(0x03) + + self.send_command(0x06) # boost soft start + self.send_data(0x17) # A + self.send_data(0x17) # B + self.send_data(0x17) # C + + self.send_command(0x04) + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # LUT from OTP,128x296 + self.send_data(0x0d) # VCOM to 0V fast + + self.send_command(0x30) # PLL setting + self.send_data(0x3a) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(self.width) + self.send_data((self.height >> 8) & 0xff) + self.send_data(self.height& 0xff) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x28) + return 0 + + def SetFullReg(self): + self.send_command(0x82) + self.send_data(0x00) + self.send_command(0X50) + self.send_data(0x97) + + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcomDC[count]) + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww[count]) + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw[count]) + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_wb[count]) + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_bb[count]) + + def SetPartReg(self): + self.send_command(0x82) + self.send_data(0x03) + self.send_command(0X50) + self.send_data(0x47) + + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcom1[count]) + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww1[count]) + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw1[count]) + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_wb1[count]) + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_bb1[count]) + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (Image == None): + return + + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + def DisplayPartial(self, image): + if (Image == None): + return + + self.SetPartReg() + self.send_command(0x91) + self.send_command(0x90) + self.send_data(0) + self.send_data(self.width - 1) + + self.send_data(0) + self.send_data(0) + self.send_data(int(self.height / 256)) + self.send_data(self.height % 256 - 1) + self.send_data(0x28) + + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~image[i]) + epdconfig.delay_ms(10) + + self.TurnOnDisplay() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) # power off + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.module_exit() + +### END OF FILE ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in23g/epd2in13g.py b/pwnagotchi/ui/hw/libs/waveshare/v2in23g/epd2in13g.py new file mode 100644 index 00000000..d767af73 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in23g/epd2in13g.py @@ -0,0 +1,242 @@ +# ***************************************************************************** +# * | File : epd2in13g.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2023-05-29 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# ******************************************************************************/ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +import PIL +from PIL import Image +import io + +# Display resolution +EPD_WIDTH = 122 +EPD_HEIGHT = 250 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.BLACK = 0x000000 # 00 BGR + self.WHITE = 0xffffff # 01 + self.YELLOW = 0x00ffff # 10 + self.RED = 0x0000ff # 11 + self.Gate_BITS = EPD_HEIGHT + if self.width < 128: + self.Source_BITS = 128 + else: + self.Source_BITS = self.width + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) # module reset + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy H") + epdconfig.delay_ms(100) + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(5) + logger.debug("e-Paper busy release") + + def SetWindow(self): + self.send_command(0x61) # SET_RAM_X_ADDRESS_START_END_POSITION + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data(self.Source_BITS / 256) + self.send_data(self.Source_BITS % 256) + self.send_data(self.Gate_BITS / 256) + self.send_data(self.Gate_BITS % 256) + + def TurnOnDisplay(self): + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0X00) + self.ReadBusy() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + + self.reset() + + self.ReadBusy() + self.send_command(0x4D) + self.send_data(0x78) + + self.send_command(0x00) + self.send_data(0x0F) + self.send_data(0x29) + + self.send_command(0x01) + self.send_data(0x07) + self.send_data(0x00) + + self.send_command(0x03) + self.send_data(0x10) + self.send_data(0x54) + self.send_data(0x44) + + self.send_command(0x06) + self.send_data(0x05) + self.send_data(0x00) + self.send_data(0x3F) + self.send_data(0x0A) + self.send_data(0x25) + self.send_data(0x12) + self.send_data(0x1A) + + self.send_command(0x50) + self.send_data(0x37) + + self.send_command(0x60) + self.send_data(0x02) + self.send_data(0x02) + + self.SetWindow() + + self.send_command(0xE7) + self.send_data(0x1C) + + self.send_command(0xE3) + self.send_data(0x22) + + self.send_command(0xB4) + self.send_data(0xD0) + self.send_command(0xB5) + self.send_data(0x03) + + self.send_command(0xE9) + self.send_data(0x01) + + self.send_command(0x30) + self.send_data(0x08) + + self.send_command(0x04) + self.ReadBusy() + return 0 + + def getbuffer(self, image): + # Create a pallette with the 4 colors supported by the panel + pal_image = Image.new("P", (1, 1)) + pal_image.putpalette((0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 0, 0) + (0, 0, 0) * 252) + + # Check if we need to rotate the image + imwidth, imheight = image.size + if (imwidth == self.width and imheight == self.height): + image_temp = image + elif (imwidth == self.height and imheight == self.width): + image_temp = image.rotate(90, expand=True) + else: + logger.warning( + "Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) + + # Convert the soruce image to the 4 colors, dithering if needed + image_4color = image_temp.convert("RGB").quantize(palette=pal_image) + buf_4color = bytearray(image_4color.tobytes('raw')) + + # into a single byte to transfer to the panel + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + buf = [0x00] * int(Width * Height) + idx = 0 + for j in range(0, Height): + for i in range(0, Width): + if i == Width - 1: + buf[i + j * Width] = (buf_4color[idx] << 6) + (buf_4color[idx + 1] << 4) + idx = idx + 2 + else: + buf[i + j * Width] = (buf_4color[idx] << 6) + (buf_4color[idx + 1] << 4) + ( + buf_4color[idx + 2] << 2) + buf_4color[idx + 3] + idx = idx + 4 + return buf + + def display(self, image): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, self.Source_BITS // 4): + if i < 31: + self.send_data(image[i + j * Width]) + else: + self.send_data(0x00) + + self.TurnOnDisplay() + + def Clear(self, color=0x55): + Width = self.Source_BITS // 4 + Height = self.height + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(color) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + epdconfig.delay_ms(100) + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in36g/epd2in36g.py b/pwnagotchi/ui/hw/libs/waveshare/v2in36g/epd2in36g.py new file mode 100644 index 00000000..ddb646bf --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in36g/epd2in36g.py @@ -0,0 +1,241 @@ +# ***************************************************************************** +# * | File : epd2in36g.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2022-08-17 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# ******************************************************************************/ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +import PIL +from PIL import Image +import io + +# Display resolution +EPD_WIDTH = 168 +EPD_HEIGHT = 296 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.BLACK = 0x000000 # 00 BGR + self.WHITE = 0xffffff # 01 + self.YELLOW = 0x00ffff # 10 + self.RED = 0x0000ff # 11 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) # module reset + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusyH(self): + logger.debug("e-Paper busy H") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(5) + logger.debug("e-Paper busy H release") + + def ReadBusyL(self): + logger.debug("e-Paper busy L") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy L release") + + def TurnOnDisplay(self): + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0x01) + self.ReadBusyH() + + self.send_command(0x02) # POWER_OFF + self.send_data(0X00) + self.ReadBusyH() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + + self.reset() + + self.send_command(0x66) + self.send_data(0x49) + self.send_data(0x55) + self.send_data(0x13) + self.send_data(0x5D) + + self.send_command(0x66) + self.send_data(0x49) + self.send_data(0x55) + + self.send_command(0xB0) + self.send_data(0x03) + + self.send_command(0x00) + self.send_data(0x4F) + self.send_data(0x69) + + self.send_command(0x03) + self.send_data(0x00) + + self.send_command(0xF0) + self.send_data(0xF6) + self.send_data(0x0D) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x06) + self.send_data(0xCF) + self.send_data(0xDE) + self.send_data(0x0F) + + self.send_command(0x41) + self.send_data(0x00) + + self.send_command(0x50) + self.send_data(0x30) + + self.send_command(0x60) + self.send_data(0x0C) + self.send_data(0x05) + + self.send_command(0x61) + self.send_data(0xA8) + self.send_data(0x01) + self.send_data(0x28) + + self.send_command(0x84) + self.send_data(0x01) + return 0 + + def getbuffer(self, image): + # Create a pallette with the 4 colors supported by the panel + pal_image = Image.new("P", (1, 1)) + pal_image.putpalette((0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 0, 0) + (0, 0, 0) * 252) + + # Check if we need to rotate the image + imwidth, imheight = image.size + if (imwidth == self.width and imheight == self.height): + image_temp = image + elif (imwidth == self.height and imheight == self.width): + image_temp = image.rotate(90, expand=True) + else: + logger.warning( + "Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) + + # Convert the soruce image to the 4 colors, dithering if needed + image_4color = image_temp.convert("RGB").quantize(palette=pal_image) + buf_4color = bytearray(image_4color.tobytes('raw')) + + # into a single byte to transfer to the panel + buf = [0x00] * int(self.width * self.height / 4) + idx = 0 + for i in range(0, len(buf_4color), 4): + buf[idx] = (buf_4color[i] << 6) + (buf_4color[i + 1] << 4) + (buf_4color[i + 2] << 2) + buf_4color[i + 3] + idx += 1 + + return buf + + def display(self, image): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x68) + self.send_data(0x01) + + self.send_command(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(image[i + j * Width]) + + self.send_command(0x68) + self.send_data(0x00) + + self.TurnOnDisplay() + + def Clear(self, color=0x55): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x68) + self.send_data(0x01) + + self.send_command(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(color) + + self.send_command(0x68) + self.send_data(0x00) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.send_data(0x00) + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in66/epd2in66.py b/pwnagotchi/ui/hw/libs/waveshare/v2in66/epd2in66.py new file mode 100644 index 00000000..67295e7e --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in66/epd2in66.py @@ -0,0 +1,232 @@ +# ***************************************************************************** +# * | File : epd2in66.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-9 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 152 +EPD_HEIGHT = 296 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + WF_PARTIAL = [ + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x00, 0x00, 0x00, 0x22, 0x17, 0x41, 0xB0, 0x32, 0x36, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(200) + logger.debug("e-Paper busy release") + + def init(self, mode): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x12) + epdconfig.delay_ms(300) + self.ReadBusy() + + self.send_command(0x11) # setting gaet number + self.send_data(0x03) + self.send_command(0x44) # set gate voltage + self.send_data(0x01) + self.send_data(0x13) + self.send_command(0x45) # set source voltage + self.send_data(0x0) + self.send_data(0x0) + self.send_data(0x28) + self.send_data(0x01) + + if (mode == 0): # full + self.send_command(0x3C) + self.send_data(0x01) + + elif (mode == 1): # partial + self.load_lut(self.WF_PARTIAL) + self.send_command(0x37) # set display option, these setting turn on previous function + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x40) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) + self.send_data(0x80) + + self.send_command(0x22) + self.send_data(0xcf) + + self.send_command(0x20) + self.ReadBusy() + + else: + logger.debug("There is no such mode") + + return 0 + + def load_lut(self, lut): + self.send_command(0x32) + # for i in range(0, 153): + # self.send_data(lut[i]) + self.send_data2(lut) + + def turnon_display(self): + self.send_command(0x20) + self.ReadBusy() + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (image == None): + return + + self.send_command(0x4E) + self.send_data(0x01) + self.send_command(0x4F) + self.send_data(0x27) + self.send_data(0x01) + + self.send_command(0x24) + self.send_data2(image) + self.turnon_display() + + def Clear(self): + self.send_command(0x4E) + self.send_data(0x01) + self.send_command(0x4F) + self.send_data(0x27) + self.send_data(0x01) + + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + buf = [0xff] * int(self.height * linewidth) + + self.send_command(0x24) + self.send_data2(buf) + + self.send_command(0x26) + self.send_data2(buf) + + self.turnon_display() + + def sleep(self): + self.send_command(0X10) # DEEP_SLEEP_MODE + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in66g/epd2in66g.py b/pwnagotchi/ui/hw/libs/waveshare/v2in66g/epd2in66g.py new file mode 100644 index 00000000..f874b201 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in66g/epd2in66g.py @@ -0,0 +1,229 @@ +# ***************************************************************************** +# * | File : epd2in9g.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2023-03-08 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# ******************************************************************************/ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +import PIL +from PIL import Image +import io + +# Display resolution +EPD_WIDTH = 184 +EPD_HEIGHT = 360 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.BLACK = 0x000000 # 00 BGR + self.WHITE = 0xffffff # 01 + self.YELLOW = 0x00ffff # 10 + self.RED = 0x0000ff # 11 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) # module reset + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusyH(self): + logger.debug("e-Paper busy H") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(5) + logger.debug("e-Paper busy H release") + + def ReadBusyL(self): + logger.debug("e-Paper busy L") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy L release") + + def TurnOnDisplay(self): + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0x00) + self.ReadBusyH() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + + self.reset() + self.ReadBusyH() + self.send_command(0x4D) + self.send_data(0x78) + + self.send_command(0x00) # PSR + self.send_data(0x0F) + self.send_data(0x29) + + self.send_command(0x01) # PWRR + self.send_data(0x07) + self.send_data(0x00) + + self.send_command(0x03) # POFS + self.send_data(0x10) + self.send_data(0x54) + self.send_data(0x44) + + self.send_command(0x06) # BTST_P + self.send_data(0x05) + self.send_data(0x00) + self.send_data(0x3F) + self.send_data(0x0A) + self.send_data(0x25) + self.send_data(0x12) + self.send_data(0x1A) + + self.send_command(0x50) # CDI + self.send_data(0x37) + + self.send_command(0x60) # TCON + self.send_data(0x02) + self.send_data(0x02) + + self.send_command(0x61) # TRES + self.send_data(self.width // 256) # Source_BITS_H + self.send_data(self.width % 256) # Source_BITS_L + self.send_data(self.height // 256) # Gate_BITS_H + self.send_data(self.height % 256) # Gate_BITS_L + + self.send_command(0xE7) + self.send_data(0x1C) + + self.send_command(0xE3) + self.send_data(0x22) + + self.send_command(0xB4) + self.send_data(0xD0) + self.send_command(0xB5) + self.send_data(0x03) + + self.send_command(0xE9) + self.send_data(0x01) + + self.send_command(0x30) + self.send_data(0x08) + + self.send_command(0x04) + self.ReadBusyH() + return 0 + + def getbuffer(self, image): + # Create a pallette with the 4 colors supported by the panel + pal_image = Image.new("P", (1, 1)) + pal_image.putpalette((0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 0, 0) + (0, 0, 0) * 252) + + # Check if we need to rotate the image + imwidth, imheight = image.size + if (imwidth == self.width and imheight == self.height): + image_temp = image + elif (imwidth == self.height and imheight == self.width): + image_temp = image.rotate(90, expand=True) + else: + logger.warning( + "Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) + + # Convert the soruce image to the 4 colors, dithering if needed + image_4color = image_temp.convert("RGB").quantize(palette=pal_image) + buf_4color = bytearray(image_4color.tobytes('raw')) + + # into a single byte to transfer to the panel + buf = [0x00] * int(self.width * self.height / 4) + idx = 0 + for i in range(0, len(buf_4color), 4): + buf[idx] = (buf_4color[i] << 6) + (buf_4color[i + 1] << 4) + (buf_4color[i + 2] << 2) + buf_4color[i + 3] + idx += 1 + + return buf + + def display(self, image): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(image[i + j * Width]) + + self.TurnOnDisplay() + + def Clear(self, color=0x55): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(color) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.send_data(0X00) + self.ReadBusyH() + epdconfig.delay_ms(2000) + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in7/epd2in7.py b/pwnagotchi/ui/hw/libs/waveshare/v2in7/epd2in7.py new file mode 100644 index 00000000..b2eba6df --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in7/epd2in7.py @@ -0,0 +1,520 @@ +# ***************************************************************************** +# * | File : epd2in7.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 176 +EPD_HEIGHT = 264 + +GRAY1 = 0xff #white +GRAY2 = 0xC0 +GRAY3 = 0x80 #gray +GRAY4 = 0x00 #Blackest +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.GRAY1 = GRAY1 #white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 #gray + self.GRAY4 = GRAY4 #Blackest + + lut_vcom_dc = [0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_ww = [ + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, + 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_bw = [ + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, + 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_bb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_wb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + ###################full screen update LUT###################### + #0~3 gray + gray_lut_vcom = [ + 0x00, 0x00, + 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x60, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x13, 0x0A, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + #R21 + gray_lut_ww =[ + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x10, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0xA0, 0x13, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + #R22H r + gray_lut_bw =[ + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x99, 0x0C, 0x01, 0x03, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + #R23H w + gray_lut_wb =[ + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x99, 0x0B, 0x04, 0x04, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + #R24H b + gray_lut_bb =[ + 0x80, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x20, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x50, 0x13, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(200) + logging.debug("e-Paper busy release") + + def set_lut(self): + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcom_dc[count]) + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww[count]) + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw[count]) + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_bb[count]) + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_wb[count]) + + def gray_SetLut(self): + self.send_command(0x20) + for count in range(0, 44): #vcom + self.send_data(self.gray_lut_vcom[count]) + + self.send_command(0x21) #red not use + for count in range(0, 42): + self.send_data(self.gray_lut_ww[count]) + + self.send_command(0x22) #bw r + for count in range(0, 42): + self.send_data(self.gray_lut_bw[count]) + + self.send_command(0x23) #wb w + for count in range(0, 42): + self.send_data(self.gray_lut_wb[count]) + + self.send_command(0x24) #bb b + for count in range(0, 42): + self.send_data(self.gray_lut_bb[count]) + + self.send_command(0x25) #vcom + for count in range(0, 42): + self.send_data(self.gray_lut_ww[count]) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x03) # VDS_EN, VDG_EN + self.send_data(0x00) # VCOM_HV, VGHL_LV[1], VGHL_LV[0] + self.send_data(0x2b) # VDH + self.send_data(0x2b) # VDL + self.send_data(0x09) # VDHR + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x07) + self.send_data(0x07) + self.send_data(0x17) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x60) + self.send_data(0xA5) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x89) + self.send_data(0xA5) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x90) + self.send_data(0x00) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x93) + self.send_data(0x2A) + + # Power optimization + self.send_command(0xF8) + self.send_data(0xA0) + self.send_data(0xA5) + + # Power optimization + self.send_command(0xF8) + self.send_data(0xA1) + self.send_data(0x00) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x73) + self.send_data(0x41) + + self.send_command(0x16) # PARTIAL_DISPLAY_REFRESH + self.send_data(0x00) + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0xAF) # KW-BF KWR-AF BWROTP 0f + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3A) # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x82) # VCM_DC_SETTING_REGISTER + self.send_data(0x12) + self.set_lut() + return 0 + + def Init_4Gray(self): + if (epdconfig.module_init() != 0): + return -1 + self.reset() + + self.send_command(0x01) #POWER SETTING + self.send_data (0x03) + self.send_data (0x00) + self.send_data (0x2b) + self.send_data (0x2b) + + + self.send_command(0x06) #booster soft start + self.send_data (0x07) #A + self.send_data (0x07) #B + self.send_data (0x17) #C + + self.send_command(0xF8) #boost?? + self.send_data (0x60) + self.send_data (0xA5) + + self.send_command(0xF8) #boost?? + self.send_data (0x89) + self.send_data (0xA5) + + self.send_command(0xF8) #boost?? + self.send_data (0x90) + self.send_data (0x00) + + self.send_command(0xF8) #boost?? + self.send_data (0x93) + self.send_data (0x2A) + + self.send_command(0xF8) #boost?? + self.send_data (0xa0) + self.send_data (0xa5) + + self.send_command(0xF8) #boost?? + self.send_data (0xa1) + self.send_data (0x00) + + self.send_command(0xF8) #boost?? + self.send_data (0x73) + self.send_data (0x41) + + self.send_command(0x16) + self.send_data(0x00) + + self.send_command(0x04) + self.ReadBusy() + + self.send_command(0x00) #panel setting + self.send_data(0xbf) #KW-BF KWR-AF BWROTP 0f + + self.send_command(0x30) #PLL setting + self.send_data (0x90) #100hz + + self.send_command(0x61) #resolution setting + self.send_data (0x00) #176 + self.send_data (0xb0) + self.send_data (0x01) #264 + self.send_data (0x08) + + self.send_command(0x82) #vcom_DC setting + self.send_data (0x12) + + self.send_command(0X50) #VCOM AND DATA INTERVAL SETTING + self.send_data(0x97) + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i=0 + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if(pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i= i+1 + if(i%4 == 0): + buf[int((x + (y * self.width))/4)] = ((pixels[x-3, y]&0xc0) | (pixels[x-2, y]&0xc0)>>2 | (pixels[x-1, y]&0xc0)>>4 | (pixels[x, y]&0xc0)>>6) + + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = x + if(pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i= i+1 + if(i%4 == 0): + buf[int((newx + (newy * self.width))/4)] = ((pixels[x, y-3]&0xc0) | (pixels[x, y-2]&0xc0)>>2 | (pixels[x, y-1]&0xc0)>>4 | (pixels[x, y]&0xc0)>>6) + return buf + + def display(self, image): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + self.send_command(0x12) + self.ReadBusy() + + def display_4Gray(self, image): + self.send_command(0x10) + for i in range(0, 5808): #5808*4 46464 + temp3=0 + for j in range(0, 2): + temp1 = image[i*2+j] + for k in range(0, 2): + temp2 = temp1&0xC0 + if(temp2 == 0xC0): + temp3 |= 0x01#white + elif(temp2 == 0x00): + temp3 |= 0x00 #black + elif(temp2 == 0x80): + temp3 |= 0x01 #gray1 + else: #0x40 + temp3 |= 0x00 #gray2 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1&0xC0 + if(temp2 == 0xC0): #white + temp3 |= 0x01 + elif(temp2 == 0x00): #black + temp3 |= 0x00 + elif(temp2 == 0x80): + temp3 |= 0x01 #gray1 + else : #0x40 + temp3 |= 0x00 #gray2 + if(j!=1 or k!=1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.send_command(0x13) + for i in range(0, 5808): #5808*4 46464 + temp3=0 + for j in range(0, 2): + temp1 = image[i*2+j] + for k in range(0, 2): + temp2 = temp1&0xC0 + if(temp2 == 0xC0): + temp3 |= 0x01#white + elif(temp2 == 0x00): + temp3 |= 0x00 #black + elif(temp2 == 0x80): + temp3 |= 0x00 #gray1 + else: #0x40 + temp3 |= 0x01 #gray2 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1&0xC0 + if(temp2 == 0xC0): #white + temp3 |= 0x01 + elif(temp2 == 0x00): #black + temp3 |= 0x00 + elif(temp2 == 0x80): + temp3 |= 0x00 #gray1 + else: #0x40 + temp3 |= 0x01 #gray2 + if(j!=1 or k!=1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.gray_SetLut() + self.send_command(0x12) + epdconfig.delay_ms(200) + self.ReadBusy() + # pass + + def Clear(self, color): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) + self.send_command(0X07) + self.send_data(0xA5) + + epdconfig.module_exit() +### END OF FILE ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in7_v2/epd2in7_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v2in7_v2/epd2in7_V2.py new file mode 100644 index 00000000..f0809f06 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in7_v2/epd2in7_V2.py @@ -0,0 +1,520 @@ +# ***************************************************************************** +# * | File : epd2in7_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2022-09-17 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 176 +EPD_HEIGHT = 264 + +GRAY1 = 0xff # white +GRAY2 = 0xC0 +GRAY3 = 0x80 # gray +GRAY4 = 0x00 # Blackest + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.GRAY1 = GRAY1 # white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 # gray + self.GRAY4 = GRAY4 # Blackest + + LUT_DATA_4Gray = [ + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x8, 0x48, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x2, 0x48, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x20, 0x48, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xA, 0x19, 0x0, 0x3, 0x8, 0x0, 0x0, + 0x14, 0x1, 0x0, 0x14, 0x1, 0x0, 0x3, + 0xA, 0x3, 0x0, 0x8, 0x19, 0x0, 0x0, + 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, + 0x22, 0x17, 0x41, 0x0, 0x32, 0x1C, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): # 1: idle, 0: busy + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xF7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Fast(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Partial(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xFF) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_4GRAY(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def Lut(self): + self.send_command(0x32) + for i in range(159): + self.send_data(self.LUT_DATA_4Gray[i]) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + # EPD hardware init start + self.reset() + self.ReadBusy() + + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x45) # set Ram-Y address start/end position + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x07) # 0x0107-->(263+1)=264 + self.send_data(0x01) + + self.send_command(0x4F) # set RAM y address count to 0; + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + return 0 + + def init_Fast(self): + if (epdconfig.module_init() != 0): + return -1 + + # EPD hardware init start + self.reset() + self.ReadBusy() + + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x18) # Read built-in temperature sensor + self.send_data(0x80) + + self.send_command(0x22) # Load temperature value + self.send_data(0xB1) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x1A) # Write to temperature register + self.send_data(0x64) + self.send_data(0x00) + + self.send_command(0x45) # set Ram-Y address start/end position + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x07) # 0x0107-->(263+1)=264 + self.send_data(0x01) + + self.send_command(0x4F) # set RAM y address count to 0; + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.send_command(0x22) # Load temperature value + self.send_data(0x91) + self.send_command(0x20) + self.ReadBusy() + return 0 + + def Init_4Gray(self): + if (epdconfig.module_init() != 0): + return -1 + self.reset() + + self.send_command(0x12) # soft reset + self.ReadBusy(); + + self.send_command(0x74) # set analog block control + self.send_data(0x54) + self.send_command(0x7E) # set digital block control + self.send_data(0x3B) + + self.send_command(0x01) # Driver output control + self.send_data(0x07) + self.send_data(0x01) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.send_command(0x44) # set Ram-X address start/end position + self.send_data(0x00) + self.send_data(0x15) # 0x15-->(21+1)*8=176 + + self.send_command(0x45) # set Ram-Y address start/end position + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x07) # 0x0107-->(263+1)=264 + self.send_data(0x01) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x00) + + self.send_command(0x2C) # VCOM Voltage + self.send_data(self.LUT_DATA_4Gray[158]) # 0x1C + + self.send_command(0x3F) # EOPQ + self.send_data(self.LUT_DATA_4Gray[153]) + + self.send_command(0x03) # VGH + self.send_data(self.LUT_DATA_4Gray[154]) + + self.send_command(0x04) # + self.send_data(self.LUT_DATA_4Gray[155]) # VSH1 + self.send_data(self.LUT_DATA_4Gray[156]) # VSH2 + self.send_data(self.LUT_DATA_4Gray[157]) # VSL + + self.Lut() # LUT + + self.send_command(0x4E) # set RAM x address count to 0; + self.send_data(0x00) + self.send_command(0x4F) # set RAM y address count to 0X199; + self.send_data(0x00) + self.send_data(0x00) + self.ReadBusy() + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i = 0 + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((x + (y * self.width)) / 4)] = ( + (pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | ( + pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = self.height - x - 1 + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((newx + (newy * self.width)) / 4)] = ( + (pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | ( + pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + return buf + + def Clear(self): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + self.send_command(0x24) + for j in range(Height): + for i in range(Width): + self.send_data(0XFF) + self.TurnOnDisplay() + + def display(self, image): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + self.send_command(0x24) + for j in range(Height): + for i in range(Width): + self.send_data(image[i + j * Width]) + self.TurnOnDisplay() + + def display_Fast(self, image): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + self.send_command(0x24) + for j in range(Height): + for i in range(Width): + self.send_data(image[i + j * Width]) + self.TurnOnDisplay_Fast() + + def display_Base(self, image): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + self.send_command(0x24) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + self.send_data(image[i + j * Width]) + + self.send_command(0x26) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + self.send_data(image[i + j * Width]) + self.TurnOnDisplay() + + def display_Base_color(self, color): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + self.send_command(0x24) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + self.send_data(color) + + self.send_command(0x26) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + self.send_data(color) + # self.TurnOnDisplay() + + def display_Partial(self, Image, Xstart, Ystart, Xend, Yend): + if ((Xstart % 8 + Xend % 8 == 8 & Xstart % 8 > Xend % 8) | Xstart % 8 + Xend % 8 == 0 | ( + Xend - Xstart) % 8 == 0): + Xstart = Xstart // 8 + Xend = Xend // 8 + else: + Xstart = Xstart // 8 + if Xend % 8 == 0: + Xend = Xend // 8 + else: + Xend = Xend // 8 + 1 + + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + + Xend -= 1 + Yend -= 1 + + # Reset + self.reset() + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x44) # set RAM x address start/end, in page 35 + self.send_data(Xstart & 0xff) # RAM x address start at 00h; + self.send_data(Xend & 0xff) # RAM x address end at 0fh(15+1)*8->128 + self.send_command(0x45) # set RAM y address start/end, in page 35 + self.send_data(Ystart & 0xff) # RAM y address start at 0127h; + self.send_data((Ystart >> 8) & 0x01) # RAM y address start at 0127h; + self.send_data(Yend & 0xff) # RAM y address end at 00h; + self.send_data((Yend >> 8) & 0x01) + + self.send_command(0x4E) # set RAM x address count to 0; + self.send_data(Xstart & 0xff) + self.send_command(0x4F) # set RAM y address count to 0X127; + self.send_data(Ystart & 0xff) + self.send_data((Ystart >> 8) & 0x01) + + self.send_command(0x24) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + if ((j > Ystart - 1) & (j < (Yend + 1)) & (i > Xstart - 1) & (i < (Xend + 1))): + self.send_data(Image[i + j * Width]) + self.TurnOnDisplay_Partial() + + def display_4Gray(self, image): + self.send_command(0x24) + for i in range(0, 48000): # 5808*4 46464 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x01 + else: # 0x40 + temp3 |= 0x00 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x01 + else: # 0x40 + temp3 |= 0x00 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.send_command(0x26) + for i in range(0, 48000): # 5808*4 46464 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x00 + else: # 0x40 + temp3 |= 0x01 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x00 + else: # 0x40 + temp3 |= 0x01 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.TurnOnDisplay_4GRAY() + + def sleep(self): + self.send_command(0X10) + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in7b/epd2in7b.py b/pwnagotchi/ui/hw/libs/waveshare/v2in7b/epd2in7b.py new file mode 100644 index 00000000..0486c07a --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in7b/epd2in7b.py @@ -0,0 +1,272 @@ +# ***************************************************************************** +# * | File : epd2in7b.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 176 +EPD_HEIGHT = 264 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + lut_vcom_dc = [ + 0x00, 0x00, + 0x00, 0x1A, 0x1A, 0x00, 0x00, 0x01, + 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x00, 0x0E, 0x01, 0x0E, 0x01, 0x10, + 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, + 0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A, + 0x00, 0x23, 0x00, 0x00, 0x00, 0x01 + ] + + lut_ww = [ + 0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01, + 0x40, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10, + 0x80, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, + 0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A, + 0x00, 0x23, 0x00, 0x00, 0x00, 0x01 + ] + + # R22H r + lut_bw = [ + 0xA0, 0x1A, 0x1A, 0x00, 0x00, 0x01, + 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10, + 0x90, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0xB0, 0x04, 0x10, 0x00, 0x00, 0x05, + 0xB0, 0x03, 0x0E, 0x00, 0x00, 0x0A, + 0xC0, 0x23, 0x00, 0x00, 0x00, 0x01 + ] + + # R23H w + lut_bb = [ + 0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01, + 0x40, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10, + 0x80, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, + 0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A, + 0x00, 0x23, 0x00, 0x00, 0x00, 0x01 + ] + # R24H b + lut_wb = [ + 0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01, + 0x20, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10, + 0x10, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, + 0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A, + 0x00, 0x23, 0x00, 0x00, 0x00, 0x01 + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def set_lut(self): + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcom_dc[count]) + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww[count]) + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw[count]) + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_bb[count]) + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_wb[count]) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0xaf) # KW-BF KWR-AF BWROTP 0f + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3a) # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x03) # VDS_EN, VDG_EN + self.send_data(0x00) # VCOM_HV, VGHL_LV[1], VGHL_LV[0] + self.send_data(0x2b) # VDH + self.send_data(0x2b) # VDL + self.send_data(0x09) # VDHR + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x07) + self.send_data(0x07) + self.send_data(0x17) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x60) + self.send_data(0xA5) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x89) + self.send_data(0xA5) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x90) + self.send_data(0x00) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x93) + self.send_data(0x2A) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x73) + self.send_data(0x41) + + self.send_command(0x82) # VCM_DC_SETTING_REGISTER + self.send_data(0x12) + self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0x87) # define by OTP + + self.set_lut() + + self.send_command(0x16) # PARTIAL_DISPLAY_REFRESH + self.send_data(0x00) + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~imageblack[i]) + self.send_command(0x11) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~imagered[i]) + self.send_command(0x11) + + self.send_command(0x12) + self.ReadBusy() + + def Clear(self, color=0x00): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(color) + self.send_command(0x11) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(color) + self.send_command(0x11) + + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) + self.send_command(0X07) + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in7b_v2/epd2in7b_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v2in7b_v2/epd2in7b_V2.py new file mode 100644 index 00000000..e22d7ba8 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in7b_v2/epd2in7b_V2.py @@ -0,0 +1,194 @@ +# ***************************************************************************** +# * | File : epd2in7b_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-10 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 176 +EPD_HEIGHT = 264 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + # Send Command + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + # Send Data + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + # Read Busy + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(10) + logger.debug("e-Paper busy release") + + # Setting the display window + def SetWindows(self, Xstart, Ystart, Xend, Yend): + self.send_command(0x44) + self.send_data((Xstart >> 3) & 0xff) + self.send_data((Xend >> 3) & 0xff) + + self.send_command(0x45) + self.send_data(Ystart & 0xff) + self.send_data((Ystart >> 8) & 0xff) + self.send_data(Yend & 0xff) + self.send_data((Yend >> 8) & 0xff) + + # Set Cursor + def SetCursor(self, Xstart, Ystart): + self.send_command(0x4E) + self.send_data(Xstart & 0xff) + self.send_command(0x4F) + self.send_data(Ystart & 0xff) + self.send_data((Ystart >> 8) & 0xff) + + # Initialize the e-Paper register + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.ReadBusy() + self.send_command(0x12) + self.ReadBusy() + + self.send_command(0x00) + self.send_data(0x27) + self.send_data(0x01) + self.send_data(0x00) + + self.send_command(0x11) + self.send_data(0x03) + + self.SetWindows(0, 0, self.width - 1, self.height - 1) + self.SetCursor(0, 0) + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + # Sends the image buffer in RAM to e-Paper and displays + def display(self, imageblack, imagered): + Width = self.width / 8 + Height = self.height + + buf = [0x00] * int(Width * Height) + for i in range(0, int(Width * Height)): + buf[i] = ~imagered[i] + + self.send_command(0x24) + self.send_data2(imageblack) + + self.send_command(0x26) + self.send_data2(buf) + + self.TurnOnDisplay() + + # Clear the screen + def Clear(self): + self.send_command(0x24) + self.send_data2([0xff] * int(self.width * self.height / 8)) + + self.send_command(0x26) + self.send_data2([0x00] * int(self.width * self.height / 8)) + + self.TurnOnDisplay() + + # Turn on display + def TurnOnDisplay(self): + self.send_command(0x20) + self.ReadBusy() + + # Enter sleep mode + def sleep(self): + self.send_command(0x10) + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in9/epd2in9.py b/pwnagotchi/ui/hw/libs/waveshare/v2in9/epd2in9.py new file mode 100644 index 00000000..5dd4d759 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in9/epd2in9.py @@ -0,0 +1,201 @@ +# ***************************************************************************** +# * | File : epd2in9.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 128 +EPD_HEIGHT = 296 + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + lut_full_update = [ + 0x50, 0xAA, 0x55, 0xAA, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x1F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + lut_partial_update = [ + 0x10, 0x18, 0x18, 0x08, 0x18, 0x18, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(200) + + def TurnOnDisplay(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0xC4) + self.send_command(0x20) # MASTER_ACTIVATION + self.send_command(0xFF) # TERMINATE_FRAME_READ_WRITE + + logging.debug("e-Paper busy") + self.ReadBusy() + logging.debug("e-Paper busy release") + + def SetWindow(self, x_start, y_start, x_end, y_end): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data((x_start >> 3) & 0xFF) + self.send_data((x_end >> 3) & 0xFF) + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(y_start & 0xFF) + self.send_data((y_start >> 8) & 0xFF) + self.send_data(y_end & 0xFF) + self.send_data((y_end >> 8) & 0xFF) + + def SetCursor(self, x, y): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data((x >> 3) & 0xFF) + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + self.ReadBusy() + + def init(self, lut): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # DRIVER_OUTPUT_CONTROL + self.send_data((EPD_HEIGHT - 1) & 0xFF) + self.send_data(((EPD_HEIGHT - 1) >> 8) & 0xFF) + self.send_data(0x00) # GD = 0 SM = 0 TB = 0 + + self.send_command(0x0C) # BOOSTER_SOFT_START_CONTROL + self.send_data(0xD7) + self.send_data(0xD6) + self.send_data(0x9D) + + self.send_command(0x2C) # WRITE_VCOM_REGISTER + self.send_data(0xA8) # VCOM 7C + + self.send_command(0x3A) # SET_DUMMY_LINE_PERIOD + self.send_data(0x1A) # 4 dummy lines per gate + + self.send_command(0x3B) # SET_GATE_TIME + self.send_data(0x08) # 2us per line + + self.send_command(0x11) # DATA_ENTRY_MODE_SETTING + self.send_data(0x03) # X increment Y increment + + self.send_command(0x32) # WRITE_LUT_REGISTER + for i in range(0, len(lut)): + self.send_data(lut[i]) + # EPD hardware init end + return 0 + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (image == None): + return + self.SetWindow(0, 0, self.width - 1, self.height - 1) + for j in range(0, self.height): + self.SetCursor(0, j) + self.send_command(0x24) # WRITE_RAM + for i in range(0, int(self.width / 8)): + self.send_data(image[i + j * int(self.width / 8)]) + self.TurnOnDisplay() + + def Clear(self, color): + self.SetWindow(0, 0, self.width - 1, self.height - 1) + for j in range(0, self.height): + self.SetCursor(0, j) + self.send_command(0x24) # WRITE_RAM + for i in range(0, int(self.width / 8)): + self.send_data(color) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP_MODE + self.send_data(0x01) + + epdconfig.module_exit() +### END OF FILE ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in9_v2/epd2in9V2.py b/pwnagotchi/ui/hw/libs/waveshare/v2in9_v2/epd2in9V2.py new file mode 100644 index 00000000..ce8bd80f --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in9_v2/epd2in9V2.py @@ -0,0 +1,530 @@ +# ***************************************************************************** +# * | File : epd2in9_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-9 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 128 +EPD_HEIGHT = 296 +GRAY1 = 0xff # white +GRAY2 = 0xC0 +GRAY3 = 0x80 # gray +GRAY4 = 0x00 # Blackest + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.GRAY1 = GRAY1 # white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 # gray + self.GRAY4 = GRAY4 # Blackest + + WF_PARTIAL_2IN9 = [ + 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0A, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, + 0x22, 0x17, 0x41, 0xB0, 0x32, 0x36, + ] + + WS_20_30 = [ + 0x80, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, + 0x10, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, + 0x80, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, + 0x10, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x14, 0x8, 0x0, 0x0, 0x0, 0x0, 0x2, + 0xA, 0xA, 0x0, 0xA, 0xA, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x14, 0x8, 0x0, 0x1, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x0, 0x0, 0x0, + 0x22, 0x17, 0x41, 0x0, 0x32, 0x36 + ] + + Gray4 = [ + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x60, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x60, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x05, 0x14, 0x00, 0x00, + 0x1E, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x05, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x22, 0x22, 0x22, 0x23, 0x32, 0x00, 0x00, 0x00, + 0x22, 0x17, 0x41, 0xAE, 0x32, 0x28, + ] + + WF_FULL = [ + 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x42, 0x22, 0x22, 0x23, 0x32, 0x00, 0x00, 0x00, + 0x22, 0x17, 0x41, 0xAE, 0x32, 0x38] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(50) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(50) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(10) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0xc7) + self.send_command(0x20) # MASTER_ACTIVATION + self.ReadBusy() + + def TurnOnDisplay_Partial(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0x0F) + self.send_command(0x20) # MASTER_ACTIVATION + self.ReadBusy() + + def lut(self, lut): + self.send_command(0x32) + for i in range(0, 153): + self.send_data(lut[i]) + self.ReadBusy() + + def SetLut(self, lut): + self.lut(lut) + self.send_command(0x3f) + self.send_data(lut[153]) + self.send_command(0x03); # gate voltage + self.send_data(lut[154]) + self.send_command(0x04); # source voltage + self.send_data(lut[155]) # VSH + self.send_data(lut[156]) # VSH2 + self.send_data(lut[157]) # VSL + self.send_command(0x2c); # VCOM + self.send_data(lut[158]) + + def SetWindow(self, x_start, y_start, x_end, y_end): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data((x_start >> 3) & 0xFF) + self.send_data((x_end >> 3) & 0xFF) + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(y_start & 0xFF) + self.send_data((y_start >> 8) & 0xFF) + self.send_data(y_end & 0xFF) + self.send_data((y_end >> 8) & 0xFF) + + def SetCursor(self, x, y): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data(x & 0xFF) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.ReadBusy() + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x01) # Driver output control + self.send_data(0x27) + self.send_data(0x01) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.SetWindow(0, 0, self.width - 1, self.height - 1) + + self.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x80) + + self.SetCursor(0, 0) + self.ReadBusy() + + self.SetLut(self.WS_20_30) + # EPD hardware init end + return 0 + + def init_Fast(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.ReadBusy() + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x01) # Driver output control + self.send_data(0x27) + self.send_data(0x01) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.SetWindow(0, 0, self.width - 1, self.height - 1) + + self.send_command(0x3C) + self.send_data(0x05) + + self.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x80) + + self.SetCursor(0, 0) + self.ReadBusy() + + self.SetLut(self.WF_FULL) + # EPD hardware init end + return 0 + + def Init_4Gray(self): + if (epdconfig.module_init() != 0): + return -1 + self.reset() + epdconfig.delay_ms(100) + + self.ReadBusy() + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x01) # Driver output control + self.send_data(0x27) + self.send_data(0x01) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.SetWindow(8, 0, self.width, self.height - 1) + + self.send_command(0x3C) + self.send_data(0x04) + + self.SetCursor(1, 0) + self.ReadBusy() + + self.SetLut(self.Gray4) + # EPD hardware init end + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i = 0 + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((x + (y * self.width)) / 4)] = ( + (pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | ( + pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = self.height - x - 1 + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((newx + (newy * self.width)) / 4)] = ( + (pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | ( + pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + return buf + + def display(self, image): + if (image == None): + return + self.send_command(0x24) # WRITE_RAM + self.send_data2(image) + self.TurnOnDisplay() + + def display_Base(self, image): + if (image == None): + return + + self.send_command(0x24) # WRITE_RAM + self.send_data2(image) + + self.send_command(0x26) # WRITE_RAM + self.send_data2(image) + + self.TurnOnDisplay() + + def display_4Gray(self, image): + self.send_command(0x24) + for i in range(0, 4736): + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x01 + else: # 0x40 + temp3 |= 0x00 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x01 + else: # 0x40 + temp3 |= 0x00 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.send_command(0x26) + for i in range(0, 4736): + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x00 + else: # 0x40 + temp3 |= 0x01 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x00 + else: # 0x40 + temp3 |= 0x01 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.TurnOnDisplay() + + def display_Partial(self, image): + if (image == None): + return + + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(2) + + self.SetLut(self.WF_PARTIAL_2IN9) + self.send_command(0x37) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x40) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x22) + self.send_data(0xC0) + self.send_command(0x20) + self.ReadBusy() + + self.SetWindow(0, 0, self.width - 1, self.height - 1) + self.SetCursor(0, 0) + + self.send_command(0x24) # WRITE_RAM + self.send_data2(image) + self.TurnOnDisplay_Partial() + + def Clear(self, color=0xFF): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x24) # WRITE_RAM + self.send_data2([color] * int(self.height * linewidth)) + self.TurnOnDisplay() + self.send_command(0x26) # WRITE_RAM + self.send_data2([color] * int(self.height * linewidth)) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP_MODE + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in9b_v3/epd2in9b_V3.py b/pwnagotchi/ui/hw/libs/waveshare/v2in9b_v3/epd2in9b_V3.py new file mode 100644 index 00000000..e3a4124e --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in9b_v3/epd2in9b_V3.py @@ -0,0 +1,164 @@ +# ***************************************************************************** +# * | File : epd2in9b_V3.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.2 +# * | Date : 2022-08-10 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 128 +EPD_HEIGHT = 296 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0X71) + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0X71) + epdconfig.delay_ms(200) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x04) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0x00) # panel setting + self.send_data(0x0f) # LUT from OTP,128x296 + self.send_data(0x89) # Temperature sensor, boost and other related timing settings + + self.send_command(0x61) # resolution setting + self.send_data(0x80) + self.send_data(0x01) + self.send_data(0x28) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x77) # WBmode:VBDF 17|D7 VBDW 97 VBDB 57 + # WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7 + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, blackimage, ryimage): # ryimage: red or yellow image + if (blackimage != None): + self.send_command(0X10) + self.send_data2(blackimage) + if (ryimage != None): + self.send_command(0X13) + self.send_data2(ryimage) + + self.send_command(0x12) + epdconfig.delay_ms(200) + self.ReadBusy() + + def Clear(self): + self.send_command(0X10) + self.send_data2([0xff] * int(self.width * self.height / 8)) + self.send_command(0X13) + self.send_data2([0xff] * int(self.width * self.height / 8)) + + self.send_command(0x12) + epdconfig.delay_ms(200) + self.ReadBusy() + + def sleep(self): + self.send_command(0X02) # power off + self.ReadBusy() + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in9b_v4/epd2in9b_V4.py b/pwnagotchi/ui/hw/libs/waveshare/v2in9b_v4/epd2in9b_V4.py new file mode 100644 index 00000000..d256cbe5 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in9b_v4/epd2in9b_V4.py @@ -0,0 +1,387 @@ +# ***************************************************************************** +# * | File : epd2in9b_V4.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2023-12-18 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 128 +EPD_HEIGHT = 296 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0X71) + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(200) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xF7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Base(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xF4) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Fast(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Partial(self): + self.send_command(0x22) # Display Update Control + self.send_data(0x1C) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + # EPD hardware init start + self.reset() + + self.ReadBusy() + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x01) # Driver output control + self.send_data((self.height - 1) % 256) + self.send_data((self.height - 1) // 256) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.send_command(0x44) # set Ram-X address start/end position + self.send_data(0x00) + self.send_data(self.width // 8 - 1) + + self.send_command(0x45) # set Ram-Y address start/end position + self.send_data(0x00) + self.send_data(0x00) + self.send_data((self.height - 1) % 256) + self.send_data((self.height - 1) // 256) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x05) + + self.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x80) + + self.send_command(0x18) # Read built-in temperature sensor + self.send_data(0x80) + + self.send_command(0x4E) # set RAM x address count to 0 + self.send_data(0x00) + self.send_command(0x4F) # set RAM y address count to 0X199 + self.send_data(0x00) + self.send_data(0x00) + self.ReadBusy() + + return 0 + + def init_Fast(self): + if (epdconfig.module_init() != 0): + return -1 + + # EPD hardware init start + self.reset() + + self.ReadBusy() + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x18) # Read built-in temperature sensor + self.send_data(0x80) + + self.send_command(0x22) # Load temperature value + self.send_data(0xB1) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x1A) # Write to temperature register + self.send_data(0x5a) # 90 + self.send_data(0x00) + + self.send_command(0x22) # Load temperature value + self.send_data(0x91) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x01) # Driver output control + self.send_data((self.height - 1) % 256) + self.send_data((self.height - 1) / 256) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.send_command(0x44) # set Ram-X address start/end position + self.send_data(0x00) + self.send_data(self.width // 8 - 1) + + self.send_command(0x45) # set Ram-Y address start/end position + self.send_data(0x00) + self.send_data(0x00) + self.send_data((self.height - 1) % 256) + self.send_data((self.height - 1) // 256) + + self.send_command(0x4E) # set RAM x address count to 0 + self.send_data(0x00) + self.send_command(0x4F) # set RAM y address count to 0X199 + self.send_data(0x00) + self.send_data(0x00) + self.ReadBusy() + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, blackimage, ryimage): # ryimage: red or yellow image + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + if (blackimage != None): + self.send_command(0x24) + self.send_data2(blackimage) + if (ryimage != None): + for j in range(Height): + for i in range(Width): + ryimage[i + j * Width] = ~ryimage[i + j * Width] + self.send_command(0x26) + self.send_data2(ryimage) + + self.TurnOnDisplay() + + def display_Fast(self, blackimage, ryimage): # ryimage: red or yellow image + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + if (blackimage != None): + self.send_command(0x24) + self.send_data2(blackimage) + if (ryimage != None): + for j in range(Height): + for i in range(Width): + ryimage[i + j * Width] = ~ryimage[i + j * Width] + self.send_command(0x26) + self.send_data2(ryimage) + + self.TurnOnDisplay_Fast() + + def Clear(self): + self.send_command(0x24) + self.send_data2([0xff] * int(self.width * self.height // 8)) + self.send_command(0x26) + self.send_data2([0x00] * int(self.width * self.height // 8)) + + self.TurnOnDisplay() + + def Clear_Fast(self): + self.send_command(0x24) + self.send_data2([0xff] * int(self.width * self.height // 8)) + self.send_command(0x26) + self.send_data2([0x00] * int(self.width * self.height // 8)) + + self.TurnOnDisplay_Fast() + + def display_Base(self, blackimage, ryimage): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + if (blackimage != None): + self.send_command(0x24) + self.send_data2(blackimage) + if (ryimage != None): + for j in range(Height): + for i in range(Width): + ryimage[i + j * Width] = ~ryimage[i + j * Width] + self.send_command(0x26) + self.send_data2(ryimage) + + self.TurnOnDisplay_Base() + + if (blackimage != None): + for j in range(Height): + for i in range(Width): + blackimage[i + j * Width] = ~blackimage[i + j * Width] + self.send_command(0x26) + self.send_data2(blackimage) + else: + self.send_command(0x26) + self.send_data2(blackimage) + + def display_Base_color(self, color): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + self.send_command(0x24) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + self.send_data(color) + + self.send_command(0x26) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + self.send_data(~color) + + self.TurnOnDisplay_Base() + self.send_command(0x26) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + self.send_data(color) + + def display_Partial(self, Image, Xstart, Ystart, Xend, Yend): + if ((Xstart % 8 + Xend % 8 == 8 & Xstart % 8 > Xend % 8) | Xstart % 8 + Xend % 8 == 0 | ( + Xend - Xstart) % 8 == 0): + Xstart = Xstart // 8 + Xend = Xend // 8 + else: + Xstart = Xstart // 8 + if Xend % 8 == 0: + Xend = Xend // 8 + else: + Xend = Xend // 8 + 1 + + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + + Xend -= 1 + Yend -= 1 + + self.send_command(0x44) # set RAM x address start/end, in page 35 + self.send_data(Xstart & 0xff) # RAM x address start at 00h + self.send_data(Xend & 0xff) # RAM x address end at 0fh(15+1)*8->128 + self.send_command(0x45) # set RAM y address start/end, in page 35 + self.send_data(Ystart & 0xff) # RAM y address start at 0127h + self.send_data((Ystart >> 8) & 0x01) # RAM y address start at 0127h + self.send_data(Yend & 0xff) # RAM y address end at 00h + self.send_data((Yend >> 8) & 0x01) + + self.send_command(0x4E) # set RAM x address count to 0 + self.send_data(Xstart & 0xff) + self.send_command(0x4F) # set RAM y address count to 0X127 + self.send_data(Ystart & 0xff) + self.send_data((Ystart >> 8) & 0x01) + + self.send_command(0x24) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + if ((j > Ystart - 1) & (j < (Yend + 1)) & (i > Xstart - 1) & (i < (Xend + 1))): + self.send_data(Image[i + j * Width]) + self.TurnOnDisplay_Partial() + + def sleep(self): + self.send_command(0x10) # deep sleep + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in9bc/epd2in9bc.py b/pwnagotchi/ui/hw/libs/waveshare/v2in9bc/epd2in9bc.py new file mode 100644 index 00000000..da2c9be9 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in9bc/epd2in9bc.py @@ -0,0 +1,158 @@ +# ***************************************************************************** +# * | File : epd2in9bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 128 +EPD_HEIGHT = 296 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(200) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x06) # boost + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + self.send_command(0x04) # POWER_ON + self.ReadBusy() + self.send_command(0X00) # PANEL_SETTING + self.send_data(0x8F) + self.send_command(0X50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0x77) + self.send_command(0x61) # TCON_RESOLUTION + self.send_data(0x80) + self.send_data(0x01) + self.send_data(0x28) + # self.send_command(VCM_DC_SETTING_REGISTER) + # self.send_data (0x0A) + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, blackimage, ryimage): # ryimage: red or yellow image + if (blackimage != None): + self.send_command(0X10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(blackimage[i]) + if (ryimage != None): + self.send_command(0X13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(ryimage[i]) + + self.send_command(0x12) + self.ReadBusy() + + def Clear(self): + self.send_command(0X10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xff) + self.send_command(0X13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xff) + + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0X02) # power off + self.ReadBusy() + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in9d/epd2in9d.py b/pwnagotchi/ui/hw/libs/waveshare/v2in9d/epd2in9d.py new file mode 100644 index 00000000..239a528c --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in9d/epd2in9d.py @@ -0,0 +1,303 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- + +# ***************************************************************************** +# * | File : epd2in9d.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V2.1 +# * | Date : 2022-08-10 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +from distutils.command.build_scripts import build_scripts +import logging +from .. import epdconfig +from PIL import Image +import RPi.GPIO as GPIO + +# Display resolution +EPD_WIDTH = 128 +EPD_HEIGHT = 296 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + lut_vcom1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ] + + lut_ww1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bw1 = [ + 0x80, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_wb1 = [ + 0x40, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bb1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0x71) + epdconfig.delay_ms(10) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x12) + epdconfig.delay_ms(10) + self.ReadBusy() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x04) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0x00) # panel setting + self.send_data(0x1f) # LUT from OTP,KW-BF KWR-AF BWROTP 0f BWOTP 1f + + self.send_command(0x61) # resolution setting + self.send_data(0x80) + self.send_data(0x01) + self.send_data(0x28) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x97) # WBmode:VBDF 17|D7 VBDW 97 VBDB 57 WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7 + + return 0 + + def SetPartReg(self): + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + self.send_data(0x03) + + self.send_command(0x06) # boost soft start + self.send_data(0x17) # A + self.send_data(0x17) # B + self.send_data(0x17) # C + + self.send_command(0x04) + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # LUT from OTP,128x296 + + self.send_command(0x30) # PLL setting + self.send_data(0x3a) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(self.width) + self.send_data((self.height >> 8) & 0xff) + self.send_data(self.height & 0xff) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x12) + + self.send_command(0X50) + self.send_data(0x97) + + self.send_command(0x20) # vcom + self.send_data2(self.lut_vcom1) + self.send_command(0x21) # ww -- + self.send_data2(self.lut_ww1) + self.send_command(0x22) # bw r + self.send_data2(self.lut_bw1) + self.send_command(0x23) # wb w + self.send_data2(self.lut_wb1) + self.send_command(0x24) # bb b + self.send_data2(self.lut_bb1) + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + self.send_command(0x10) + self.send_data2([0x00] * int(self.width * self.height / 8)) + epdconfig.delay_ms(10) + + self.send_command(0x13) + self.send_data2(image) + epdconfig.delay_ms(10) + + self.TurnOnDisplay() + + def DisplayPartial(self, image): + self.SetPartReg() + self.send_command(0x91) + self.send_command(0x90) + self.send_data(0) + self.send_data(self.width - 1) + + self.send_data(0) + self.send_data(0) + self.send_data(int(self.height / 256)) + self.send_data(self.height % 256 - 1) + self.send_data(0x28) + + buf = [0x00] * int(self.width * self.height / 8) + for i in range(0, int(self.width * self.height / 8)): + buf[i] = ~image[i] + self.send_command(0x10) + self.send_data2(image) + epdconfig.delay_ms(10) + + self.send_command(0x13) + self.send_data2(buf) + epdconfig.delay_ms(10) + + self.TurnOnDisplay() + + def Clear(self): + self.send_command(0x10) + self.send_data2([0x00] * int(self.width * self.height / 8)) + epdconfig.delay_ms(10) + + self.send_command(0x13) + self.send_data2([0xFF] * int(self.width * self.height / 8)) + epdconfig.delay_ms(10) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) # power off + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v3in0g/epd3in0g.py b/pwnagotchi/ui/hw/libs/waveshare/v3in0g/epd3in0g.py new file mode 100644 index 00000000..ec076d01 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v3in0g/epd3in0g.py @@ -0,0 +1,222 @@ +# ***************************************************************************** +# * | File : epd3in0g.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1 +# * | Date : 2022-07-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# ******************************************************************************/ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +import PIL +from PIL import Image +import io + +# Display resolution +EPD_WIDTH = 168 +EPD_HEIGHT = 400 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.BLACK = 0x000000 # 00 BGR + self.WHITE = 0xffffff # 01 + self.YELLOW = 0x00ffff # 10 + self.RED = 0x0000ff # 11 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) # module reset + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusyH(self): + logger.debug("e-Paper busy H") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(5) + logger.debug("e-Paper busy H release") + + def ReadBusyL(self): + logger.debug("e-Paper busy L") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy L release") + + def TurnOnDisplay(self): + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0x01) + self.ReadBusyH() + + self.send_command(0x02) # POWER_OFF + self.send_data(0X00) + self.ReadBusyH() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + + self.reset() + + self.send_command(0x66) + self.send_data(0x49) + self.send_data(0x55) + self.send_data(0x13) + self.send_data(0x5D) + self.send_data(0x05) + self.send_data(0x10) + + self.send_command(0xB0) + self.send_data(0x00) # 1 boost + + self.send_command(0x01) + self.send_data(0x0F) + self.send_data(0x00) + + self.send_command(0x00) + self.send_data(0x4F) + self.send_data(0x6B) + + self.send_command(0x06) + self.send_data(0xD7) + self.send_data(0xDE) + self.send_data(0x12) + + self.send_command(0x61) + self.send_data(0x00) + self.send_data(0xA8) + self.send_data(0x01) + self.send_data(0x90) + + self.send_command(0x50) + self.send_data(0x37) + + self.send_command(0x60) + self.send_data(0x0C) + self.send_data(0x05) + + self.send_command(0xE3) + self.send_data(0xFF) + + self.send_command(0x84) + self.send_data(0x00) + return 0 + + def getbuffer(self, image): + # Create a pallette with the 4 colors supported by the panel + pal_image = Image.new("P", (1, 1)) + pal_image.putpalette((0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 0, 0) + (0, 0, 0) * 252) + + # Check if we need to rotate the image + imwidth, imheight = image.size + if (imwidth == self.width and imheight == self.height): + image_temp = image + elif (imwidth == self.height and imheight == self.width): + image_temp = image.rotate(90, expand=True) + else: + logger.warning( + "Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) + + # Convert the soruce image to the 4 colors, dithering if needed + image_4color = image_temp.convert("RGB").quantize(palette=pal_image) + buf_4color = bytearray(image_4color.tobytes('raw')) + + # into a single byte to transfer to the panel + buf = [0x00] * int(self.width * self.height / 4) + idx = 0 + for i in range(0, len(buf_4color), 4): + buf[idx] = (buf_4color[i] << 6) + (buf_4color[i + 1] << 4) + (buf_4color[i + 2] << 2) + buf_4color[i + 3] + idx += 1 + + return buf + + def display(self, image): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(image[i + j * Width]) + + self.TurnOnDisplay() + + def Clear(self, color=0x55): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(color) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.send_data(0x00) + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v3in52/epd3in52.py b/pwnagotchi/ui/hw/libs/waveshare/v3in52/epd3in52.py new file mode 100644 index 00000000..eb7d720a --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v3in52/epd3in52.py @@ -0,0 +1,460 @@ +# ***************************************************************************** +# * | File : epd3in52.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2022-07-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from multiprocessing.reduction import recv_handle +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 240 +EPD_HEIGHT = 360 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.Flag = 0 + self.WHITE = 0xFF + self.BLACK = 0x00 + self.Source_Line = 0xAA + self.Gate_Line = 0x55 + self.UP_BLACK_DOWN_WHITE = 0xF0 + self.LEFT_BLACK_RIGHT_WHITE = 0x0F + self.Frame = 0x01 + self.Crosstalk = 0x02 + self.Chessboard = 0x03 + self.Image = 0x04 + + # GC 0.9S + lut_R20_GC = [ + 0x01, 0x0f, 0x0f, 0x0f, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_R21_GC = [ + 0x01, 0x4f, 0x8f, 0x0f, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_R22_GC = [ + 0x01, 0x0f, 0x8f, 0x0f, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_R23_GC = [ + 0x01, 0x4f, 0x8f, 0x4f, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_R24_GC = [ + 0x01, 0x0f, 0x8f, 0x4f, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + # DU 0.3s + lut_R20_DU = [ + 0x01, 0x0f, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_R21_DU = [ + 0x01, 0x0f, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_R22_DU = [ + 0x01, 0x8f, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_R23_DU = [ + 0x01, 0x4f, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_R24_DU = [ + 0x01, 0x0f, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + lut_vcom = [ + 0x01, 0x19, 0x19, 0x19, 0x19, 0x01, 0x01, + 0x01, 0x19, 0x19, 0x19, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_ww = [ + 0x01, 0x59, 0x99, 0x59, 0x99, 0x01, 0x01, + 0x01, 0x59, 0x99, 0x19, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_bw = [ + 0x01, 0x59, 0x99, 0x59, 0x99, 0x01, 0x01, + 0x01, 0x59, 0x99, 0x19, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_wb = [ + 0x01, 0x19, 0x99, 0x59, 0x99, 0x01, 0x01, + 0x01, 0x59, 0x99, 0x59, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_bb = [ + 0x01, 0x19, 0x99, 0x59, 0x99, 0x01, 0x01, + 0x01, 0x59, 0x99, 0x59, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy release") + + def lut(self): + self.send_command(0x20) # vcom + self.send_data2(self.lut_vcom[:42]) + + self.send_command(0x21) # ww -- + self.send_data2(self.lut_ww[:42]) + + self.send_command(0x22) # bw r + self.send_data2(self.lut_bw[:42]) + + self.send_command(0x23) # wb w + self.send_data2(self.lut_bb[:42]) + + self.send_command(0x24) # bb b + self.send_data2(self.lut_wb[:42]) + + def refresh(self): + self.send_command(0x17) + self.send_data(0xA5) + self.ReadBusy() + epdconfig.delay_ms(200) + + # LUT download + def lut_GC(self): + self.send_command(0x20); # vcom + self.send_data2(self.lut_R20_GC[:56]) + + self.send_command(0x21); # red not use + self.send_data2(self.lut_R21_GC[:42]) + + self.send_command(0x24); # bb b + self.send_data2(self.lut_R24_GC[:42]) + + if (self.Flag == 0): + self.send_command(0x22); # bw r + self.send_data2(self.lut_R22_GC[:56]) + + self.send_command(0x23); # wb w + self.send_data2(self.lut_R23_GC[:42]) + self.Flag = 1 + + else: + self.send_command(0x22); # bw r + self.send_data2(self.lut_R23_GC[:56]) + + self.send_command(0x23); # wb w + self.send_data2(self.lut_R22_GC[:42]) + self.Flag = 0 + + # LUT download + def lut_DU(self): + self.send_command(0x20); # vcom + self.send_data2(self.lut_R20_DU[:56]) + + self.send_command(0x21); # red not use + self.send_data2(self.lut_R21_DU[:42]) + + self.send_command(0x24); # bb b + self.send_data2(self.lut_R24_DU[:42]) + + if (self.Flag == 0): + self.send_command(0x22); # bw r + self.send_data2(self.lut_R22_DU[:56]) + + self.send_command(0x23); # wb w + self.send_data2(self.lut_R23_DU[:42]) + + self.Flag = 1 + + else: + self.send_command(0x22); # bw r + self.send_data2(self.lut_R23_DU[:56]) + + self.send_command(0x23); # wb w + self.send_data2(self.lut_R22_DU[:42]) + + self.Flag = 0 + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.Flag = 0 + self.reset() + + self.send_command(0x00) # panel setting PSR + self.send_data(0xFF) # RES1 RES0 REG KW/R UD SHL SHD_N RST_N + self.send_data(0x01) # x x x VCMZ TS_AUTO TIGE NORG VC_LUTZ + + self.send_command(0x01) # POWER SETTING PWR + self.send_data(0x03) # x x x x x x VDS_EN VDG_EN + self.send_data(0x10) # x x x VCOM_SLWE VGH[3:0] VGH=20V, VGL=-20V + self.send_data(0x3F) # x x VSH[5:0] VSH = 15V + self.send_data(0x3F) # x x VSL[5:0] VSL=-15V + self.send_data(0x03) # OPTEN VDHR[6:0] VHDR=6.4V + # T_VDS_OFF[1:0] 00=1 frame; 01=2 frame; 10=3 frame; 11=4 frame + self.send_command(0x06) # booster soft start BTST + self.send_data(0x37) # BT_PHA[7:0] + self.send_data(0x3D) # BT_PHB[7:0] + self.send_data(0x3D) # x x BT_PHC[5:0] + + self.send_command(0x60) # TCON setting TCON + self.send_data(0x22) # S2G[3:0] G2S[3:0] non-overlap = 12 + + self.send_command(0x82) # VCOM_DC setting VDCS + self.send_data(0x07) # x VDCS[6:0] VCOM_DC value= -1.9v 00~3f,0x12=-1.9v + + self.send_command(0x30) + self.send_data(0x09) + + self.send_command(0xe3) # power saving PWS + self.send_data(0x88) # VCOM_W[3:0] SD_W[3:0] + + self.send_command(0x61) # resoultion setting + self.send_data(0xf0) # HRES[7:3] 0 0 0 + self.send_data(0x01) # x x x x x x x VRES[8] + self.send_data(0x68) # VRES[7:0] + + self.send_command(0x50); + self.send_data(0xB7); + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (image == None): + return + self.send_command(0x13); # Transfer new data + self.send_data2(image) + + def display_NUM(self, NUM): + # pcnt = 0 + + self.send_command(0x13); # Transfer new data + for column in range(0, self.height): + for row in range(0, self.width // 8): + if NUM == self.WHITE: + self.send_data(0xFF) + + elif NUM == self.BLACK: + self.send_data(0x00) + + elif NUM == self.Source_Line: + self.send_data(0xAA) + + elif NUM == self.Gate_Line: + if (column % 2): + self.send_data(0xff) # An odd number of Gate line + else: + self.send_data(0x00) # The even line Gate + + elif NUM == self.Chessboard: + if (row >= (self.width / 8 / 2) and column >= (self.height / 2)): + self.send_data(0xff) + elif (row < (self.width / 8 / 2) and column < (self.height / 2)): + self.send_data(0xff) + else: + self.send_data(0x00) + + elif NUM == self.LEFT_BLACK_RIGHT_WHITE: + if (row >= (self.width / 8 / 2)): + self.send_data(0xff) + else: + self.send_data(0x00) + + elif NUM == self.UP_BLACK_DOWN_WHITE: + if (column >= (self.height / 2)): + self.send_data(0xFF) + else: + self.send_data(0x00) + + elif NUM == self.Frame: + if (column == 0 or column == (self.height - 1)): + self.send_data(0x00) + elif (row == 0): + self.send_data(0x7F) + elif (row == (self.width / 8 - 1)): + self.send_data(0xFE); + else: + self.send_data(0xFF); + + elif NUM == self.Crosstalk: + if ((row >= (self.width / 8 / 3) and row <= (self.width / 8 / 3 * 2) and column <= ( + self.height / 3)) or ( + row >= (self.width / 8 / 3) and row <= (self.width / 8 / 3 * 2) and column >= ( + self.height / 3 * 2))): + self.send_data(0x00) + else: + self.send_data(0xFF) + + elif NUM == self.Image: + epdconfig.delay_ms(1) + # self.send_data(gImage_1[pcnt++]) + + def Clear(self): + self.send_command(0x13); # Transfer new data + self.send_data2([0xFF] * int(self.width * self.height / 8)) + self.lut_GC() + self.refresh() + + def sleep(self): + self.send_command(0X07) # DEEP_SLEEP_MODE + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v3in7/epd3in7.py b/pwnagotchi/ui/hw/libs/waveshare/v3in7/epd3in7.py new file mode 100644 index 00000000..d8e8d042 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v3in7/epd3in7.py @@ -0,0 +1,458 @@ +# ***************************************************************************** +# * | File : epd3in7.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-10 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 280 +EPD_HEIGHT = 480 + +GRAY1 = 0xff # white +GRAY2 = 0xC0 # Close to white +GRAY3 = 0x80 # Close to black +GRAY4 = 0x00 # black + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.GRAY1 = GRAY1 # white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 # gray + self.GRAY4 = GRAY4 # Blackest + + lut_4Gray_GC = [ + 0x2A, 0x06, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x06, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x06, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x0A, 0x00, 0x00, 0x00, 0x08, 0x08, 0x02, + 0x00, 0x02, 0x02, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x22 + ] + + lut_1Gray_GC = [ + 0x2A, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x03, 0x0A, 0x00, 0x02, 0x06, 0x0A, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x22 + ] + + lut_1Gray_DU = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0A, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x05, 0x00, 0x05, 0x03, 0x05, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x22 + ] + + lut_1Gray_A2 = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x22 + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(10) + logger.debug("e-Paper busy release") + + def init(self, mode): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x12) + epdconfig.delay_ms(300) + + self.send_command(0x46) + self.send_data(0xF7) + self.ReadBusy() + self.send_command(0x47) + self.send_data(0xF7) + self.ReadBusy() + + self.send_command(0x01) # setting gaet number + self.send_data(0xDF) + self.send_data(0x01) + self.send_data(0x00) + + self.send_command(0x03) # set gate voltage + self.send_data(0x00) + + self.send_command(0x04) # set source voltage + self.send_data(0x41) + self.send_data(0xA8) + self.send_data(0x32) + + self.send_command(0x11) # set data entry sequence + self.send_data(0x03) + + self.send_command(0x3C) # set border + self.send_data(0x03) + + self.send_command(0x0C) # set booster strength + self.send_data(0xAE) + self.send_data(0xC7) + self.send_data(0xC3) + self.send_data(0xC0) + self.send_data(0xC0) + + self.send_command(0x18) # set internal sensor on + self.send_data(0x80) + + self.send_command(0x2C) # set vcom value + self.send_data(0x44) + + if (mode == 0): # 4Gray + self.send_command(0x37) # set display option, these setting turn on previous function + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + elif (mode == 1): # 1Gray + self.send_command(0x37) # set display option, these setting turn on previous function + self.send_data(0x00) # can switch 1 gray or 4 gray + self.send_data(0xFF) + self.send_data(0xFF) + self.send_data(0xFF) + self.send_data(0xFF) + self.send_data(0x4F) + self.send_data(0xFF) + self.send_data(0xFF) + self.send_data(0xFF) + self.send_data(0xFF) + else: + logger.debug("There is no such mode") + + self.send_command(0x44) # setting X direction start/end position of RAM + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x17) + self.send_data(0x01) + + self.send_command(0x45) # setting Y direction start/end position of RAM + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0xDF) + self.send_data(0x01) + + self.send_command(0x22) # Display Update Control 2 + self.send_data(0xCF) + return 0 + + def load_lut(self, lut): + self.send_command(0x32) + self.send_data2(lut) + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i = 0 + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((x + (y * self.width)) / 4)] = ( + (pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | ( + pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = imwidth - x - 1 + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((newx + (newy * self.width)) / 4)] = ( + (pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | ( + pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + return buf + + def display_4Gray(self, image): + if (image == None): + return + + self.send_command(0x4E) + self.send_data(0x00) + self.send_data(0x00) + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + buf = [0x00] * self.height * linewidth + + self.send_command(0x24) + for i in range(0, (int)(self.height * (self.width / 8))): + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x01 # white + elif (temp2 == 0x00): + temp3 |= 0x00 # black + elif (temp2 == 0x80): + temp3 |= 0x00 # gray1 + else: # 0x40 + temp3 |= 0x01 # gray2 + temp3 <<= 1 + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): # white + temp3 |= 0x01 + elif (temp2 == 0x00): # black + temp3 |= 0x00 + elif (temp2 == 0x80): + temp3 |= 0x00 # gray1 + else: # 0x40 + temp3 |= 0x01 # gray2 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + buf[i] = temp3 + self.send_data2(buf) + + self.send_command(0x4E) + self.send_data(0x00) + self.send_data(0x00) + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x26) + for i in range(0, (int)(self.height * (self.width / 8))): + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x01 # white + elif (temp2 == 0x00): + temp3 |= 0x00 # black + elif (temp2 == 0x80): + temp3 |= 0x01 # gray1 + else: # 0x40 + temp3 |= 0x00 # gray2 + temp3 <<= 1 + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): # white + temp3 |= 0x01 + elif (temp2 == 0x00): # black + temp3 |= 0x00 + elif (temp2 == 0x80): + temp3 |= 0x01 # gray1 + else: # 0x40 + temp3 |= 0x00 # gray2 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + buf[i] = temp3 + self.send_data2(buf) + + self.load_lut(self.lut_4Gray_GC) + self.send_command(0x22) + self.send_data(0xC7) + self.send_command(0x20) + self.ReadBusy() + + def display_1Gray(self, image): + if (image == None): + return + + self.send_command(0x4E) + self.send_data(0x00) + self.send_data(0x00) + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x24) + self.send_data2(image) + + self.load_lut(self.lut_1Gray_A2) + self.send_command(0x20) + self.ReadBusy() + + def Clear(self, color, mode): + self.send_command(0x4E) + self.send_data(0x00) + self.send_data(0x00) + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x24) + self.send_data2([0xff] * int(self.height * linewidth)) + + if (mode == 0): # 4Gray + self.send_command(0x26) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.load_lut(self.lut_4Gray_GC) + self.send_command(0x22) + self.send_data(0xC7) + elif (mode == 1): # 1Gray + self.load_lut(self.lut_1Gray_DU) + else: + logger.debug("There is no such mode") + + self.send_command(0x20) + self.ReadBusy() + + def sleep(self): + self.send_command(0X10) # deep sleep + self.send_data(0x03) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4in01f/epd4in01f.py b/pwnagotchi/ui/hw/libs/waveshare/v4in01f/epd4in01f.py new file mode 100644 index 00000000..95ce0061 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4in01f/epd4in01f.py @@ -0,0 +1,238 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- +# ***************************************************************************** +# * | File : epd4in01f.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-10 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 640 +EPD_HEIGHT = 400 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.BLACK = 0x000000 # 0000 BGR + self.WHITE = 0xffffff # 0001 + self.GREEN = 0x00ff00 # 0010 + self.BLUE = 0xff0000 # 0011 + self.RED = 0x0000ff # 0100 + self.YELLOW = 0x00ffff # 0101 + self.ORANGE = 0x0080ff # 0110 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(1) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusyHigh(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(10) + logger.debug("e-Paper busy release") + + def ReadBusyLow(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(10) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.ReadBusyHigh() + self.send_command(0x00) + self.send_data(0x2f) + self.send_data(0x00) + self.send_command(0x01) + self.send_data(0x37) + self.send_data(0x00) + self.send_data(0x05) + self.send_data(0x05) + self.send_command(0x03) + self.send_data(0x00) + self.send_command(0x06) + self.send_data(0xC7) + self.send_data(0xC7) + self.send_data(0x1D) + self.send_command(0x41) + self.send_data(0x00) + self.send_command(0x50) + self.send_data(0x37) + self.send_command(0x60) + self.send_data(0x22) + self.send_command(0x61) + self.send_data(0x02) + self.send_data(0x80) + self.send_data(0x01) + self.send_data(0x90) + self.send_command(0xE3) + self.send_data(0xAA) + + # EPD hardware init end + return 0 + + def getbuffer(self, image): + buf = [0x00] * int(self.width * self.height / 2) + image_monocolor = image.convert('RGB') # Picture mode conversion + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logger.debug('imwidth = %d imheight = %d ', imwidth, imheight) + if (imwidth == self.width and imheight == self.height): + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + Add = int((x + y * self.width) / 2) + Color = 0; + if (pixels[x, y][0] == 0 and pixels[x, y][1] == 0 and pixels[x, y][2] == 0): + Color = 0 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 255 and pixels[x, y][2] == 255): + Color = 1 + elif (pixels[x, y][0] == 0 and pixels[x, y][1] == 255 and pixels[x, y][2] == 0): + Color = 2 + elif (pixels[x, y][0] == 0 and pixels[x, y][1] == 0 and pixels[x, y][2] == 255): + Color = 3 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 0 and pixels[x, y][2] == 0): + Color = 4 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 255 and pixels[x, y][2] == 0): + Color = 5 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 128 and pixels[x, y][2] == 0): + Color = 6 + + data_t = buf[Add] & (~(0xF0 >> ((x % 2) * 4))) + buf[Add] = data_t | ((Color << 4) >> ((x % 2) * 4)); + + elif (imwidth == self.height and imheight == self.width): + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + Add = int((newx + newy * self.width) / 2) + Color = 0; + if (pixels[x, y][0] == 0 and pixels[x, y][1] == 0 and pixels[x, y][2] == 0): + Color = 0 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 255 and pixels[x, y][2] == 255): + Color = 1 + elif (pixels[x, y][0] == 0 and pixels[x, y][1] == 255 and pixels[x, y][2] == 0): + Color = 2 + elif (pixels[x, y][0] == 0 and pixels[x, y][1] == 0 and pixels[x, y][2] == 255): + Color = 3 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 0 and pixels[x, y][2] == 0): + Color = 4 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 255 and pixels[x, y][2] == 0): + Color = 5 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 128 and pixels[x, y][2] == 0): + Color = 6 + + data_t = buf[Add] & (~(0xF0 >> ((newx % 2) * 4))) + buf[Add] = data_t | ((Color << 4) >> ((newx % 2) * 4)); + return buf + + def display(self, image): + self.send_command(0x61) # Set Resolution setting + self.send_data(0x02) + self.send_data(0x80) + self.send_data(0x01) + self.send_data(0x90) + self.send_command(0x10) + self.send_data2(image) + self.send_command(0x04) # 0x04 + self.ReadBusyHigh() + self.send_command(0x12) # 0x12 + self.ReadBusyHigh() + self.send_command(0x02) # 0x02 + self.ReadBusyLow() + # epdconfig.delay_ms(500) + + def Clear(self): + self.send_command(0x61) # Set Resolution setting + self.send_data(0x02) + self.send_data(0x80) + self.send_data(0x01) + self.send_data(0x90) + self.send_command(0x10) + self.send_data2([0x11] * int(EPD_HEIGHT) * int(EPD_WIDTH / 2)) + # BLACK 0x00 /// 0000 + # WHITE 0x11 /// 0001 + # GREEN 0x22 /// 0010 + # BLUE 0x33 /// 0011 + # RED 0x44 /// 0100 + # YELLOW 0x55 /// 0101 + # ORANGE 0x66 /// 0110 + # CLEAN 0x77 /// 0111 unavailable Afterimage + self.send_command(0x04) # 0x04 + self.ReadBusyHigh() + self.send_command(0x12) # 0x12 + self.ReadBusyHigh() + self.send_command(0x02) # 0x02 + self.ReadBusyLow() + # epdconfig.delay_ms(500) + + def sleep(self): + # epdconfig.delay_ms(500) + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4in2/epd4in2.py b/pwnagotchi/ui/hw/libs/waveshare/v4in2/epd4in2.py new file mode 100644 index 00000000..83180526 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4in2/epd4in2.py @@ -0,0 +1,678 @@ +# ***************************************************************************** +# * | File : epd4in2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.2 +# * | Date : 2022-10-29 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig +from PIL import Image +import RPi.GPIO as GPIO + +# Display resolution +EPD_WIDTH = 400 +EPD_HEIGHT = 300 + +GRAY1 = 0xff # white +GRAY2 = 0xC0 +GRAY3 = 0x80 # gray +GRAY4 = 0x00 # Blackest + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.GRAY1 = GRAY1 # white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 # gray + self.GRAY4 = GRAY4 # Blackest + self.DATA = [0x00] * 15000 + + lut_vcom0 = [ + 0x00, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x01, + 0x00, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ] + lut_ww = [ + 0x50, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x01, + 0xA0, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_bw = [ + 0x50, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x01, + 0xA0, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_wb = [ + 0xA0, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x01, + 0x50, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_bb = [ + 0x20, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x01, + 0x10, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + # ******************************partial screen update LUT*********************************/ + EPD_4IN2_Partial_lut_vcom1 = [ + 0x00, 0x01, 0x20, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + EPD_4IN2_Partial_lut_ww1 = [ + 0x00, 0x01, 0x20, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + EPD_4IN2_Partial_lut_bw1 = [ + 0x20, 0x01, 0x20, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + EPD_4IN2_Partial_lut_wb1 = [ + 0x10, 0x01, 0x20, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + EPD_4IN2_Partial_lut_bb1 = [ + 0x00, 0x01, 0x20, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + # ******************************gray*********************************/ + # 0~3 gray + EPD_4IN2_4Gray_lut_vcom = [ + 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x60, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x13, 0x0A, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + # R21 + EPD_4IN2_4Gray_lut_ww = [ + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x10, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0xA0, 0x13, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + # R22H r + EPD_4IN2_4Gray_lut_bw = [ + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x99, 0x0C, 0x01, 0x03, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + # R23H w + EPD_4IN2_4Gray_lut_wb = [ + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x99, 0x0B, 0x04, 0x04, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + # R24H b + EPD_4IN2_4Gray_lut_bb = [ + 0x80, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x20, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x50, 0x13, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(10) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + self.send_command(0x71) + while epdconfig.digital_read(self.busy_pin) == 0: # 0: idle, 1: busy + self.send_command(0x71) + epdconfig.delay_ms(100) + + def set_lut(self): + self.send_command(0x20) # vcom + self.send_data2(self.lut_vcom0) + + self.send_command(0x21) # ww -- + self.send_data2(self.lut_ww) + + self.send_command(0x22) # bw r + self.send_data2(self.lut_bw) + + self.send_command(0x23) # wb w + self.send_data2(self.lut_bb) + + self.send_command(0x24) # bb b + self.send_data2(self.lut_wb) + + def Partial_SetLut(self): + self.send_command(0x20) + self.send_data2(self.EPD_4IN2_Partial_lut_vcom1) + + self.send_command(0x21) + self.send_data2(self.EPD_4IN2_Partial_lut_ww1) + + self.send_command(0x22) + self.send_data2(self.EPD_4IN2_Partial_lut_bw1) + + self.send_command(0x23) + self.send_data2(self.EPD_4IN2_Partial_lut_wb1) + + self.send_command(0x24) + self.send_data2(self.EPD_4IN2_Partial_lut_bb1) + + def Gray_SetLut(self): + self.send_command(0x20) # vcom + self.send_data2(self.EPD_4IN2_4Gray_lut_vcom) + + self.send_command(0x21) # red not use + self.send_data2(self.EPD_4IN2_4Gray_lut_ww) + + self.send_command(0x22) # bw r + self.send_data2(self.EPD_4IN2_4Gray_lut_bw) + + self.send_command(0x23) # wb w + self.send_data2(self.EPD_4IN2_4Gray_lut_wb) + + self.send_command(0x24) # bb b + self.send_data2(self.EPD_4IN2_4Gray_lut_bb) + + self.send_command(0x25) # vcom + self.send_data2(self.EPD_4IN2_4Gray_lut_ww) + + def init(self): + if epdconfig.module_init() != 0: + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) # VDS_EN, VDG_EN + self.send_data(0x00) # VCOM_HV, VGHL_LV[1], VGHL_LV[0] + self.send_data(0x2b) # VDH + self.send_data(0x2b) # VDL + + self.send_command(0x06) # boost soft start + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # KW-BF KWR-AF BWROTP 0f + + self.send_command(0x30) # PLL setting + self.send_data(0x3c) # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(0x01) + self.send_data(0x90) # 128 + self.send_data(0x01) + self.send_data(0x2c) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x12) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data( + 0x97) # 97white border 77black border VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7 + + self.set_lut() + # EPD hardware init end + return 0 + + def init_Partial(self): + if epdconfig.module_init() != 0: + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) # VDS_EN, VDG_EN + self.send_data(0x00) # VCOM_HV, VGHL_LV[1], VGHL_LV[0] + self.send_data(0x2b) # VDH + self.send_data(0x2b) # VDL + + self.send_command(0x06) # boost soft start + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # KW-BF KWR-AF BWROTP 0f + + self.send_command(0x30) # PLL setting + self.send_data(0x3c) # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(0x01) + self.send_data(0x90) # 128 + self.send_data(0x01) + self.send_data(0x2c) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x12) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data( + 0x07) # 97white border 77black border VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7 + + self.Partial_SetLut() + # EPD hardware init end + return 0 + + def Init_4Gray(self): + if epdconfig.module_init() != 0: + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) + self.send_data(0x00) # VGH=20V,VGL=-20V + self.send_data(0x2b) # VDH=15V + self.send_data(0x2b) # VDL=-15V + self.send_data(0x13) + + self.send_command(0x06) # booster soft start + self.send_data(0x17) # A + self.send_data(0x17) # B + self.send_data(0x17) # C + + self.send_command(0x04) + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0x3f) # KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0x30) # PLL setting + self.send_data(0x3c) # 100hz + + self.send_command(0x61) # resolution setting + self.send_data(0x01) # 400 + self.send_data(0x90) + self.send_data(0x01) # 300 + self.send_data(0x2c) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x12) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x97) + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif imwidth == self.height and imheight == self.width: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i = 0 + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0xC0: + pixels[x, y] = 0x80 + elif pixels[x, y] == 0x80: + pixels[x, y] = 0x40 + i = i + 1 + if i % 4 == 0: + buf[int((x + (y * self.width)) / 4)] = ( + (pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | ( + pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + elif imwidth == self.height and imheight == self.width: + logger.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = x + if pixels[x, y] == 0xC0: + pixels[x, y] = 0x80 + elif pixels[x, y] == 0x80: + pixels[x, y] = 0x40 + i = i + 1 + if i % 4 == 0: + buf[int((newx + (newy * self.width)) / 4)] = ( + (pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | ( + pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + return buf + + def display(self, image): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x92) + self.set_lut() + self.send_command(0x10) + self.send_data2([0xFF] * int(self.width * linewidth)) + + self.send_command(0x13) + self.send_data2(image) + + self.send_command(0x12) + self.ReadBusy() + + def EPD_4IN2_PartialDisplay(self, X_start, Y_start, X_end, Y_end, Image): + # EPD_WIDTH = 400 + # EPD_HEIGHT = 300 + + if EPD_WIDTH % 8 != 0: + Width = int(EPD_WIDTH / 8) + 1 + else: + Width = int(EPD_WIDTH / 8) + Height = EPD_HEIGHT + + if X_start % 8 != 0: + X_start = int(X_start / 8) + 1 + else: + X_start = int(X_start / 8) + if X_end % 8 != 0: + X_end = int(X_end / 8) + 1 + else: + X_end = int(X_end / 8) + + buf = [0x00] * (Y_end - Y_start) * (X_end - X_start) + + self.send_command(0x91) # This command makes the display enter partial mode + self.send_command(0x90) # resolution setting + self.send_data(int(X_start * 8 / 256)) + self.send_data(int(X_start * 8 % 256)) # x-start + + self.send_data(int(X_end * 8 / 256)) + self.send_data(int(X_end * 8 % 256) - 1) # x-end + + self.send_data(int(Y_start / 256)) + self.send_data(int(Y_start % 256)) # y-start + + self.send_data(int(Y_end / 256)) + self.send_data(int(Y_end % 256) - 1) # y-end + self.send_data(0x28) + + self.send_command(0x10) # writes Old data to SRAM for programming + for j in range(0, Y_end - Y_start): + for i in range(0, X_end - X_start): + buf[j * (X_end - X_start) + i] = self.DATA[(Y_start + j) * Width + X_start + i] + self.send_data2(buf) + + self.send_command(0x13) # writes New data to SRAM. + for j in range(0, Y_end - Y_start): + for i in range(0, X_end - X_start): + buf[j * (X_end - X_start) + i] = ~Image[(Y_start + j) * Width + X_start + i] + self.DATA[(Y_start + j) * Width + X_start + i] = ~Image[(Y_start + j) * Width + X_start / 8 + i] + self.send_data2(buf) + + self.send_command(0x12) # DISPLAY REFRESH + epdconfig.delay_ms(200) # The delay here is necessary, 200uS at least!!! + self.ReadBusy() + + def display_4Gray(self, image): + self.send_command(0x92) + self.set_lut() + self.send_command(0x10) + + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + buf = [0x00] * self.height * linewidth + + for i in range(0, int(EPD_WIDTH * EPD_HEIGHT / 8)): # EPD_WIDTH * EPD_HEIGHT / 4 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: + temp3 |= 0x01 # white + elif temp2 == 0x00: + temp3 |= 0x00 # black + elif temp2 == 0x80: + temp3 |= 0x01 # gray1 + else: # 0x40 + temp3 |= 0x00 # gray2 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: # white + temp3 |= 0x01 + elif temp2 == 0x00: # black + temp3 |= 0x00 + elif temp2 == 0x80: + temp3 |= 0x01 # gray1 + else: # 0x40 + temp3 |= 0x00 # gray2 + if j != 1 or k != 1: + temp3 <<= 1 + temp1 <<= 2 + buf[i] = temp3 + self.send_data2(buf) + + self.send_command(0x13) + + for i in range(0, int(EPD_WIDTH * EPD_HEIGHT / 8)): # 5808*4 46464 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: + temp3 |= 0x01 # white + elif temp2 == 0x00: + temp3 |= 0x00 # black + elif temp2 == 0x80: + temp3 |= 0x00 # gray1 + else: # 0x40 + temp3 |= 0x01 # gray2 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: # white + temp3 |= 0x01 + elif temp2 == 0x00: # black + temp3 |= 0x00 + elif temp2 == 0x80: + temp3 |= 0x00 # gray1 + else: # 0x40 + temp3 |= 0x01 # gray2 + if j != 1 or k != 1: + temp3 <<= 1 + temp1 <<= 2 + buf[i] = temp3 + self.send_data2(buf) + + self.Gray_SetLut() + self.send_command(0x12) + epdconfig.delay_ms(200) + self.ReadBusy() + # pass + + def Clear(self): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x10) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.send_command(0x13) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4in26/epd4in26.py b/pwnagotchi/ui/hw/libs/waveshare/v4in26/epd4in26.py new file mode 100644 index 00000000..8da379bd --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4in26/epd4in26.py @@ -0,0 +1,516 @@ +# ***************************************************************************** +# * | File : epd4in26.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2023-12-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 800 +EPD_HEIGHT = 480 + +GRAY1 = 0xff # white +GRAY2 = 0xC0 +GRAY3 = 0x80 # gray +GRAY4 = 0x00 # Blackest + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.GRAY1 = GRAY1 # white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 # gray + self.GRAY4 = GRAY4 # Blackest + + LUT_DATA_4Gray = [ # #112bytes + 0x80, 0x48, 0x4A, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0A, 0x48, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x48, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xA8, 0x48, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x1E, 0x1C, 0x02, 0x00, + 0x05, 0x01, 0x05, 0x01, 0x02, + 0x08, 0x01, 0x01, 0x04, 0x04, + 0x00, 0x02, 0x00, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, + 0x22, 0x22, 0x22, 0x22, 0x22, + 0x17, 0x41, 0xA8, 0x32, 0x30, + 0x00, 0x00] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.SPI.writebytes2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 1): + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(20) + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xF7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Fast(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Part(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xFF) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_4GRAY(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Setting the display window + parameter: + xstart : X-axis starting position + ystart : Y-axis starting position + xend : End position of X-axis + yend : End position of Y-axis + ''' + + def SetWindow(self, x_start, y_start, x_end, y_end): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + self.send_data(x_start & 0xFF) + self.send_data((x_start >> 8) & 0x03) + self.send_data(x_end & 0xFF) + self.send_data((x_end >> 8) & 0x03) + + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(y_start & 0xFF) + self.send_data((y_start >> 8) & 0xFF) + self.send_data(y_end & 0xFF) + self.send_data((y_end >> 8) & 0xFF) + + ''' + function : Set Cursor + parameter: + x : X-axis starting position + y : Y-axis starting position + ''' + + def SetCursor(self, x, y): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data(x & 0xFF) + self.send_data((x >> 8) & 0x03) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + self.ReadBusy() + + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x18) # use the internal temperature sensor + self.send_data(0x80) + + self.send_command(0x0C) # set soft start + self.send_data(0xAE) + self.send_data(0xC7) + self.send_data(0xC3) + self.send_data(0xC0) + self.send_data(0x80) + + self.send_command(0x01) # drive output control + self.send_data((self.height - 1) % 256) # Y + self.send_data((self.height - 1) // 256) # Y + self.send_data(0x02) + + self.send_command(0x3C) # Border Border setting + self.send_data(0x01) + + self.send_command(0x11) # data entry mode + self.send_data(0x01) # X-mode x+ y- + + self.SetWindow(0, self.height - 1, self.width - 1, 0) + + self.SetCursor(0, 0) + self.ReadBusy() + + # EPD hardware init end + return 0 + + def init_Fast(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + self.ReadBusy() + + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x18) # use the internal temperature sensor + self.send_data(0x80) + + self.send_command(0x0C) # set soft start + self.send_data(0xAE) + self.send_data(0xC7) + self.send_data(0xC3) + self.send_data(0xC0) + self.send_data(0x80) + + self.send_command(0x01) # drive output control + self.send_data((self.height - 1) % 256) # Y + self.send_data((self.height - 1) // 256) # Y + self.send_data(0x02) + + self.send_command(0x3C) # Border Border setting + self.send_data(0x01) + + self.send_command(0x11) # data entry mode + self.send_data(0x01) # X-mode x+ y- + + self.SetWindow(0, self.height - 1, self.width - 1, 0) + + self.SetCursor(0, 0) + self.ReadBusy() + + # TEMP (1.5s) + self.send_command(0x1A) + self.send_data(0x5A) + + self.send_command(0x22) + self.send_data(0x91) + self.send_command(0x20) + + self.ReadBusy() + + # EPD hardware init end + return 0 + + def Lut(self): + self.send_command(0x32) + for count in range(0, 105): + self.send_data(self.LUT_DATA_4Gray[count]) + + self.send_command(0x03) # VGH + self.send_data(self.LUT_DATA_4Gray[105]) + + self.send_command(0x04) # + self.send_data(self.LUT_DATA_4Gray[106]) # VSH1 + self.send_data(self.LUT_DATA_4Gray[107]) # VSH2 + self.send_data(self.LUT_DATA_4Gray[108]) # VSL + + self.send_command(0x2C) # VCOM Voltage + self.send_data(self.LUT_DATA_4Gray[109]) # 0x1C + + def init_4GRAY(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + self.ReadBusy() + + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x18) # use the internal temperature sensor + self.send_data(0x80) + + self.send_command(0x0C) # set soft start + self.send_data(0xAE) + self.send_data(0xC7) + self.send_data(0xC3) + self.send_data(0xC0) + self.send_data(0x80) + + self.send_command(0x01) # drive output control + self.send_data((self.height - 1) % 256) # Y + self.send_data((self.height - 1) // 256) # Y + self.send_data(0x02) + + self.send_command(0x3C) # Border Border setting + self.send_data(0x01) + + self.send_command(0x11) # data entry mode + self.send_data(0x01) # X-mode x+ y- + + self.SetWindow(0, self.height - 1, self.width - 1, 0) + + self.SetCursor(0, 0) + self.ReadBusy() + + self.Lut() + # EPD hardware init end + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif imwidth == self.height and imheight == self.width: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i = 0 + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((x + (y * self.width)) / 4)] = ( + (pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | ( + pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = self.height - x - 1 + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((newx + (newy * self.width)) / 4)] = ( + (pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | ( + pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + return buf + + def display(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.TurnOnDisplay() + + def display_Base(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + + self.TurnOnDisplay() + + def display_Fast(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.TurnOnDisplay_Fast() + + def display_Partial(self, Image): + + # Reset + self.reset() + + self.send_command(0x18) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x01) # drive output control + self.send_data((self.height - 1) % 256) # Y + self.send_data((self.height - 1) // 256) # Y + + self.send_command(0x11) # data entry mode + self.send_data(0x01) # X-mode x+ y- + + self.SetWindow(0, self.height - 1, self.width - 1, 0) + + self.SetCursor(0, 0) + + self.send_command(0x24) # Write Black and White image to RAM + self.send_data2(Image) + + self.TurnOnDisplay_Part() + + def display_4Gray(self, image): + self.send_command(0x24) + for i in range(0, 48000): # 5808*4 46464 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x01 + else: # 0x40 + temp3 |= 0x00 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x01 + else: # 0x40 + temp3 |= 0x00 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.send_command(0x26) + for i in range(0, 48000): # 5808*4 46464 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x00 + else: # 0x40 + temp3 |= 0x01 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x00 + else: # 0x40 + temp3 |= 0x01 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.TurnOnDisplay_4GRAY() + + def Clear(self): + self.send_command(0x24) + self.send_data2([0xFF] * (int(self.width / 8) * self.height)) + + self.send_command(0x26) + self.send_data2([0xFF] * (int(self.width / 8) * self.height)) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4in2_v2/epd4in2_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v4in2_v2/epd4in2_V2.py new file mode 100644 index 00000000..81b8b246 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4in2_v2/epd4in2_V2.py @@ -0,0 +1,528 @@ +# ***************************************************************************** +# * | File : epd4in2_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2023-09-13 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig +from PIL import Image +import RPi.GPIO as GPIO + +# Display resolution +EPD_WIDTH = 400 +EPD_HEIGHT = 300 + +GRAY1 = 0xff # white +GRAY2 = 0xC0 +GRAY3 = 0x80 # gray +GRAY4 = 0x00 # Blackest + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.Seconds_1_5S = 0 + self.Seconds_1S = 1 + self.GRAY1 = GRAY1 # white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 # gray + self.GRAY4 = GRAY4 # Blackest + + LUT_ALL = [0x01, 0x0A, 0x1B, 0x0F, 0x03, 0x01, 0x01, + 0x05, 0x0A, 0x01, 0x0A, 0x01, 0x01, 0x01, + 0x05, 0x08, 0x03, 0x02, 0x04, 0x01, 0x01, + 0x01, 0x04, 0x04, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x0A, 0x1B, 0x0F, 0x03, 0x01, 0x01, + 0x05, 0x4A, 0x01, 0x8A, 0x01, 0x01, 0x01, + 0x05, 0x48, 0x03, 0x82, 0x84, 0x01, 0x01, + 0x01, 0x84, 0x84, 0x82, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x0A, 0x1B, 0x8F, 0x03, 0x01, 0x01, + 0x05, 0x4A, 0x01, 0x8A, 0x01, 0x01, 0x01, + 0x05, 0x48, 0x83, 0x82, 0x04, 0x01, 0x01, + 0x01, 0x04, 0x04, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x8A, 0x1B, 0x8F, 0x03, 0x01, 0x01, + 0x05, 0x4A, 0x01, 0x8A, 0x01, 0x01, 0x01, + 0x05, 0x48, 0x83, 0x02, 0x04, 0x01, 0x01, + 0x01, 0x04, 0x04, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x8A, 0x9B, 0x8F, 0x03, 0x01, 0x01, + 0x05, 0x4A, 0x01, 0x8A, 0x01, 0x01, 0x01, + 0x05, 0x48, 0x03, 0x42, 0x04, 0x01, 0x01, + 0x01, 0x04, 0x04, 0x42, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x07, 0x17, 0x41, 0xA8, + 0x32, 0x30] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(100) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(100) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xF7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Fast(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Partial(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xFF) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_4GRAY(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xCF) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def init(self): + if epdconfig.module_init() != 0: + return -1 + # EPD hardware init start + self.reset() + self.ReadBusy() + + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x21) # Display update control + self.send_data(0x40) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x05) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) # X-mode + + self.send_command(0x44) + self.send_data(0x00) + self.send_data(0x31) + + self.send_command(0x45) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x2B) + self.send_data(0x01) + + self.send_command(0x4E) + self.send_data(0x00) + + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + self.ReadBusy() + + return 0 + + def init_fast(self, mode): + if epdconfig.module_init() != 0: + return -1 + # EPD hardware init start + self.reset() + self.ReadBusy() + + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x21) # Display update control + self.send_data(0x40) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x05) + + if mode == self.Seconds_1_5S: + self.send_command(0x1A) + self.send_data(0x6E) + else: + self.send_command(0x1A) + self.send_data(0x5A) + + self.send_command(0x22) # Load temperature value + self.send_data(0x91) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x11) # data entry mode + self.send_data(0x03) # X-mode + + self.send_command(0x44) + self.send_data(0x00) + self.send_data(0x31) + + self.send_command(0x45) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x2B) + self.send_data(0x01) + + self.send_command(0x4E) + self.send_data(0x00) + + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + self.ReadBusy() + + return 0 + + def Lut(self): + self.send_command(0x32) + for i in range(227): + self.send_data(self.LUT_ALL[i]) + + self.send_command(0x3F) + self.send_data(self.LUT_ALL[227]) + + self.send_command(0x03) + self.send_data(self.LUT_ALL[228]) + + self.send_command(0x04) + self.send_data(self.LUT_ALL[229]) + self.send_data(self.LUT_ALL[230]) + self.send_data(self.LUT_ALL[231]) + + self.send_command(0x2c) + self.send_data(self.LUT_ALL[232]) + + def Init_4Gray(self): + if epdconfig.module_init() != 0: + return -1 + # EPD hardware init start + self.reset() + self.ReadBusy() + + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x03) + + self.send_command(0x0C) # BTST + self.send_data(0x8B) # 8B + self.send_data(0x9C) # 9C + self.send_data(0xA4) # 96 A4 + self.send_data(0x0F) # 0F + + self.Lut() + + self.send_command(0x11) # data entry mode + self.send_data(0x03) # X-mode + + self.send_command(0x44) + self.send_data(0x00) + self.send_data(0x31) + + self.send_command(0x45) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x2B) + self.send_data(0x01) + + self.send_command(0x4E) + self.send_data(0x00) + + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + self.ReadBusy() + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif imwidth == self.height and imheight == self.width: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i = 0 + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0xC0: + pixels[x, y] = 0x80 + elif pixels[x, y] == 0x80: + pixels[x, y] = 0x40 + i = i + 1 + if i % 4 == 0: + buf[int((x + (y * self.width)) / 4)] = ( + (pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | ( + pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + elif imwidth == self.height and imheight == self.width: + logger.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = x + if pixels[x, y] == 0xC0: + pixels[x, y] = 0x80 + elif pixels[x, y] == 0x80: + pixels[x, y] = 0x40 + i = i + 1 + if i % 4 == 0: + buf[int((newx + (newy * self.width)) / 4)] = ( + (pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | ( + pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + return buf + + def Clear(self): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x24) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.send_command(0x26) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.TurnOnDisplay() + + def display(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + + self.TurnOnDisplay() + + def display_Fast(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + + self.TurnOnDisplay_Fast() + + def display_Partial(self, Image): + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x44) + self.send_data(0x00) + self.send_data(0x31) + + self.send_command(0x45) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x2B) + self.send_data(0x01) + + self.send_command(0x4E) + self.send_data(0x00) + + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x24) # WRITE_RAM + self.send_data2(Image) + self.TurnOnDisplay_Partial() + + def display_4Gray(self, image): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + buf = [0x00] * self.height * linewidth + + self.send_command(0x24) + for i in range(0, int(EPD_WIDTH * EPD_HEIGHT / 8)): # EPD_WIDTH * EPD_HEIGHT / 4 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: + temp3 |= 0x01 # white + elif temp2 == 0x00: + temp3 |= 0x00 # black + elif temp2 == 0x80: + temp3 |= 0x00 # gray1 + else: # 0x40 + temp3 |= 0x01 # gray2 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: # white + temp3 |= 0x01 + elif temp2 == 0x00: # black + temp3 |= 0x00 + elif temp2 == 0x80: + temp3 |= 0x00 # gray1 + else: # 0x40 + temp3 |= 0x01 # gray2 + if j != 1 or k != 1: + temp3 <<= 1 + temp1 <<= 2 + buf[i] = temp3 + self.send_data2(buf) + + self.send_command(0x26) + for i in range(0, int(EPD_WIDTH * EPD_HEIGHT / 8)): + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: + temp3 |= 0x01 # white + elif temp2 == 0x00: + temp3 |= 0x00 # black + elif temp2 == 0x80: + temp3 |= 0x01 # gray1 + else: # 0x40 + temp3 |= 0x00 # gray2 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: # white + temp3 |= 0x01 + elif temp2 == 0x00: # black + temp3 |= 0x00 + elif temp2 == 0x80: + temp3 |= 0x01 # gray1 + else: # 0x40 + temp3 |= 0x00 # gray2 + if j != 1 or k != 1: + temp3 <<= 1 + temp1 <<= 2 + buf[i] = temp3 + self.send_data2(buf) + + self.TurnOnDisplay_4GRAY() + # pass + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4in2b_v2/epd4in2b_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v4in2b_v2/epd4in2b_V2.py new file mode 100644 index 00000000..d605dabc --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4in2b_v2/epd4in2b_V2.py @@ -0,0 +1,161 @@ +# ***************************************************************************** +# * | File : epd4in2bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.1 +# * | Date : 2022-08-10 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 400 +EPD_HEIGHT = 300 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0x71) + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0x71) + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x04); + self.ReadBusy(); + + self.send_command(0x00); + self.send_data(0x0f); + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + self.send_data2(imageblack) + + self.send_command(0x13) + self.send_data2(imagered) + + self.send_command(0x12) + epdconfig.delay_ms(20) + self.ReadBusy() + + def Clear(self): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x10) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.send_command(0x13) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.send_command(0x12) + epdconfig.delay_ms(20) + self.ReadBusy() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) # border floating + + self.send_command(0X02) # power off + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4in2bc/epd4in2bc.py b/pwnagotchi/ui/hw/libs/waveshare/v4in2bc/epd4in2bc.py new file mode 100644 index 00000000..84a7875f --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4in2bc/epd4in2bc.py @@ -0,0 +1,151 @@ +# ***************************************************************************** +# * | File : epd4in2bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 400 +EPD_HEIGHT = 300 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) # 07 0f 17 1f 27 2F 37 2f + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0x0F) # LUT from OTP + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imagered[i]) + + self.send_command(0x12) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0xA5) # check code + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4in37g/epd4in37g.py b/pwnagotchi/ui/hw/libs/waveshare/v4in37g/epd4in37g.py new file mode 100644 index 00000000..94814a67 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4in37g/epd4in37g.py @@ -0,0 +1,242 @@ +# ***************************************************************************** +# * | File : epd4in37g.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2022-08-15 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# ******************************************************************************/ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +import PIL +from PIL import Image +import io + +# Display resolution +EPD_WIDTH = 512 +EPD_HEIGHT = 368 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.BLACK = 0x000000 # 00 BGR + self.WHITE = 0xffffff # 01 + self.YELLOW = 0x00ffff # 10 + self.RED = 0x0000ff # 11 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) # module reset + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusyH(self): + logger.debug("e-Paper busy H") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(5) + logger.debug("e-Paper busy H release") + + def ReadBusyL(self): + logger.debug("e-Paper busy L") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy L release") + + def TurnOnDisplay(self): + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0x00) + self.ReadBusyH() + + self.send_command(0x02) # POWER_OFF + self.send_data(0X00) + self.ReadBusyH() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + self.ReadBusyH() + epdconfig.delay_ms(30) + + self.send_command(0xAA) + self.send_data(0x49) + self.send_data(0x55) + self.send_data(0x20) + self.send_data(0x08) + self.send_data(0x09) + self.send_data(0x18) + + self.send_command(0x01) + self.send_data(0x3F) + + self.send_command(0x00) + self.send_data(0x4F) + self.send_data(0x69) + + self.send_command(0x05) + self.send_data(0x40) + self.send_data(0x1F) + self.send_data(0x1F) + self.send_data(0x2C) + + self.send_command(0x08) + self.send_data(0x6F) + self.send_data(0x1F) + self.send_data(0x1F) + self.send_data(0x22) + + # =================== + # 20211212 + # First setting + self.send_command(0x06) + self.send_data(0x6F) + self.send_data(0x1F) + self.send_data(0x17) + self.send_data(0x17) + # =================== + + self.send_command(0x03) + self.send_data(0x00) + self.send_data(0x54) + self.send_data(0x00) + self.send_data(0x44) + + self.send_command(0x60) + self.send_data(0x02) + self.send_data(0x00) + # Please notice that PLL must be set for version 2 IC + self.send_command(0x30) + self.send_data(0x08) + + self.send_command(0x50) + self.send_data(0x3F) + + self.send_command(0x61) + self.send_data(0x02) + self.send_data(0x00) + self.send_data(0x01) + self.send_data(0x70) + + self.send_command(0xE3) + self.send_data(0x2F) + + self.send_command(0x84) + self.send_data(0x01) + return 0 + + def getbuffer(self, image): + # Create a pallette with the 4 colors supported by the panel + pal_image = Image.new("P", (1, 1)) + pal_image.putpalette((0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 0, 0) + (0, 0, 0) * 252) + + # Check if we need to rotate the image + imwidth, imheight = image.size + if (imwidth == self.width and imheight == self.height): + image_temp = image + elif (imwidth == self.height and imheight == self.width): + image_temp = image.rotate(90, expand=True) + else: + logger.warning( + "Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) + + # Convert the soruce image to the 4 colors, dithering if needed + image_4color = image_temp.convert("RGB").quantize(palette=pal_image) + buf_4color = bytearray(image_4color.tobytes('raw')) + + # into a single byte to transfer to the panel + buf = [0x00] * int(self.width * self.height / 4) + idx = 0 + for i in range(0, len(buf_4color), 4): + buf[idx] = (buf_4color[i] << 6) + (buf_4color[i + 1] << 4) + (buf_4color[i + 2] << 2) + buf_4color[i + 3] + idx += 1 + return buf + + def display(self, image): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(image[i + j * Width]) + self.TurnOnDisplay() + + def Clear(self, color=0x55): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(color) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.send_data(0x00) + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v5in65f/epd5in65f.py b/pwnagotchi/ui/hw/libs/waveshare/v5in65f/epd5in65f.py new file mode 100644 index 00000000..cac1f701 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v5in65f/epd5in65f.py @@ -0,0 +1,217 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- +# ***************************************************************************** +# * | File : epd5in65f.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2020-03-02 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +import PIL +from PIL import Image +import io + +# Display resolution +EPD_WIDTH = 600 +EPD_HEIGHT = 448 + +logger = logging.getLogger(__name__) + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.BLACK = 0x000000 # 0000 BGR + self.WHITE = 0xffffff # 0001 + self.GREEN = 0x00ff00 # 0010 + self.BLUE = 0xff0000 # 0011 + self.RED = 0x0000ff # 0100 + self.YELLOW = 0x00ffff # 0101 + self.ORANGE = 0x0080ff # 0110 + + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(600) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusyHigh(self): + logger.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def ReadBusyLow(self): + logger.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.ReadBusyHigh() + self.send_command(0x00) + self.send_data(0xEF) + self.send_data(0x08) + self.send_command(0x01) + self.send_data(0x37) + self.send_data(0x00) + self.send_data(0x23) + self.send_data(0x23) + self.send_command(0x03) + self.send_data(0x00) + self.send_command(0x06) + self.send_data(0xC7) + self.send_data(0xC7) + self.send_data(0x1D) + self.send_command(0x30) + self.send_data(0x3c) + self.send_command(0x41) + self.send_data(0x00) + self.send_command(0x50) + self.send_data(0x37) + self.send_command(0x60) + self.send_data(0x22) + self.send_command(0x61) + self.send_data(0x02) + self.send_data(0x58) + self.send_data(0x01) + self.send_data(0xC0) + self.send_command(0xE3) + self.send_data(0xAA) + + epdconfig.delay_ms(100) + self.send_command(0x50) + self.send_data(0x37) + # EPD hardware init end + return 0 + + def getbuffer(self, image): + # Create a pallette with the 7 colors supported by the panel + pal_image = Image.new("P", (1,1)) + pal_image.putpalette( (0,0,0, 255,255,255, 0,255,0, 0,0,255, 255,0,0, 255,255,0, 255,128,0) + (0,0,0)*249) + + # Check if we need to rotate the image + imwidth, imheight = image.size + if(imwidth == self.width and imheight == self.height): + image_temp = image + elif(imwidth == self.height and imheight == self.width): + image_temp = image.rotate(90, expand=True) + else: + logger.warning("Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) + + # Convert the soruce image to the 7 colors, dithering if needed + image_7color = image_temp.convert("RGB").quantize(palette=pal_image) + buf_7color = bytearray(image_7color.tobytes('raw')) + + # PIL does not support 4 bit color, so pack the 4 bits of color + # into a single byte to transfer to the panel + buf = [0x00] * int(self.width * self.height / 2) + idx = 0 + for i in range(0, len(buf_7color), 2): + buf[idx] = (buf_7color[i] << 4) + buf_7color[i+1] + idx += 1 + + return buf + + def display(self,image): + self.send_command(0x61) #Set Resolution setting + self.send_data(0x02) + self.send_data(0x58) + self.send_data(0x01) + self.send_data(0xC0) + self.send_command(0x10) + + self.send_data2(image) + self.send_command(0x04) #0x04 + self.ReadBusyHigh() + self.send_command(0x12) #0x12 + self.ReadBusyHigh() + self.send_command(0x02) #0x02 + self.ReadBusyLow() + epdconfig.delay_ms(500) + + def Clear(self): + self.send_command(0x61) #Set Resolution setting + self.send_data(0x02) + self.send_data(0x58) + self.send_data(0x01) + self.send_data(0xC0) + self.send_command(0x10) + + # Set all pixels to white + buf = [0x11] * int(self.width * self.height / 2) + self.send_data2(buf) + + self.send_command(0x04) #0x04 + self.ReadBusyHigh() + self.send_command(0x12) #0x12 + self.ReadBusyHigh() + self.send_command(0x02) #0x02 + self.ReadBusyLow() + epdconfig.delay_ms(500) + + def sleep(self): + epdconfig.delay_ms(500) + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + epdconfig.digital_write(self.reset_pin, 0) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v5in83/epd5in83.py b/pwnagotchi/ui/hw/libs/waveshare/v5in83/epd5in83.py new file mode 100644 index 00000000..aab289fb --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v5in83/epd5in83.py @@ -0,0 +1,203 @@ +# ***************************************************************************** +# * | File : epd5in83.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 600 +EPD_HEIGHT = 448 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x37) + self.send_data(0x00) + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0xCF) + self.send_data(0x08) + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0xc7) + self.send_data(0xcc) + self.send_data(0x28) + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3c) + + self.send_command(0x41) # TEMPERATURE_CALIBRATION + self.send_data(0x00) + + self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0x77) + + self.send_command(0x60) # TCON_SETTING + self.send_data(0x22) + + self.send_command(0x61) # TCON_RESOLUTION + self.send_data(0x02) # source 600 + self.send_data(0x58) + self.send_data(0x01) # gate 448 + self.send_data(0xC0) + + self.send_command(0x82) # VCM_DC_SETTING + self.send_data(0x1E) # decide by LUT file + + self.send_command(0xe5) # FLASH MODE + self.send_data(0x03) + + # EPD hardware init end + return 0 + + def getbuffer(self, image): + buf = [0x00] * int(self.width * self.height / 4) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logger.debug('imwidth = %d imheight = %d ', imwidth, imheight) + if (imwidth == self.width and imheight == self.height): + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] < 64: # black + buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2)) + elif pixels[x, y] < 192: # convert gray to red + buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2)) + buf[int((x + y * self.width) / 4)] |= 0x40 >> (x % 4 * 2) + else: # white + buf[int((x + y * self.width) / 4)] |= 0xC0 >> (x % 4 * 2) + elif (imwidth == self.height and imheight == self.width): + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] < 64: # black + buf[int((newx + newy * self.width) / 4)] &= ~(0xC0 >> (y % 4 * 2)) + elif pixels[x, y] < 192: # convert gray to red + buf[int((newx + newy * self.width) / 4)] &= ~(0xC0 >> (y % 4 * 2)) + buf[int((newx + newy * self.width) / 4)] |= 0x40 >> (y % 4 * 2) + else: # white + buf[int((newx + newy * self.width) / 4)] |= 0xC0 >> (y % 4 * 2) + return buf + + def display(self, image): + self.send_command(0x10) + for i in range(0, int(self.width / 4 * self.height)): + temp1 = image[i] + j = 0 + while (j < 4): + if ((temp1 & 0xC0) == 0xC0): + temp2 = 0x03 + elif ((temp1 & 0xC0) == 0x00): + temp2 = 0x00 + else: + temp2 = 0x04 + temp2 = (temp2 << 4) & 0xFF + temp1 = (temp1 << 2) & 0xFF + j += 1 + if ((temp1 & 0xC0) == 0xC0): + temp2 |= 0x03 + elif ((temp1 & 0xC0) == 0x00): + temp2 |= 0x00 + else: + temp2 |= 0x04 + temp1 = (temp1 << 2) & 0xFF + self.send_data(temp2) + j += 1 + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width / 4 * self.height)): + for j in range(0, 4): + self.send_data(0x33) + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v5in83_v2/epd5in83_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v5in83_v2/epd5in83_V2.py new file mode 100644 index 00000000..852551c3 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v5in83_v2/epd5in83_V2.py @@ -0,0 +1,176 @@ +# ***************************************************************************** +# * | File : epd5in83_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-10 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 648 +EPD_HEIGHT = 480 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x12); # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy(); + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x07) + self.send_data(0x07) # VGH=20V,VGL=-20V + self.send_data(0x3f) # VDH=15V + self.send_data(0x3f) # VDL=-15V + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x1F) # KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0x61) # tres + self.send_data(0x02) # source 648 + self.send_data(0x88) + self.send_data(0x01) # gate 480 + self.send_data(0xE0) + + self.send_command(0X15) + self.send_data(0x00) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x10) + self.send_data(0x07) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + # EPD hardware init end + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + buf = [0x00] * int(self.width * self.height / 8) + for i in range(0, int(self.width * self.height / 8)): + buf[i] = ~image[i] + self.send_command(0x10) + self.send_data2([0x00] * int(self.width * self.height / 8)) + self.send_command(0x13) + self.send_data2(buf) + self.TurnOnDisplay() + + def Clear(self): + self.send_command(0x10) + self.send_data2([0x00] * int(self.width * self.height / 8)) + self.send_command(0x13) + self.send_data2([0x00] * int(self.width * self.height / 8)) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v5in83b_v2/epd5in83b_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v5in83b_v2/epd5in83b_V2.py new file mode 100644 index 00000000..56c498ce --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v5in83b_v2/epd5in83b_V2.py @@ -0,0 +1,181 @@ +# ***************************************************************************** +# * | File : epd5in83b_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-10 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 648 +EPD_HEIGHT = 480 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(1) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0X71) + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0X71) + epdconfig.delay_ms(200) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x07) + self.send_data(0x07) # VGH=20V,VGL=-20V + self.send_data(0x3f) # VDH=15V + self.send_data(0x3f) # VDL=-15V + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x0F) # KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0x61) # tres + self.send_data(0x02) # source 648 + self.send_data(0x88) + self.send_data(0x01) # gate 480 + self.send_data(0xe0) + + self.send_command(0X15) + self.send_data(0x00) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x11) + self.send_data(0x07) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + buf = [0x00] * int(self.width * self.height / 8) + for i in range(0, int(self.width * self.height / 8)): + buf[i] = ~imagered[i] + + if (imageblack != None): + self.send_command(0X10) + self.send_data2(imageblack) + if (imagered != None): + self.send_command(0X13) + self.send_data2(buf) + + self.send_command(0x12) + epdconfig.delay_ms(200) + self.ReadBusy() + + def Clear(self): + self.send_command(0X10) + self.send_data2([0xFF] * int(self.width * self.height / 8)) + self.send_command(0X13) + self.send_data2([0x00] * int(self.width * self.height / 8)) + + self.send_command(0x12) + epdconfig.delay_ms(200) + self.ReadBusy() + + def sleep(self): + self.send_command(0X02) # power off + self.ReadBusy() + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v5in83bc/epd5in83bc.py b/pwnagotchi/ui/hw/libs/waveshare/v5in83bc/epd5in83bc.py new file mode 100644 index 00000000..57cf208e --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v5in83bc/epd5in83bc.py @@ -0,0 +1,203 @@ +# ***************************************************************************** +# * | File : epd5in83b.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 600 +EPD_HEIGHT = 448 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x37) + self.send_data(0x00) + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0xCF) + self.send_data(0x08) + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3A) # PLL: 0-15:0x3C, 15+:0x3A + self.send_command(0X82) # VCOM VOLTAGE SETTING + self.send_data(0x28) # all temperature range + + self.send_command(0x06) # boost + self.send_data(0xc7) + self.send_data(0xcc) + self.send_data(0x15) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x77) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + self.send_command(0X65) # FLASH CONTROL + self.send_data(0x00) + + self.send_command(0x61) # tres + self.send_data(0x02) # source 600 + self.send_data(0x58) + self.send_data(0x01) # gate 448 + self.send_data(0xc0) + + self.send_command(0xe5) # FLASH MODE + self.send_data(0x03) + self.send_data(0x03) + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logger.debug('imwidth = %d imheight = %d ', imwidth, imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width / 8 * self.height)): + temp1 = imageblack[i] + temp2 = imagered[i] + j = 0 + while (j < 8): + if ((temp2 & 0x80) == 0x00): + temp3 = 0x04 # red + elif ((temp1 & 0x80) == 0x00): + temp3 = 0x00 # black + else: + temp3 = 0x03 # white + + temp3 = (temp3 << 4) & 0xFF + temp1 = (temp1 << 1) & 0xFF + temp2 = (temp2 << 1) & 0xFF + j += 1 + if ((temp2 & 0x80) == 0x00): + temp3 |= 0x04 # red + elif ((temp1 & 0x80) == 0x00): + temp3 |= 0x00 # black + else: + temp3 |= 0x03 # white + temp1 = (temp1 << 1) & 0xFF + temp2 = (temp2 << 1) & 0xFF + self.send_data(temp3) + j += 1 + + self.send_command(0x04) # POWER ON + self.ReadBusy() + self.send_command(0x12) # display refresh + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width / 8 * self.height)): + self.send_data(0x33) + self.send_data(0x33) + self.send_data(0x33) + self.send_data(0x33) + + self.send_command(0x04) # POWER ON + self.ReadBusy() + self.send_command(0x12) # display refresh + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0xA5) # check code + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in3f/epd7in3f.py b/pwnagotchi/ui/hw/libs/waveshare/v7in3f/epd7in3f.py new file mode 100644 index 00000000..bf756751 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in3f/epd7in3f.py @@ -0,0 +1,249 @@ +# ***************************************************************************** +# * | File : epd7in3f.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2022-10-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# ******************************************************************************/ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +import PIL +from PIL import Image +import io + +# Display resolution +EPD_WIDTH = 800 +EPD_HEIGHT = 480 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.BLACK = 0x000000 # 0000 BGR + self.WHITE = 0xffffff # 0001 + self.GREEN = 0x00ff00 # 0010 + self.BLUE = 0xff0000 # 0011 + self.RED = 0x0000ff # 0100 + self.YELLOW = 0x00ffff # 0101 + self.ORANGE = 0x0080ff # 0110 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) # module reset + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + # send a lot of data + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusyH(self): + logger.debug("e-Paper busy H") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy H release") + + def TurnOnDisplay(self): + self.send_command(0x04) # POWER_ON + self.ReadBusyH() + + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0X00) + self.ReadBusyH() + + self.send_command(0x02) # POWER_OFF + self.send_data(0X00) + self.ReadBusyH() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + self.ReadBusyH() + epdconfig.delay_ms(30) + + self.send_command(0xAA) # CMDH + self.send_data(0x49) + self.send_data(0x55) + self.send_data(0x20) + self.send_data(0x08) + self.send_data(0x09) + self.send_data(0x18) + + self.send_command(0x01) + self.send_data(0x3F) + self.send_data(0x00) + self.send_data(0x32) + self.send_data(0x2A) + self.send_data(0x0E) + self.send_data(0x2A) + + self.send_command(0x00) + self.send_data(0x5F) + self.send_data(0x69) + + self.send_command(0x03) + self.send_data(0x00) + self.send_data(0x54) + self.send_data(0x00) + self.send_data(0x44) + + self.send_command(0x05) + self.send_data(0x40) + self.send_data(0x1F) + self.send_data(0x1F) + self.send_data(0x2C) + + self.send_command(0x06) + self.send_data(0x6F) + self.send_data(0x1F) + self.send_data(0x1F) + self.send_data(0x22) + + self.send_command(0x08) + self.send_data(0x6F) + self.send_data(0x1F) + self.send_data(0x1F) + self.send_data(0x22) + + self.send_command(0x13) # IPC + self.send_data(0x00) + self.send_data(0x04) + + self.send_command(0x30) + self.send_data(0x3C) + + self.send_command(0x41) # TSE + self.send_data(0x00) + + self.send_command(0x50) + self.send_data(0x3F) + + self.send_command(0x60) + self.send_data(0x02) + self.send_data(0x00) + + self.send_command(0x61) + self.send_data(0x03) + self.send_data(0x20) + self.send_data(0x01) + self.send_data(0xE0) + + self.send_command(0x82) + self.send_data(0x1E) + + self.send_command(0x84) + self.send_data(0x00) + + self.send_command(0x86) # AGID + self.send_data(0x00) + + self.send_command(0xE3) + self.send_data(0x2F) + + self.send_command(0xE0) # CCSET + self.send_data(0x00) + + self.send_command(0xE6) # TSSET + self.send_data(0x00) + return 0 + + def getbuffer(self, image): + # Create a pallette with the 7 colors supported by the panel + pal_image = Image.new("P", (1, 1)) + pal_image.putpalette( + (0, 0, 0, 255, 255, 255, 0, 255, 0, 0, 0, 255, 255, 0, 0, 255, 255, 0, 255, 128, 0) + (0, 0, 0) * 249) + + # Check if we need to rotate the image + imwidth, imheight = image.size + if (imwidth == self.width and imheight == self.height): + image_temp = image + elif (imwidth == self.height and imheight == self.width): + image_temp = image.rotate(90, expand=True) + else: + logger.warning( + "Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) + + # Convert the soruce image to the 7 colors, dithering if needed + image_7color = image_temp.convert("RGB").quantize(palette=pal_image) + buf_7color = bytearray(image_7color.tobytes('raw')) + + # PIL does not support 4 bit color, so pack the 4 bits of color + # into a single byte to transfer to the panel + buf = [0x00] * int(self.width * self.height / 2) + idx = 0 + for i in range(0, len(buf_7color), 2): + buf[idx] = (buf_7color[i] << 4) + buf_7color[i + 1] + idx += 1 + + return buf + + def display(self, image): + self.send_command(0x10) + self.send_data2(image) + + self.TurnOnDisplay() + + def Clear(self, color=0x11): + self.send_command(0x10) + self.send_data2([color] * int(self.height) * int(self.width / 2)) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in3g/epd7in3g.py b/pwnagotchi/ui/hw/libs/waveshare/v7in3g/epd7in3g.py new file mode 100644 index 00000000..7ef4a46b --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in3g/epd7in3g.py @@ -0,0 +1,243 @@ +# ***************************************************************************** +# * | File : epd7in3g.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1 +# * | Date : 2022-07-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# ******************************************************************************/ +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +import logging +from .. import epdconfig + +import PIL +from PIL import Image +import io + +# Display resolution +EPD_WIDTH = 800 +EPD_HEIGHT = 480 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + self.BLACK = 0x000000 # 00 BGR + self.WHITE = 0xffffff # 01 + self.YELLOW = 0x00ffff # 10 + self.RED = 0x0000ff # 11 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) # module reset + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusyH(self): + logger.debug("e-Paper busy H") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(5) + logger.debug("e-Paper busy H release") + + def ReadBusyL(self): + logger.debug("e-Paper busy L") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy L release") + + def TurnOnDisplay(self): + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0x01) + self.ReadBusyH() + + self.send_command(0x02) # POWER_OFF + self.send_data(0X00) + self.ReadBusyH() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + self.ReadBusyH() + epdconfig.delay_ms(30) + + self.send_command(0xAA) + self.send_data(0x49) + self.send_data(0x55) + self.send_data(0x20) + self.send_data(0x08) + self.send_data(0x09) + self.send_data(0x18) + + self.send_command(0x01) + self.send_data(0x3F) + + self.send_command(0x00) + self.send_data(0x4F) + self.send_data(0x69) + + self.send_command(0x05) + self.send_data(0x40) + self.send_data(0x1F) + self.send_data(0x1F) + self.send_data(0x2C) + + self.send_command(0x08) + self.send_data(0x6F) + self.send_data(0x1F) + self.send_data(0x1F) + self.send_data(0x22) + + # =================== + # 20211212 + # First setting + self.send_command(0x06) + self.send_data(0x6F) + self.send_data(0x1F) + self.send_data(0x14) + self.send_data(0x14) + # =================== + + self.send_command(0x03) + self.send_data(0x00) + self.send_data(0x54) + self.send_data(0x00) + self.send_data(0x44) + + self.send_command(0x60) + self.send_data(0x02) + self.send_data(0x00) + # Please notice that PLL must be set for version 2 IC + self.send_command(0x30) + self.send_data(0x08) + + self.send_command(0x50) + self.send_data(0x3F) + + self.send_command(0x61) + self.send_data(0x03) + self.send_data(0x20) + self.send_data(0x01) + self.send_data(0xE0) + + self.send_command(0xE3) + self.send_data(0x2F) + + self.send_command(0x84) + self.send_data(0x01) + return 0 + + def getbuffer(self, image): + # Create a pallette with the 4 colors supported by the panel + pal_image = Image.new("P", (1, 1)) + pal_image.putpalette((0, 0, 0, 255, 255, 255, 255, 255, 0, 255, 0, 0) + (0, 0, 0) * 252) + + # Check if we need to rotate the image + imwidth, imheight = image.size + if (imwidth == self.width and imheight == self.height): + image_temp = image + elif (imwidth == self.height and imheight == self.width): + image_temp = image.rotate(90, expand=True) + else: + logger.warning( + "Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) + + # Convert the soruce image to the 4 colors, dithering if needed + image_4color = image_temp.convert("RGB").quantize(palette=pal_image) + buf_4color = bytearray(image_4color.tobytes('raw')) + + # into a single byte to transfer to the panel + buf = [0x00] * int(self.width * self.height / 4) + idx = 0 + for i in range(0, len(buf_4color), 4): + buf[idx] = (buf_4color[i] << 6) + (buf_4color[i + 1] << 4) + (buf_4color[i + 2] << 2) + buf_4color[i + 3] + idx += 1 + return buf + + def display(self, image): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(image[i + j * Width]) + self.TurnOnDisplay() + + def Clear(self, color=0x55): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(color) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.send_data(0x00) + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5/epd7in5.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5/epd7in5.py new file mode 100644 index 00000000..1beef452 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5/epd7in5.py @@ -0,0 +1,185 @@ +# ***************************************************************************** +# * | File : epd7in5.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 640 +EPD_HEIGHT = 384 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data2([0x37, 0x00]) + + self.send_command(0x00) # PANEL_SETTING + self.send_data2([0xCF, 0x08]) + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data2([0xc7, 0xcc, 0x28]) + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3c) + + self.send_command(0x41) # TEMPERATURE_CALIBRATION + self.send_data(0x00) + + self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0x77) + + self.send_command(0x60) # TCON_SETTING + self.send_data(0x22) + + self.send_command(0x61) # TCON_RESOLUTION + self.send_data(EPD_WIDTH >> 8) # source 640 + self.send_data(EPD_WIDTH & 0xff) + self.send_data(EPD_HEIGHT >> 8) # gate 384 + self.send_data(EPD_HEIGHT & 0xff) + + self.send_command(0x82) # VCM_DC_SETTING + self.send_data(0x1E) # decide by LUT file + + self.send_command(0xe5) # FLASH MODE + self.send_data(0x03) + + # EPD hardware init end + return 0 + + def getbuffer(self, image): + img = image + imwidth, imheight = img.size + halfwidth = int(self.width / 2) + buf = [0x33] * halfwidth * self.height + + if (imwidth == self.width and imheight == self.height): + img = img.convert('1') + elif (imwidth == self.height and imheight == self.width): + img = img.rotate(90, expand=True).convert('1') + imwidth, imheight = img.size + else: + logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) + # return a blank buffer + return buf + + pixels = img.load() + + for y in range(imheight): + offset = y * halfwidth + for x in range(1, imwidth, 2): + i = offset + x // 2 + if (pixels[x - 1, y] > 191): + if (pixels[x, y] > 191): + buf[i] = 0x33 + else: + buf[i] = 0x30 + else: + if (pixels[x, y] > 191): + buf[i] = 0x03 + else: + buf[i] = 0x00 + return buf + + def display(self, image): + self.send_command(0x10) + self.send_data2(image) + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + buf = [0x33] * int(self.width * self.height / 2) + self.send_command(0x10) + self.send_data2(buf) + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5_HD/epd7in5_HD.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5_HD/epd7in5_HD.py new file mode 100644 index 00000000..e42307ed --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5_HD/epd7in5_HD.py @@ -0,0 +1,181 @@ +# ***************************************************************************** +# * | File : epd7in5.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 880 +EPD_HEIGHT = 528 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 1): + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(200) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.ReadBusy(); + self.send_command(0x12); # SWRESET + self.ReadBusy(); + + self.send_command(0x46); # Auto Write Red RAM + self.send_data(0xf7); + self.ReadBusy(); + self.send_command(0x47); # Auto Write B/W RAM + self.send_data(0xf7); + self.ReadBusy(); + + self.send_command(0x0C); # Soft start setting + self.send_data2([0xAE, 0xC7, 0xC3, 0xC0, 0x40]) + + self.send_command(0x01); # Set MUX as 527 + self.send_data2([0xAF, 0x02, 0x01]) + + self.send_command(0x11); # Data entry mode + self.send_data(0x01); + + self.send_command(0x44); + self.send_data2([0x00, 0x00, 0x6F, 0x03]) # RAM x address start at 0 + self.send_command(0x45); + self.send_data2([0xAF, 0x02, 0x00, 0x00]) + + self.send_command(0x3C); # VBD + self.send_data(0x05); # LUT1, for white + + self.send_command(0x18); + self.send_data(0X80); + + self.send_command(0x22); + self.send_data(0XB1); # Load Temperature and waveform setting. + self.send_command(0x20); + self.ReadBusy(); + + self.send_command(0x4E); # set RAM x address count to 0; + self.send_data2([0x00, 0x00]) + self.send_command(0x4F); + self.send_data2([0x00, 0x00]) + # EPD hardware init end + return 0 + + def getbuffer(self, image): + img = image + imwidth, imheight = img.size + if (imwidth == self.width and imheight == self.height): + img = img.convert('1') + elif (imwidth == self.height and imheight == self.width): + img = img.rotate(90, expand=True).convert('1') + else: + logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) + # return a blank buffer + return [0xff] * int(self.width * self.height / 8) + + buf = bytearray(img.tobytes('raw')) + return buf + + def display(self, image): + self.send_command(0x4F); + self.send_data2([0x00, 0x00]) + self.send_command(0x24); + self.send_data2(image) + self.send_command(0x22); + self.send_data(0xF7); # Load LUT from MCU(0x32) + self.send_command(0x20); + epdconfig.delay_ms(10); + self.ReadBusy(); + + def Clear(self): + buf = [0xff] * int(self.width * self.height / 8) + self.send_command(0x4F); + self.send_data2([0x00, 0x00]) + self.send_command(0x24) + self.send_data2(buf) + + self.send_command(0x26) + self.send_data2(buf) + + self.send_command(0x22); + self.send_data(0xF7); # Load LUT from MCU(0x32) + self.send_command(0x20); + epdconfig.delay_ms(10); + self.ReadBusy(); + + def sleep(self): + self.send_command(0x10); + self.send_data(0x01); + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2.py new file mode 100644 index 00000000..d122149d --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2.py @@ -0,0 +1,289 @@ +# ***************************************************************************** +# * | File : epd7in5.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 800 +EPD_HEIGHT = 480 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.SPI.writebytes2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 0): + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x06) # btst + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x28) # If an exception is displayed, try using 0x38 + self.send_data(0x17) + + self.send_command(0x01) # POWER SETTING + self.send_data(0x07) + self.send_data(0x07) # VGH=20V,VGL=-20V + self.send_data(0x3f) # VDH=15V + self.send_data(0x3f) # VDL=-15V + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x1F) # KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0x61) # tres + self.send_data(0x03) # source 800 + self.send_data(0x20) + self.send_data(0x01) # gate 480 + self.send_data(0xE0) + + self.send_command(0X15) + self.send_data(0x00) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x10) + self.send_data(0x07) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + # EPD hardware init end + return 0 + + def init_fast(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x1F) # KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x10) + self.send_data(0x07) + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + # Enhanced display drive(Add 0x06 command) + self.send_command(0x06) # Booster Soft Start + self.send_data(0x27) + self.send_data(0x27) + self.send_data(0x18) + self.send_data(0x17) + + self.send_command(0xE0) + self.send_data(0x02) + self.send_command(0xE5) + self.send_data(0x5A) + + # EPD hardware init end + return 0 + + def init_part(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x1F) # KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0xE0) + self.send_data(0x02) + self.send_command(0xE5) + self.send_data(0x6E) + + # EPD hardware init end + return 0 + + def getbuffer(self, image): + img = image + imwidth, imheight = img.size + if (imwidth == self.width and imheight == self.height): + img = img.convert('1') + elif (imwidth == self.height and imheight == self.width): + # image has correct dimensions, but needs to be rotated + img = img.rotate(90, expand=True).convert('1') + else: + logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) + # return a blank buffer + return [0x00] * (int(self.width / 8) * self.height) + + buf = bytearray(img.tobytes('raw')) + # The bytes need to be inverted, because in the PIL world 0=black and 1=white, but + # in the e-paper world 0=white and 1=black. + for i in range(len(buf)): + buf[i] ^= 0xFF + return buf + + def display(self, image): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + image1 = [0xFF] * int(self.width * self.height / 8) + for j in range(Height): + for i in range(Width): + image1[i + j * Width] = ~image[i + j * Width] + self.send_command(0x10) + self.send_data2(image1) + + self.send_command(0x13) + self.send_data2(image) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + self.send_data2([0xFF] * int(self.width * self.height / 8)) + self.send_command(0x13) + self.send_data2([0x00] * int(self.width * self.height / 8)) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def display_Partial(self, Image, Xstart, Ystart, Xend, Yend): + if ((Xstart % 8 + Xend % 8 == 8 & Xstart % 8 > Xend % 8) | Xstart % 8 + Xend % 8 == 0 | ( + Xend - Xstart) % 8 == 0): + Xstart = Xstart // 8 * 8 + Xend = Xend // 8 * 8 + else: + Xstart = Xstart // 8 * 8 + if Xend % 8 == 0: + Xend = Xend // 8 * 8 + else: + Xend = Xend // 8 * 8 + 1 + + Width = (Xend - Xstart) / 8 + Height = Yend - Ystart + + self.send_command(0x50) + self.send_data(0xA9) + self.send_data(0x07) + + self.send_command(0x91) # This command makes the display enter partial mode + self.send_command(0x90) # resolution setting + self.send_data(Xstart // 256) + self.send_data(Xstart % 256) # x-start + + self.send_data((Xend - 1) // 256) + self.send_data((Xend - 1) % 256) # x-end + + self.send_data(Ystart // 256) # + self.send_data(Ystart % 256) # y-start + + self.send_data((Yend - 1) // 256) + self.send_data((Yend - 1) % 256) # y-end + self.send_data(0x01) + + image1 = [0xFF] * int(self.width * self.height / 8) + for j in range(Height): + for i in range(Width): + image1[i + j * Width] = ~Image[i + j * Width] + + self.send_command(0x13) # Write Black and White image to RAM + self.send_data2(image1) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2_fast.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2_fast.py new file mode 100644 index 00000000..4c481e70 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2_fast.py @@ -0,0 +1,280 @@ +# ***************************************************************************** +# * | File : epd7in5.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 800 +EPD_HEIGHT = 480 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + Voltage_Frame_7IN5_V2 = [ + 0x6, 0x3F, 0x3F, 0x11, 0x24, 0x7, 0x17, + ] + + LUT_VCOM_7IN5_V2 = [ + 0x0, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x0, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x0, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + ] + + LUT_WW_7IN5_V2 = [ + 0x10, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x20, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + ] + + LUT_BW_7IN5_V2 = [ + 0x10, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x20, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + ] + + LUT_WB_7IN5_V2 = [ + 0x80, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x40, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + ] + + LUT_BB_7IN5_V2 = [ + 0x80, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x40, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 0): + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def SetLut(self, lut_vcom, lut_ww, lut_bw, lut_wb, lut_bb): + self.send_command(0x20) + for count in range(0, 42): + self.send_data(lut_vcom[count]) + + self.send_command(0x21) + for count in range(0, 42): + self.send_data(lut_ww[count]) + + self.send_command(0x22) + for count in range(0, 42): + self.send_data(lut_bw[count]) + + self.send_command(0x23) + for count in range(0, 42): + self.send_data(lut_wb[count]) + + self.send_command(0x24) + for count in range(0, 42): + self.send_data(lut_bb[count]) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + # self.send_command(0x06) # btst + # self.send_data(0x17) + # self.send_data(0x17) + # self.send_data(0x28) # If an exception is displayed, try using 0x38 + # self.send_data(0x17) + + # self.send_command(0x01) #POWER SETTING + # self.send_data(0x07) + # self.send_data(0x07) #VGH=20V,VGL=-20V + # self.send_data(0x3f) #VDH=15V + # self.send_data(0x3f) #VDL=-15V + + self.send_command(0x01) # power setting + self.send_data(0x17) # 1-0=11: internal power + self.send_data(self.Voltage_Frame_7IN5_V2[6]) # VGH&VGL + self.send_data(self.Voltage_Frame_7IN5_V2[1]) # VSH + self.send_data(self.Voltage_Frame_7IN5_V2[2]) # VSL + self.send_data(self.Voltage_Frame_7IN5_V2[3]) # VSHR + + self.send_command(0x82) # VCOM DC Setting + self.send_data(self.Voltage_Frame_7IN5_V2[4]) # VCOM + + self.send_command(0x06) # Booster Setting + self.send_data(0x27) + self.send_data(0x27) + self.send_data(0x2F) + self.send_data(0x17) + + self.send_command(0x30) # OSC Setting + self.send_data(self.Voltage_Frame_7IN5_V2[0]) # 3C=50Hz, 3A=100HZ + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x3F) # KW-3f KWR-2F BWROTP-0f BWOTP-1f + + self.send_command(0x61) # tres + self.send_data(0x03) # source 800 + self.send_data(0x20) + self.send_data(0x01) # gate 480 + self.send_data(0xE0) + + self.send_command(0X15) + self.send_data(0x00) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x10) + self.send_data(0x07) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + self.send_command(0x65) # Resolution setting + self.send_data(0x00) + self.send_data(0x00) # 800*480 + self.send_data(0x00) + self.send_data(0x00) + + self.SetLut(self.LUT_VCOM_7IN5_V2, self.LUT_WW_7IN5_V2, self.LUT_BW_7IN5_V2, self.LUT_WB_7IN5_V2, + self.LUT_BB_7IN5_V2) + # EPD hardware init end + return 0 + + def getbuffer(self, image): + img = image + imwidth, imheight = img.size + if (imwidth == self.width and imheight == self.height): + img = img.convert('1') + elif (imwidth == self.height and imheight == self.width): + # image has correct dimensions, but needs to be rotated + img = img.rotate(90, expand=True).convert('1') + else: + logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) + # return a blank buffer + return [0x00] * (int(self.width / 8) * self.height) + + buf = bytearray(img.tobytes('raw')) + # The bytes need to be inverted, because in the PIL world 0=black and 1=white, but + # in the e-paper world 0=white and 1=black. + for i in range(len(buf)): + buf[i] ^= 0xFF + return buf + + def display(self, image): + self.send_command(0x13) + self.send_data2(image) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + buf = [0x00] * (int(self.width / 8) * self.height) + self.send_command(0x10) + self.send_data2(buf) + self.send_command(0x13) + self.send_data2(buf) + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2_old.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2_old.py new file mode 100644 index 00000000..54bef805 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2_old.py @@ -0,0 +1,530 @@ +# ***************************************************************************** +# * | File : epd7in5.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 800 +EPD_HEIGHT = 480 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + Voltage_Frame_7IN5_V2 = [ + 0x6, 0x3F, 0x3F, 0x11, 0x24, 0x7, 0x17, + ] + + LUT_VCOM_7IN5_V2 = [ + 0x0, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x0, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x0, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + ] + + LUT_WW_7IN5_V2 = [ + 0x10, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x20, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + ] + + LUT_BW_7IN5_V2 = [ + 0x10, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x20, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + ] + + LUT_WB_7IN5_V2 = [ + 0x80, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x40, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + ] + + LUT_BB_7IN5_V2 = [ + 0x80, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x40, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + ] + + Lut_all_fresh = [0x67, 0xBF, 0x3F, 0x0D, 0x00, 0x1C, + # VCOM + 0x00, 0x32, 0x32, 0x00, 0x00, 0x01, + 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x00, + 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + # WW + 0x60, 0x32, 0x32, 0x00, 0x00, 0x01, + 0x60, 0x0A, 0x0A, 0x00, 0x00, 0x00, + 0x80, 0x28, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + # BW + 0x60, 0x32, 0x32, 0x00, 0x00, 0x01, + 0x60, 0x0A, 0x0A, 0x00, 0x00, 0x00, + 0x80, 0x28, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + # WB + 0x90, 0x32, 0x32, 0x00, 0x00, 0x01, + 0x60, 0x0A, 0x0A, 0x00, 0x00, 0x00, + 0x40, 0x28, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + # BB + 0x90, 0x32, 0x32, 0x00, 0x00, 0x01, + 0x60, 0x0A, 0x0A, 0x00, 0x00, 0x00, + 0x40, 0x28, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + # Reserved + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, + ] + + Lut_partial = [0x67, 0xBF, 0x3F, 0x0D, 0x00, 0x1C, + # VCOM + 0x00, 0x14, 0x02, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + # WW + 0x20, 0x14, 0x02, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + # BW + 0x80, 0x14, 0x02, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + # WB + 0x40, 0x14, 0x02, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + # BB + 0x00, 0x14, 0x02, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + # Reserved + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 0): + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def SetLut(self, lut_vcom, lut_ww, lut_bw, lut_wb, lut_bb): + self.send_command(0x20) + for count in range(0, 42): + self.send_data(lut_vcom[count]) + + self.send_command(0x21) + for count in range(0, 42): + self.send_data(lut_ww[count]) + + self.send_command(0x22) + for count in range(0, 42): + self.send_data(lut_bw[count]) + + self.send_command(0x23) + for count in range(0, 42): + self.send_data(lut_wb[count]) + + self.send_command(0x24) + for count in range(0, 42): + self.send_data(lut_bb[count]) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + # self.send_command(0x06) # btst + # self.send_data(0x17) + # self.send_data(0x17) + # self.send_data(0x28) # If an exception is displayed, try using 0x38 + # self.send_data(0x17) + + # self.send_command(0x01) #POWER SETTING + # self.send_data(0x07) + # self.send_data(0x07) #VGH=20V,VGL=-20V + # self.send_data(0x3f) #VDH=15V + # self.send_data(0x3f) #VDL=-15V + + self.send_command(0x01) # power setting + self.send_data(0x17) # 1-0=11: internal power + self.send_data(self.Voltage_Frame_7IN5_V2[6]) # VGH&VGL + self.send_data(self.Voltage_Frame_7IN5_V2[1]) # VSH + self.send_data(self.Voltage_Frame_7IN5_V2[2]) # VSL + self.send_data(self.Voltage_Frame_7IN5_V2[3]) # VSHR + + self.send_command(0x82) # VCOM DC Setting + self.send_data(self.Voltage_Frame_7IN5_V2[4]) # VCOM + + self.send_command(0x06) # Booster Setting + self.send_data(0x27) + self.send_data(0x27) + self.send_data(0x2F) + self.send_data(0x17) + + self.send_command(0x30) # OSC Setting + self.send_data(self.Voltage_Frame_7IN5_V2[0]) # 3C=50Hz, 3A=100HZ + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x3F) # KW-3f KWR-2F BWROTP-0f BWOTP-1f + + self.send_command(0x61) # tres + self.send_data(0x03) # source 800 + self.send_data(0x20) + self.send_data(0x01) # gate 480 + self.send_data(0xE0) + + self.send_command(0X15) + self.send_data(0x00) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x10) + self.send_data(0x07) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + self.send_command(0x65) # Resolution setting + self.send_data(0x00) + self.send_data(0x00) # 800*480 + self.send_data(0x00) + self.send_data(0x00) + + self.SetLut(self.LUT_VCOM_7IN5_V2, self.LUT_WW_7IN5_V2, self.LUT_BW_7IN5_V2, self.LUT_WB_7IN5_V2, + self.LUT_BB_7IN5_V2) + # EPD hardware init end + return 0 + + def Epaper_LUT_By_MCU(self, wavedata): + + VCEND = wavedata[0] & 0x08 + BDEND = (wavedata[1] & 0xC0) >> 6 + EVS = VCEND | BDEND + PLL = (wavedata[0] & 0xF0) >> 4 + XON = wavedata[2] & 0xC0 + + self.send_command(0x52) # EVS + self.send_data(EVS) + + self.send_command(0x30) # PLL setting + self.send_data(PLL) + + self.send_command(0x01) # Set VGH VGL VSH VSL VSHR + self.send_data(0x17) + self.send_data(wavedata[0] & 0x07) # VGH/VGL Voltage Level selection + self.send_data(wavedata[1] & 0x3F) # VSH for black + self.send_data(wavedata[2] & 0x3F) # VSL for white + self.send_data(wavedata[3] & 0x3F) # VSHR for red + + self.send_command(0x2A) # LUTOPT + self.send_data(XON) + self.send_data(wavedata[4]) + + self.send_command(0x82) # VCOM_DC setting + self.send_data(wavedata[5]) # Vcom value + + self.send_command(0x20) + self.send_data2(wavedata[6:48]) + + self.send_command(0x21) + self.send_data2(wavedata[48:90]) + + self.send_command(0x22) + self.send_data2(wavedata[90:132]) + + self.send_command(0x23) + self.send_data2(wavedata[132:174]) + + self.send_command(0x24) + self.send_data2(wavedata[174:216]) + + def init2(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x00) # Panel setting + self.send_data(0x3F) + + self.send_command(0x06) # Booster Setting + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x28) + self.send_data(0x18) + + self.send_command(0x50) # VCOM and DATA interval setting + self.send_data(0x22) + self.send_data(0x07) + + self.send_command(0x60) # TCON setting + self.send_data(0x22) # S-G G-S + + self.send_command(0x61) # Resolution setting + self.send_data(0x03) # 800*480 + self.send_data(0x20) + self.send_data(0x01) + self.send_data(0xE0) + + self.send_command(0x65) # Resolution setting + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() + + return 0 + + def init_fast(self): + self.init2() + self.Epaper_LUT_By_MCU(self.Lut_all_fresh) + return 0 + + def init_part(self): + self.init2() + self.Epaper_LUT_By_MCU(self.Lut_partial) + return 0 + + def getbuffer(self, image): + img = image + imwidth, imheight = img.size + if (imwidth == self.width and imheight == self.height): + img = img.convert('1') + elif (imwidth == self.height and imheight == self.width): + # image has correct dimensions, but needs to be rotated + img = img.rotate(90, expand=True).convert('1') + else: + logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) + # return a blank buffer + return [0x00] * (int(self.width / 8) * self.height) + + buf = bytearray(img.tobytes('raw')) + # The bytes need to be inverted, because in the PIL world 0=black and 1=white, but + # in the e-paper world 0=white and 1=black. + for i in range(len(buf)): + buf[i] ^= 0xFF + return buf + + def display(self, image): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + image1 = [0xFF] * int(self.width * self.height / 8) + for j in range(Height): + for i in range(Width): + image1[i + j * Width] = ~image[i + j * Width] + self.send_command(0x10) + self.send_data2(image1) + + self.send_command(0x13) + self.send_data2(image) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + self.send_data2([0xFF] * int(self.width * self.height / 8)) + self.send_command(0x13) + self.send_data2([0x00] * int(self.width * self.height / 8)) + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def display_Partial(self, Image, Xstart, Ystart, Xend, Yend): + if ((Xstart % 8 + Xend % 8 == 8 & Xstart % 8 > Xend % 8) | Xstart % 8 + Xend % 8 == 0 | ( + Xend - Xstart) % 8 == 0): + Xstart = Xstart // 8 * 8 + Xend = Xend // 8 * 8 + else: + Xstart = Xstart // 8 * 8 + if Xend % 8 == 0: + Xend = Xend // 8 * 8 + else: + Xend = Xend // 8 * 8 + 1 + + Width = (Xend - Xstart) / 8 + Height = Yend - Ystart + + self.send_command(0x50) + self.send_data(0xA9) + self.send_data(0x07) + + self.send_command(0x91) # This command makes the display enter partial mode + self.send_command(0x90) # resolution setting + self.send_data(Xstart // 256) + self.send_data(Xstart % 256) # x-start + + self.send_data((Xend - 1) // 256) + self.send_data((Xend - 1) % 256) # x-end + + self.send_data(Ystart // 256) # + self.send_data(Ystart % 256) # y-start + + self.send_data((Yend - 1) // 256) + self.send_data((Yend - 1) % 256) # y-end + self.send_data(0x01) + + image1 = [0xFF] * int(self.width * self.height / 8) + for j in range(Height): + for i in range(Width): + image1[i + j * Width] = ~Image[i + j * Width] + + self.send_command(0x13) # Write Black and White image to RAM + self.send_data2(image1) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5b_HD/epd7in5b_HD.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5b_HD/epd7in5b_HD.py new file mode 100644 index 00000000..a1a24827 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5b_HD/epd7in5b_HD.py @@ -0,0 +1,206 @@ +# ***************************************************************************** +# * | File : epd7in5bc_HD.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 880 +EPD_HEIGHT = 528 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(4) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 1): + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(200) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x12); # SWRESET + self.ReadBusy(); # waiting for the electronic paper IC to release the idle signal + + self.send_command(0x46); # Auto Write RAM + self.send_data(0xF7); + self.ReadBusy(); # waiting for the electronic paper IC to release the idle signal + + self.send_command(0x47); # Auto Write RAM + self.send_data(0xF7); + self.ReadBusy(); # waiting for the electronic paper IC to release the idle signal + + self.send_command(0x0C); # Soft start setting + self.send_data(0xAE); + self.send_data(0xC7); + self.send_data(0xC3); + self.send_data(0xC0); + self.send_data(0x40); + + self.send_command(0x01); # Set MUX as 527 + self.send_data(0xAF); + self.send_data(0x02); + self.send_data(0x01); + + self.send_command(0x11); # Data entry mode + self.send_data(0x01); + + self.send_command(0x44); + self.send_data(0x00); # RAM x address start at 0 + self.send_data(0x00); + self.send_data(0x6F); # RAM x address end at 36Fh -> 879 + self.send_data(0x03); + self.send_command(0x45); + self.send_data(0xAF); # RAM y address start at 20Fh; + self.send_data(0x02); + self.send_data(0x00); # RAM y address end at 00h; + self.send_data(0x00); + + self.send_command(0x3C); # VBD + self.send_data(0x01); # LUT1, for white + + self.send_command(0x18); + self.send_data(0X80); + self.send_command(0x22); + self.send_data(0XB1); # Load Temperature and waveform setting. + self.send_command(0x20); + self.ReadBusy(); # waiting for the electronic paper IC to release the idle signal + + self.send_command(0x4E); + self.send_data(0x00); + self.send_data(0x00); + self.send_command(0x4F); + self.send_data(0xAF); + self.send_data(0x02); + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logger.debug('imwidth = %d imheight = %d ', imwidth, imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x4F); + self.send_data(0xAf); + + self.send_command(0x24) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]); + + self.send_command(0x26) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~imagered[i]); + + self.send_command(0x22); + self.send_data(0xC7); # Load LUT from MCU(0x32) + self.send_command(0x20); + epdconfig.delay_ms(200); # !!!The delay here is necessary, 200uS at least!!! + self.ReadBusy(); + + def Clear(self): + self.send_command(0x4F); + self.send_data(0xAf); + + self.send_command(0x24) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xff); + + self.send_command(0x26) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00); + + self.send_command(0x22); + self.send_data(0xC7); # Load LUT from MCU(0x32) + self.send_command(0x20); + epdconfig.delay_ms(200); # !!!The delay here is necessary, 200uS at least!!! + self.ReadBusy(); + + def sleep(self): + self.send_command(0x10); # deep sleep + self.send_data(0x01); + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5b_v2/epd7in5b_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5b_v2/epd7in5b_V2.py new file mode 100644 index 00000000..c58cdc3b --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5b_v2/epd7in5b_V2.py @@ -0,0 +1,192 @@ +# ***************************************************************************** +# * | File : epd7in5b_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.2 +# * | Date : 2022-01-08 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 800 +EPD_HEIGHT = 480 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(4) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data2(self, data): # faster + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 0): + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(200) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + # self.send_command(0x06) # btst + # self.send_data(0x17) + # self.send_data(0x17) + # self.send_data(0x38) # If an exception is displayed, try using 0x38 + # self.send_data(0x17) + + self.send_command(0x01) # POWER SETTING + self.send_data(0x07) + self.send_data(0x07) # VGH=20V,VGL=-20V + self.send_data(0x3f) # VDH=15V + self.send_data(0x3f) # VDL=-15V + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x0F) # KW-3f KWR-2F BWROTP-0f BWOTP-1f + + self.send_command(0x61) # tres + self.send_data(0x03) # source 800 + self.send_data(0x20) + self.send_data(0x01) # gate 480 + self.send_data(0xE0) + + self.send_command(0X15) + self.send_data(0x00) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x11) + self.send_data(0x07) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + self.send_command(0x65) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + return 0 + + def getbuffer(self, image): + img = image + imwidth, imheight = img.size + if (imwidth == self.width and imheight == self.height): + img = img.convert('1') + elif (imwidth == self.height and imheight == self.width): + # image has correct dimensions, but needs to be rotated + img = img.rotate(90, expand=True).convert('1') + else: + logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) + # return a blank buffer + return [0x00] * (int(self.width / 8) * self.height) + + buf = bytearray(img.tobytes('raw')) + # The bytes need to be inverted, because in the PIL world 0=black and 1=white, but + # in the e-paper world 0=white and 1=black. + for i in range(len(buf)): + buf[i] ^= 0xFF + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + # The black bytes need to be inverted back from what getbuffer did + for i in range(len(imageblack)): + imageblack[i] ^= 0xFF + self.send_data2(imageblack) + + self.send_command(0x13) + self.send_data2(imagered) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + buf = [0x00] * (int(self.width / 8) * self.height) + buf2 = [0xff] * (int(self.width / 8) * self.height) + self.send_command(0x10) + self.send_data2(buf2) + + self.send_command(0x13) + self.send_data2(buf) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5bc/epd7in5bc.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5bc/epd7in5bc.py new file mode 100644 index 00000000..356efc61 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5bc/epd7in5bc.py @@ -0,0 +1,204 @@ +# ***************************************************************************** +# * | File : epd7in5bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 640 +EPD_HEIGHT = 384 + +logger = logging.getLogger(__name__) + + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x37) + self.send_data(0x00) + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0xCF) + self.send_data(0x08) + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3A) # PLL: 0-15:0x3C, 15+:0x3A + + self.send_command(0x82) # VCM_DC_SETTING + self.send_data(0x28) # all temperature range + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0xc7) + self.send_data(0xcc) + self.send_data(0x15) + + self.send_command(0x50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x77) + + self.send_command(0x60) # TCON_SETTING + self.send_data(0x22) + + self.send_command(0x65) # FLASH CONTROL + self.send_data(0x00) + + self.send_command(0x61) # TCON_RESOLUTION + self.send_data(self.width >> 8) # source 640 + self.send_data(self.width & 0xff) + self.send_data(self.height >> 8) # gate 384 + self.send_data(self.height & 0xff) + + self.send_command(0xe5) # FLASH MODE + self.send_data(0x03) + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logger.debug('imwidth = %d imheight = %d ', imwidth, imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width / 8 * self.height)): + temp1 = imageblack[i] + temp2 = imagered[i] + j = 0 + while (j < 8): + if ((temp2 & 0x80) == 0x00): + temp3 = 0x04 # red + elif ((temp1 & 0x80) == 0x00): + temp3 = 0x00 # black + else: + temp3 = 0x03 # white + + temp3 = (temp3 << 4) & 0xFF + temp1 = (temp1 << 1) & 0xFF + temp2 = (temp2 << 1) & 0xFF + j += 1 + if ((temp2 & 0x80) == 0x00): + temp3 |= 0x04 # red + elif ((temp1 & 0x80) == 0x00): + temp3 |= 0x00 # black + else: + temp3 |= 0x03 # white + temp1 = (temp1 << 1) & 0xFF + temp2 = (temp2 << 1) & 0xFF + self.send_data(temp3) + j += 1 + + self.send_command(0x04) # POWER ON + self.ReadBusy() + self.send_command(0x12) # display refresh + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width / 8 * self.height)): + self.send_data(0x33) + self.send_data(0x33) + self.send_data(0x33) + self.send_data(0x33) + + self.send_command(0x04) # POWER ON + self.ReadBusy() + self.send_command(0x12) # display refresh + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/spotpear24in.py b/pwnagotchi/ui/hw/spotpear24in.py new file mode 100644 index 00000000..73f4b9ba --- /dev/null +++ b/pwnagotchi/ui/hw/spotpear24in.py @@ -0,0 +1,52 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + +import os,time + +class Spotpear24inch(DisplayImpl): + def __init__(self, config): + super(Spotpear24inch, self).__init__(config, 'spotpear24inch') + self._display = None + + def layout(self): + fonts.setup(12, 10, 12, 70, 25, 9) + self._layout['width'] = 320 + self._layout['height'] = 240 + self._layout['face'] = (35, 50) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (40, 0) + self._layout['uptime'] = (240, 0) + self._layout['line1'] = [0, 14, 320, 14] + self._layout['line2'] = [0, 220, 320, 220] + self._layout['friend_face'] = (0, 130) + self._layout['friend_name'] = (40, 135) + self._layout['shakes'] = (0, 220) + self._layout['mode'] = (280, 220) + self._layout['status'] = { + 'pos': (80, 160), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + + return self._layout + + def refresh(self): + time.sleep(0.1) + + def initialize(self): + from pwnagotchi.ui.hw.libs.fb import fb + self._display = fb + logging.info("initializing spotpear 24inch lcd display") + self._display.ready_fb(i=1) + self._display.black_scr() + + def render(self, canvas): + self._display.show_img(canvas.rotate(180)) + self.refresh() + + def clear(self): + self._display.black_scr() + self.refresh() diff --git a/pwnagotchi/ui/hw/waveshare13in3k.py b/pwnagotchi/ui/hw/waveshare13in3k.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare1in02.py b/pwnagotchi/ui/hw/waveshare1in02.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare1in44lcd.py b/pwnagotchi/ui/hw/waveshare1in44lcd.py new file mode 100644 index 00000000..0cef5f2c --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare1in44lcd.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare144lcd(DisplayImpl): + def __init__(self, config): + super(Waveshare144lcd, self).__init__(config, 'waveshare144lcd') + self._display = None + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 128 + self._layout['height'] = 128 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 127, 12] + self._layout['line2'] = [0, 116, 127, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 1.44 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.lcdhat144.epd import EPD + self._display = EPD() + self._display.init() + self._display.clear() + + def render(self, canvas): + self._display.display(canvas) + + def clear(self): + pass + #self._display.clear() diff --git a/pwnagotchi/ui/hw/waveshare1in54.py b/pwnagotchi/ui/hw/waveshare1in54.py new file mode 100644 index 00000000..50abf40d --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare1in54.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare154(DisplayImpl): + def __init__(self, config): + super(Waveshare154, self).__init__(config, 'waveshare1in54') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 200 + self._layout['height'] = 200 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (135, 0) + self._layout['line1'] = [0, 14, 200, 14] + self._layout['line2'] = [0, 186, 200, 186] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 187) + self._layout['mode'] = (170, 187) + self._layout['status'] = { + 'pos': (5, 90), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare v154 display") + from pwnagotchi.ui.hw.libs.waveshare.v1in54.epd1in54 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare1in54_V2.py b/pwnagotchi/ui/hw/waveshare1in54_V2.py new file mode 100644 index 00000000..09116cd3 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare1in54_V2.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare154V2(DisplayImpl): + def __init__(self, config): + super(Waveshare154V2, self).__init__(config, 'waveshare1in54_v2') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 200 + self._layout['height'] = 200 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (135, 0) + self._layout['line1'] = [0, 14, 200, 14] + self._layout['line2'] = [0, 186, 200, 186] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 187) + self._layout['mode'] = (170, 187) + self._layout['status'] = { + 'pos': (5, 90), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare v154 display") + from pwnagotchi.ui.hw.libs.waveshare.v1in54_v2.epd1in54_V2 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare1in54b.py b/pwnagotchi/ui/hw/waveshare1in54b.py new file mode 100644 index 00000000..1348aef9 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare1in54b.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare154inchb(DisplayImpl): + def __init__(self, config): + super(Waveshare154inchb, self).__init__(config, 'waveshare1in54b') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 200 + self._layout['height'] = 200 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (135, 0) + self._layout['line1'] = [0, 14, 200, 14] + self._layout['line2'] = [0, 186, 200, 186] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 187) + self._layout['mode'] = (170, 187) + self._layout['status'] = { + 'pos': (5, 90), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare v154 display") + from pwnagotchi.ui.hw.libs.waveshare.v1in54b.epd1in54b import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare1in54b_V2.py b/pwnagotchi/ui/hw/waveshare1in54b_V2.py new file mode 100644 index 00000000..33b04d7a --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare1in54b_V2.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare154bV2(DisplayImpl): + def __init__(self, config): + super(Waveshare154bV2, self).__init__(config, 'waveshare1in54b_v2') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 200 + self._layout['height'] = 200 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (135, 0) + self._layout['line1'] = [0, 14, 200, 14] + self._layout['line2'] = [0, 186, 200, 186] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 187) + self._layout['mode'] = (170, 187) + self._layout['status'] = { + 'pos': (5, 90), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare v154 display") + from pwnagotchi.ui.hw.libs.waveshare.v1in54b_v2.epd1in54b_v2 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare1in54c.py b/pwnagotchi/ui/hw/waveshare1in54c.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare1in64g.py b/pwnagotchi/ui/hw/waveshare1in64g.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare2in13.py b/pwnagotchi/ui/hw/waveshare2in13.py new file mode 100644 index 00000000..b6b8742e --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13.py @@ -0,0 +1,90 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class WaveshareV1(DisplayImpl): + def __init__(self, config): + super(WaveshareV1, self).__init__(config, 'waveshare_1') + self._display = None + + def layout(self): + if self.config['color'] == 'black': + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 250 + self._layout['height'] = 122 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (185, 0) + self._layout['line1'] = [0, 14, 250, 14] + self._layout['line2'] = [0, 108, 250, 108] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 109) + self._layout['mode'] = (225, 109) + self._layout['status'] = { + 'pos': (125, 20), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + else: + fonts.setup(10, 8, 10, 25, 25, 9) + self._layout['width'] = 212 + self._layout['height'] = 104 + self._layout['face'] = (0, 26) + self._layout['name'] = (5, 15) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (147, 0) + self._layout['line1'] = [0, 12, 212, 12] + self._layout['line2'] = [0, 92, 212, 92] + self._layout['friend_face'] = (0, 76) + self._layout['friend_name'] = (40, 78) + self._layout['shakes'] = (0, 93) + self._layout['mode'] = (187, 93) + self._layout['status'] = { + 'pos': (91, 15), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + return self._layout + + def initialize(self): + if self.config['color'] == 'black': + logging.info("initializing waveshare v2in13_V1 display in monochromatic mode") + from pwnagotchi.ui.hw.libs.waveshare.v2in13_V1.epd2in13 import EPD + self._display = EPD() + self._display.init(self._display.lut_full_update) + self._display.Clear(0xFF) + self._display.init(self._display.lut_partial_update) + elif self.config['color'] == 'fastAndFurious': + logging.info("initializing waveshare v2in13_V1 3-color display in FAST MODE") + logging.info("THIS MAY BE POTENTIALLY DANGEROUS. NO WARRANTY IS PROVIDED") + logging.info("USE THIS DISPLAY IN THIS MODE AT YOUR OWN RISK") + from pwnagotchi.ui.hw.libs.waveshare.v2in13_V1.epd2in13bcFAST import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + else: + logging.info("initializing waveshare v2in13_V1 display 3-color mode") + from pwnagotchi.ui.hw.libs.waveshare.v2in13_V1.epd2in13bc import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + if self.config['color'] == 'black': + buf = self._display.getbuffer(canvas) + self._display.display(buf) + elif self.config['color'] == 'fastAndFurious': + buf_black = self._display.getbuffer(canvas) + self._display.DisplayPartial(buf_black) + else: + buf_black = self._display.getbuffer(canvas) + self._display.displayBlack(buf_black) + + def clear(self): + self._display.Clear(0xff) diff --git a/pwnagotchi/ui/hw/waveshare2in13_V2.py b/pwnagotchi/ui/hw/waveshare2in13_V2.py new file mode 100644 index 00000000..d9153441 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13_V2.py @@ -0,0 +1,69 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class WaveshareV2(DisplayImpl): + def __init__(self, config): + super(WaveshareV2, self).__init__(config, 'waveshare_2') + self._display = None + + def layout(self): + if self.config['color'] == 'black': + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 250 + self._layout['height'] = 122 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (185, 0) + self._layout['line1'] = [0, 14, 250, 14] + self._layout['line2'] = [0, 108, 250, 108] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 109) + self._layout['mode'] = (225, 109) + self._layout['status'] = { + 'pos': (125, 20), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + else: + fonts.setup(10, 8, 10, 25, 25, 9) + self._layout['width'] = 212 + self._layout['height'] = 104 + self._layout['face'] = (0, 26) + self._layout['name'] = (5, 15) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['status'] = (91, 15) + self._layout['uptime'] = (147, 0) + self._layout['line1'] = [0, 12, 212, 12] + self._layout['line2'] = [0, 92, 212, 92] + self._layout['friend_face'] = (0, 76) + self._layout['friend_name'] = (40, 78) + self._layout['shakes'] = (0, 93) + self._layout['mode'] = (187, 93) + self._layout['status'] = { + 'pos': (125, 20), + 'font': fonts.status_font(fonts.Medium), + 'max': 14 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare v2in13_V2 display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13_V2.epd2in13_V2 import EPD + self._display = EPD() + self._display.init(self._display.FULL_UPDATE) + self._display.Clear(0xff) + self._display.init(self._display.PART_UPDATE) + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.displayPartial(buf) + + def clear(self): + self._display.Clear(0xff) diff --git a/pwnagotchi/ui/hw/waveshare2in13_V3.py b/pwnagotchi/ui/hw/waveshare2in13_V3.py new file mode 100644 index 00000000..4ef80097 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13_V3.py @@ -0,0 +1,48 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class WaveshareV3(DisplayImpl): + def __init__(self, config): + super(WaveshareV3, self).__init__(config, 'waveshare_3') + self._display = None + + def layout(self): + fonts.setup(10, 8, 10, 35, 25, 9) + self._layout['width'] = 250 + self._layout['height'] = 122 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (185, 0) + self._layout['line1'] = [0, 14, 250, 14] + self._layout['line2'] = [0, 108, 250, 108] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 109) + self._layout['mode'] = (225, 109) + self._layout['status'] = { + 'pos': (125, 20), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + + return self._layout + + def initialize(self): + logging.info("initializing waveshare v2in13_V3 display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13_V3.epd2in13_V3 import EPD + self._display = EPD() + self._display.init() + self._display.Clear(0xFF) + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.displayPartial(buf) + + def clear(self): + #pass + self._display.Clear(0xFF) diff --git a/pwnagotchi/ui/hw/waveshare2in13_V4.py b/pwnagotchi/ui/hw/waveshare2in13_V4.py new file mode 100644 index 00000000..92fa4ae4 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13_V4.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class WaveshareV4(DisplayImpl): + def __init__(self, config): + super(WaveshareV4, self).__init__(config, 'waveshare_4') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 250 + self._layout['height'] = 122 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (185, 0) + self._layout['line1'] = [0, 14, 250, 14] + self._layout['line2'] = [0, 108, 250, 108] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 109) + self._layout['mode'] = (225, 109) + self._layout['status'] = { + 'pos': (125, 20), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare v2in13_V4 display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13_V4.epd2in13_V4 import EPD + self._display = EPD() + self._display.init() + self._display.Clear(0xFF) + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.displayPartial(buf) + + def clear(self): + self._display.Clear(0xFF) \ No newline at end of file diff --git a/pwnagotchi/ui/hw/waveshare2in13b_V3.py b/pwnagotchi/ui/hw/waveshare2in13b_V3.py new file mode 100644 index 00000000..fac3235a --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13b_V3.py @@ -0,0 +1,72 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl +from PIL import Image + + +class Waveshare213bV3(DisplayImpl): + def __init__(self, config): + super(Waveshare213bV3, self).__init__(config, 'waveshare2in13b_v3') + self._display = None + + def layout(self): + if self.config['color'] == 'black': + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 250 + self._layout['height'] = 122 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (185, 0) + self._layout['line1'] = [0, 14, 250, 14] + self._layout['line2'] = [0, 108, 250, 108] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 109) + self._layout['mode'] = (225, 109) + self._layout['status'] = { + 'pos': (125, 20), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + else: + fonts.setup(10, 8, 10, 25, 25, 9) + self._layout['width'] = 212 + self._layout['height'] = 104 + self._layout['face'] = (0, 26) + self._layout['name'] = (5, 15) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['status'] = (91, 15) + self._layout['uptime'] = (147, 0) + self._layout['line1'] = [0, 12, 212, 12] + self._layout['line2'] = [0, 92, 212, 92] + self._layout['friend_face'] = (0, 76) + self._layout['friend_name'] = (40, 78) + self._layout['shakes'] = (0, 93) + self._layout['mode'] = (187, 93) + self._layout['status'] = { + 'pos': (125, 20), + 'font': fonts.status_font(fonts.Medium), + 'max': 14 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 2.13inb v2in13_V3 display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13b_v3.epd2in13b_V3 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvasBlack = None, canvasRed = None): + buffer = self._display.getbuffer + image = Image.new('1', (self._layout['height'], self._layout['width'])) + imageBlack = image if canvasBlack is None else canvasBlack + imageRed = image if canvasRed is None else canvasRed + self._display.display(buffer(imageBlack), buffer(imageRed)) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in13b_V4.py b/pwnagotchi/ui/hw/waveshare2in13b_V4.py new file mode 100644 index 00000000..ffeeaaf5 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13b_V4.py @@ -0,0 +1,72 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl +from PIL import Image + + +class Waveshare213bV4(DisplayImpl): + def __init__(self, config): + super(Waveshare213bV4, self).__init__(config, 'waveshare213inb_v4') + self._display = None + + def layout(self): + if self.config['color'] == 'black': + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 250 + self._layout['height'] = 122 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (185, 0) + self._layout['line1'] = [0, 14, 250, 14] + self._layout['line2'] = [0, 108, 250, 108] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 109) + self._layout['mode'] = (225, 109) + self._layout['status'] = { + 'pos': (125, 20), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + else: + fonts.setup(10, 8, 10, 25, 25, 9) + self._layout['width'] = 212 + self._layout['height'] = 104 + self._layout['face'] = (0, 26) + self._layout['name'] = (5, 15) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['status'] = (91, 15) + self._layout['uptime'] = (147, 0) + self._layout['line1'] = [0, 12, 212, 12] + self._layout['line2'] = [0, 92, 212, 92] + self._layout['friend_face'] = (0, 76) + self._layout['friend_name'] = (40, 78) + self._layout['shakes'] = (0, 93) + self._layout['mode'] = (187, 93) + self._layout['status'] = { + 'pos': (125, 20), + 'font': fonts.status_font(fonts.Medium), + 'max': 14 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 2.13inb v2in13_V4 display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13b_v4.epd2in13b_V4 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvasBlack = None, canvasRed = None): + buffer = self._display.getbuffer + image = Image.new('1', (self._layout['height'], self._layout['width'])) + imageBlack = image if canvasBlack is None else canvasBlack + imageRed = image if canvasRed is None else canvasRed + self._display.display(buffer(imageBlack), buffer(imageRed)) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in13bc.py b/pwnagotchi/ui/hw/waveshare2in13bc.py new file mode 100644 index 00000000..40c5fcae --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13bc.py @@ -0,0 +1,47 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare213bc(DisplayImpl): + def __init__(self, config): + super(Waveshare213bc, self).__init__(config, 'waveshare2in13bc') + self._display = None + + def layout(self): + fonts.setup(10, 8, 10, 25, 25, 9) + self._layout['width'] = 212 + self._layout['height'] = 104 + self._layout['face'] = (0, 26) + self._layout['name'] = (5, 15) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (147, 0) + self._layout['line1'] = [0, 12, 212, 12] + self._layout['line2'] = [0, 92, 212, 92] + self._layout['friend_face'] = (0, 76) + self._layout['friend_name'] = (40, 78) + self._layout['shakes'] = (0, 93) + self._layout['mode'] = (187, 93) + self._layout['status'] = { + 'pos': (91, 15), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 213bc display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13bc.epd2in13bc import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.pwndisplay(buf) + + def clear(self): + #pass + self._display.pwnclear() diff --git a/pwnagotchi/ui/hw/waveshare2in13d.py b/pwnagotchi/ui/hw/waveshare2in13d.py new file mode 100644 index 00000000..c3862fdd --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13d.py @@ -0,0 +1,47 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare213d(DisplayImpl): + def __init__(self, config): + super(Waveshare213d, self).__init__(config, 'waveshare2in13d') + self._display = None + + def layout(self): + fonts.setup(10, 8, 10, 25, 25, 9) + self._layout['width'] = 212 + self._layout['height'] = 104 + self._layout['face'] = (0, 26) + self._layout['name'] = (5, 15) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (147, 0) + self._layout['line1'] = [0, 12, 212, 12] + self._layout['line2'] = [0, 92, 212, 92] + self._layout['friend_face'] = (0, 76) + self._layout['friend_name'] = (40, 78) + self._layout['shakes'] = (0, 93) + self._layout['mode'] = (187, 93) + self._layout['status'] = { + 'pos': (91, 15), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 213d display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13d.epd2in13d import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + #pass + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in23g.py b/pwnagotchi/ui/hw/waveshare2in23g.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare2in36g.py b/pwnagotchi/ui/hw/waveshare2in36g.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare2in66.py b/pwnagotchi/ui/hw/waveshare2in66.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare2in7.py b/pwnagotchi/ui/hw/waveshare2in7.py new file mode 100644 index 00000000..0c2e98f6 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in7.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare27inch(DisplayImpl): + def __init__(self, config): + super(Waveshare27inch, self).__init__(config, 'waveshare2in7') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 264 + self._layout['height'] = 176 + self._layout['face'] = (66, 27) + self._layout['name'] = (5, 73) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (199, 0) + self._layout['line1'] = [0, 14, 264, 14] + self._layout['line2'] = [0, 162, 264, 162] + self._layout['friend_face'] = (0, 146) + self._layout['friend_name'] = (40, 146) + self._layout['shakes'] = (0, 163) + self._layout['mode'] = (239, 163) + self._layout['status'] = { + 'pos': (38, 93), + 'font': fonts.status_font(fonts.Medium), + 'max': 40 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V1 2.7 inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in7.epd2in7 import EPD + self._display = EPD() + self._display.init() + self._display.Clear(0xFF) + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear(0xff) diff --git a/pwnagotchi/ui/hw/waveshare2in7_V2.py b/pwnagotchi/ui/hw/waveshare2in7_V2.py new file mode 100644 index 00000000..20b07a66 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in7_V2.py @@ -0,0 +1,49 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare27inchV2(DisplayImpl): + def __init__(self, config): + super(Waveshare27inchV2, self).__init__(config, 'waveshare2in7_v2') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 264 + self._layout['height'] = 176 + self._layout['face'] = (66, 27) + self._layout['name'] = (5, 73) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (199, 0) + self._layout['line1'] = [0, 14, 264, 14] + self._layout['line2'] = [0, 162, 264, 162] + self._layout['friend_face'] = (0, 146) + self._layout['friend_name'] = (40, 146) + self._layout['shakes'] = (0, 163) + self._layout['mode'] = (239, 163) + self._layout['status'] = { + 'pos': (38, 93), + 'font': fonts.status_font(fonts.Medium), + 'max': 40 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V2 2.7 inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in7_v2.epd2in7_V2 import EPD + self._display = EPD() + self._display.init() + # this must have changed by waveshare + # remove the 0xFF(Clear(0xFF)) other wise it errors. can't pass oxff and self + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display_Partial(buf,0,0,176,264) + + def clear(self): + # This line also removes the 0xFF + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in7b.py b/pwnagotchi/ui/hw/waveshare2in7b.py new file mode 100644 index 00000000..b47f22d7 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in7b.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare27b(DisplayImpl): + def __init__(self, config): + super(Waveshare27b, self).__init__(config, 'waveshare2in7b') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 264 + self._layout['height'] = 176 + self._layout['face'] = (66, 27) + self._layout['name'] = (5, 73) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (199, 0) + self._layout['line1'] = [0, 14, 264, 14] + self._layout['line2'] = [0, 162, 264, 162] + self._layout['friend_face'] = (0, 146) + self._layout['friend_name'] = (40, 146) + self._layout['shakes'] = (0, 163) + self._layout['mode'] = (239, 163) + self._layout['status'] = { + 'pos': (38, 93), + 'font': fonts.status_font(fonts.Medium), + 'max': 40 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V1 2.7b inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in7b.epd2in7b import EPD + self._display = EPD() + self._display.init() + self._display.Clear(0xFF) + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear(0xff) diff --git a/pwnagotchi/ui/hw/waveshare2in7b_V2.py b/pwnagotchi/ui/hw/waveshare2in7b_V2.py new file mode 100644 index 00000000..22665462 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in7b_V2.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare27bV2(DisplayImpl): + def __init__(self, config): + super(Waveshare27bV2, self).__init__(config, 'waveshare2in7b_v2') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 264 + self._layout['height'] = 176 + self._layout['face'] = (66, 27) + self._layout['name'] = (5, 73) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (199, 0) + self._layout['line1'] = [0, 14, 264, 14] + self._layout['line2'] = [0, 162, 264, 162] + self._layout['friend_face'] = (0, 146) + self._layout['friend_name'] = (40, 146) + self._layout['shakes'] = (0, 163) + self._layout['mode'] = (239, 163) + self._layout['status'] = { + 'pos': (38, 93), + 'font': fonts.status_font(fonts.Medium), + 'max': 40 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V1 2.7b inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in7b.epd2in7b import EPD + self._display = EPD() + self._display.init() + self._display.Clear(0xFF) + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear(0xff) diff --git a/pwnagotchi/ui/hw/waveshare2in9.py b/pwnagotchi/ui/hw/waveshare2in9.py new file mode 100644 index 00000000..c86de360 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in9.py @@ -0,0 +1,47 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare29inch(DisplayImpl): + def __init__(self, config): + super(Waveshare29inch, self).__init__(config, 'waveshare2in9') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 296 + self._layout['height'] = 128 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 25) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (230, 0) + self._layout['line1'] = [0, 14, 296, 14] + self._layout['line2'] = [0, 112, 296, 112] + self._layout['friend_face'] = (0, 96) + self._layout['friend_name'] = (40, 96) + self._layout['shakes'] = (0, 114) + self._layout['mode'] = (268, 114) + self._layout['status'] = { + 'pos': (130, 25), + 'font': fonts.status_font(fonts.Medium), + 'max': 28 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V1 2.9 inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in9.epd2in9 import EPD + self._display = EPD() + self._display.init(self._display.lut_full_update) + self._display.Clear(0xFF) + self._display.init(self._display.lut_partial_update) + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear(0xFF) diff --git a/pwnagotchi/ui/hw/waveshare2in9_V2.py b/pwnagotchi/ui/hw/waveshare2in9_V2.py new file mode 100644 index 00000000..cf2697f7 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in9_V2.py @@ -0,0 +1,47 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare29inchV2(DisplayImpl): + def __init__(self, config): + super(Waveshare29inchV2, self).__init__(config, 'waveshare2in9_v2') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 296 + self._layout['height'] = 128 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 25) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (230, 0) + self._layout['line1'] = [0, 14, 296, 14] + self._layout['line2'] = [0, 112, 296, 112] + self._layout['friend_face'] = (0, 96) + self._layout['friend_name'] = (40, 96) + self._layout['shakes'] = (0, 114) + self._layout['mode'] = (268, 114) + self._layout['status'] = { + 'pos': (130, 25), + 'font': fonts.status_font(fonts.Medium), + 'max': 28 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V2 2.9 inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in9_v2.epd2in9V2 import EPD + self._display = EPD() + self._display.init() + self._display.Clear(0xFF) + self._display.init() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear(0xFF) diff --git a/pwnagotchi/ui/hw/waveshare2in9b_V3.py b/pwnagotchi/ui/hw/waveshare2in9b_V3.py new file mode 100644 index 00000000..f5f28b4a --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in9b_V3.py @@ -0,0 +1,47 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare29bV3(DisplayImpl): + def __init__(self, config): + super(Waveshare29bV3, self).__init__(config, 'waveshare2in9b_v3') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 296 + self._layout['height'] = 128 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 25) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (230, 0) + self._layout['line1'] = [0, 14, 296, 14] + self._layout['line2'] = [0, 112, 296, 112] + self._layout['friend_face'] = (0, 96) + self._layout['friend_name'] = (40, 96) + self._layout['shakes'] = (0, 114) + self._layout['mode'] = (268, 114) + self._layout['status'] = { + 'pos': (130, 25), + 'font': fonts.status_font(fonts.Medium), + 'max': 28 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V3 2.9 inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in9b_v3.epd2in9b_V3 import EPD + self._display = EPD() + self._display.init() + self._display.Clear(0xFF) + self._display.init() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear(0xFF) diff --git a/pwnagotchi/ui/hw/waveshare2in9b_V4.py b/pwnagotchi/ui/hw/waveshare2in9b_V4.py new file mode 100644 index 00000000..f827ef6c --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in9b_V4.py @@ -0,0 +1,47 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare29bV4(DisplayImpl): + def __init__(self, config): + super(Waveshare29bV4, self).__init__(config, 'waveshare2in9b_v4') + self._display = None + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 296 + self._layout['height'] = 128 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 25) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (230, 0) + self._layout['line1'] = [0, 14, 296, 14] + self._layout['line2'] = [0, 112, 296, 112] + self._layout['friend_face'] = (0, 96) + self._layout['friend_name'] = (40, 96) + self._layout['shakes'] = (0, 114) + self._layout['mode'] = (268, 114) + self._layout['status'] = { + 'pos': (130, 25), + 'font': fonts.status_font(fonts.Medium), + 'max': 28 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V4 2.9 inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in9b_v4.epd2in9b_V4 import EPD + self._display = EPD() + self._display.init() + self._display.Clear(0xFF) + self._display.init() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear(0xFF) diff --git a/pwnagotchi/ui/hw/waveshare2in9bc.py b/pwnagotchi/ui/hw/waveshare2in9bc.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare2in9d.py b/pwnagotchi/ui/hw/waveshare2in9d.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare3in0g.py b/pwnagotchi/ui/hw/waveshare3in0g.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare3in52.py b/pwnagotchi/ui/hw/waveshare3in52.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare3in5lcd.py b/pwnagotchi/ui/hw/waveshare3in5lcd.py new file mode 100644 index 00000000..91470e56 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare3in5lcd.py @@ -0,0 +1,53 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + +import os, time + + +class Waveshare35lcd(DisplayImpl): + def __init__(self, config): + super(Waveshare35lcd, self).__init__(config, 'waveshare35lcd') + self._display = None + + def layout(self): + fonts.setup(12, 10, 12, 70, 25, 9) + self._layout['width'] = 480 + self._layout['height'] = 320 + self._layout['face'] = (110, 100) + self._layout['name'] = (10, 30) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (80, 0) + self._layout['uptime'] = (400, 0) + self._layout['line1'] = [0, 14, 480, 14] + self._layout['line2'] = [0,300, 480, 300] + self._layout['friend_face'] = (0, 220) + self._layout['friend_name'] = (50, 225) + self._layout['shakes'] = (10, 300) + self._layout['mode'] = (440, 300) + self._layout['status'] = { + 'pos': (80, 200), + 'font': fonts.status_font(fonts.Medium), + 'max': 100 + } + + return self._layout + + def refresh(self): + time.sleep(0.1) + + def initialize(self): + from pwnagotchi.ui.hw.libs.fb import fb + self._display = fb + logging.info("initializing waveshare 3,5inch lcd display") + self._display.ready_fb(i=0) + self._display.black_scr() + + def render(self, canvas): + self._display.show_img(canvas.rotate(0)) + self.refresh() + + def clear(self): + self._display.black_scr() + self.refresh() diff --git a/pwnagotchi/ui/hw/waveshare3in7.py b/pwnagotchi/ui/hw/waveshare3in7.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare4in01f.py b/pwnagotchi/ui/hw/waveshare4in01f.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare4in2.py b/pwnagotchi/ui/hw/waveshare4in2.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare4in26.py b/pwnagotchi/ui/hw/waveshare4in26.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare4in2_V2.py b/pwnagotchi/ui/hw/waveshare4in2_V2.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare4in2b_V2.py b/pwnagotchi/ui/hw/waveshare4in2b_V2.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare4in2bc.py b/pwnagotchi/ui/hw/waveshare4in2bc.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare4in37g.py b/pwnagotchi/ui/hw/waveshare4in37g.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare5in65f.py b/pwnagotchi/ui/hw/waveshare5in65f.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare5in83.py b/pwnagotchi/ui/hw/waveshare5in83.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare5in83_V2.py b/pwnagotchi/ui/hw/waveshare5in83_V2.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare5in83b_V2.py b/pwnagotchi/ui/hw/waveshare5in83b_V2.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare5in83bc.py b/pwnagotchi/ui/hw/waveshare5in83bc.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare7in3f.py b/pwnagotchi/ui/hw/waveshare7in3f.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare7in3g.py b/pwnagotchi/ui/hw/waveshare7in3g.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare7in5.py b/pwnagotchi/ui/hw/waveshare7in5.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare7in5_HD.py b/pwnagotchi/ui/hw/waveshare7in5_HD.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare7in5_V2.py b/pwnagotchi/ui/hw/waveshare7in5_V2.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare7in5b_HD.py b/pwnagotchi/ui/hw/waveshare7in5b_HD.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare7in5b_V2.py b/pwnagotchi/ui/hw/waveshare7in5b_V2.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare7in5bc.py b/pwnagotchi/ui/hw/waveshare7in5bc.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/view.py b/pwnagotchi/ui/view.py index 0953838e..d93f21a8 100644 --- a/pwnagotchi/ui/view.py +++ b/pwnagotchi/ui/view.py @@ -27,7 +27,7 @@ class View(object): # setup faces from the configuration in case the user customized them faces.load_from_config(config['ui']['faces']) - + self._agent = None self._render_cbs = [] self._config = config diff --git a/pwnagotchi/ui/web/handler.py b/pwnagotchi/ui/web/handler.py index 6bd0ed14..da1f6748 100644 --- a/pwnagotchi/ui/web/handler.py +++ b/pwnagotchi/ui/web/handler.py @@ -6,11 +6,11 @@ import secrets import json from functools import wraps +import flask + # https://stackoverflow.com/questions/14888799/disable-console-messages-in-flask-server logging.getLogger('werkzeug').setLevel(logging.ERROR) - -# Set to false, on bookworm it wouldn't load the flask server for webui -os.environ['WERKZEUG_RUN_MAIN'] = 'false' +os.environ['WERKZEUG_RUN_MAIN'] = 'true' import pwnagotchi import pwnagotchi.grid as grid diff --git a/pwnagotchi/ui/web/server.py b/pwnagotchi/ui/web/server.py index 7007798f..b3fd0b62 100644 --- a/pwnagotchi/ui/web/server.py +++ b/pwnagotchi/ui/web/server.py @@ -3,17 +3,15 @@ import secrets import logging import os +# https://stackoverflow.com/questions/14888799/disable-console-messages-in-flask-server +logging.getLogger('werkzeug').setLevel(logging.ERROR) +os.environ['WERKZEUG_RUN_MAIN'] = 'true' + from flask import Flask from flask_cors import CORS from flask_wtf.csrf import CSRFProtect from pwnagotchi.ui.web.handler import Handler -# https://stackoverflow.com/questions/14888799/disable-console-messages-in-flask-server -logging.getLogger('werkzeug').setLevel(logging.ERROR) - -# Set to false, on bookworm it wouldn't load the flask server for webui -os.environ['WERKZEUG_RUN_MAIN'] = 'false' - class Server: def __init__(self, agent, config): diff --git a/pwnagotchi/ui/web/static/js/jquery-3.7.1.min.js b/pwnagotchi/ui/web/static/js/jquery-3.7.1.min.js new file mode 100644 index 00000000..7f37b5d9 --- /dev/null +++ b/pwnagotchi/ui/web/static/js/jquery-3.7.1.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.7.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},C=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var r,i,o=(n=n||C).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},M=function(){V()},R=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&U(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&R(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function X(a){return F(function(o){return o=+o,F(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function U(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",M),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="",le.option=!!xe.lastChild;var ke={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n",""]);var je=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="
",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0")[0].getContext("2d");i.font=o;var a=i.measureText(e.label).width,u=e.mSize,f=a/n,l=(1-f)*e.mPosX,c=(1-u)*e.mPosY,g=l+f,s=c+u;1===e.mode?t.add_blank(0,c-.01,n,s+.01):t.add_blank(l-.01,c-.01,.01+g,s+.01),r.fillStyle=e.fontcolor,r.font=o,r.fillText(e.label,l*n,c*n+.75*e.mSize*n)}(t,r,e):!p(e.image)||3!==v&&4!==v||(n=t,o=r,a=(i=e).size,u=i.image.naturalWidth||1,f=i.image.naturalHeight||1,l=i.mSize,g=(1-(c=l*u/f))*i.mPosX,s=(1-l)*i.mPosY,h=g+c,d=s+l,3===i.mode?n.add_blank(0,s-.01,a,d+.01):n.add_blank(g-.01,s-.01,.01+h,d+.01),o.drawImage(i.image,g*a,s*a,c*a,l*a))}function s(t,r,e,n,o,i,a,u){t.is_dark(a,u)&&r.r(n,o,i,i)}function d(t,r,e,n,o,i,a,u){var f,l,c,g,s,h,d,v,p,w,y,m,b,k,x,B,C,A,M,S=n+i,L=o+i,D=e.radius*i,T=a-1,z=a+1,P=u-1,j=u+1,I=t.is_dark,_=I(a,u),q=I(T,P),O=I(T,u),F=I(T,j),R=I(a,j),H=I(z,j),N=I(z,u),U=I(z,P),V=I(a,P);_?(w=r,y=n,m=o,b=S,k=L,x=D,C=!O&&!R,A=!N&&!R,M=!N&&!V,(B=!O&&!V)?w.m(y+x,m):w.m(y,m),C?w.l(b-x,m).a(b,m,b,k,x):w.l(b,m),A?w.l(b,k-x).a(b,k,y,k,x):w.l(b,k),M?w.l(y+x,k).a(y,k,y,m,x):w.l(y,k),B?w.l(y,m+x).a(y,m,b,m,x):w.l(y,m)):(f=r,l=n,c=o,g=S,s=L,h=D,d=O&&R&&F,v=N&&R&&H,p=N&&V&&U,O&&V&&q&&f.m(l+h,c).l(l,c).l(l,c+h).a(l,c,l+h,c,h),d&&f.m(g-h,c).l(g,c).l(g,c+h).a(g,c,g-h,c,h),v&&f.m(g-h,s).l(g,s).l(g,s-h).a(g,s,g-h,s,h),p&&f.m(l+h,s).l(l,s).l(l,s-h).a(l,s,l+h,s,h))}function n(t,r){var e=h(r.text,r.ecLevel,r.minVersion,r.maxVersion,r.quiet);if(!e)return null;var n=w(t).data("qrcode",e),o=n[0].getContext("2d");return i(e,o,r),function(t,e,r){var n=t.module_count,o=r.size/n,i=s;0").attr("width",t.size).attr("height",t.size);return n(r,t)}function o(t){return"canvas"===t.render?e(t):"image"===t.render?(r=t,w("").attr("src",e(r)[0].toDataURL("image/png"))):function(t){var r=h(t.text,t.ecLevel,t.minVersion,t.maxVersion,t.quiet);if(!r)return null;var e=t.size,n=t.background,o=Math.floor,i=r.module_count,a=o(e/i),u=o(.5*(e-a*i)),f={position:"relative",left:0,top:0,padding:0,margin:0,width:e,height:e},l={position:"absolute",padding:0,margin:0,width:a,height:a,"background-color":t.fill},c=w("
").data("qrcode",r).css(f);n&&c.css("background-color",n);for(var g=0;g").css(l).css({left:u+s*a,top:u+g*a}).appendTo(c);return c}(t);var r}var w=window.jQuery,a={render:"canvas",minVersion:1,maxVersion:40,ecLevel:"L",left:0,top:0,size:200,fill:"#000",background:"#fff",text:"no text",radius:0,quiet:0,mode:0,mSize:.1,mPosX:.5,mPosY:.5,label:"no label",fontname:"sans",fontcolor:"#000",image:null};w.fn.qrcode=t.exports=function(t){var e=w.extend({},a,t);return this.each(function(t,r){"canvas"===r.nodeName.toLowerCase()?n(r,e):w(r).append(o(e))})}},function(t,r,e){var n,o,i,a=function(){function i(t,r){function a(t,r){c=function(t){for(var r=new Array(t),e=0;e>e&1);c[Math.floor(e/3)][e%3+g-8-3]=n}for(e=0;e<18;e+=1){n=!t&&1==(r>>e&1);c[e%3+g-8-3][Math.floor(e/3)]=n}},d=function(t,r){for(var e=f<<3|r,n=m.getBCHTypeInfo(e),o=0;o<15;o+=1){var i=!t&&1==(n>>o&1);o<6?c[o][8]=i:o<8?c[o+1][8]=i:c[g-15+o][8]=i}for(o=0;o<15;o+=1){i=!t&&1==(n>>o&1);o<8?c[8][g-o-1]=i:o<9?c[8][15-o-1+1]=i:c[8][15-o-1]=i}c[g-8][8]=!t},v=function(t,r){for(var e=-1,n=g-1,o=7,i=0,a=m.getMaskFunction(r),u=g-1;0>>o&1)),a(n,u-l)&&(f=!f),c[n][u-l]=f,-1==--o&&(i+=1,o=7))}if((n+=e)<0||g<=n){n-=e,e=-e;break}}},p=function(t,r,e){for(var n=A.getRSBlocks(t,r),o=M(),i=0;i8*u)throw"code length overflow. ("+o.getLengthInBits()+">"+8*u+")";for(o.getLengthInBits()+4<=8*u&&o.put(0,4);o.getLengthInBits()%8!=0;)o.putBit(!1);for(;!(o.getLengthInBits()>=8*u||(o.put(236,8),o.getLengthInBits()>=8*u));)o.put(17,8);return function(t,r){for(var e=0,n=0,o=0,i=new Array(r.length),a=new Array(r.length),u=0;u',e+="";for(var n=0;n";for(var o=0;o';e+=""}return e+="",e+=""},h.createSvgTag=function(t,r,e,n){var o={};"object"==typeof arguments[0]&&(t=(o=arguments[0]).cellSize,r=o.margin,e=o.alt,n=o.title),t=t||2,r=void 0===r?4*t:r,(e="string"==typeof e?{text:e}:e||{}).text=e.text||null,e.id=e.text?e.id||"qrcode-description":null,(n="string"==typeof n?{text:n}:n||{}).text=n.text||null,n.id=n.text?n.id||"qrcode-title":null;var i,a,u,f=h.getModuleCount()*t+2*r,l="",c="l"+t+",0 0,"+t+" -"+t+",0 0,-"+t+"z ";for(l+=''+w(n.text)+"":"",l+=e.text?''+w(e.text)+"":"",l+='',l+='":r+=">";break;case"&":r+="&";break;case'"':r+=""";break;default:r+=n}}return r};return h.createASCII=function(t,r){if((t=t||1)<2)return function(t){t=void 0===t?2:t;for(var r,e,n,o,i=+h.getModuleCount()+2*t,a=t,u=i-t,f={"██":"█","█ ":"▀"," █":"▄"," ":" "},l={"██":"▀","█ ":"▀"," █":" "," ":" "},c="",g=0;g>>8),r.push(255&n)):r.push(a)}return r}};var r,t,a=1,u=2,o=4,f=8,y={L:1,M:0,Q:3,H:2},e=0,n=1,l=2,c=3,g=4,s=5,h=6,d=7,m=(r=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],(t={}).getBCHTypeInfo=function(t){for(var r=t<<10;0<=v(r)-v(1335);)r^=1335<>>=1;return r}var p=function(){for(var r=new Array(256),e=new Array(256),t=0;t<8;t+=1)r[t]=1<>>8)},writeBytes:function(t,r,e){r=r||0,e=e||t.length;for(var n=0;n>>o-6),o-=6},t.flush=function(){if(0>>r!=0)throw"length over";for(;8<=u+r;)a.writeByte(255&(t<>>=8-u,u=f=0;f|=t<>>7-t%8&1)},put:function(t,r){for(var e=0;e>>r-e-1&1))},getLengthInBits:function(){return n},putBit:function(t){var r=Math.floor(n/8);e.length<=r&&e.push(0),t&&(e[r]|=128>>>n%8),n+=1}};return o},S=function(t){var r=a,n=t,e={getMode:function(){return r},getLength:function(t){return n.length},write:function(t){for(var r=n,e=0;e+2>>8&255)+(255&n),t.put(n,13),e+=2}if(e=e.length){if(0==i)return-1;throw"unexpected end of file./"+i}var t=e.charAt(n);if(n+=1,"="==t)return i=0,-1;t.match(/^\s$/)||(o=o<<6|a(t.charCodeAt(0)),i+=6)}var r=o>>>i-8&255;return i-=8,r}},a=function(t){if(65<=t&&t<=90)return t-65;if(97<=t&&t<=122)return t-97+26;if(48<=t&&t<=57)return t-48+52;if(43==t)return 62;if(47==t)return 63;throw"c:"+t};return r},P=function(t,r,e){for(var n=x(t,r),o=0;o>6,128|63&n):n<55296||57344<=n?r.push(224|n>>12,128|n>>6&63,128|63&n):(e++,n=65536+((1023&n)<<10|1023&t.charCodeAt(e)),r.push(240|n>>18,128|n>>12&63,128|n>>6&63,128|63&n))}return r}(t)},o=[],void 0===(i="function"==typeof(n=function(){return a})?n.apply(r,o):n)||(t.exports=i)}],o.c=n,o.d=function(t,r,e){o.o(t,r)||Object.defineProperty(t,r,{enumerable:!0,get:e})},o.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o.t=function(r,t){if(1&t&&(r=o(r)),8&t)return r;if(4&t&&"object"==typeof r&&r&&r.__esModule)return r;var e=Object.create(null);if(o.r(e),Object.defineProperty(e,"default",{enumerable:!0,value:r}),2&t&&"string"!=typeof r)for(var n in r)o.d(e,n,function(t){return r[t]}.bind(null,n));return e},o.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(r,"a",r),r},o.o=function(t,r){return Object.prototype.hasOwnProperty.call(t,r)},o.p="",o(o.s=0);function o(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,o),r.l=!0,r.exports}var e,n}); \ No newline at end of file diff --git a/pwnagotchi/ui/web/static/js/kjua-0.9.0.min.js b/pwnagotchi/ui/web/static/js/kjua-0.9.0.min.js new file mode 100644 index 00000000..bd1f4845 --- /dev/null +++ b/pwnagotchi/ui/web/static/js/kjua-0.9.0.min.js @@ -0,0 +1,2 @@ +/*! kjua v0.9.0 - https://larsjung.de/kjua/ */ +!function(t,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define("kjua",[],r):"object"==typeof exports?exports.kjua=r():t.kjua=r()}("undefined"!=typeof self?self:this,function(){return n={},o.m=e=[function(t,r,e){function n(t){var r=Object.assign({},o,t),e=i(r.text,r.ecLevel,r.minVersion,r.quiet);return"svg"===r.render?u(e,r):a(e,r,"image"===r.render)}var o=e(1),i=e(2),a=e(4),u=e(8);t.exports=n;try{jQuery.fn.kjua=function(e){return this.each(function(t,r){return r.appendChild(n(e))})}}catch(t){}},function(t,r){t.exports={render:"image",crisp:!0,minVersion:1,ecLevel:"L",size:200,ratio:null,fill:"#333",back:"#fff",text:"no text",rounded:0,quiet:0,mode:"plain",mSize:30,mPosX:50,mPosY:50,label:"no label",fontname:"sans",fontcolor:"#333",image:null}},function(t,r,e){function u(t){return(u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var f=/code length overflow/i,c=e(3);c.stringToBytes=c.stringToBytesFuncs["UTF-8"];t.exports=function(t,r,e,n){var o,i=3>e&1);l[Math.floor(e/3)][e%3+s-8-3]=n}for(e=0;e<18;e+=1){n=!t&&1==(r>>e&1);l[e%3+s-8-3][Math.floor(e/3)]=n}},d=function(t,r){for(var e=f<<3|r,n=m.getBCHTypeInfo(e),o=0;o<15;o+=1){var i=!t&&1==(n>>o&1);o<6?l[o][8]=i:o<8?l[o+1][8]=i:l[s-15+o][8]=i}for(o=0;o<15;o+=1){i=!t&&1==(n>>o&1);o<8?l[8][s-o-1]=i:o<9?l[8][15-o-1+1]=i:l[8][15-o-1]=i}l[s-8][8]=!t},v=function(t,r){for(var e=-1,n=s-1,o=7,i=0,a=m.getMaskFunction(r),u=s-1;0>>o&1)),a(n,u-c)&&(f=!f),l[n][u-c]=f,-1==--o&&(i+=1,o=7))}if((n+=e)<0||s<=n){n-=e,e=-e;break}}},p=function(t,r,e){for(var n=S.getRSBlocks(t,r),o=M(),i=0;i8*u)throw"code length overflow. ("+o.getLengthInBits()+">"+8*u+")";for(o.getLengthInBits()+4<=8*u&&o.put(0,4);o.getLengthInBits()%8!=0;)o.putBit(!1);for(;!(o.getLengthInBits()>=8*u||(o.put(236,8),o.getLengthInBits()>=8*u));)o.put(17,8);return function(t,r){for(var e=0,n=0,o=0,i=new Array(r.length),a=new Array(r.length),u=0;u',e+="";for(var n=0;n";for(var o=0;o';e+=""}return e+="",e+=""},h.createSvgTag=function(t,r,e,n){var o={};"object"==typeof arguments[0]&&(t=(o=arguments[0]).cellSize,r=o.margin,e=o.alt,n=o.title),t=t||2,r=void 0===r?4*t:r,(e="string"==typeof e?{text:e}:e||{}).text=e.text||null,e.id=e.text?e.id||"qrcode-description":null,(n="string"==typeof n?{text:n}:n||{}).text=n.text||null,n.id=n.text?n.id||"qrcode-title":null;var i,a,u,f=h.getModuleCount()*t+2*r,c="",l="l"+t+",0 0,"+t+" -"+t+",0 0,-"+t+"z ";for(c+=''+y(n.text)+"":"",c+=e.text?''+y(e.text)+"":"",c+='',c+='":r+=">";break;case"&":r+="&";break;case'"':r+=""";break;default:r+=n}}return r};return h.createASCII=function(t,r){if((t=t||1)<2)return function(t){t=void 0===t?2:t;for(var r,e,n,o,i=+h.getModuleCount()+2*t,a=t,u=i-t,f={"██":"█","█ ":"▀"," █":"▄"," ":" "},c={"██":"▀","█ ":"▀"," █":" "," ":" "},l="",s=0;s>>8),r.push(255&n)):r.push(a)}return r}};var r,t,a=1,u=2,o=4,f=8,w={L:1,M:0,Q:3,H:2},e=0,n=1,c=2,l=3,s=4,g=5,h=6,d=7,m=(r=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],(t={}).getBCHTypeInfo=function(t){for(var r=t<<10;0<=v(r)-v(1335);)r^=1335<>>=1;return r}var p=function(){for(var r=new Array(256),e=new Array(256),t=0;t<8;t+=1)r[t]=1<>>8)},writeBytes:function(t,r,e){r=r||0,e=e||t.length;for(var n=0;n>>o-6),o-=6},t.flush=function(){if(0>>r!=0)throw"length over";for(;8<=u+r;)a.writeByte(255&(t<>>=8-u,u=f=0;f|=t<>>7-t%8&1)},put:function(t,r){for(var e=0;e>>r-e-1&1))},getLengthInBits:function(){return n},putBit:function(t){var r=Math.floor(n/8);e.length<=r&&e.push(0),t&&(e[r]|=128>>>n%8),n+=1}};return o},A=function(t){var r=a,n=t,e={getMode:function(){return r},getLength:function(t){return n.length},write:function(t){for(var r=n,e=0;e+2>>8&255)+(255&n),t.put(n,13),e+=2}if(e=e.length){if(0==i)return-1;throw"unexpected end of file./"+i}var t=e.charAt(n);if(n+=1,"="==t)return i=0,-1;t.match(/^\s$/)||(o=o<<6|a(t.charCodeAt(0)),i+=6)}var r=o>>>i-8&255;return i-=8,r}},a=function(t){if(65<=t&&t<=90)return t-65;if(97<=t&&t<=122)return t-97+26;if(48<=t&&t<=57)return t-48+52;if(43==t)return 62;if(47==t)return 63;throw"c:"+t};return r},P=function(t,r,e){for(var n=k(t,r),o=0;o>6,128|63&n):n<55296||57344<=n?r.push(224|n>>12,128|n>>6&63,128|63&n):(e++,n=65536+((1023&n)<<10|1023&t.charCodeAt(e)),r.push(240|n>>18,128|n>>12&63,128|n>>6&63,128|63&n))}return r}(t)},o=[],void 0===(i="function"==typeof(n=function(){return a})?n.apply(r,o):n)||(t.exports=i)},function(t,r,e){function c(t,r,e,n,o,i){t.is_dark(o,i)&&r.rect(i*n,o*n,n,n)}function a(t,r,e){var n,o;n=r,(o=e).back&&(n.fillStyle=o.back,n.fillRect(0,0,o.size,o.size)),function(t,r,e){if(t){var n=0 - +
-
-

- {{name}} -

- {% if has_info %} - {{ loaded[name].__description__ }} - {% else %} - Description can't be loaded yet. - {% endif %} -
-
- - - -
+
+

+ {{name}} +

+ {% if has_info %} + {{ loaded[name].__description__ }} + {% else %} + Description can't be loaded yet. + {% endif %} +
+
+ + + +
@@ -84,4 +100,4 @@ $(function(){
{% endfor %}
-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/pwnagotchi/ui/web/templates/profile.html b/pwnagotchi/ui/web/templates/profile.html index 60cfb7ad..710413bc 100644 --- a/pwnagotchi/ui/web/templates/profile.html +++ b/pwnagotchi/ui/web/templates/profile.html @@ -7,9 +7,9 @@ Profile {% block script %} $(function(){ - $('#qrcode').qrcode({ + $('#qrcode').kjua({ text: 'https://opwngrid.xyz/search/{{ fingerprint }}', - render: 'div', + render: 'svg', mode: 0, size: 400, fontname: 'sans', diff --git a/requirements.txt b/requirements.txt index c344481f..21031411 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,7 +21,7 @@ spidev stable_baselines3; platform_machine!="armv6l" stable_baselines3==1.8.0; platform_machine=="armv6l" toml -torch==2.0.1 -torchvision==0.15.2 +torch +torchvision tweepy websockets \ No newline at end of file