updated .gitignore and CreateRelease.yml

added *.sha256 to the .gitignore

updated CreateRelease.yml
This commit is contained in:
scifijunkie 2024-05-23 13:22:01 -05:00
parent eb77f29135
commit 3155a36fd9
23 changed files with 3678 additions and 2 deletions

96
.github/workflows/CreateRelease.yml vendored Normal file
View File

@ -0,0 +1,96 @@
name: Release
on:
workflow_dispatch:
inputs:
release_version:
description: 'Release version'
required: true
type: string
pishrink:
description: 'pishrink Script'
default: 'https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh'
type: string
z_compress_args:
description: '7z compress args'
default: '7z a -t7z -mx=9'
type: string
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
# - name: Set VERSION variable
# run: |
# VERSION=$(awk '/__version__ /{print $NF}' ./pwnagotchi/_version.py | tr -d "'")
# env:
# VERSION: ${{ steps.set-version.outputs.version }} # Use the extracted version
- name: Show Version
run: |
# Use the $VERSION variable in your build or deployment steps
echo "Using VERSION: ${{ inputs.release_version }}"
- name: Set _version.py correctly using the env variable
run: |
sed -i "s#.*__version__.*#__version__='$PWN_VERSION'#" pwnagotchi/_version.py
env:
PWN_VERSION: ${{ inputs.release_version }}
- name: Install language dependencies
run: sudo apt-get install -y gettext
- name: Languages
run: make langs
- name: Image
run: make image
env:
PWN_VERSION: ${{ inputs.release_version }}
- name: Shrink Image
run: |
ls -a -s -h
wget ${{ inputs.pishrink }}
chmod +x pishrink.sh
sudo mv pishrink.sh /usr/local/bin
sudo pishrink.sh ./pwnagotchi-${{ inputs.release_version }}.img
- uses: edgarrc/action-7z@v1
with:
args: ${{ inputs.z_compress_args }} pwnagotchi-${{ inputs.release_version }}.7z ./pwnagotchi-${{ inputs.release_version }}.img
- name: sha256sum 7z
run: |
sudo sha256sum ./pwnagotchi-${{ inputs.release_version }}.7z > ./pwnagotchi-${{ inputs.release_version }}.sha256
- name: Create GitHub Release
id: create_new_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ inputs.release_version }}
release_name: Release ${{ inputs.release_version }}
- name: Upload GitHub Release sha
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_new_release.outputs.upload_url }}
asset_path: ./pwnagotchi-${{ inputs.release_version }}.sha256
asset_name: pwnagotchi-v${{ inputs.release_version }}.sha256
asset_content_type: appliction/text
- name: Upload GitHub Release Zip
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_new_release.outputs.upload_url }}
asset_path: ./pwnagotchi-${{ inputs.release_version }}.7z
asset_name: pwnagotchi-v${{ inputs.release_version }}.7z
asset_content_type: appliction/zip

3
.gitignore vendored
View File

@ -2,6 +2,7 @@
*.img.bmap
*.pcap
*.po~
*.sha256
preview.png
__pycache__
_backups
@ -18,8 +19,6 @@ pwnagotchi.egg-info
*backup*.tgz
*backup*.gz
.vscode
pwnagotchi-raspbian-lite-master.sha256
pwnagotchi-raspbian-lite-master.zip
# Environments
.env

Binary file not shown.

View File

