FatGotchi! pwnagotchi on a 2,4inch SpotPear display

As i previously wrote on this blog, i really apprecitated evilsocket‘s last idea: pwnagotchi.

As I wanted to try it as soon as possible (and I didn’t want to wait for delivery times for an e-ink display), I used what I have available for the first experiments: a Raspberry Pi Zero W, a Raspi UPS HAT and a 2.4-inch touchscreen SpotPear display.

Unfortunately my 2,4″ SpotPear display is not (yet?) fully supported and it’s necessary to make some tweaks to view the nice smiley face looking for PSK handshakes 😉

After some unsuccessful tries, including the “on_frame” method available in last releases, i decide to write down a quick-and-dirty patch to support as best as possible the 320×240 resolution of the display and let my fatgotchi smile 😀

Since 12.11.2019, this patch was merget to official pwnagotchi repository (https://github.com/evilsocket/pwnagotchi/commit/14064c3b5bc8e02d7a0a62c7adbc133f5b72e746)

At the moment, the patch is quite simple: just add support for a new display (“spotpear24inch“) in existing files and add the driver (“spotpear24.inc.py”), that tell pwnagotchi to render in 320×240, then rotate the image of 180 degrees and display it rendering to framebuffer device using a pure python implementation (thanks to chidea’s FBpyGIF).

diff --git a/pwnagotchi/ui/display.py b/pwnagotchi/ui/display.py
index 25aed67..cd9d60c 100644
--- a/pwnagotchi/ui/display.py
+++ b/pwnagotchi/ui/display.py
@@ -57,6 +57,9 @@ class Display(View):
     def is_waveshare213d(self):
         return self._implementation.name == 'waveshare213d'
 
+    def is_spotpear24inch(self):
+        return self._implementation.name == 'spotpear24inch'
+
     def is_waveshare_any(self):
         return self.is_waveshare_v1() or self.is_waveshare_v2()
 
diff --git a/pwnagotchi/ui/hw/__init__.py b/pwnagotchi/ui/hw/__init__.py
index b7db3bd..a339dc5 100644
--- a/pwnagotchi/ui/hw/__init__.py
+++ b/pwnagotchi/ui/hw/__init__.py
@@ -40,4 +40,7 @@ def display_for(config):
         return Waveshare154inch(config)
 
     elif config['ui']['display']['type'] == 'waveshare213d':
-        return Waveshare213d(config)
\ No newline at end of file
+        return Waveshare213d(config)
+
+    elif config['ui']['display']['type'] == 'spotpear24inch':
+        return Spotpear24inch(config)
\ No newline at end of file
diff --git a/pwnagotchi/utils.py b/pwnagotchi/utils.py
index 86da38e..7d02f3d 100644
--- a/pwnagotchi/utils.py
+++ b/pwnagotchi/utils.py
@@ -103,6 +103,9 @@ def load_config(args):
     elif config['ui']['display']['type'] in ('ws_213d', 'ws213d', 'waveshare_213d', 'waveshare213d'):
         config['ui']['display']['type'] = 'waveshare213d'
 
+    elif config['ui']['display']['type'] in ('spotpear24inch'):
+        config['ui']['display']['type'] = 'spotpear24inch'
+
     else:
         print("unsupported display type %s" % config['ui']['display']['type'])
         exit(1)

pwnagotch/ui/hw/spotpear24inch.py:

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)
        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, 92)
        self._layout['friend_name'] = (40, 94)
        self._layout['shakes'] = (0, 220)
        self._layout['mode'] = (280, 220)
        self._layout['status'] = {
            'pos': (80, 160),
            '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()

Then you need to add pure python framebuffer memory mapping implementation creating new dir ./libs/fb/ and copying there fb.py and __init__.py that you can conveniently get from this GitHub repo: github.com/michelep/pwnagotchi/tree/master/pwnagotchi/ui/hw/libs/fb

il mio fatgotchi

To preserve battery power, i also decided to invert colors (black background on white text), changing colors definitions in pwagotchi/ui/view.py as follow:

WHITE=0x00
BLACK=0xFF

(yep, i know: not the best trick but it works)

Finally, don’t forget to update your /etc/pwnagotchi/config.yml:

ui:
    display:
        enabled: true
        rotation: 180
        type: 'spotpear24inch'

and if you want to use the three buttons on the left, you can use gpio_buttons plugin, added from v1.1.0, and add to config.yml:

  gpio_buttons:
    enabled: true
    gpios:
    - 12: 'sudo touch /root/.pwnagotchi-auto && sudo systemctl restart pwnagotchi'
    - 16: ''
    - 18: 'shutdown -h now'

N.B. This patch could be adapted to newest code changes! Always fetch latest stable release from github.com/evilsocket/pwnagotchi/releases

2 comments
  1. I’m not sure about your unsuccessful attempts given that instead of reporting the issue you just went reimplementing the wheel, but your solution does *exactly* what on_frame does as I tried to explain you multiple times. It calls *exactly* the same command (fbi) in the same exact circumstance (when a new frame is available).

    1. I’m sure that on_frame is the best way to render on FB displays. But also with on_frame i need to setup the best rendering coordinates for 320×240 displays (or other different resolutions) and i feel more comfortable with this solution. By the way i’ll try to achieve better rendering method instead of fbi. When i have some spare time, i’ll try again with on_frame and, if needed, i’ll open an issue.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.