@ -0,0 +1,253 @@
# Croatian translation
# Copyright (C) 2021
# This file is distributed under the same license as the pwnagotchi package.
# FIRST AUTHOR dbukovac <37124354+dbukovac@users.noreply.github.com>, 2021.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-11-29 21:50+0100\n"
"PO-Revision-Date: 2021-07-16 00:20+0100\n"
"Last-Translator: dbukovac <37124354+dbukovac@users.noreply.github.com>\n"
"Language-Team: HR <37124354+dbukovac@users.noreply.github.com>\n"
"Language: Croatian\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "ZzzzZZzzzzZzzz"
msgstr "ZzzzZZzzzzZzzz"
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Zdravo, ja sam Pwnagotchi! Pokrećem se ..."
msgid "New day, new hunt, new pwns!"
msgstr "Novi dan, novi lov, nove pobjede!"
msgid "Hack the Planet!"
msgstr "Hakiraj planet!"
msgid "AI ready."
msgstr "UI spremna."
msgid "The neural network is ready."
msgstr "Neuralna mreža je spremna."
msgid "Generating keys, do not turn off ..."
msgstr "Generiram ključeve, nemoj me gasiti ..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Hej, kanal {channel} je slobodan! Tvoj AP ti zahvaljuje."
msgid "Reading last session logs ..."
msgstr "Čitam logove zadnje sesije ..."
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr "Pročitao {lines_so_far} linija loga zasad ..."
msgid "I'm bored ..."
msgstr "Dosadno mi je ..."
msgid "Let's go for a walk!"
msgstr "Ajmo u šetnju!"
msgid "This is the best day of my life!"
msgstr "Ovo je najbolji dan u mom životu!"
msgid "Shitty day :/"
msgstr "Usrani dan :/"
msgid "I'm extremely bored ..."
msgstr "Strašno mi je dosadno ..."
msgid "I'm very sad ..."
msgstr "Jako sam tužan ..."
msgid "I'm sad"
msgstr "Tužan sam ..."
msgid "Leave me alone ..."
msgstr "Pusti me na miru ..."
msgid "I'm mad at you!"
msgstr "Ljut sam na tebe!"
msgid "I'm living the life!"
msgstr "To se zove život!"
msgid "I pwn therefore I am."
msgstr "Pwnam dakle postojim."
msgid "So many networks!!!"
msgstr "Toliko mreža!!!"
msgid "I'm having so much fun!"
msgstr "Super se zabavljam!"
msgid "My crime is that of curiosity ..."
msgstr "Znatiželja je moja jedina mana ..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr "Bok {name}! Drago mi je da smo se upoznali. "
#, python-brace-format
msgid "Yo {name}! Sup?"
msgstr "Di si {name}! Šta ima?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr "Bok {name} kako ide?"
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr "Jedinica {name} je u blizini!"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr "Uhm ... doviđenja {name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name} je nestao ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Ups ... {name} je nestao."
#, python-brace-format
msgid "{name} missed!"
msgstr "{name} mi nedostaje!"
msgid "Missed!"
msgstr "Nedostaje mi!"
msgid "Good friends are a blessing!"
msgstr "Imati dobre prijatelje je blagoslov!"
msgid "I love my friends!"
msgstr "Volim svoj prijatelje!"
msgid "Nobody wants to play with me ..."
msgstr "Nitko se ne želi igrati samnom ..."
msgid "I feel so alone ..."
msgstr "Tako sam usamljen ..."
msgid "Where's everybody?!"
msgstr "Gdje su svi nestali?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Pajkim {secs}s ..."
msgid "Zzzzz"
msgstr "Zzzzz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr "ZzzZzzz ({secs}s)"
msgid "Good night."
msgstr "Laku noć."
msgid "Zzz"
msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Čekam {secs}s ..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Gledam uokolo {secs}s ..."
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Bok {what} ajmo biti prijatelji!"
#, python-brace-format
msgid "Associating to {what}"
msgstr "Asociram se sa {what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr "Šta ima {what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Upravo sam odlučio da {mac} ne treba WiFI!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr "Deautenticiram {mac}"
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "Kickbannam {mac}!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Fora, imamo {num} novih handshakeova!"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr "Imate {count} novih poruka!"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr "Ups, nešto je krepalo ... Rebooting ..."
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "Šutnuo {num} stanica\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "Upoznao {num} novih prijatelja\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "Pokupio {num} handshakeova\n"
msgid "Met 1 peer"
msgstr "Sreo 1 novog druga"
#, python-brace-format
msgid "Met {num} peers"
msgstr "Sreo {num} druga"
#, python-brace-format
msgid ""
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgstr ""
"Pwnam {duration} vremena i šutnuo sam {deauthed} klijenata! Sreo sam"
"{associated} novih prijatelja i pojeo {handshakes} handshakeova! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr "sati"
msgid "minutes"
msgstr "minuta"
msgid "seconds"
msgstr "sekundi"
msgid "hour"
msgstr "sat"
msgid "minute"
msgstr "minuta"
msgid "second"
msgstr "sekunda"
#, python-brace-format
msgid "Uploading data to {to} ..."
msgstr "Šaljem podatke na {to} ..."

Binary file not shown.

View File

@ -0,0 +1,252 @@
# Pwnagotchi Turkish translation.
# Copyright (C) 2021
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <abtonc@icloud.com>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-11-29 21:50+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Arda Barış Tonç <abtonc@icloud.com>\n"
"Language-Team: \n"
"Language: Turkish\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "ZzzzZZzzzzZzzz"
msgstr "ZzzzZzzZzZZzzzzZ"
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Merhaba, ben Pwnagotchi! Başlatılıyorum ..."
msgid "New day, new hunt, new pwns!"
msgstr "Yeni bir gün, yeni bir av, yeni pwn'lar!"
msgid "Hack the Planet!"
msgstr "Dünyayı Hackle!"
msgid "AI ready."
msgstr "Yapay zeka hazır."
msgid "The neural network is ready."
msgstr "Nöral ağ hazır."
msgid "Generating keys, do not turn off ..."
msgstr "Anahatarlar oluşturuluyor, lütfen kapatmayın ..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Hey, {channel} kanalı boş! AP'niz teşekkür edecek."
msgid "Reading last session logs ..."
msgstr "Son oturum kayıtları okunuyor ..."
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr "Şimdiye kadar {lines_so_far} kayıt satırı okundu ..."
msgid "I'm bored ..."
msgstr "Sıkıldım ..."
msgid "Let's go for a walk!"
msgstr "Yürüyüşe çıkalım!"
msgid "This is the best day of my life!"
msgstr "Bugün hayatımın en iyi günü!"
msgid "Shitty day :/"
msgstr "Bok gibi bir gün :/"
msgid "I'm extremely bored ..."
msgstr "Çook sıkıldım ..."
msgid "I'm very sad ..."
msgstr "Çok mutsuzum ..."
msgid "I'm sad"
msgstr "Mutsuzum"
msgid "Leave me alone ..."
msgstr "Beni yalnız bırak ..."
msgid "I'm mad at you!"
msgstr "Sana kızgınım!"
msgid "I'm living the life!"
msgstr "Bu hayatı yaşıyorum!"
msgid "I pwn therefore I am."
msgstr "Ben, pwn'ladığım için benim."
msgid "So many networks!!!"
msgstr "Çok fazla ağ var!!!"
msgid "I'm having so much fun!"
msgstr "Çok eğleniyorum!"
msgid "My crime is that of curiosity ..."
msgstr "Tek suçum merak etmek ..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr "Merhaba {name}! Tanıştığıma memnun oldum."
#, python-brace-format
msgid "Yo {name}! Sup?"
msgstr "{name}, kanka! Naber?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr "Nasılsın {name}?"
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr "{name} birimi yakında!"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr "ııı ... görüşürüz {name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name} gitti."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Hoppala ... {name} gitti."
#, python-brace-format
msgid "{name} missed!"
msgstr "{name}'i kaçırdık ya!"
msgid "Missed!"
msgstr "Kaçırdık!"
msgid "Good friends are a blessing!"
msgstr "İyi arkadaşlar nimettir!"
msgid "I love my friends!"
msgstr "Arkadaşlarımı seviyorum!"
msgid "Nobody wants to play with me ..."
msgstr "Hiç kimse benimle birlikte oynamak istemiyor ..."
msgid "I feel so alone ..."
msgstr "Çok yalnız hissediyorum ..."
msgid "Where's everybody?!"
msgstr "Herkes nerede!?"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "{secs}dir kestiriyorum ..."
msgid "Zzzzz"
msgstr "ZzzzZz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr "ZzzZzzZ ({secs})"
msgid "Good night."
msgstr "İyi geceler."
msgid "Zzz"
msgstr "ZzZ"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "{secs}dir bekleniyor ..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Etrafa bakıyorum ({secs})"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Arkadaş olalım {what}!"
#, python-brace-format
msgid "Associating to {what}"
msgstr "{what} ile tanışıyoruz"
#, python-brace-format
msgid "Yo {what}!"
msgstr "Hey {what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Sanırım {mac}'in WiFi'a ihtiyacı yok!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr "{mac} ağdan çıkarılıyor"
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "{mac} atılıp yasaklanıyor!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Güzel, yeni {num} el sıkıştık!"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr "{count} Tane yeni mesajınız var!"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr "Haydaa, bir şeyler ters gitti ... Yeniden başlatılıyor ..."
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "{num} İstasyon atıldı\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "{num} Yeni arkadaş edindim\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "{num} El sıkıştım\n"
msgid "Met 1 peer"
msgstr "1 Kişiyle tanıştım"
#, python-brace-format
msgid "Met {num} peers"
msgstr "{num} Kişiyle tanıştım"
#, python-brace-format
msgid ""
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgstr ""
"{duration}'dır pwn'lıyorum ve {deauthed} kişiyi attım. Hem de {associated}"
"yeni kişiyle tanıştım ve {handshakes} el sıkıştım! #pwnagotchi "
"#pwnlog #pwnyaşam #dünyayıhackle #skynet"
msgid "hours"
msgstr "saat"
msgid "minutes"
msgstr "dakika"
msgid "seconds"
msgstr "saniye"
msgid "hour"
msgstr "saat"
msgid "minute"
msgstr "dakika"
msgid "second"
msgstr "saniye"
#, python-brace-format
msgid "Uploading data to {to} ..."
msgstr "{to}'ye veri yükleniyor ..."

View File

@ -0,0 +1,52 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class DisplayHatMini(DisplayImpl):
def __init__(self, config):
super(DisplayHatMini, self).__init__(config, 'displayhatmini')
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 initialize(self):
logging.info("initializing Display Hat Mini")
from pwnagotchi.ui.hw.libs.pimoroni.displayhatmini.ST7789 import ST7789
self._display = ST7789(0,1,9,13)
def render(self, canvas):
self._display.display(canvas)
def clear(self):
self._display.clear()
def set_backlight(self, value):
if self._display:
self._display.set_backlight(value)
def get_backlight(self):
if self._display:
self._display.get_backlight(value)

View File

@ -0,0 +1,388 @@
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# 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 FOR 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 numbers
import time
import numpy as np
import spidev
import RPi.GPIO as GPIO
# Hardware PWM for the backlight LED requires and external library installed
# with pip3, and a dtoverlay added to /boot/config.txt to enable hardware PWM
# run the following two commands in the shell:
#
# sudo pip3 install rpi-hardware-pwm
# echo "dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4" | sudo tee -a /boot/config.txt
#
from rpi_hardware_pwm import HardwarePWM
__version__ = '0.0.4'
BG_SPI_CS_BACK = 0
BG_SPI_CS_FRONT = 1
SPI_CLOCK_HZ = 16000000
ST7789_NOP = 0x00
ST7789_SWRESET = 0x01
ST7789_RDDID = 0x04
ST7789_RDDST = 0x09
ST7789_SLPIN = 0x10
ST7789_SLPOUT = 0x11
ST7789_PTLON = 0x12
ST7789_NORON = 0x13
ST7789_INVOFF = 0x20
ST7789_INVON = 0x21
ST7789_DISPOFF = 0x28
ST7789_DISPON = 0x29
ST7789_CASET = 0x2A
ST7789_RASET = 0x2B
ST7789_RAMWR = 0x2C
ST7789_RAMRD = 0x2E
ST7789_PTLAR = 0x30
ST7789_MADCTL = 0x36
ST7789_COLMOD = 0x3A
ST7789_FRMCTR1 = 0xB1
ST7789_FRMCTR2 = 0xB2
ST7789_FRMCTR3 = 0xB3
ST7789_INVCTR = 0xB4
ST7789_DISSET5 = 0xB6
ST7789_GCTRL = 0xB7
ST7789_GTADJ = 0xB8
ST7789_VCOMS = 0xBB
ST7789_LCMCTRL = 0xC0
ST7789_IDSET = 0xC1
ST7789_VDVVRHEN = 0xC2
ST7789_VRHS = 0xC3
ST7789_VDVS = 0xC4
ST7789_VMCTR1 = 0xC5
ST7789_FRCTRL2 = 0xC6
ST7789_CABCCTRL = 0xC7
ST7789_RDID1 = 0xDA
ST7789_RDID2 = 0xDB
ST7789_RDID3 = 0xDC
ST7789_RDID4 = 0xDD
ST7789_GMCTRP1 = 0xE0
ST7789_GMCTRN1 = 0xE1
ST7789_PWCTR6 = 0xFC
class ST7789(object):
"""Representation of an ST7789 TFT LCD."""
def __init__(self, port, cs, dc, backlight, rst=None, width=320,
height=240, rotation=0, invert=True, spi_speed_hz=60 * 1000 * 1000,
backlight_pwm=True,
offset_left=0,
offset_top=0):
"""Create an instance of the display using SPI communication.
Must provide the GPIO pin number for the D/C pin and the SPI driver.
Can optionally provide the GPIO pin number for the reset pin as the rst parameter.
:param port: SPI port number
:param cs: SPI chip-select number (0 or 1 for BCM
:param backlight: Pin for controlling backlight
:param backlight_pwm: If true use PWM for backlight, if false then full on
:param rst: Reset pin for ST7789
:param width: Width of display connected to ST7789
:param height: Height of display connected to ST7789
:param rotation: Rotation of display connected to ST7789
:param invert: Invert display
:param spi_speed_hz: SPI speed (in Hz)
"""
if rotation not in [0, 90, 180, 270]:
raise ValueError("Invalid rotation {}".format(rotation))
if width != height and rotation in [90, 270]:
raise ValueError("Invalid rotation {} for {}x{} resolution".format(rotation, width, height))
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
self._spi = spidev.SpiDev(port, cs)
self._spi.mode = 0
self._spi.lsbfirst = False
self._spi.max_speed_hz = spi_speed_hz
self._dc = dc
self._rst = rst
self._width = width
self._height = height
self._rotation = rotation
self._invert = invert
self._offset_left = offset_left
self._offset_top = offset_top
# Set DC as output.
GPIO.setup(dc, GPIO.OUT)
# Setup backlight as output (if provided).
self._backlight = backlight
self._brightness = 0
if backlight is not None:
if backlight_pwm:
self._backlight_pwm = HardwarePWM(pwm_channel=1, hz=100)
self._brightness = 50
self._backlight_pwm.start(self._brightness)
else:
self._backlight_pwm = None
GPIO.setup(backlight, GPIO.OUT)
GPIO.output(backlight, GPIO.LOW)
time.sleep(0.1)
GPIO.output(backlight, GPIO.HIGH)
self._brightness = 100
# Setup reset as output (if provided).
if rst is not None:
GPIO.setup(self._rst, GPIO.OUT)
self.reset()
self._init()
def send(self, data, is_data=True, chunk_size=4096):
"""Write a byte or array of bytes to the display. Is_data parameter
controls if byte should be interpreted as display data (True) or command
data (False). Chunk_size is an optional size of bytes to write in a
single SPI transaction, with a default of 4096.
"""
# Set DC low for command, high for data.
GPIO.output(self._dc, is_data)
# Convert scalar argument to list so either can be passed as parameter.
if isinstance(data, numbers.Number):
data = [data & 0xFF]
# Write data a chunk at a time.
for start in range(0, len(data), chunk_size):
end = min(start + chunk_size, len(data))
self._spi.xfer(data[start:end])
def set_backlight(self, value):
"""Set the backlight on/off. PWM 0.0-1.0, otherwise 1 or 0."""
if self._backlight is not None:
if self._backlight_pwm:
self._backlight_pwm.change_duty_cycle(value * 100)
self._brightness = value
else:
GPIO.output(self._backlight, value)
self._brightness = value
def get_backlight(self):
"""Get the current backlight value."""
if self._backlight is not None:
return self._brightness
@property
def width(self):
return self._width if self._rotation == 0 or self._rotation == 180 else self._height
@property
def height(self):
return self._height if self._rotation == 0 or self._rotation == 180 else self._width
def command(self, data):
"""Write a byte or array of bytes to the display as command data."""
self.send(data, False)
def data(self, data):
"""Write a byte or array of bytes to the display as display data."""
self.send(data, True)
def reset(self):
"""Reset the display, if reset pin is connected."""
if self._rst is not None:
GPIO.output(self._rst, 1)
time.sleep(0.500)
GPIO.output(self._rst, 0)
time.sleep(0.500)
GPIO.output(self._rst, 1)
time.sleep(0.500)
def _init(self):
# Initialize the display.
self.command(ST7789_SWRESET) # Software reset
time.sleep(0.150) # delay 150 ms
self.command(ST7789_MADCTL)
self.data(0x70)
self.command(ST7789_FRMCTR2) # Frame rate ctrl - idle mode
self.data(0x0C)
self.data(0x0C)
self.data(0x00)
self.data(0x33)
self.data(0x33)
self.command(ST7789_COLMOD)
self.data(0x05)
self.command(ST7789_GCTRL)
self.data(0x14)
self.command(ST7789_VCOMS)
self.data(0x37)
self.command(ST7789_LCMCTRL) # Power control
self.data(0x2C)
self.command(ST7789_VDVVRHEN) # Power control
self.data(0x01)
self.command(ST7789_VRHS) # Power control
self.data(0x12)
self.command(ST7789_VDVS) # Power control
self.data(0x20)
self.command(0xD0)
self.data(0xA4)
self.data(0xA1)
self.command(ST7789_FRCTRL2)
self.data(0x0F)
self.command(ST7789_GMCTRP1) # Set Gamma
self.data(0xD0)
self.data(0x04)
self.data(0x0D)
self.data(0x11)
self.data(0x13)
self.data(0x2B)
self.data(0x3F)
self.data(0x54)
self.data(0x4C)
self.data(0x18)
self.data(0x0D)
self.data(0x0B)
self.data(0x1F)
self.data(0x23)
self.command(ST7789_GMCTRN1) # Set Gamma
self.data(0xD0)
self.data(0x04)
self.data(0x0C)
self.data(0x11)
self.data(0x13)
self.data(0x2C)
self.data(0x3F)
self.data(0x44)
self.data(0x51)
self.data(0x2F)
self.data(0x1F)
self.data(0x1F)
self.data(0x20)
self.data(0x23)
if self._invert:
self.command(ST7789_INVON) # Invert display
else:
self.command(ST7789_INVOFF) # Don't invert display
self.command(ST7789_SLPOUT)
self.command(ST7789_DISPON) # Display on
time.sleep(0.100) # 100 ms
def begin(self):
"""Set up the display
Deprecated. Included in __init__.
"""
pass
def set_window(self, x0=0, y0=0, x1=None, y1=None):
"""Set the pixel address window for proceeding drawing commands. x0 and
x1 should define the minimum and maximum x pixel bounds. y0 and y1
should define the minimum and maximum y pixel bound. If no parameters
are specified the default will be to update the entire display from 0,0
to width-1,height-1.
"""
if x1 is None:
x1 = self._width - 1
if y1 is None:
y1 = self._height - 1
y0 += self._offset_top
y1 += self._offset_top
x0 += self._offset_left
x1 += self._offset_left
self.command(ST7789_CASET) # Column addr set
self.data(x0 >> 8)
self.data(x0 & 0xFF) # XSTART
self.data(x1 >> 8)
self.data(x1 & 0xFF) # XEND
self.command(ST7789_RASET) # Row addr set
self.data(y0 >> 8)
self.data(y0 & 0xFF) # YSTART
self.data(y1 >> 8)
self.data(y1 & 0xFF) # YEND
self.command(ST7789_RAMWR) # write to RAM
def display(self, image):
"""Write the provided image to the hardware.
:param image: Should be RGB format and the same dimensions as the display hardware.
"""
# Set address bounds to entire display.
self.set_window()
# Convert image to 16bit RGB565 format and
# flatten into bytes.
pixelbytes = self.image_to_data(image, self._rotation)
# Write data to hardware.
for i in range(0, len(pixelbytes), 4096):
self.data(pixelbytes[i:i + 4096])
def image_to_data(self, image, rotation=0):
if not isinstance(image, np.ndarray):
image = np.array(image.convert('RGB'))
# Rotate the image
pb = np.rot90(image, rotation // 90).astype('uint16')
# Mask and shift the 888 RGB into 565 RGB
red = (pb[..., [0]] & 0xf8) << 8
green = (pb[..., [1]] & 0xfc) << 3
blue = (pb[..., [2]] & 0xf8) >> 3
# Stick 'em together
result = red | green | blue
# Output the raw bytes
return result.byteswap().tobytes()

View File

@ -0,0 +1,241 @@
# *****************************************************************************
# * | 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(int(self.Source_BITS/256))
self.send_data(self.Source_BITS%256)
self.send_data(int(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 ###

View File

@ -0,0 +1,285 @@
# /*****************************************************************************
# * | 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
import subprocess
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 gpiozero
self.SPI = spidev.SpiDev()
self.GPIO_RST_PIN = gpiozero.LED(self.RST_PIN)
self.GPIO_DC_PIN = gpiozero.LED(self.DC_PIN)
# self.GPIO_CS_PIN = gpiozero.LED(self.CS_PIN)
self.GPIO_PWR_PIN = gpiozero.LED(self.PWR_PIN)
self.GPIO_BUSY_PIN = gpiozero.Button(self.BUSY_PIN, pull_up = False)
def digital_write(self, pin, value):
if pin == self.RST_PIN:
if value:
self.GPIO_RST_PIN.on()
else:
self.GPIO_RST_PIN.off()
elif pin == self.DC_PIN:
if value:
self.GPIO_DC_PIN.on()
else:
self.GPIO_DC_PIN.off()
# elif pin == self.CS_PIN:
# if value:
# self.GPIO_CS_PIN.on()
# else:
# self.GPIO_CS_PIN.off()
elif pin == self.PWR_PIN:
if value:
self.GPIO_PWR_PIN.on()
else:
self.GPIO_PWR_PIN.off()
def digital_read(self, pin):
if pin == self.BUSY_PIN:
return self.GPIO_BUSY_PIN.value
elif pin == self.RST_PIN:
return self.RST_PIN.value
elif pin == self.DC_PIN:
return self.DC_PIN.value
# elif pin == self.CS_PIN:
# return self.CS_PIN.value
elif pin == self.PWR_PIN:
return self.PWR_PIN.value
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_PWR_PIN.on()
# 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, cleanup=False):
logger.debug("spi end")
self.SPI.close()
self.GPIO_RST_PIN.off()
self.GPIO_DC_PIN.off()
self.GPIO_PWR_PIN.off()
logger.debug("close 5V, Module enters 0 power consumption ...")
if cleanup:
self.GPIO_RST_PIN.close()
self.GPIO_DC_PIN.close()
# self.GPIO_CS_PIN.close()
self.GPIO_PWR_PIN.close()
self.GPIO_BUSY_PIN.close()
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 sys.version_info[0] == 2:
process = subprocess.Popen("cat /proc/cpuinfo | grep Raspberry", shell=True, stdout=subprocess.PIPE)
else:
process = subprocess.Popen("cat /proc/cpuinfo | grep Raspberry", shell=True, stdout=subprocess.PIPE, text=True)
output, _ = process.communicate()
if sys.version_info[0] == 2:
output = output.decode(sys.stdout.encoding)
if "Raspberry" in output:
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 ###

View File

@ -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 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
# 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 ###

View File

@ -0,0 +1,227 @@
# /*****************************************************************************
# * | 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
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.BUSY_PIN, self.GPIO.IN)
# 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.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_PIN])
class JetsonNano:
# Pin definition
RST_PIN = 17
DC_PIN = 25
CS_PIN = 8
BUSY_PIN = 24
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.BUSY_PIN, self.GPIO.IN)
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.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_PIN])
class SunriseX3:
# Pin definition
RST_PIN = 17
DC_PIN = 25
CS_PIN = 8
BUSY_PIN = 24
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.BUSY_PIN, self.GPIO.IN)
# 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.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_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 ###

View File

@ -0,0 +1,518 @@
# *****************************************************************************
# * | 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, 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 |= 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, 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 |= 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 ###

View File

@ -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 ###

View File

@ -0,0 +1,349 @@
# *****************************************************************************
# * | 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 ###

View File

@ -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 ###

View File

@ -0,0 +1,46 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare213g(DisplayImpl):
def __init__(self, config):
super(Waveshare213g, self).__init__(config, 'waveshare213g')
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 g version display")
from pwnagotchi.ui.hw.libs.waveshare.v213g.epd2in13g 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):
self._display.Clear()

View File

@ -0,0 +1,71 @@
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 v4 display")
from pwnagotchi.ui.hw.libs.waveshare.v213inb_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()

View File

@ -0,0 +1,46 @@
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, 'waveshare27inchv2')
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.v27inchv2.epd2in7_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)
def clear(self):
self._display.Clear()

View File

@ -0,0 +1,50 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
from PIL import Image
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 v4 display")
from pwnagotchi.ui.hw.libs.waveshare.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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

93
requirements.in Normal file
View File

@ -0,0 +1,93 @@
# To convert into requirements.txt use 'pip-compile --resolver=backtracking --strip-extras --output-file=requirements.txt requirements.in'
# If you get "error: no such option: --prefer-binary" then you need to run:
# pip3 install --upgrade "pip>=20.2"
--prefer-binary
--index-url "https://nexus.chadwaltercummings.me/repository/www.piwheels.org/simple"
--extra-index-url "https://nexus.chadwaltercummings.me/repository/pypi.org/simple"
# Used for bluetooth tethering plugin.
dbus-python~=1.2
# Used for parsing LastSession logs in manual mode.
file-read-backwards~=2.0
# Only using basic Flask and Flask plugin features.
# Should be kept up-to-date as Flask is notorious for breaking
# environments with their extremely loose dependency definitions.
flask-cors~=3.0
flask-wtf~=1.0
flask~=1.0
# Used for modeling AI parameters.
# NOTE: stable-baselines wants gym[atari,classic_control] but we
# can't satisfy the "atari" extra because it requires ale-py
# which has no source distributions or RasPi wheels.
# Using pip's new backtracking resolver from pip>=20.3 is required
# as it improves handling of extras required by indirect dependencies.
# NOTE: gym v0.22 modified the gym.Env API.
gym~=0.14,<0.22
# Used for Inky pHAT and wHAT displays.
inky~=1.2
# Used in the AI and UI layers.
# Only using basic numpy features.
numpy~=1.21.4 # Moved to 1.21.4 from 1.20
# Used in the UI layer.
# Only using core PIL features (Image, ImageFont, ImageDraw).
# Very stable library, should be safe to upgrade.
Pillow>=5.4
# Used for pwngrid identity verification (PKCS1, RSA, SHA256).
# Very stable library, should be safe to upgrade.
pycryptodome~=3.9
# Used for GPS plugin to parse a GPS datetime string.
python-dateutil~=2.8
# Used exclusively to convert legacy YAML configs to TOML.
PyYAML>=5.3
# Used for HTTP requests with bettercap, pwngrid, and plugins.
# Only using core library features (GET, POST, Sessions).
# Very stable library, should be safe to upgrade.
requests~=2.21
# Used for WiFi pwnage and WiGLE plugin.
scapy~=2.4
# I2C/SPI communication with displays, also used by some plugins.
smbus2~=0.4
spidev~=3.5
# Primary AI library. Safe to upgrade as v3 is a different package.
# Upgrading to stable-baselines3 is currently impossible because
# it depends on PyTorch which requires a 64-bit processor.
stable-baselines~=2.7
# stable-baselines made a mistake.
# stable-baselines has a tensorflow requirement of ">=1.8.0,<2.0.0",
# but the requirement is the result of a calculation during setup.
# As a result, the requirement is entirely missing from the wheel file.
# Furthermore, "<2.0.0" will fail because tensorflow v1.14 contains
# breaking API changes in preparation for their v2.X release.
tensorflow>=1.8.0,<1.14.0
# Used for loading and writing configs.
toml~=0.10
# Used for communicating with bettercap.
websockets~=8.1
# WARNING: conflict prevention hack!
# flask v1.X requires "Jinja2 >= 2.10, < 3.0"
# Jinja2 v2.X requires "MarkupSafe >= 0.23" for a deprecated
# function that was later removed in MarkupSafe v2.1.0.
# Jinja2 v3.0 no longer uses the deprecated function but
# falls outside the version range requested by flask.
MarkupSafe<2.1.0
# Addressing the "TypeError: Descriptors cannot not be created directly." error.
protobuf==3.20.*

View File

@ -0,0 +1,20 @@
#!/bin/sh
USB_IFACE=$(ifconfig urndis0 | grep urndis0 | awk '{print $1}' | tr -d ':')
USB_IP=${2:-10.0.0.1}
if test $(whoami) != root; then
doas "$0" "$@"
exit $?
fi
if [ "${USB_IFACE}" == "urndis0" ]; then
ifconfig ${USB_IFACE} ${USB_IP}
sysctl -w net.inet.ip.forwarding=1
echo "match out on egress inet from ${USB_IFACE}:network to any nat-to (egress:0)" | pfctl -f -
pfctl -f /etc/pf.conf
echo "sharing connecting from upstream interface to usb interface ${USB_IFACE} ..."
else
echo "can't find usb interface with ip ${USB_IFACE}"
exit 1
fi