Compare commits

..

2 Commits

Author SHA1 Message Date
744a50aa67 Update defaults.toml
Signed-off-by: wpa-2 <9049886+wpa-2@users.noreply.github.com>
2024-02-28 19:09:14 +00:00
eb6e229d36 Update defaults.toml
added some bluetooth information 

Signed-off-by: wpa-2 <9049886+wpa-2@users.noreply.github.com>
2024-02-28 17:57:04 +00:00
89 changed files with 2545 additions and 4292 deletions

1
.github/FUNDING.yml vendored
View File

@ -1,4 +1,3 @@
# These are supported funding model platforms
github: jayofelony
custom: https://tikkie.me/pay/dubcto94hnskg539kar0

View File

@ -61,7 +61,7 @@ jobs:
run: mv "pwnagotchi-64bit.img.xz" "pwnagotchi-$VERSION-64bit.img.xz"
- name: Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v1
with:
prerelease: true
tag_name: v${{ env.VERSION }}

View File

@ -46,16 +46,15 @@ packer: clean
sudo mv /tmp/packer /usr/bin/packer
image: clean packer
export LC_ALL=en_GB.UTF-8
cd builder && sudo /usr/bin/packer init combined.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" combined.json.pkr.hcl
bullseye: clean packer
export LC_ALL=en_GB.UTF-8
cd builder && sudo /usr/bin/packer init raspberrypi32.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" raspberrypi32.json.pkr.hcl
cd builder && sudo /usr/bin/packer init data/32bit/raspberrypi32.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" data/32bit/raspberrypi32.json.pkr.hcl
sudo pishrink -vaZ pwnagotchi-32bit.img
bookworm: clean packer
export LC_ALL=en_GB.UTF-8
cd builder && sudo /usr/bin/packer init raspberrypi64.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" raspberrypi64.json.pkr.hcl
cd builder && sudo /usr/bin/packer init data/64bit/raspberrypi64.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" data/64bit/raspberrypi64.json.pkr.hcl
sudo pishrink -vaZ pwnagotchi-64bit.img
clean:
- rm -rf /tmp/packer*

View File

@ -1 +0,0 @@
bluez-tools

View File

@ -7,7 +7,6 @@ import sys
import toml
import requests
import os
import re
import pwnagotchi
from pwnagotchi import utils
@ -19,12 +18,12 @@ from pwnagotchi import fs
from pwnagotchi.utils import DottedTomlEncoder, parse_version as version_to_tuple
def pwnagotchi_cli():
def do_clear(display):
logging.info("clearing the display ...")
display.clear()
sys.exit(0)
def do_manual_mode(agent):
logging.info("entering manual mode ...")
@ -46,6 +45,7 @@ def pwnagotchi_cli():
if grid.is_connected():
plugins.on('internet_available', agent)
def do_auto_mode(agent):
logging.info("entering auto mode ...")
@ -60,7 +60,6 @@ def pwnagotchi_cli():
channels = agent.get_access_points_by_channel()
# for each channel
for ch, aps in channels:
time.sleep(0.2)
agent.set_channel(ch)
if not agent.is_stale() and agent.any_activity():
@ -96,6 +95,8 @@ def pwnagotchi_cli():
else:
logging.exception("main loop exception (%s)", e)
if __name__ == '__main__':
def add_parsers(parser):
"""
Adds the plugins and google subcommands
@ -132,8 +133,6 @@ def pwnagotchi_cli():
help="Print the configuration.")
# Jayofelony added these
parser.add_argument('--wizard', dest="wizard", action="store_true", default=False,
help="Interactive installation of your personal configuration.")
parser.add_argument('--check-update', dest="check_update", action="store_true", default=False,
help="Check for updates on Pwnagotchi. And tells current version.")
parser.add_argument('--donate', dest="donate", action="store_true", default=False,
@ -158,111 +157,6 @@ def pwnagotchi_cli():
print(pwnagotchi.__version__)
sys.exit(0)
if args.wizard:
def is_valid_hostname(hostname):
if len(hostname) > 255:
return False
if hostname[-1] == ".":
hostname = hostname[:-1] # strip exactly one dot from the right, if present
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
return all(allowed.match(x) for x in hostname.split("."))
pwn_restore = input("Do you want to restore the previous configuration?\n\n"
"[Y/N]: ")
if pwn_restore in ('y', 'yes'):
os.system("cp -f /etc/pwnagotchi/config.toml.bak /etc/pwnagotchi/config.toml")
print("Your previous configuration is restored, and I will restart in 5 seconds.")
time.sleep(5)
os.system("service pwnagotchi restart")
else:
pwn_check = input("This will create a new configuration file and overwrite your current backup, are you sure?\n\n"
"[Y/N]: ")
if pwn_check.lower() in ('y', 'yes'):
os.system("mv -f /etc/pwnagotchi/config.toml /etc/pwnagotchi/config.toml.bak")
with open("/etc/pwnagotchi/config.toml", "a+") as f:
f.write("# Do not edit this file if you do not know what you are doing!!!\n\n")
# Set pwnagotchi name
print("Welcome to the interactive installation of your personal Pwnagotchi configuration!\n"
"My name is Jayofelony, how may I call you?\n\n")
pwn_name = input("Pwnagotchi name (no spaces): ")
if pwn_name == "":
pwn_name = "Pwnagotchi"
print("I shall go by Pwnagotchi from now on!")
pwn_name = f"main.name = \"{pwn_name}\"\n"
f.write(pwn_name)
else:
if is_valid_hostname(pwn_name):
print(f"I shall go by {pwn_name} from now on!")
pwn_name = f"main.name = \"{pwn_name}\"\n"
f.write(pwn_name)
else:
print("You have chosen an invalid name. Please start over.")
exit()
pwn_whitelist = input("How many networks do you want to whitelist? "
"We will also ask a MAC for each network?\n"
"Each SSID and BSSID count as 1 network. \n\n"
"Be sure to use digits as your answer.\n\n"
"Amount of networks: ")
if int(pwn_whitelist) > 0:
f.write("main.whitelist = [\n")
for x in range(int(pwn_whitelist)):
ssid = input("SSID (Name): ")
bssid = input("BSSID (MAC): ")
f.write(f"\t\"{ssid}\",\n")
f.write(f"\t\"{bssid}\",\n")
f.write("]\n")
# set bluetooth tether
pwn_bluetooth = input("Do you want to enable BT-Tether?\n\n"
"[Y/N] ")
if pwn_bluetooth.lower() in ('y', 'yes'):
f.write("main.plugins.bt-tether.enabled = true\n\n")
pwn_bluetooth_device = input("What device do you use? Android or iOS?\n\n"
"Device: ")
if pwn_bluetooth_device.lower() == "android":
f.write("main.plugins.bt-tether.devices.android-phone.enabled = true\n")
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
"MAC: ")
if pwn_bluetooth_mac != "":
f.write(f"main.plugins.bt-tether.devices.android-phone.mac = \"{pwn_bluetooth_mac}\"\n")
elif pwn_bluetooth_device.lower() == "ios":
f.write("main.plugins.bt-tether.devices.ios-phone.enabled = true\n")
pwn_bluetooth_mac = input("What is the bluetooth MAC of your device?\n\n"
"MAC: ")
if pwn_bluetooth_mac != "":
f.write(f"main.plugins.bt-tether.devices.ios-phone.mac = \"{pwn_bluetooth_mac}\"\n")
# set up display settings
pwn_display_enabled = input("Do you want to enable a display?\n\n"
"[Y/N]: ")
if pwn_display_enabled.lower() in ('y', 'yes'):
f.write("ui.display.enabled = true\n")
pwn_display_type = input("What display do you use?\n\n"
"Be sure to check for the correct display type @ \n"
"https://github.com/jayofelony/pwnagotchi/blob/master/pwnagotchi/utils.py#L240-L431\n\n"
"Display type: ")
if pwn_display_type != "":
f.write(f"ui.display.type = \"{pwn_display_type}\"\n")
pwn_display_invert = input("Do you want to invert the display colors?\n"
"N = Black background\n"
"Y = White background\n\n"
"[Y/N]: ")
if pwn_display_invert.lower() in ('y', 'yes'):
f.write("ui.invert = true\n")
f.close()
if pwn_bluetooth.lower() in ('y', 'yes'):
if pwn_bluetooth_device.lower == "android":
print("To visit the webui when connected with your phone, visit: http://192.168.44.44:8080\n"
"Your configuration is done, and I will restart in 5 seconds.")
elif pwn_bluetooth_device.lower == "ios":
print("To visit the webui when connected with your phone, visit: http://172.20.10.6:8080\n"
"Your configuration is done, and I will restart in 5 seconds.")
else:
print("Your configuration is done, and I will restart in 5 seconds.")
time.sleep(5)
os.system("service pwnagotchi restart")
else:
print("Ok, doing nothing.")
sys.exit(0)
if args.donate:
print("Donations can made @ \n "
"https://www.patreon.com/pwnagotchi_torch \n "
@ -278,7 +172,7 @@ def pwnagotchi_cli():
local = version_to_tuple(pwnagotchi.__version__)
remote = version_to_tuple(latest_ver)
if remote > local:
user_input = input("There is a new version available! Update from v%s to v%s?\n[Y/N] "
user_input = input("There is a new version available! Update from v%s to v%s?\n[y(es)/n(o)]"
% (pwnagotchi.__version__, latest_ver))
# input validation
if user_input.lower() in ('y', 'yes'):
@ -333,6 +227,3 @@ def pwnagotchi_cli():
do_manual_mode(agent)
else:
do_auto_mode(agent)
if __name__ == '__main__':
pwnagotchi_cli()

View File

@ -20,8 +20,8 @@ variable "pwn_version" {
}
source "arm" "rpi64-pwnagotchi" {
file_checksum_url = "https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2024-03-15/2024-03-15-raspios-bookworm-arm64-lite.img.xz.sha256"
file_urls = ["https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2024-03-15/2024-03-15-raspios-bookworm-arm64-lite.img.xz"]
file_checksum_url = "https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz.sha256"
file_urls = ["https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz"]
file_checksum_type = "sha256"
file_target_extension = "xz"
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
@ -50,8 +50,8 @@ source "arm" "rpi64-pwnagotchi" {
}
source "arm" "rpi32-pwnagotchi" {
file_checksum_url = "https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2024-03-12/2024-03-12-raspios-bullseye-armhf-lite.img.xz.sha256"
file_urls = ["https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2024-03-12/2024-03-12-raspios-bullseye-armhf-lite.img.xz"]
file_checksum_url = "https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-12-06/2023-12-05-raspios-bullseye-armhf-lite.img.xz.sha256"
file_urls = ["https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-12-06/2023-12-05-raspios-bullseye-armhf-lite.img.xz"]
file_checksum_type = "sha256"
file_target_extension = "xz"
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
@ -123,7 +123,7 @@ build {
provisioner "ansible-local" {
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
playbook_file = "raspberrypi64.yml"
playbook_file = "data/64bit/raspberrypi64.yml"
}
}
@ -167,7 +167,7 @@ build {
provisioner "ansible-local" {
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
playbook_dir = "extras/"
playbook_file = "raspberrypi32.yml"
playbook_dir = "data/32bit/extras/"
playbook_file = "data/32bit/raspberrypi32.yml"
}
}

View File

@ -1,67 +0,0 @@
# For more options and information see
# http://rptl.io/configtxt
# Some settings may impact device functionality. See link above for details
# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
#dtparam=i2s=on
#dtparam=spi=on
# Enable audio (loads snd_bcm2835)
dtparam=audio=on
# Additional overlays and parameters are documented
# /boot/overlays/README
# Automatically load overlays for detected cameras
camera_auto_detect=1
# Automatically load overlays for detected DSI displays
display_auto_detect=1
# Automatically load initramfs files, if found
auto_initramfs=1
# Enable DRM VC4 V3D driver
dtoverlay=vc4-kms-v3d
max_framebuffers=2
# Don't have the firmware create an initial video= setting in cmdline.txt.
# Use the kernel's default instead.
disable_fw_kms_setup=1
# Run in 64-bit mode
arm_64bit=0
# Disable compensation for displays with overscan
disable_overscan=1
# Run as fast as firmware / board allows
arm_boost=1
[cm4]
# Enable host mode on the 2711 built-in XHCI USB controller.
# This line should be removed if the legacy DWC2 controller is required
# (e.g. for USB device mode) or if USB support is not required.
otg_mode=1
[all]
dtoverlay=dwc2
dtparam=i2c1=on
dtparam=i2c_arm=on
dtparam=spi=on
gpu_mem=1
dtoverlay=dwc2
#dtoverlay=disable-wifi
[pi0]
dtoverlay=spi1-3cs
#dtoverlay=disable-wifi
[pi3]
dtoverlay=spi1-3cs
#dtoverlay=disable-wifi
[pi4]
dtoverlay=spi1-3cs
#dtoverlay=disable-wifi

View File

@ -1,6 +0,0 @@
# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
# Parameters can be specified after the module name.
i2c-dev

View File

@ -1,40 +0,0 @@
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).
if [ "$(id -u)" -eq 0 ]; then
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
else
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games"
fi
export PATH
if [ "${PS1-}" ]; then
if [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then
# The file bash.bashrc already sets the default PS1.
# PS1='\h:\w\$ '
if [ -f /etc/bash.bashrc ]; then
. /etc/bash.bashrc
fi
else
if [ "$(id -u)" -eq 0 ]; then
PS1='# '
else
PS1='$ '
fi
fi
fi
if [ -d /etc/profile.d ]; then
for i in /etc/profile.d/*.sh; do
if [ -r $i ]; then
. $i
fi
done
unset i
fi
alias custom='cd /usr/local/share/pwnagotchi/custom-plugins/'
alias config='sudo nano /etc/pwnagotchi/config.toml'
alias pwnlog='tail -f -n300 /etc/pwnagotchi/log/pwn*.log | sed --unbuffered "s/,[[:digit:]]\\{3\\}\\]//g" | cut -d " " -f 2-'
alias pwnver='python3 -c "import pwnagotchi as p; print(p.__version__)"'
alias pwnkill='sudo killall -USR1 pwnagotchi'

View File

@ -1,16 +0,0 @@
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
timedatectl set-ntp true
exit 0

View File

@ -20,8 +20,8 @@ variable "pwn_version" {
}
source "arm" "rpi32-pwnagotchi" {
file_checksum_url = "https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2024-03-12/2024-03-12-raspios-bullseye-armhf-lite.img.xz.sha256"
file_urls = ["https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2024-03-12/2024-03-12-raspios-bullseye-armhf-lite.img.xz"]
file_checksum_url = "https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-12-06/2023-12-05-raspios-bullseye-armhf-lite.img.xz.sha256"
file_urls = ["https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-12-06/2023-12-05-raspios-bullseye-armhf-lite.img.xz"]
file_checksum_type = "sha256"
file_target_extension = "xz"
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
@ -88,7 +88,7 @@ build {
provisioner "ansible-local" {
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
playbook_dir = "extras/"
playbook_file = "raspberrypi32.yml"
playbook_dir = "data/32bit/extras/"
playbook_file = "data/32bit/raspberrypi32.yml"
}
}

View File

@ -39,6 +39,26 @@
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi-torch', true) }}"
custom_plugin_dir: "/usr/local/share/pwnagotchi/custom-plugins"
system:
boot_options:
- "#### pwnagotchi additions"
- "# this pwnagotchi image is 32-bit only no v8+ headers to build nexmon for 64 bit"
- "arm_64bit=0"
- "# dwc2 for RNDIS. comment out, and remove dwc2 and g_ether from cmdline.txt for X306 usb battery hat"
- "dtoverlay=dwc2"
- "dtoverlay=spi1-3cs"
- "dtparam=i2c1=on"
- "dtparam=i2c_arm=on"
- "dtparam=spi=on"
- "gpu_mem=16"
- "#### audio out on pins 18 and 19"
- "#dtoverlay=audremap,pins_18_19"
- "#### touchscreen on waveshare touch e-paper"
- "#dtoverlay=goodix,interrupt=27,reset=22"
- "#### for PWM backlighting on pimoroni displayhatmini"
- "dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4"
modules:
- "i2c-dev"
services:
enable:
- bettercap.service
@ -97,7 +117,6 @@
- bc
- bison
- bluez
- bluez-tools
- build-essential
- curl
- dkms
@ -158,8 +177,6 @@
- libusb-1.0-0-dev
- lsof
- make
- ntp
- python3-dbus
- python3-flask
- python3-flask-cors
- python3-flaskext.wtf
@ -211,11 +228,6 @@
path: '{{ pwnagotchi.custom_plugin_dir }}'
state: directory
- name: remove current rc.local
file:
path: /etc/rc.local
state: absent
- name: update apt package cache
apt:
update_cache: yes
@ -439,7 +451,7 @@
# Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
# Example:
# ui.display.enabled = true
# ui.display.type = "waveshare_4"
# ui.display.type = "waveshare_2"
when: not user_config.stat.exists
- name: Delete motd 10-uname
@ -452,6 +464,20 @@
path: /boot/ssh
state: touch
- name: adjust /boot/config.txt
lineinfile:
dest: /boot/config.txt
insertafter: EOF
line: '{{ item }}'
with_items: "{{system.boot_options}}"
- name: adjust /etc/modules
lineinfile:
dest: /etc/modules
insertafter: EOF
line: '{{ item }}'
with_items: "{{system.modules}}"
- name: change root partition
replace:
dest: /boot/cmdline.txt
@ -468,6 +494,24 @@
regexp: '(.*)$'
line: '\1 modules-load=dwc2,g_ether'
- name: Add pwnlog alias
lineinfile:
dest: /home/pi/.bashrc
line: "\nalias pwnlog='tail -f -n300 /etc/pwnagotchi/log/pwn*.log | sed --unbuffered \"s/,[[:digit:]]\\{3\\}\\]//g\" | cut -d \" \" -f 2-'"
insertafter: EOF
- name: Add pwnver alias
lineinfile:
dest: /home/pi/.bashrc
line: "\nalias pwnver='python3 -c \"import pwnagotchi as p; print(p.__version__)\"'"
insertafter: EOF
- name: Add pwnkill alias to restart pwnagotchi with a signal
lineinfile:
dest: /home/pi/.bashrc
line: "\nalias pwnkill='sudo killall -USR1 pwnagotchi'"
insertafter: EOF
- name: add firmware packages to hold
dpkg_selections:
name: "{{ item }}"

View File

@ -11,7 +11,6 @@ fi
if is_auto_mode; then
/usr/local/bin/pwnagotchi
systemctl restart bettercap
else
/usr/local/bin/pwnagotchi --manual
fi

View File

@ -1,70 +0,0 @@
# For more options and information see
# http://rptl.io/configtxt
# Some settings may impact device functionality. See link above for details
# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
#dtparam=i2s=on
#dtparam=spi=on
# Enable audio (loads snd_bcm2835)
dtparam=audio=on
# Additional overlays and parameters are documented
# /boot/firmware/overlays/README
# Automatically load overlays for detected cameras
camera_auto_detect=1
# Automatically load overlays for detected DSI displays
display_auto_detect=1
# Automatically load initramfs files, if found
auto_initramfs=1
# Enable DRM VC4 V3D driver
dtoverlay=vc4-kms-v3d
max_framebuffers=2
# Don't have the firmware create an initial video= setting in cmdline.txt.
# Use the kernel's default instead.
disable_fw_kms_setup=1
# Run in 64-bit mode
arm_64bit=1
# Disable compensation for displays with overscan
disable_overscan=1
# Run as fast as firmware / board allows
arm_boost=1
[cm4]
# Enable host mode on the 2711 built-in XHCI USB controller.
# This line should be removed if the legacy DWC2 controller is required
# (e.g. for USB device mode) or if USB support is not required.
otg_mode=1
[all]
dtparam=i2c1=on
dtparam=i2c_arm=on
dtparam=spi=on
gpu_mem=1
dtoverlay=dwc2
#dtoverlay=disable-wifi
[pi0]
dtoverlay=spi0-0cs
#dtoverlay=disable-wifi
[pi3]
dtoverlay=spi0-0cs
#dtoverlay=disable-wifi
[pi4]
dtoverlay=spi0-0cs
#dtoverlay=disable-wifi
[pi5]
dtoverlay=spi0-0cs
#dtoverlay=disable-wifi

View File

@ -2,7 +2,7 @@ _show_complete()
{
local cur opts node_names all_options opt_line
all_options="
pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config --wizard --check-update --donate {plugins,google}
pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config --check-update --donate {plugins,google}
pwnagotchi plugins -h --help {list,install,enable,disable,uninstall,update,upgrade}
pwnagotchi plugins list -i --installed -h --help
pwnagotchi plugins install -h --help

View File

@ -1,6 +0,0 @@
# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
# Parameters can be specified after the module name.
i2c-dev

View File

@ -1,40 +0,0 @@
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).
if [ "$(id -u)" -eq 0 ]; then
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
else
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games"
fi
export PATH
if [ "${PS1-}" ]; then
if [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then
# The file bash.bashrc already sets the default PS1.
# PS1='\h:\w\$ '
if [ -f /etc/bash.bashrc ]; then
. /etc/bash.bashrc
fi
else
if [ "$(id -u)" -eq 0 ]; then
PS1='# '
else
PS1='$ '
fi
fi
fi
if [ -d /etc/profile.d ]; then
for i in /etc/profile.d/*.sh; do
if [ -r $i ]; then
. $i
fi
done
unset i
fi
alias custom='cd /usr/local/share/pwnagotchi/custom-plugins/'
alias config='sudo nano /etc/pwnagotchi/config.toml'
alias pwnlog='tail -f -n300 /etc/pwnagotchi/log/pwn*.log | sed --unbuffered "s/,[[:digit:]]\\{3\\}\\]//g" | cut -d " " -f 2-'
alias pwnver='python3 -c "import pwnagotchi as p; print(p.__version__)"'
alias pwnkill='sudo killall -USR1 pwnagotchi'

View File

@ -1,16 +0,0 @@
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
timedatectl set-ntp true
exit 0

View File

@ -20,7 +20,6 @@ echo " I'm managed by systemd. Here are some basic commands."
echo
echo " If you want to know what I'm doing, you can check my logs with the command"
echo " - pwnlog"
echo " - sudo pwnagotchi --wizard, to help set up a config.toml"
echo " - sudo pwnagotchi --version, to check the current version"
echo " - sudo pwnagotchi --donate, to see how you can donate to this project"
echo " - sudo pwnagotchi --check-update, to see if there is a new version available"

View File

@ -20,8 +20,8 @@ variable "pwn_version" {
}
source "arm" "rpi64-pwnagotchi" {
file_checksum_url = "https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2024-03-15/2024-03-15-raspios-bookworm-arm64-lite.img.xz.sha256"
file_urls = ["https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2024-03-15/2024-03-15-raspios-bookworm-arm64-lite.img.xz"]
file_checksum_url = "https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz.sha256"
file_urls = ["https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz"]
file_checksum_type = "sha256"
file_target_extension = "xz"
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
@ -95,6 +95,6 @@ build {
provisioner "ansible-local" {
command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook"
extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""]
playbook_file = "raspberrypi64.yml"
playbook_file = "data/64bit/raspberrypi64.yml"
}
}

View File

@ -5,12 +5,22 @@
become: true
vars:
kernel:
min: "6.6"
full: "6.6.20+rpt-rpi-v8"
full_pi5: "6.6.20+rpt-rpi-2712"
min: "6.1"
full: "6.1.0-rpi8-rpi-v8"
full_pi5: "6.1.0-rpi8-rpi-2712"
pwnagotchi:
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi', true) }}"
system:
boot_options:
- "dtoverlay=dwc2"
- "dtoverlay=spi1-3cs"
- "dtparam=i2c1=on"
- "dtparam=i2c_arm=on"
- "dtparam=spi=on"
- "gpu_mem=16"
modules:
- "i2c-dev"
services:
enable:
- bettercap.service
@ -64,7 +74,6 @@
- bc
- bison
- bluez
- bluez-tools
- build-essential
- curl
- dkms
@ -128,7 +137,6 @@
- libusb-1.0-0-dev
- lsof
- make
- ntp
- python3-dbus
- python3-flask
- python3-flask-cors
@ -172,12 +180,6 @@
update_cache: yes
install_recommends: false
- name: update pip3, setuptools, wheel
shell: "python3 -m pip install --upgrade pip setuptools wheel --break-system-packages"
args:
executable: /bin/bash
chdir: /usr/local/src
# Now we set up /boot/firmware
- name: Create pi user
copy:
@ -190,10 +192,12 @@
path: /boot/firmware/ssh
state: touch
- name: remove current rc.local
file:
path: /etc/rc.local
state: absent
- name: adjust /boot/firmware/config.txt
lineinfile:
dest: /boot/firmware/config.txt
insertafter: EOF
line: '{{ item }}'
with_items: "{{ system.boot_options }}"
- name: change root partition
replace:
@ -482,7 +486,7 @@
- name: Update .bashrc for go-1.21
blockinfile:
dest: /etc/profile
dest: /home/pi/.bashrc
state: present
block: |
export GOPATH=$HOME/go
@ -575,7 +579,7 @@
# Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
# Example:
# ui.display.enabled = true
# ui.display.type = "waveshare_4"
# ui.display.type = "waveshare_2"
when: not user_config.stat.exists
- name: Delete motd
@ -588,6 +592,24 @@
state: absent
path: /etc/update-motd.d/10-uname
- name: Add pwnlog alias
lineinfile:
dest: /home/pi/.bashrc
line: "\nalias pwnlog='tail -f -n300 /etc/pwnagotchi/log/pwn*.log | sed --unbuffered \"s/,[[:digit:]]\\{3\\}\\]//g\" | cut -d \" \" -f 2-'"
insertafter: EOF
- name: Add pwnver alias
lineinfile:
dest: /home/pi/.bashrc
line: "\nalias pwnver='python3 -c \"import pwnagotchi as p; print(p.__version__)\"'"
insertafter: EOF
- name: Add pwnkill alias to restart pwnagotchi with a signal
lineinfile:
dest: /home/pi/.bashrc
line: "\nalias pwnkill='sudo killall -USR1 pwnagotchi'"
insertafter: EOF
- name: add firmware packages to hold
dpkg_selections:
name: "{{ item }}"

View File

@ -11,7 +11,6 @@ fi
if is_auto_mode; then
/usr/local/bin/pwnagotchi
systemctl restart bettercap
else
/usr/local/bin/pwnagotchi --manual
fi

View File

@ -1,5 +1,18 @@
#!/usr/bin/env bash
# well ... it blinks the led
blink_led() {
# shellcheck disable=SC2034
for i in $(seq 1 "$1"); do
echo 0 >/sys/class/leds/led0/brightness
sleep 0.3
echo 1 >/sys/class/leds/led0/brightness
sleep 0.3
done
echo 0 >/sys/class/leds/led0/brightness
sleep 0.3
}
# reload mod
reload_brcm() {
if ! modprobe -r brcmfmac; then

View File

@ -1 +1 @@
__version__ = '2.8.8'
__version__ = '2.8.5'

View File

@ -254,7 +254,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
txt = '%d (%d)' % (len(self._handshakes), tot)
if self._last_pwnd is not None:
txt += ' [%s]' % self._last_pwnd
txt += ' [%s]' % self._last_pwnd[:11] # So it doesn't overlap with fix_brcmfmac_plugin
self._view.set('shakes', txt)

View File

@ -100,6 +100,7 @@ class Client(object):
await asyncio.sleep(sleep_time)
continue
except OSError:
sleep_time = min_sleep + max_sleep * random.random()
logging.warning('connection to the bettercap endpoint failed...')
pwnagotchi.restart("AUTO")

View File

@ -24,27 +24,30 @@ main.plugins.auto-update.interval = 1
main.plugins.bt-tether.enabled = false
# Configuration for Android Phone
main.plugins.bt-tether.devices.android-phone.enabled = false
main.plugins.bt-tether.devices.android-phone.search_order = 1
main.plugins.bt-tether.devices.android-phone.mac = ""
main.plugins.bt-tether.devices.android-phone.ip = "192.168.44.44"
main.plugins.bt-tether.devices.android-phone.netmask = 24
main.plugins.bt-tether.devices.android-phone.interval = 1
main.plugins.bt-tether.devices.android-phone.scantime = 10
main.plugins.bt-tether.devices.android-phone.max_tries = 10
main.plugins.bt-tether.devices.android-phone.share_internet = true
main.plugins.bt-tether.devices.android-phone.priority = 1
main.plugins.bt-tether.devices.android-phone.mac = "" # Bluetooth MAC address of the Android phone
main.plugins.bt-tether.devices.android-phone.ip = "192.168.44.44" # Static IP of the Pwnagotchi
main.plugins.bt-tether.devices.android-phone.netmask = 24 # Netmask of the PAN
main.plugins.bt-tether.devices.android-phone.interval = 1 # Search interval in minutes
main.plugins.bt-tether.devices.android-phone.scantime = 10 # Duration of each search in seconds
main.plugins.bt-tether.devices.android-phone.max_tries = 10 # Maximum attempts to find the phone
main.plugins.bt-tether.devices.android-phone.share_internet = false # Enable internet sharing via Bluetooth
main.plugins.bt-tether.devices.android-phone.priority = 1 # Priority level for tethering
# Configuration for iOS Phone
main.plugins.bt-tether.devices.ios-phone.enabled = false
main.plugins.bt-tether.devices.ios-phone.search_order = 2
main.plugins.bt-tether.devices.ios-phone.mac = ""
main.plugins.bt-tether.devices.ios-phone.ip = "172.20.10.6"
main.plugins.bt-tether.devices.ios-phone.netmask = 24
main.plugins.bt-tether.devices.ios-phone.interval = 5
main.plugins.bt-tether.devices.ios-phone.scantime = 20
main.plugins.bt-tether.devices.ios-phone.max_tries = 0
main.plugins.bt-tether.devices.ios-phone.share_internet = true
main.plugins.bt-tether.devices.ios-phone.priority = 999
main.plugins.bt-tether.devices.ios-phone.search_order = 1
main.plugins.bt-tether.devices.ios-phone.mac = "" # Bluetooth MAC address of the iOS phone
main.plugins.bt-tether.devices.ios-phone.ip = "" # Static IP of the Pwnagotchi when tethered to iOS
main.plugins.bt-tether.devices.ios-phone.netmask = 24 # Netmask of the PAN
main.plugins.bt-tether.devices.ios-phone.interval = 1 # Search interval in minutes
main.plugins.bt-tether.devices.ios-phone.scantime = 10 # Duration of each search in seconds
main.plugins.bt-tether.devices.ios-phone.max_tries = 10 # Maximum attempts to find the phone
main.plugins.bt-tether.devices.ios-phone.share_internet = false # Enable internet sharing via Bluetooth
main.plugins.bt-tether.devices.ios-phone.priority = 1 # Priority level for tethering (edited)
main.plugins.fix_services.enabled = true
@ -112,6 +115,8 @@ main.mon_stop_cmd = "/usr/bin/monstop"
main.mon_max_blind_epochs = 50
main.no_restart = false
main.filter = ""
main.log.path = "/etc/pwnagotchi/log/pwnagotchi.log"
main.log.rotation.enabled = true
main.log.rotation.size = "10M"
@ -152,8 +157,6 @@ personality.throttle_d = 0.9
personality.clear_on_exit = true # clear display when shutting down cleanly
ui.invert = false # false = black background, true = white background
ui.fps = 0.0
ui.font.name = "DejaVuSansMono" # for japanese: fonts-japanese-gothic
ui.font.size_offset = 0 # will be added to the font size

View File

@ -248,7 +248,7 @@ msgid "minutes"
msgstr "minuten"
msgid "seconds"
msgstr "sekondes"
msgstr "seconds"
msgid "hour"
msgstr "oere"

View File

@ -199,9 +199,6 @@ def list_plugins(args, config, pattern='*'):
available_not_installed = set(available.keys()) - set(installed.keys())
max_len_list = available_and_installed if args.installed else available_not_installed
if not max_len_list:
print('Maybe try: sudo pwnagotchi plugins update')
return 1
max_len = max(map(len, max_len_list))
header = line.format(name='Plugin', width=max_len, version='Version', enabled='Active', status='Status')
line_length = max(max_len, len('Plugin')) + len(header) - len('Plugin') - 12 # lol
@ -242,7 +239,7 @@ def list_plugins(args, config, pattern='*'):
print('-' * line_length)
if not found:
print('Maybe try: sudo pwnagotchi plugins update')
logging.info('Maybe try: pwnagotchi plugins update')
return 1
return 0

View File

@ -29,7 +29,6 @@ def check(version, repo, native=True):
latest = resp.json()
info['available'] = latest_ver = latest['tag_name'].replace('v', '')
is_armhf = info['arch'].startswith('arm')
is_aarch = info['arch'].startswith('aarch')
local = version_to_tuple(info['current'])
remote = version_to_tuple(latest_ver)
@ -45,14 +44,6 @@ def check(version, repo, native=True):
(info['arch'] in download_url or (is_armhf and 'armhf' in download_url))):
info['url'] = download_url
break
elif is_aarch:
# check if this release is compatible with arm64/aarch64
for asset in latest['assets']:
download_url = asset['browser_download_url']
if (download_url.endswith('.zip') and
(info['arch'] in download_url or (is_aarch and 'aarch' in download_url))):
info['url'] = download_url
break
return info

View File

@ -4,8 +4,6 @@ import subprocess
import time
import random
from io import TextIOWrapper
import os
import platform
import pwnagotchi
from pwnagotchi import plugins
@ -34,16 +32,17 @@ class FixServices(plugins.Plugin):
self.pattern2 = re.compile(r'wifi error while hopping to channel')
self.pattern3 = re.compile(r'Firmware has halted or crashed')
self.pattern4 = re.compile(r'error 400: could not find interface wlan0mon')
self.pattern5 = re.compile(r'fatal error: concurrent map iteration and map write')
self.pattern6 = re.compile(r'panic: runtime error')
self.isReloadingMon = False
self.connection = None
self.LASTTRY = 0
self._status = "--"
self._count = 0
def on_loaded(self):
"""
Gets called when the plugin gets loaded
"""
self._status = "ld"
logging.info("[Fix_Services] plugin loaded.")
def on_ready(self, agent):
@ -51,27 +50,31 @@ class FixServices(plugins.Plugin):
stdout=subprocess.PIPE).stdout))[-10:])
try:
cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True)
logging.debug("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
logging.info("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
if ",UP," in str(cmd_output):
logging.debug("wlan0mon is up.")
logging.info("wlan0mon is up.")
self._status = "up"
if len(self.pattern.findall(last_lines)) >= 3:
self._status = "XX"
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Blind-Bug detected. Restarting.')
display.update(force=True)
logging.debug('[Fix_Services] Blind-Bug detected. Restarting.')
logging.info('[Fix_Services] Blind-Bug detected. Restarting.')
try:
self._tryTurningItOffAndOnAgain(agent)
except Exception as err:
logging.warning("[Fix_Services turnOffAndOn] %s" % repr(err))
else:
logging.debug("[Fix_Services] Logs look good!")
logging.info("[Fix_Services] Logs look good!")
self._status = ""
except Exception as err:
logging.error("[Fix_Services ip link show wlan0mon]: %s" % repr(err))
try:
self._status = "xx"
self._tryTurningItOffAndOnAgain(agent)
except Exception as err:
logging.error("[Fix_Services OffNOn]: %s" % repr(err))
@ -81,12 +84,12 @@ class FixServices(plugins.Plugin):
# apparently this only gets messages from bettercap going to syslog, not from syslog
def on_bcap_sys_log(self, agent, event):
if re.search('wifi error while hopping to channel', event['data']['Message']):
logging.debug("[Fix_Services]SYSLOG MATCH: %s" % event['data']['Message'])
logging.debug("[Fix_Services]**** restarting wifi.recon")
logging.info("[Fix_Services]SYSLOG MATCH: %s" % event['data']['Message'])
logging.info("[Fix_Services]**** restarting wifi.recon")
try:
result = agent.run("wifi.recon off; wifi.recon on")
if result["success"]:
logging.debug("[Fix_Services] wifi.recon flip: success!")
logging.info("[Fix_Services] wifi.recon flip: success!")
if hasattr(agent, 'view'):
display = agent.view()
if display:
@ -117,14 +120,13 @@ class FixServices(plugins.Plugin):
logging.debug("[Fix_Services]**** checking")
# Look for pattern 1
if platform.machine().startswith('arm'):
if len(self.pattern.findall(last_lines)) >= 3:
logging.debug("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
logging.info("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Blind-Bug detected. Restarting.')
display.update(force=True)
logging.debug('[Fix_Services] Blind-Bug detected. Restarting.')
logging.info('[Fix_Services] Blind-Bug detected. Restarting.')
try:
self._tryTurningItOffAndOnAgain(agent)
except Exception as err:
@ -132,20 +134,20 @@ class FixServices(plugins.Plugin):
# Look for pattern 2
elif len(self.pattern2.findall(other_last_lines)) >= 5:
if platform.machine().startswith('arm'):
logging.debug("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
logging.info("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Wifi channel stuck. Restarting recon.')
display.update(force=True)
logging.debug('[Fix_Services] Wifi channel stuck. Restarting recon.')
logging.info('[Fix_Services] Wifi channel stuck. Restarting recon.')
try:
result = agent.run("wifi.recon off; wifi.recon on")
if result["success"]:
logging.debug("[Fix_Services] wifi.recon flip: success!")
logging.info("[Fix_Services] wifi.recon flip: success!")
if display:
display.update(force=True, new_data={"status": "Wifi recon flipped!",
"brcmfmac_status": self._status,
"face": faces.COOL})
else:
print("Wifi recon flipped\nthat was easy!")
@ -157,7 +159,7 @@ class FixServices(plugins.Plugin):
# Look for pattern 3
elif len(self.pattern3.findall(other_last_lines)) >= 1:
logging.debug("[Fix_Services] Firmware has halted or crashed. Restarting wlan0mon.")
logging.info("[Fix_Services] Firmware has halted or crashed. Restarting wlan0mon.")
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Firmware has halted or crashed. Restarting wlan0mon.')
@ -165,13 +167,13 @@ class FixServices(plugins.Plugin):
try:
# Run the monstart command to restart wlan0mon
cmd_output = subprocess.check_output("monstart", shell=True)
logging.debug("[Fix_Services monstart]: %s" % repr(cmd_output))
logging.info("[Fix_Services monstart]: %s" % repr(cmd_output))
except Exception as err:
logging.error("[Fix_Services monstart]: %s" % repr(err))
# Look for pattern 4
elif len(self.pattern4.findall(other_other_last_lines)) >= 3:
logging.debug("[Fix_Services] wlan0 is down!")
logging.info("[Fix_Services] wlan0 is down!")
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Restarting wlan0 now!')
@ -179,29 +181,10 @@ class FixServices(plugins.Plugin):
try:
# Run the monstart command to restart wlan0mon
cmd_output = subprocess.check_output("monstart", shell=True)
logging.debug("[Fix_Services monstart]: %s" % repr(cmd_output))
logging.info("[Fix_Services monstart]: %s" % repr(cmd_output))
except Exception as err:
logging.error("[Fix_Services monstart]: %s" % repr(err))
# Look for pattern 5
elif len(self.pattern5.findall(other_other_last_lines)) >= 1:
logging.debug("[Fix_Services] Bettercap has crashed!")
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Restarting pwnagotchi!')
display.update(force=True)
os.system("systemctl restart bettercap")
pwnagotchi.restart("AUTO")
# Look for pattern 6
elif len(self.pattern6.findall(other_other_last_lines)) >= 1:
logging.debug("[Fix_Services] Bettercap has crashed!")
if hasattr(agent, 'view'):
display = agent.view()
display.set('status', 'Restarting pwnagotchi!')
display.update(force=True)
os.system("systemctl restart bettercap")
pwnagotchi.restart("AUTO")
else:
print("logs look good")
@ -214,7 +197,7 @@ class FixServices(plugins.Plugin):
elif level == "debug":
logging.debug(message)
else:
logging.debug(message)
logging.info(message)
if ui:
ui.update(force=force, new_data=displayData)
@ -229,16 +212,17 @@ class FixServices(plugins.Plugin):
# avoid overlapping restarts, but allow it if it's been a while
# (in case the last attempt failed before resetting "isReloadingMon")
if self.isReloadingMon and (time.time() - self.LASTTRY) < 180:
logging.debug("[Fix_Services] Duplicate attempt ignored")
logging.info("[Fix_Services] Duplicate attempt ignored")
else:
self.isReloadingMon = True
self.LASTTRY = time.time()
self._status = "BL"
if hasattr(connection, 'view'):
display = connection.view()
if display:
display.update(force=True, new_data={"status": "I'm blind! Try turning it off and on again",
"face": faces.BORED})
"brcmfmac_status": self._status, "face": faces.BORED})
else:
display = None
@ -254,9 +238,9 @@ class FixServices(plugins.Plugin):
# is it up?
try:
cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True)
logging.debug("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
logging.info("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output))
if ",UP," in str(cmd_output):
logging.debug("wlan0mon is up. Skip reset?")
logging.info("wlan0mon is up. Skip reset?")
# not reliable, so don't skip just yet
# print("wlan0mon is up. Skipping reset.")
# self.isReloadingMon = False
@ -277,10 +261,11 @@ class FixServices(plugins.Plugin):
except Exception as err:
logging.error("[Fix_Services wifi.recon off] error %s" % (repr(err)))
logging.debug("[Fix_Services] recon paused. Now trying wlan0mon reload")
logging.info("[Fix_Services] recon paused. Now trying wlan0mon reload")
try:
cmd_output = subprocess.check_output("monstop", shell=True)
self._status = "dn"
self.logPrintView("info", "[Fix_Services] wlan0mon down and deleted: %s" % cmd_output,
display, {"status": "wlan0mon d-d-d-down!", "face": faces.BORED})
except Exception as nope:
@ -300,6 +285,7 @@ class FixServices(plugins.Plugin):
cmd_output = subprocess.check_output("sudo modprobe -r brcmfmac", shell=True)
self.logPrintView("info", "[Fix_Services] unloaded brcmfmac", display,
{"status": "Turning it off #%s" % tries, "face": faces.SMART})
self._status = "ul"
# reload the module
try:
@ -307,24 +293,28 @@ class FixServices(plugins.Plugin):
cmd_output = subprocess.check_output("sudo modprobe brcmfmac", shell=True)
self.logPrintView("info", "[Fix_Services] reloaded brcmfmac")
self._status = "rl"
# success! now make the mon0
try:
cmd_output = subprocess.check_output("monstart", shell=True)
self.logPrintView("info", "[Fix_Services interface add wlan0mon worked #%s: %s"
% (tries, cmd_output))
self._status = "up"
try:
# try accessing mon0 in bettercap
result = connection.run("set wifi.interface wlan0mon")
if "success" in result:
logging.debug("[Fix_Services set wifi.interface wlan0mon worked!")
logging.info("[Fix_Services set wifi.interface wlan0mon worked!")
self._status = ""
self._count = self._count + 1
# stop looping and get back to recon
break
else:
logging.debug(
logging.info(
"[Fix_Services set wifi.interfaceface wlan0mon] failed? %s" % repr(result))
except Exception as err:
logging.debug(
logging.info(
"[Fix_Services set wifi.interface wlan0mon] except: %s" % repr(err))
except Exception as cerr: #
if not display:
@ -343,38 +333,41 @@ class FixServices(plugins.Plugin):
tries = tries + 1
if tries < 3:
logging.debug("[Fix_Services] wlan0mon didn't make it. trying again")
logging.info("[Fix_Services] wlan0mon didn't make it. trying again")
if not display:
print(" wlan0mon didn't make it. trying again")
else:
logging.debug("[Fix_Services] wlan0mon loading failed, no choice but to reboot ..")
logging.info("[Fix_Services] wlan0mon loading failed, no choice but to reboot ..")
pwnagotchi.reboot()
# exited the loop, so hopefully it loaded
if tries < 3:
if display:
display.update(force=True, new_data={"status": "And back on again...",
"brcmfmac_status": self._status,
"face": faces.INTENSE})
else:
print("And back on again...")
logging.debug("[Fix_Services] wlan0mon back up")
logging.info("[Fix_Services] wlan0mon back up")
else:
self.LASTTRY = time.time()
time.sleep(8 + tries * 2) # give it a bit before restarting recon in bettercap
self.isReloadingMon = False
logging.debug("[Fix_Services] re-enable recon")
logging.info("[Fix_Services] re-enable recon")
try:
result = connection.run("wifi.clear; wifi.recon on")
if "success" in result: # and result["success"] is True:
self._status = ""
if display:
display.update(force=True, new_data={"status": "I can see again! (probably)",
"brcmfmac_status": self._status,
"face": faces.HAPPY})
else:
print("I can see again")
logging.debug("[Fix_Services] wifi.recon on")
logging.info("[Fix_Services] wifi.recon on")
self.LASTTRY = time.time() + 120 # 2-minute pause until next time.
else:
logging.error("[Fix_Services] wifi.recon did not start up")
@ -395,14 +388,25 @@ class FixServices(plugins.Plugin):
else:
pos = (ui.width() / 2 + 35, ui.height() - 11)
logging.debug("Got here")
logging.info("Got here")
ui.add_element('brcmfmac_status', Text(color=BLACK, value='--', position=pos, font=fonts.Small))
# called when the ui is updated
def on_ui_update(self, ui):
return
# update those elements
if self._status:
ui.set('brcmfmac_status', "wlan0mon %s" % self._status)
else:
ui.set('brcmfmac_status', "rst#%s" % self._count)
def on_unload(self, ui):
return
with ui._lock:
try:
ui.remove_element('brcmfmac_status')
logging.info("[Fix_Services] unloaded")
except Exception as err:
logging.info("[Fix_Services] unload err %s " % repr(err))
pass
# run from command line to brute force a reload

View File

@ -98,7 +98,7 @@ class GPS(plugins.Plugin):
lat_pos = (127, 74)
lon_pos = (122, 84)
alt_pos = (127, 94)
elif ui.is_waveshare2in7():
elif ui.is_waveshare27inch():
lat_pos = (6, 120)
lon_pos = (1, 135)
alt_pos = (6, 150)

View File

@ -5,6 +5,7 @@ import glob
import re
import pwnagotchi.grid as grid
import pwnagotchi.plugins
import pwnagotchi.plugins as plugins
from pwnagotchi.utils import StatusFile, WifiInfo, extract_from_pcap
from threading import Lock
@ -86,7 +87,7 @@ class Grid(plugins.Plugin):
agent.view().on_unread_messages(self.unread_messages, self.total_messages)
def check_handshakes(self, agent):
logging.debug("checking pcap's")
logging.debug("checking pcaps")
config = agent.config()
pcap_files = glob.glob(os.path.join(agent.config()['bettercap']['handshakes'], "*.pcap"))

View File

@ -0,0 +1,42 @@
import logging
import requests
import pwnagotchi.plugins as plugins
'''
You need an bluetooth connection to your android phone which is running PAW server with the GPS "hack" from Systemik and edited by shaynemk
GUIDE HERE: https://community.pwnagotchi.ai/t/setting-up-paw-gps-on-android
'''
class PawGPS(plugins.Plugin):
__author__ = 'leont'
__version__ = '1.0.1'
__name__ = 'pawgps'
__license__ = 'GPL3'
__description__ = 'Saves GPS coordinates whenever an handshake is captured. The GPS data is get from PAW on android.'
def __init__(self):
self.options = dict()
def on_loaded(self):
logging.info("[paw-gps] plugin loaded")
if 'ip' not in self.options or ('ip' in self.options and self.options['ip'] is None) or (len('ip' in self.options and self.options['ip']) is 0):
logging.info("[paw-gps] no IP Address defined in the config file, will uses paw server default (192.168.44.1:8080)")
def on_handshake(self, agent, filename, access_point, client_station):
if 'ip' not in self.options or ('ip' in self.options and self.options['ip'] is None or (len('ip' in self.options and self.options['ip']) is 0)):
ip = "192.168.44.1:8080"
else:
ip = self.options['ip']
try:
gps = requests.get('http://' + ip + '/gps.xhtml')
try:
gps_filename = filename.replace('.pcap', '.paw-gps.json')
logging.info("[paw-gps] saving GPS data to %s" % (gps_filename))
with open(gps_filename, 'w+t') as f:
f.write(gps.text)
except Exception as error:
logging.error(f"[paw-gps] encountered error while saving gps data: {error}")
except Exception as error:
logging.error(f"[paw-gps] encountered error while getting gps data: {error}")

View File

@ -14,7 +14,7 @@ from dateutil.parser import parse
the plugin does the following:
- search for *.pcap files in your /handshakes/ dir
- for every found .pcap file it looks for a .geo.json or .gps.json or file with
- for every found .pcap file it looks for a .geo.json or .gps.json or .paw-gps.json file with
latitude+longitude data inside and shows this position on the map
- if also an .cracked file with a plaintext password inside exist, it reads the content and shows the
position as green instead of red and the password inside the infopox of the position
@ -87,8 +87,7 @@ class Webgpsmap(plugins.Plugin):
# returns all positions
try:
self.ALREADY_SENT = list()
response_data = bytes(
json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes'])), "utf-8")
response_data = bytes(json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes'])), "utf-8")
response_status = 200
response_mimetype = "application/json"
response_header_contenttype = 'application/json'
@ -101,8 +100,7 @@ class Webgpsmap(plugins.Plugin):
self.ALREADY_SENT = list()
json_data = json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes']))
html_data = self.get_html()
html_data = html_data.replace('var positions = [];',
'var positions = ' + json_data + ';positionsLoaded=true;drawPositions();')
html_data = html_data.replace('var positions = [];', 'var positions = ' + json_data + ';positionsLoaded=true;drawPositions();')
response_data = bytes(html_data, "utf-8")
response_status = 200
response_mimetype = "application/xhtml+xml"
@ -165,8 +163,7 @@ class Webgpsmap(plugins.Plugin):
all_files = os.listdir(handshake_dir)
# print(all_files)
all_pcap_files = [os.path.join(handshake_dir, filename) for filename in all_files if
filename.endswith('.pcap')]
all_pcap_files = [os.path.join(handshake_dir, filename) for filename in all_files if filename.endswith('.pcap')]
all_geo_or_gps_files = []
for filename_pcap in all_pcap_files:
filename_base = filename_pcap[:-5] # remove ".pcap"
@ -183,6 +180,11 @@ class Webgpsmap(plugins.Plugin):
if check_for in all_files:
filename_position = str(os.path.join(handshake_dir, check_for))
logging.debug("[webgpsmap] search for .paw-gps.json")
check_for = os.path.basename(filename_base) + ".paw-gps.json"
if check_for in all_files:
filename_position = str(os.path.join(handshake_dir, check_for))
logging.debug(f"[webgpsmap] end search for position data files and use {filename_position}")
if filename_position is not None:
@ -193,8 +195,7 @@ class Webgpsmap(plugins.Plugin):
if newest_only:
all_geo_or_gps_files = set(all_geo_or_gps_files) - set(self.ALREADY_SENT)
logging.info(
f"[webgpsmap] Found {len(all_geo_or_gps_files)} position-data files from {len(all_pcap_files)} handshakes. Fetching positions ...")
logging.info(f"[webgpsmap] Found {len(all_geo_or_gps_files)} position-data files from {len(all_pcap_files)} handshakes. Fetching positions ...")
for pos_file in all_geo_or_gps_files:
try:
@ -212,6 +213,8 @@ class Webgpsmap(plugins.Plugin):
pos_type = 'gps'
elif pos.type() == PositionFile.GEO:
pos_type = 'geo'
elif pos.type() == PositionFile.PAWGPS:
pos_type = 'paw'
gps_data[ssid+"_"+mac] = {
'ssid': ssid,
'mac': mac,
@ -262,6 +265,7 @@ class PositionFile:
"""
GPS = 1
GEO = 2
PAWGPS = 3
def __init__(self, path):
self._file = path
@ -278,7 +282,7 @@ class PositionFile:
"""
Returns the mac from filename
"""
parsed_mac = re.search(r'.*_?([a-zA-Z0-9]{12})\.(?:gps|geo)\.json', self._filename)
parsed_mac = re.search(r'.*_?([a-zA-Z0-9]{12})\.(?:gps|geo|paw-gps)\.json', self._filename)
if parsed_mac:
mac = parsed_mac.groups()[0]
return mac
@ -288,7 +292,7 @@ class PositionFile:
"""
Returns the ssid from filename
"""
parsed_ssid = re.search(r'(.+)_[a-zA-Z0-9]{12}\.(?:gps|geo)\.json', self._filename)
parsed_ssid = re.search(r'(.+)_[a-zA-Z0-9]{12}\.(?:gps|geo|paw-gps)\.json', self._filename)
if parsed_ssid:
return parsed_ssid.groups()[0]
return None
@ -350,6 +354,8 @@ class PositionFile:
return PositionFile.GPS
if self._file.endswith('.geo.json'):
return PositionFile.GEO
if self._file.endswith('.paw-gps.json'):
return PositionFile.PAWGPS
return None
def lat(self):
@ -397,6 +403,8 @@ class PositionFile:
def accuracy(self):
if self.type() == PositionFile.GPS:
return 50.0 # a default
if self.type() == PositionFile.PAWGPS:
return 50.0 # a default
if self.type() == PositionFile.GEO:
try:
return self._json['accuracy']

View File

@ -118,9 +118,6 @@ class Display(View):
def is_waveshare2in66g(self):
return self._implementation.name == 'waveshare2in66g'
def is_weact2in9(self):
return self._implementation.name == 'weact2in9'
def is_waveshare3in0g(self):
return self._implementation.name == 'waveshare3in0g'
@ -154,12 +151,6 @@ class Display(View):
def is_waveshare5in65f(self):
return self._implementation.name == 'waveshare5in65f'
def is_waveshare5in79(self):
return self._implementation.name == 'waveshare5in79'
def is_waveshare5in79b(self):
return self._implementation.name == 'waveshare5in79b'
def is_waveshare5in83(self):
return self._implementation.name == 'waveshare5in83'
@ -202,9 +193,6 @@ class Display(View):
def is_inky(self):
return self._implementation.name == 'inky'
def is_dummy_display(self):
return self._implementation.name == 'dummydisplay'
def is_papirus(self):
return self._implementation.name == 'papirus'
@ -220,24 +208,9 @@ class Display(View):
def is_displayhatmini(self):
return self._implementation.name == 'displayhatmini'
def is_pirateaudio(self):
return self._implementation.name == 'pirateaudio'
def is_pitft(self):
return self._implementation.name == 'pitft'
def is_tftbonnet(self):
return self._implementation.name == 'tftbonnet'
def is_waveshareoledlcd(self):
return self._implementation.name == 'waveshareoledlcd'
def is_waveshare35lcd(self):
return self._implementation.name == 'waveshare35lcd'
def is_adfruit213v3(self):
return self._implementation.name == 'adafruit2in13_v3'
def is_waveshare_any(self):
return self.is_waveshare_v1() or self.is_waveshare_v2()

View File

@ -27,7 +27,6 @@ PNG = False
POSITION_X = 0
POSITION_Y = 40
def load_from_config(config):
for face_name, face_value in config.items():
globals()[face_name.upper()] = face_value

View File

@ -1,5 +1,4 @@
from pwnagotchi.ui.hw.inky import Inky
from pwnagotchi.ui.hw.dummydisplay import DummyDisplay
from pwnagotchi.ui.hw.papirus import Papirus
from pwnagotchi.ui.hw.oledhat import OledHat
from pwnagotchi.ui.hw.lcdhat import LcdHat
@ -22,11 +21,6 @@ from pwnagotchi.ui.hw.waveshare2in13b_V4 import Waveshare213bV4
from pwnagotchi.ui.hw.waveshare3in5lcd import Waveshare35lcd
from pwnagotchi.ui.hw.spotpear24in import Spotpear24inch
from pwnagotchi.ui.hw.displayhatmini import DisplayHatMini
from pwnagotchi.ui.hw.pirateaudio import PirateAudio
from pwnagotchi.ui.hw.pitft import Pitft
from pwnagotchi.ui.hw.tftbonnet import TftBonnet
from pwnagotchi.ui.hw.adafruit2in13 import Adafruit2in13V3
from pwnagotchi.ui.hw.waveshareoledlcd import Waveshareoledlcd
from pwnagotchi.ui.hw.waveshare1in02 import Waveshare1in02
from pwnagotchi.ui.hw.waveshare1in54 import Waveshare154
from pwnagotchi.ui.hw.waveshare1in54_V2 import Waveshare154V2
@ -55,8 +49,6 @@ from pwnagotchi.ui.hw.waveshare4in2bc import Waveshare4in2bc
from pwnagotchi.ui.hw.waveshare4in26 import Waveshare4in26
from pwnagotchi.ui.hw.waveshare4in37g import Waveshare4in37g
from pwnagotchi.ui.hw.waveshare5in65f import Waveshare5in65f
from pwnagotchi.ui.hw.waveshare5in79 import Waveshare5in79
from pwnagotchi.ui.hw.waveshare5in79b import Waveshare5in79b
from pwnagotchi.ui.hw.waveshare5in83 import Waveshare5in83
from pwnagotchi.ui.hw.waveshare5in83_V2 import Waveshare5in83V2
from pwnagotchi.ui.hw.waveshare5in83b_V2 import Waveshare5in83bV2
@ -77,9 +69,6 @@ def display_for(config):
if config['ui']['display']['type'] == 'inky':
return Inky(config)
elif config['ui']['display']['type'] == 'dummydisplay':
return DummyDisplay(config)
elif config['ui']['display']['type'] == 'papirus':
return Papirus(config)
@ -107,18 +96,6 @@ def display_for(config):
elif config['ui']['display']['type'] == 'displayhatmini':
return DisplayHatMini(config)
elif config['ui']['display']['type'] == 'pirateaudio':
return PirateAudio(config)
elif config['ui']['display']['type'] == 'pitft':
return Pitft(config)
elif config['ui']['display']['type'] == 'tftbonnet':
return TftBonnet(config)
elif config['ui']['display']['type'] == 'waveshareoledlcd':
return Waveshareoledlcd(config)
elif config['ui']['display']['type'] == 'waveshare1in02':
return Waveshare1in02(config)
@ -182,9 +159,6 @@ def display_for(config):
elif config['ui']['display']['type'] == 'waveshare_4':
return WaveshareV4(config)
elif config['ui']['display']['type'] == 'adafruit2in13':
return Adafruit2in13(config)
elif config['ui']['display']['type'] == 'waveshare2in13bc':
return Waveshare213bc(config)
@ -245,12 +219,6 @@ def display_for(config):
elif config['ui']['display']['type'] == 'waveshare5in65f':
return Waveshare5in65f(config)
elif config['ui']['display']['type'] == 'waveshare5in79':
return Waveshare5in79(config)
elif config['ui']['display']['type'] == 'waveshare5in79b':
return Waveshare5in79b(config)
elif config['ui']['display']['type'] == 'waveshare5in83':
return Waveshare5in83(config)

View File

@ -1,45 +0,0 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class Adafruit2in13V3(DisplayImpl):
def __init__(self, config):
super(Adafruit2in13V3, self).__init__(config, 'adafruit2in13_v3')
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 adafruit 2in13 V3 display")
from pwnagotchi.ui.hw.libs.adafruit.v2in13_v3.epd2in13_v3 import EPD
self._display = EPD()
self._display.init()
self._display.Clear(0xFF)
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.displayPartial(buf)
def clear(self):
self._display.Clear(0xFF)

View File

@ -1,43 +0,0 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class DummyDisplay(DisplayImpl):
def __init__(self, config):
super(DummyDisplay, self).__init__(config, 'DummyDisplay')
def layout(self):
width = 480 if 'width' not in self.config else self.config['width']
height = 720 if 'height' not in self.config else self.config['height']
fonts.setup(int(height/30), int(height/40), int(height/30), int(height/6), int(height/30), int(height/35))
self._layout['width'] = width
self._layout['height'] = height
self._layout['face'] = (0, int(width/12))
self._layout['name'] = (5, int(width/25))
self._layout['channel'] = (0, 0)
self._layout['aps'] = (int(width/8), 0)
self._layout['uptime'] = (width-int(width/12), 0)
self._layout['line1'] = [0, int(height/32), width, int(height/32)]
self._layout['line2'] = [0, height-int(height/25)-1, width, height-int(height/25)-1]
self._layout['friend_face'] = (0, int(height/10))
self._layout['friend_name'] = (int(width/12), int(height/10))
self._layout['shakes'] = (0, height-int(height/25))
self._layout['mode'] = (width-int(width/8), height - int (height/25))
lw, lh = fonts.Small.getsize("W")
self._layout['status'] = {
'pos': (int(width/48), int(height/3)),
'font': fonts.status_font(fonts.Small),
'max': int(width / lw)
}
return self._layout
def initialize(self):
return
def render(self, canvas):
return
def clear(self):
return

View File

@ -1,132 +0,0 @@
# /*****************************************************************************
# * | 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 = 27
DC_PIN = 22
CS_PIN = 8
BUSY_PIN = 17
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()
implementation = RaspberryPi()
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

@ -1,362 +0,0 @@
# 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.
# Modified for Pwnagotchi by RasTacsko
# Based on ST7899 driver for pimoroni displayhatmini by Do-Ki
import numbers
import time
import numpy as np
from PIL import Image
from PIL import ImageDraw
import spidev
import RPi.GPIO as GPIO
__version__ = '0.0.1'
# Constants for interacting with display registers.
ILI9341_TFTWIDTH = 320
ILI9341_TFTHEIGHT = 240
ILI9341_NOP = 0x00
ILI9341_SWRESET = 0x01
ILI9341_RDDID = 0x04
ILI9341_RDDST = 0x09
ILI9341_SLPIN = 0x10
ILI9341_SLPOUT = 0x11
ILI9341_PTLON = 0x12
ILI9341_NORON = 0x13
ILI9341_RDMODE = 0x0A
ILI9341_RDMADCTL = 0x0B
ILI9341_RDPIXFMT = 0x0C
ILI9341_RDIMGFMT = 0x0A
ILI9341_RDSELFDIAG = 0x0F
ILI9341_INVOFF = 0x20
ILI9341_INVON = 0x21
ILI9341_GAMMASET = 0x26
ILI9341_DISPOFF = 0x28
ILI9341_DISPON = 0x29
ILI9341_CASET = 0x2A
ILI9341_PASET = 0x2B
ILI9341_RAMWR = 0x2C
ILI9341_RAMRD = 0x2E
ILI9341_PTLAR = 0x30
ILI9341_MADCTL = 0x36
ILI9341_PIXFMT = 0x3A
ILI9341_FRMCTR1 = 0xB1
ILI9341_FRMCTR2 = 0xB2
ILI9341_FRMCTR3 = 0xB3
ILI9341_INVCTR = 0xB4
ILI9341_DFUNCTR = 0xB6
ILI9341_PWCTR1 = 0xC0
ILI9341_PWCTR2 = 0xC1
ILI9341_PWCTR3 = 0xC2
ILI9341_PWCTR4 = 0xC3
ILI9341_PWCTR5 = 0xC4
ILI9341_VMCTR1 = 0xC5
ILI9341_VMCTR2 = 0xC7
ILI9341_RDID1 = 0xDA
ILI9341_RDID2 = 0xDB
ILI9341_RDID3 = 0xDC
ILI9341_RDID4 = 0xDD
ILI9341_GMCTRP1 = 0xE0
ILI9341_GMCTRN1 = 0xE1
ILI9341_PWCTR6 = 0xFC
class ILI9341(object):
"""Representation of an ILI9341 TFT LCD."""
def __init__(self, port, cs, dc, backlight, rst=None,
width=ILI9341_TFTWIDTH, height=ILI9341_TFTHEIGHT,
rotation=270, invert=False, spi_speed_hz=64000000,
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 -> 0
:param cs: SPI chip-select number (0 or 1 for BCM) -> 1
:param backlight: Pin for controlling backlight -> 18
:param rst: Reset pin for ILI9341 -> 24?
:param width: Width of display connected to ILI9341 -> 240
:param height: Height of display connected to ILI9341 -> 320
:param rotation: Rotation of display connected to ILI9341
: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))
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
if backlight is not None:
GPIO.setup(backlight, GPIO.OUT)
GPIO.output(backlight, GPIO.LOW)
time.sleep(0.05)
GPIO.output(backlight, GPIO.HIGH)
# Setup reset as output (if provided).
if rst is not None:
GPIO.setup(self._rst, GPIO.OUT)
self.reset()
# Create an image buffer.
self.buffer = Image.new('RGB', (width, height))
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."""
if self._backlight is not None:
GPIO.output(self._backlight, value)
@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.005)
GPIO.output(self._rst, 0)
time.sleep(0.02)
GPIO.output(self._rst, 1)
time.sleep(0.150)
def _init(self):
# Initialize the display. Broken out as a separate function so it can
# be overridden by other displays in the future.
self.command(0xEF)
self.data(0x03)
self.data(0x80)
self.data(0x02)
self.command(0xCF)
self.data(0x00)
self.data(0XC1)
self.data(0X30)
self.command(0xED)
self.data(0x64)
self.data(0x03)
self.data(0X12)
self.data(0X81)
self.command(0xE8)
self.data(0x85)
self.data(0x00)
self.data(0x78)
self.command(0xCB)
self.data(0x39)
self.data(0x2C)
self.data(0x00)
self.data(0x34)
self.data(0x02)
self.command(0xF7)
self.data(0x20)
self.command(0xEA)
self.data(0x00)
self.data(0x00)
self.command(ILI9341_PWCTR1) # Power control
self.data(0x23) # VRH[5:0]
self.command(ILI9341_PWCTR2) # Power control
self.data(0x10) # SAP[2:0];BT[3:0]
self.command(ILI9341_VMCTR1) # VCM control
self.data(0x3e)
self.data(0x28)
self.command(ILI9341_VMCTR2) # VCM control2
self.data(0x86) # --
self.command(ILI9341_MADCTL) # Memory Access Control
self.data(0x48)
self.command(ILI9341_PIXFMT)
self.data(0x55)
self.command(ILI9341_FRMCTR1)
self.data(0x00)
self.data(0x18)
self.command(ILI9341_DFUNCTR) # Display Function Control
self.data(0x08)
self.data(0x82)
self.data(0x27)
self.command(0xF2) # 3Gamma Function Disable
self.data(0x00)
self.command(ILI9341_GAMMASET) # Gamma curve selected
self.data(0x01)
self.command(ILI9341_GMCTRP1) # Set Gamma
self.data(0x0F)
self.data(0x31)
self.data(0x2B)
self.data(0x0C)
self.data(0x0E)
self.data(0x08)
self.data(0x4E)
self.data(0xF1)
self.data(0x37)
self.data(0x07)
self.data(0x10)
self.data(0x03)
self.data(0x0E)
self.data(0x09)
self.data(0x00)
self.command(ILI9341_GMCTRN1) # Set Gamma
self.data(0x00)
self.data(0x0E)
self.data(0x14)
self.data(0x03)
self.data(0x11)
self.data(0x07)
self.data(0x31)
self.data(0xC1)
self.data(0x48)
self.data(0x08)
self.data(0x0F)
self.data(0x0C)
self.data(0x31)
self.data(0x36)
self.data(0x0F)
if self._invert:
self.command(ILI9341_INVON) # Invert display
else:
self.command(ILI9341_INVOFF) # Don't invert display
self.command(ILI9341_SLPOUT) # Exit Sleep
time.sleep(0.120)
self.command(ILI9341_DISPON) # Display on
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 239,319.
"""
if x1 is None:
x1 = self.width-1
if y1 is None:
y1 = self.height-1
self.command(ILI9341_CASET) # Column addr set
self.data(x0 >> 8)
self.data(x0 & 0xFF) # XSTART
self.data(x1 >> 8)
self.data(x1 & 0xFF) # XEND
self.command(ILI9341_PASET) # Row addr set
self.data(y0 >> 8)
self.data(y0 & 0xFF) # YSTART
self.data(y1 >> 8)
self.data(y1 & 0xFF) # YEND
self.command(ILI9341_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

@ -1,360 +0,0 @@
# 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
__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=240,
height=240, rotation=90, invert=True, spi_speed_hz=60 * 1000 * 1000,
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 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
if backlight is not None:
GPIO.setup(backlight, GPIO.OUT)
GPIO.output(backlight, GPIO.LOW)
time.sleep(0.1)
GPIO.output(backlight, GPIO.HIGH)
# 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."""
if self._backlight is not None:
GPIO.output(self._backlight, value)
@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

@ -90,8 +90,8 @@ ST7789_PWCTR6 = 0xFC
class ST7789(object):
"""Representation of an ST7789 TFT LCD."""
def __init__(self, port, cs, dc, backlight=None, rst=None, width=240,
height=240, rotation=90, invert=True, spi_speed_hz=4000000,
def __init__(self, port, cs, dc, backlight, rst=None, width=320,
height=240, rotation=0, invert=True, spi_speed_hz=60 * 1000 * 1000,
offset_left=0,
offset_top=0):
"""Create an instance of the display using SPI communication.

View File

@ -1,360 +0,0 @@
# 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
__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=240,
height=240, rotation=0, invert=True, spi_speed_hz=60 * 1000 * 1000,
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 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
if backlight is not None:
GPIO.setup(backlight, GPIO.OUT)
GPIO.output(backlight, GPIO.LOW)
time.sleep(0.1)
GPIO.output(backlight, GPIO.HIGH)
# 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."""
if self._backlight is not None:
GPIO.output(self._backlight, value)
@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

@ -31,7 +31,6 @@ import os
import logging
import sys
import time
import subprocess
logger = logging.getLogger(__name__)
@ -46,48 +45,16 @@ class RaspberryPi:
def __init__(self):
import spidev
import gpiozero
import RPi.GPIO
self.GPIO = RPi.GPIO
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()
self.GPIO.output(pin, value)
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
return self.GPIO.input(pin)
def delay_ms(self, delaytime):
time.sleep(delaytime / 1000.0)
@ -99,7 +66,15 @@ class RaspberryPi:
self.SPI.writebytes2(data)
def module_init(self):
self.GPIO_PWR_PIN.on()
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)
@ -107,23 +82,159 @@ class RaspberryPi:
self.SPI.mode = 0b00
return 0
def module_exit(self, cleanup=False):
def module_exit(self):
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 ...")
self.GPIO.output(self.RST_PIN, 0)
self.GPIO.output(self.DC_PIN, 0)
self.GPIO.output(self.PWR_PIN, 0)
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()
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 = RaspberryPi()
for func in [x for x in dir(implementation) if not x.startswith('_')]:

View File

@ -1,360 +0,0 @@
# 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
__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=180, invert=True, spi_speed_hz=60 * 1000 * 1000,
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 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
if backlight is not None:
GPIO.setup(backlight, GPIO.OUT)
GPIO.output(backlight, GPIO.LOW)
time.sleep(0.1)
GPIO.output(backlight, GPIO.HIGH)
# 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."""
if self._backlight is not None:
GPIO.output(self._backlight, value)
@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,224 @@
# *****************************************************************************
# * | File : epd2in13.py
# * | Author : Waveshare team
# * | Function : Electronic paper driver
# * | Info :
# *----------------
# * | This version: V4.0
# * | Date : 2019-06-20
# # | Info : python demo
# -----------------------------------------------------------------------------
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import logging
from . import epdconfig
# Display resolution
EPD_WIDTH = 122
EPD_HEIGHT = 250
class EPD:
def __init__(self):
self.reset_pin = epdconfig.RST_PIN
self.dc_pin = epdconfig.DC_PIN
self.busy_pin = epdconfig.BUSY_PIN
self.cs_pin = epdconfig.CS_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
lut_full_update = [
0x22, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x11,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00
]
lut_partial_update = [
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]
# Hardware reset
def reset(self):
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
epdconfig.digital_write(self.reset_pin, 0)
epdconfig.delay_ms(10)
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
epdconfig.digital_write(self.cs_pin, 1)
def send_command(self, command):
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.digital_write(self.dc_pin, 0)
epdconfig.spi_writebyte([command])
epdconfig.digital_write(self.cs_pin, 1)
def send_data(self, data):
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.spi_writebyte([data])
epdconfig.digital_write(self.cs_pin, 1)
def ReadBusy(self):
while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy
epdconfig.delay_ms(100)
def TurnOnDisplay(self):
self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2
self.send_data(0xC4)
self.send_command(0x20) # MASTER_ACTIVATION
self.send_command(0xFF) # TERMINATE_FRAME_READ_WRITE
logging.debug("e-Paper busy")
self.ReadBusy()
logging.debug("e-Paper busy release")
def init(self, lut):
if (epdconfig.module_init() != 0):
return -1
# EPD hardware init start
self.reset()
self.send_command(0x01) # DRIVER_OUTPUT_CONTROL
self.send_data((EPD_HEIGHT - 1) & 0xFF)
self.send_data(((EPD_HEIGHT - 1) >> 8) & 0xFF)
self.send_data(0x00) # GD = 0 SM = 0 TB = 0
self.send_command(0x0C) # BOOSTER_SOFT_START_CONTROL
self.send_data(0xD7)
self.send_data(0xD6)
self.send_data(0x9D)
self.send_command(0x2C) # WRITE_VCOM_REGISTER
self.send_data(0xA8) # VCOM 7C
self.send_command(0x3A) # SET_DUMMY_LINE_PERIOD
self.send_data(0x1A) # 4 dummy lines per gate
self.send_command(0x3B) # SET_GATE_TIME
self.send_data(0x08) # 2us per line
self.send_command(0X3C) # BORDER_WAVEFORM_CONTROL
self.send_data(0x03)
self.send_command(0X11) # DATA_ENTRY_MODE_SETTING
self.send_data(0x03) # X increment; Y increment
# WRITE_LUT_REGISTER
self.send_command(0x32)
for count in range(30):
self.send_data(lut[count])
return 0
##
# @brief: specify the memory area for data R/W
##
def SetWindows(self, x_start, y_start, x_end, y_end):
self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION
self.send_data((x_start >> 3) & 0xFF)
self.send_data((x_end >> 3) & 0xFF)
self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION
self.send_data(y_start & 0xFF)
self.send_data((y_start >> 8) & 0xFF)
self.send_data(y_end & 0xFF)
self.send_data((y_end >> 8) & 0xFF)
##
# @brief: specify the start point for data R/W
##
def SetCursor(self, x, y):
self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER
# x point must be the multiple of 8 or the last 3 bits will be ignored
self.send_data((x >> 3) & 0xFF)
self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER
self.send_data(y & 0xFF)
self.send_data((y >> 8) & 0xFF)
self.ReadBusy()
def getbuffer(self, image):
if self.width%8 == 0:
linewidth = int(self.width/8)
else:
linewidth = int(self.width/8) + 1
buf = [0xFF] * (linewidth * self.height)
image_monocolor = image.convert('1')
imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load()
if(imwidth == self.width and imheight == self.height):
for y in range(imheight):
for x in range(imwidth):
if pixels[x, y] == 0:
# x = imwidth - x
buf[int(x / 8) + y * linewidth] &= ~(0x80 >> (x % 8))
elif(imwidth == self.height and imheight == self.width):
for y in range(imheight):
for x in range(imwidth):
newx = y
newy = self.height - x - 1
if pixels[x, y] == 0:
# newy = imwidth - newy - 1
buf[int(newx / 8) + newy*linewidth] &= ~(0x80 >> (y % 8))
return buf
def display(self, image):
if self.width%8 == 0:
linewidth = int(self.width/8)
else:
linewidth = int(self.width/8) + 1
self.SetWindows(0, 0, self.width, self.height);
for j in range(0, self.height):
self.SetCursor(0, j);
self.send_command(0x24);
for i in range(0, linewidth):
self.send_data(image[i + j * linewidth])
self.TurnOnDisplay()
def Clear(self, color):
if self.width%8 == 0:
linewidth = int(self.width/8)
else:
linewidth = int(self.width/8) + 1
self.SetWindows(0, 0, self.width, self.height);
for j in range(0, self.height):
self.SetCursor(0, j);
self.send_command(0x24);
for i in range(0, linewidth):
self.send_data(color)
self.TurnOnDisplay()
def sleep(self):
self.send_command(0x10) #enter deep sleep
self.send_data(0x01)
epdconfig.delay_ms(100)
epdconfig.module_exit()
### END OF FILE ###

View File

@ -0,0 +1,164 @@
# *****************************************************************************
# * | File : epd2in13bc.py
# * | Author : Waveshare team
# * | Function : Electronic paper driver
# * | Info :
# *----------------
# * | This version: V4.0
# * | Date : 2019-06-20
# # | Info : python demo
# -----------------------------------------------------------------------------
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
from . import epdconfig
import RPi.GPIO as GPIO
# import numpy as np
# Display resolution
EPD_WIDTH = 104
EPD_HEIGHT = 212
class EPD:
def __init__(self):
self.reset_pin = epdconfig.RST_PIN
self.dc_pin = epdconfig.DC_PIN
self.busy_pin = epdconfig.BUSY_PIN
self.cs_pin = epdconfig.CS_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
# Hardware reset
def reset(self):
epdconfig.digital_write(self.reset_pin, GPIO.HIGH)
epdconfig.delay_ms(200)
epdconfig.digital_write(self.reset_pin, GPIO.LOW) # module reset
epdconfig.delay_ms(200)
epdconfig.digital_write(self.reset_pin, GPIO.HIGH)
epdconfig.delay_ms(200)
def send_command(self, command):
epdconfig.digital_write(self.dc_pin, GPIO.LOW)
epdconfig.digital_write(self.cs_pin, GPIO.LOW)
epdconfig.spi_writebyte([command])
epdconfig.digital_write(self.cs_pin, GPIO.HIGH)
def send_data(self, data):
epdconfig.digital_write(self.dc_pin, GPIO.HIGH)
epdconfig.digital_write(self.cs_pin, GPIO.LOW)
epdconfig.spi_writebyte([data])
epdconfig.digital_write(self.cs_pin, GPIO.HIGH)
def ReadBusy(self):
epdconfig.delay_ms(20)
while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy
epdconfig.delay_ms(100)
def init(self):
if (epdconfig.module_init() != 0):
return -1
# EPD hardware init start
self.reset()
self.send_command(0x06) # BOOSTER_SOFT_START
self.send_data(0x17)
self.send_data(0x17)
self.send_data(0x17)
self.send_command(0x04) # POWER_ON
self.ReadBusy()
self.send_command(0x00) # PANEL_SETTING
self.send_data(0x8F)
self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING
self.send_data(0xF0)
self.send_command(0x61) # RESOLUTION_SETTING
self.send_data(self.width & 0xff)
self.send_data(self.height >> 8)
self.send_data(self.height & 0xff)
return 0
def getbuffer(self, image):
buf = [0xFF] * (int(self.width/8) * self.height)
image_monocolor = image.convert('1')
imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load()
if(imwidth == self.width and imheight == self.height):
for y in range(imheight):
for x in range(imwidth):
# Set the bits for the column of pixels at the current position.
if pixels[x, y] == 0:
buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
elif(imwidth == self.height and imheight == self.width):
for y in range(imheight):
for x in range(imwidth):
newx = y
newy = self.height - x - 1
if pixels[x, y] == 0:
buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8))
return buf
def displayBlack(self, imageblack):
self.send_command(0x10)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(imageblack[i])
self.send_command(0x92)
self.send_command(0x12) # REFRESH
self.ReadBusy()
def display(self, imageblack, imagecolor):
self.send_command(0x10)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(imageblack[i])
self.send_command(0x92)
self.send_command(0x13)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(imagecolor[i])
self.send_command(0x92)
self.send_command(0x12) # REFRESH
self.ReadBusy()
def Clear(self):
self.send_command(0x10)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(0xFF)
self.send_command(0x92)
self.send_command(0x13)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(0xFF)
self.send_command(0x92)
self.send_command(0x12) # REFRESH
self.ReadBusy()
def sleep(self):
self.send_command(0x02) # POWER_OFF
self.ReadBusy()
self.send_command(0x07) # DEEP_SLEEP
self.send_data(0xA5) # check code
# epdconfig.module_exit()
### END OF FILE ###

View File

@ -0,0 +1,359 @@
# *****************************************************************************
# * | File : epd2in13d.py
# * | Author : Waveshare team
# * | Function : Electronic paper driver
# * | Info :
# *----------------
# * | This version: V4.0
# * | Date : 2019-06-20
# # | Info : python demo
# -----------------------------------------------------------------------------
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# =============================================================================
# THIS FILE HAS BEEN MODIFIED FROM ORIGINAL, IT HAS BEEN MODIFIED TO RUN THE
# THREE COLOR WAVESHARE 2.13IN DISPLAY AT A MUCH, MUCH FASTER RATE THAN NORMAL
# AND IT COULD DAMAGE YOUR DISPLAY. THERE IS NO WARRANTY INCLUDED AND YOU USE
# THIS CODE AT YOUR OWN RISK. WE ARE NOT RESPONSIBLE FOR ANYTHING THAT HAPPENS
# INCLUDING BUT NOT LIMITED TO: DESTRUCTION OF YOUR DISPLAY, PI, HOUSE, CAR,
# SPACE-TIME-CONTINUUM, OR IF THE CODE KILLS YOUR CAT. IF YOU AREN'T WILLING TO
# TAKE THESE RISKS, PLEASE DO NOT USE THIS CODE.
# =============================================================================
import logging
from . import epdconfig
from PIL import Image
import RPi.GPIO as GPIO
# Display resolution
EPD_WIDTH = 104
EPD_HEIGHT = 212
class EPD:
def __init__(self):
self.reset_pin = epdconfig.RST_PIN
self.dc_pin = epdconfig.DC_PIN
self.busy_pin = epdconfig.BUSY_PIN
self.cs_pin = epdconfig.CS_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
lut_vcomDC = [
0x00, 0x08, 0x00, 0x00, 0x00, 0x02,
0x60, 0x28, 0x28, 0x00, 0x00, 0x01,
0x00, 0x14, 0x00, 0x00, 0x00, 0x01,
0x00, 0x12, 0x12, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
]
lut_ww = [
0x40, 0x08, 0x00, 0x00, 0x00, 0x02,
0x90, 0x28, 0x28, 0x00, 0x00, 0x01,
0x40, 0x14, 0x00, 0x00, 0x00, 0x01,
0xA0, 0x12, 0x12, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_bw = [
0x40, 0x17, 0x00, 0x00, 0x00, 0x02,
0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03,
0x40, 0x0A, 0x01, 0x00, 0x00, 0x01,
0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_wb = [
0x80, 0x08, 0x00, 0x00, 0x00, 0x02,
0x90, 0x28, 0x28, 0x00, 0x00, 0x01,
0x80, 0x14, 0x00, 0x00, 0x00, 0x01,
0x50, 0x12, 0x12, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_bb = [
0x80, 0x08, 0x00, 0x00, 0x00, 0x02,
0x90, 0x28, 0x28, 0x00, 0x00, 0x00,
0x80, 0x14, 0x00, 0x00, 0x00, 0x01,
0x50, 0x12, 0x12, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_vcom1 = [
0xA0, 0x10, 0x10, 0x00, 0x00, 0x02,
0x00, 0x10, 0x10, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
]
lut_ww1 = [
0x50, 0x01, 0x01, 0x00, 0x00, 0x01,
0xA0, 0x42, 0x42, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_bw1 = [
0x50, 0x01, 0x01, 0x00, 0x00, 0x01,
0xA0, 0x42, 0x42, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_wb1 = [
0xA0, 0x01, 0x01, 0x00, 0x00, 0x01,
0x50, 0x42, 0x42, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
lut_bb1 = [
0xA0, 0x01, 0x01, 0x00, 0x00, 0x01,
0x50, 0x42, 0x42, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
# Hardware reset
def reset(self):
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
epdconfig.digital_write(self.reset_pin, 0)
epdconfig.delay_ms(10)
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
def send_command(self, command):
epdconfig.digital_write(self.dc_pin, 0)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([command])
epdconfig.digital_write(self.cs_pin, 1)
def send_data(self, data):
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([data])
epdconfig.digital_write(self.cs_pin, 1)
def ReadBusy(self):
logging.debug("e-Paper busy")
while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy
self.send_command(0x71)
epdconfig.delay_ms(100)
logging.debug("e-Paper busy release")
def TurnOnDisplay(self):
self.send_command(0x12)
epdconfig.delay_ms(10)
self.ReadBusy()
def init(self):
if (epdconfig.module_init() != 0):
return -1
# EPD hardware init start
self.reset()
self.send_command(0x01) # POWER SETTING
self.send_data(0x03)
self.send_data(0x00)
self.send_data(0x2b)
self.send_data(0x2b)
self.send_data(0x03)
self.send_command(0x06) # boost soft start
self.send_data(0x17) # A
self.send_data(0x17) # B
self.send_data(0x17) # C
self.send_command(0x04)
self.ReadBusy()
self.send_command(0x00) # panel setting
self.send_data(0xbf) # LUT from OTP,128x296
self.send_data(0x0d) # VCOM to 0V fast
self.send_command(0x30) # PLL setting
self.send_data(0x21) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ
self.send_command(0x61) # resolution setting
self.send_data(self.width)
self.send_data((self.height >> 8) & 0xff)
self.send_data(self.height& 0xff)
self.send_command(0x82) # vcom_DC setting
self.send_data(0x28)
return 0
def SetFullReg(self):
self.send_command(0x82)
self.send_data(0x00)
self.send_command(0X50)
self.send_data(0x97)
# self.send_command(0x00) # panel setting
# self.send_data(0x9f) # LUT from OTP,128x296
def SetPartReg(self):
# self.send_command(0x00) # panel setting
# self.send_data(0xbf) # LUT from OTP,128x296
self.send_command(0x82)
self.send_data(0x03)
self.send_command(0X50)
self.send_data(0x47)
self.send_command(0x20) # vcom
for count in range(0, 44):
self.send_data(self.lut_vcom1[count])
self.send_command(0x21) # ww --
for count in range(0, 42):
self.send_data(self.lut_ww1[count])
self.send_command(0x22) # bw r
for count in range(0, 42):
self.send_data(self.lut_bw1[count])
self.send_command(0x23) # wb w
for count in range(0, 42):
self.send_data(self.lut_wb1[count])
self.send_command(0x24) # bb b
for count in range(0, 42):
self.send_data(self.lut_bb1[count])
def getbuffer(self, image):
# logging.debug("bufsiz = ",int(self.width/8) * self.height)
buf = [0xFF] * (int(self.width/8) * self.height)
image_monocolor = image.convert('1')
imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load()
# logging.debug("imwidth = %d, imheight = %d",imwidth,imheight)
if(imwidth == self.width and imheight == self.height):
logging.debug("Vertical")
for y in range(imheight):
for x in range(imwidth):
# Set the bits for the column of pixels at the current position.
if pixels[x, y] == 0:
buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
elif(imwidth == self.height and imheight == self.width):
logging.debug("Horizontal")
for y in range(imheight):
for x in range(imwidth):
newx = y
newy = self.height - x - 1
if pixels[x, y] == 0:
buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8))
return buf
def display(self, image):
if (Image == None):
return
self.send_command(0x10)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(0x00)
epdconfig.delay_ms(10)
self.send_command(0x13)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(image[i])
epdconfig.delay_ms(10)
self.SetFullReg()
self.TurnOnDisplay()
def DisplayPartial(self, image):
if (Image == None):
return
self.SetPartReg()
self.send_command(0x91)
self.send_command(0x90)
self.send_data(0)
self.send_data(self.width - 1)
self.send_data(0)
self.send_data(0)
self.send_data(int(self.height / 256))
self.send_data(self.height % 256 - 1)
self.send_data(0x28)
self.send_command(0x10)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(image[i])
epdconfig.delay_ms(10)
self.send_command(0x13)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(~image[i])
epdconfig.delay_ms(10)
self.TurnOnDisplay()
def Clear(self):
self.send_command(0x10)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(0x00)
epdconfig.delay_ms(10)
self.send_command(0x13)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(0x00)
epdconfig.delay_ms(10)
self.SetFullReg()
self.TurnOnDisplay()
def sleep(self):
self.send_command(0X50)
self.send_data(0xf7)
self.send_command(0X02) # power off
self.send_command(0X07) # deep sleep
self.send_data(0xA5)
epdconfig.module_exit()
### END OF FILE ###

View File

@ -0,0 +1,154 @@
# /*****************************************************************************
# * | File : epdconfig.py
# * | Author : Waveshare team
# * | Function : Hardware underlying interface
# * | Info :
# *----------------
# * | This version: V1.0
# * | Date : 2019-06-21
# * | Info :
# ******************************************************************************
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import os
import logging
import sys
import time
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
# SPI device, bus = 0, device = 0
self.SPI = spidev.SpiDev(0, 0)
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.writebytes(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)
self.SPI.max_speed_hz = 4000000
self.SPI.mode = 0b00
return 0
def module_exit(self):
logging.debug("spi end")
self.SPI.close()
logging.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()
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 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):
logging.debug("spi end")
self.SPI.SYSFS_software_spi_end()
logging.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()
if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'):
implementation = RaspberryPi()
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,338 @@
# //*****************************************************************************
# * | File : epd2in13.py
# * | Author : Waveshare team
# * | Function : Electronic paper driver
# * | Info :
# *----------------
# * | This version: V3.0
# * | Date : 2018-11-01
# * | Info : python2 demo
# * 1.Remove:
# digital_write(self, pin, value)
# digital_read(self, pin)
# delay_ms(self, delaytime)
# set_lut(self, lut)
# self.lut = self.lut_full_update
# * 2.Change:
# display_frame -> TurnOnDisplay
# set_memory_area -> SetWindow
# set_memory_pointer -> SetCursor
# * 3.How to use
# epd = epd2in13.EPD()
# epd.init(epd.lut_full_update)
# image = Image.new('1', (epd2in13.EPD_WIDTH, epd2in13.EPD_HEIGHT), 255)
# ...
# drawing ......
# ...
# epd.display(getbuffer(image))
# ******************************************************************************//
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and//or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import time
import spidev
import RPi.GPIO as GPIO
from PIL import Image
# Pin definition
RST_PIN = 17
DC_PIN = 25
CS_PIN = 8
BUSY_PIN = 24
# SPI device, bus = 0, device = 0
SPI = spidev.SpiDev(0, 0)
def digital_write(pin, value):
GPIO.output(pin, value)
def digital_read(pin):
return GPIO.input(BUSY_PIN)
def delay_ms(delaytime):
time.sleep(delaytime / 1000.0)
def spi_writebyte(data):
SPI.writebytes(data)
def module_init():
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(RST_PIN, GPIO.OUT)
GPIO.setup(DC_PIN, GPIO.OUT)
GPIO.setup(CS_PIN, GPIO.OUT)
GPIO.setup(BUSY_PIN, GPIO.IN)
SPI.max_speed_hz = 2000000
SPI.mode = 0b00
return 0;
# Display resolution
EPD_WIDTH = 122
EPD_HEIGHT = 250
class EPD:
def __init__(self):
self.reset_pin = RST_PIN
self.dc_pin = DC_PIN
self.busy_pin = BUSY_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
FULL_UPDATE = 0
PART_UPDATE = 1
lut_full_update = [
0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, # LUT0: BB: VS 0 ~7
0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, # LUT1: BW: VS 0 ~7
0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, # LUT2: WB: VS 0 ~7
0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, # LUT3: WW: VS 0 ~7
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT4: VCOM: VS 0 ~7
0x03, 0x03, 0x00, 0x00, 0x02, # TP0 A~D RP0
0x09, 0x09, 0x00, 0x00, 0x02, # TP1 A~D RP1
0x03, 0x03, 0x00, 0x00, 0x02, # TP2 A~D RP2
0x00, 0x00, 0x00, 0x00, 0x00, # TP3 A~D RP3
0x00, 0x00, 0x00, 0x00, 0x00, # TP4 A~D RP4
0x00, 0x00, 0x00, 0x00, 0x00, # TP5 A~D RP5
0x00, 0x00, 0x00, 0x00, 0x00, # TP6 A~D RP6
0x15, 0x41, 0xA8, 0x32, 0x30, 0x0A,
]
lut_partial_update = [ # 20 bytes
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT0: BB: VS 0 ~7
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT1: BW: VS 0 ~7
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT2: WB: VS 0 ~7
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT3: WW: VS 0 ~7
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT4: VCOM: VS 0 ~7
0x0A, 0x00, 0x00, 0x00, 0x00, # TP0 A~D RP0
0x00, 0x00, 0x00, 0x00, 0x00, # TP1 A~D RP1
0x00, 0x00, 0x00, 0x00, 0x00, # TP2 A~D RP2
0x00, 0x00, 0x00, 0x00, 0x00, # TP3 A~D RP3
0x00, 0x00, 0x00, 0x00, 0x00, # TP4 A~D RP4
0x00, 0x00, 0x00, 0x00, 0x00, # TP5 A~D RP5
0x00, 0x00, 0x00, 0x00, 0x00, # TP6 A~D RP6
0x15, 0x41, 0xA8, 0x32, 0x30, 0x0A,
]
# Hardware reset
def reset(self):
digital_write(self.reset_pin, GPIO.HIGH)
delay_ms(200)
digital_write(self.reset_pin, GPIO.LOW) # module reset
delay_ms(200)
digital_write(self.reset_pin, GPIO.HIGH)
delay_ms(200)
def send_command(self, command):
digital_write(self.dc_pin, GPIO.LOW)
spi_writebyte([command])
def send_data(self, data):
digital_write(self.dc_pin, GPIO.HIGH)
spi_writebyte([data])
def wait_until_idle(self):
while (digital_read(self.busy_pin) == 1): # 0: idle, 1: busy
delay_ms(100)
def TurnOnDisplay(self):
self.send_command(0x22)
self.send_data(0xC7)
self.send_command(0x20)
self.wait_until_idle()
def init(self, update):
if (module_init() != 0):
return -1
# EPD hardware init start
self.reset()
if (update == self.FULL_UPDATE):
self.wait_until_idle()
self.send_command(0x12) # soft reset
self.wait_until_idle()
self.send_command(0x74) # set analog block control
self.send_data(0x54)
self.send_command(0x7E) # set digital block control
self.send_data(0x3B)
self.send_command(0x01) # Driver output control
self.send_data(0xF9)
self.send_data(0x00)
self.send_data(0x00)
self.send_command(0x11) # data entry mode
self.send_data(0x01)
self.send_command(0x44) # set Ram-X address start//end position
self.send_data(0x00)
self.send_data(0x0F) # 0x0C-->(15+1)*8=128
self.send_command(0x45) # set Ram-Y address start//end position
self.send_data(0xF9) # 0xF9-->(249+1)=250
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x00)
self.send_command(0x3C) # BorderWavefrom
self.send_data(0x03)
self.send_command(0x2C) # VCOM Voltage
self.send_data(0x55) #
self.send_command(0x03)
self.send_data(self.lut_full_update[70])
self.send_command(0x04) #
self.send_data(self.lut_full_update[71])
self.send_data(self.lut_full_update[72])
self.send_data(self.lut_full_update[73])
self.send_command(0x3A) # Dummy Line
self.send_data(self.lut_full_update[74])
self.send_command(0x3B) # Gate time
self.send_data(self.lut_full_update[75])
self.send_command(0x32)
for count in range(70):
self.send_data(self.lut_full_update[count])
self.send_command(0x4E) # set RAM x address count to 0
self.send_data(0x00)
self.send_command(0x4F) # set RAM y address count to 0X127
self.send_data(0xF9)
self.send_data(0x00)
self.wait_until_idle()
else:
self.send_command(0x2C) # VCOM Voltage
self.send_data(0x26)
self.wait_until_idle()
self.send_command(0x32)
for count in range(70):
self.send_data(self.lut_partial_update[count])
self.send_command(0x37)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x40)
self.send_data(0x00)
self.send_data(0x00)
self.send_command(0x22)
self.send_data(0xC0)
self.send_command(0x20)
self.wait_until_idle()
self.send_command(0x3C) # BorderWavefrom
self.send_data(0x01)
return 0
def getbuffer(self, image):
if self.width % 8 == 0:
linewidth = self.width // 8
else:
linewidth = self.width // 8 + 1
buf = [0xFF] * (linewidth * self.height)
image_monocolor = image.convert('1')
imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load()
if (imwidth == self.width and imheight == self.height):
# print("Vertical")
for y in range(imheight):
for x in range(imwidth):
if pixels[x, y] == 0:
x = imwidth - x
buf[x // 8 + y * linewidth] &= ~(0x80 >> (x % 8))
elif (imwidth == self.height and imheight == self.width):
# print("Horizontal")
for y in range(imheight):
for x in range(imwidth):
newx = y
newy = self.height - x - 1
if pixels[x, y] == 0:
newy = imwidth - newy - 1
buf[newx // 8 + newy * linewidth] &= ~(0x80 >> (y % 8))
return buf
def display(self, image):
if self.width % 8 == 0:
linewidth = self.width // 8
else:
linewidth = self.width // 8 + 1
self.send_command(0x24)
for j in range(0, self.height):
for i in range(0, linewidth):
self.send_data(image[i + j * linewidth])
self.TurnOnDisplay()
def displayPartial(self, image):
if self.width % 8 == 0:
linewidth = self.width // 8
else:
linewidth = self.width // 8 + 1
self.send_command(0x24)
for j in range(0, self.height):
for i in range(0, linewidth):
self.send_data(image[i + j * linewidth])
self.send_command(0x26)
for j in range(0, self.height):
for i in range(0, linewidth):
self.send_data(~image[i + j * linewidth])
self.TurnOnDisplay()
def Clear(self, color):
if self.width % 8 == 0:
linewidth = self.width // 8
else:
linewidth = self.width // 8 + 1
# print(linewidth)
self.send_command(0x24)
for j in range(0, self.height):
for i in range(0, linewidth):
self.send_data(color)
self.TurnOnDisplay()
def sleep(self):
self.send_command(0x22) # POWER OFF
self.send_data(0xC3)
self.send_command(0x20)
self.send_command(0x10) # enter deep sleep
self.send_data(0x01)
delay_ms(100)
### END OF FILE ###

View File

@ -28,7 +28,7 @@
#
import logging
from .. import epdconfig
from . import epdconfig
# Display resolution
EPD_WIDTH = 128

View File

@ -33,6 +33,8 @@
from distutils.command.build_scripts import build_scripts
import logging
from .. import epdconfig
from PIL import Image
import RPi.GPIO as GPIO
# Display resolution
EPD_WIDTH = 128

View File

@ -4,8 +4,8 @@
# * | Function : Electronic paper driver
# * | Info :
# *----------------
# * | This version: V1.2
# * | Date : 2022-08-9
# * | This version: V1.1
# * | Date : 2021-10-30
# # | Info : python demo
# -----------------------------------------------------------------------------
# Permission is hereby granted, free of charge, to any person obtaining a copy
@ -29,7 +29,8 @@
import logging
from .. import epdconfig
from . import epdconfig
import numpy as np
# Display resolution
EPD_WIDTH = 122
@ -37,7 +38,6 @@ EPD_HEIGHT = 250
logger = logging.getLogger(__name__)
class EPD:
def __init__(self):
self.reset_pin = epdconfig.RST_PIN
@ -95,7 +95,6 @@ class EPD:
function :Hardware reset
parameter:
'''
def reset(self):
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(20)
@ -109,7 +108,6 @@ class EPD:
parameter:
command : Command register
'''
def send_command(self, command):
epdconfig.digital_write(self.dc_pin, 0)
epdconfig.digital_write(self.cs_pin, 0)
@ -121,25 +119,16 @@ class EPD:
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
@ -150,7 +139,6 @@ class EPD:
function : Turn On Display
parameter:
'''
def TurnOnDisplay(self):
self.send_command(0x22) # Display Update Control
self.send_data(0xC7)
@ -161,7 +149,6 @@ class EPD:
function : Turn On Display Part
parameter:
'''
def TurnOnDisplayPart(self):
self.send_command(0x22) # Display Update Control
self.send_data(0x0f) # fast:0x0c, quality:0x0f, 0xcf
@ -173,7 +160,6 @@ class EPD:
parameter:
lut : lut data
'''
def Lut(self, lut):
self.send_command(0x32)
for i in range(0, 153):
@ -185,7 +171,6 @@ class EPD:
parameter:
lut : lut data
'''
def SetLut(self, lut):
self.Lut(lut)
self.send_command(0x3f)
@ -207,7 +192,6 @@ class EPD:
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
@ -226,7 +210,6 @@ class EPD:
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
@ -240,7 +223,6 @@ class EPD:
function : Initialize the e-Paper register
parameter:
'''
def init(self):
if (epdconfig.module_init() != 0):
return -1
@ -282,7 +264,6 @@ class EPD:
parameter:
image : Image data
'''
def getbuffer(self, image):
img = image
imwidth, imheight = img.size
@ -304,7 +285,6 @@ class EPD:
parameter:
image : Image data
'''
def display(self, image):
if self.width%8 == 0:
linewidth = int(self.width/8)
@ -322,8 +302,12 @@ class EPD:
parameter:
image : Image data
'''
def displayPartial(self, image):
if self.width%8 == 0:
linewidth = int(self.width/8)
else:
linewidth = int(self.width/8) + 1
epdconfig.digital_write(self.reset_pin, 0)
epdconfig.delay_ms(1)
epdconfig.digital_write(self.reset_pin, 1)
@ -353,10 +337,9 @@ class EPD:
self.SetCursor(0, 0)
self.send_command(0x24) # WRITE_RAM
# for j in range(0, self.height):
# for i in range(0, linewidth):
# self.send_data(image[i + j * linewidth])
self.send_data2(image)
for j in range(0, self.height):
for i in range(0, linewidth):
self.send_data(image[i + j * linewidth])
self.TurnOnDisplayPart()
'''
@ -364,21 +347,28 @@ class EPD:
parameter:
image : Image data
'''
def displayPartBaseImage(self, image):
if self.width%8 == 0:
linewidth = int(self.width/8)
else:
linewidth = int(self.width/8) + 1
self.send_command(0x24)
self.send_data2(image)
for j in range(0, self.height):
for i in range(0, linewidth):
self.send_data(image[i + j * linewidth])
self.send_command(0x26)
self.send_data2(image)
for j in range(0, self.height):
for i in range(0, linewidth):
self.send_data(image[i + j * linewidth])
self.TurnOnDisplay()
'''
function : Clear screen
parameter:
'''
def Clear(self, color=0xFF):
def Clear(self, color):
if self.width%8 == 0:
linewidth = int(self.width/8)
else:
@ -386,14 +376,16 @@ class EPD:
# logger.debug(linewidth)
self.send_command(0x24)
self.send_data2([color] * int(self.height * linewidth))
for j in range(0, self.height):
for i in range(0, linewidth):
self.send_data(color)
self.TurnOnDisplay()
'''
function : Enter sleep mode
parameter:
'''
def sleep(self):
self.send_command(0x10) #enter deep sleep
self.send_data(0x01)
@ -402,3 +394,4 @@ class EPD:
epdconfig.module_exit()
### END OF FILE ###

View File

@ -0,0 +1,154 @@
# /*****************************************************************************
# * | File : epdconfig.py
# * | Author : Waveshare team
# * | Function : Hardware underlying interface
# * | Info :
# *----------------
# * | This version: V1.0
# * | Date : 2019-06-21
# * | 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
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
# SPI device, bus = 0, device = 0
self.SPI = spidev.SpiDev(0, 0)
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 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.max_speed_hz = 4000000
self.SPI.mode = 0b00
return 0
def module_exit(self):
logging.debug("spi end")
self.SPI.close()
logging.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()
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 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):
logging.debug("spi end")
self.SPI.SYSFS_software_spi_end()
logging.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()
if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'):
implementation = RaspberryPi()
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

@ -1,9 +1,39 @@
# *****************************************************************************
# * | 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
from . import epdconfig
# Display resolution
EPD_WIDTH = 296
EPD_HEIGHT = 128
EPD_WIDTH = 122
EPD_HEIGHT = 250
logger = logging.getLogger(__name__)
@ -17,55 +47,10 @@ class EPD:
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
lut_partial_update = [
0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0,
0x22, 0x17, 0x41, 0x00, 0x32, 0x36,
]
lut_full_update = [
0x80, 0x4A, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x40, 0x4A, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x80, 0x4A, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x40, 0x4A, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xF, 0x0, 0x0, 0xF, 0x0, 0x0, 0x2,
0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0,
0x22, 0x17, 0x41, 0x0, 0x32, 0x36,
]
'''
function :Hardware reset
parameter:
'''
def reset(self):
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(20)
@ -79,7 +64,6 @@ class EPD:
parameter:
command : Command register
'''
def send_command(self, command):
epdconfig.digital_write(self.dc_pin, 0)
epdconfig.digital_write(self.cs_pin, 0)
@ -91,7 +75,6 @@ class EPD:
parameter:
data : Write data
'''
def send_data(self, data):
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
@ -109,7 +92,6 @@ class EPD:
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
@ -120,10 +102,19 @@ class EPD:
function : Turn On Display
parameter:
'''
def TurnOnDisplay(self):
self.send_command(0x22) # Display Update Control
self.send_data(0xC7)
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()
@ -131,43 +122,12 @@ class EPD:
function : Turn On Display Part
parameter:
'''
def TurnOnDisplayPart(self):
self.send_command(0x22) # Display Update Control
self.send_data(0x0f) # fast:0x0c, quality:0x0f, 0xcf
self.send_data(0xff) # fast:0x0c, quality:0x0f, 0xcf
self.send_command(0x20) # Activate Display Update Sequence
self.ReadBusy()
'''
function : Set lut
parameter:
lut : lut data
'''
def Lut(self, lut):
self.send_command(0x32)
for i in range(0, 153):
self.send_data(lut[i])
self.ReadBusy()
'''
function : Send lut data and configuration
parameter:
lut : lut data
'''
def SetLut(self, lut):
self.Lut(lut)
self.send_command(0x3f)
self.send_data(lut[153])
self.send_command(0x03) # gate voltage
self.send_data(lut[154])
self.send_command(0x04) # source voltage
self.send_data(lut[155]) # VSH
self.send_data(lut[156]) # VSH2
self.send_data(lut[157]) # VSL
self.send_command(0x2c) # VCOM
self.send_data(lut[158])
'''
function : Setting the display window
@ -177,7 +137,6 @@ class EPD:
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
@ -196,7 +155,6 @@ class EPD:
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
@ -210,7 +168,6 @@ class EPD:
function : Initialize the e-Paper register
parameter:
'''
def init(self):
if (epdconfig.module_init() != 0):
return -1
@ -244,15 +201,50 @@ class EPD:
self.ReadBusy()
self.SetLut(self.lut_full_update)
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
@ -274,58 +266,45 @@ class EPD:
parameter:
image : Image data
'''
def display(self, image):
if self.width % 8 == 0:
linewidth = int(self.width / 8)
else:
linewidth = int(self.width / 8) + 1
self.send_command(0x24)
for j in range(0, self.height):
for i in range(0, linewidth):
self.send_data(image[i + j * linewidth])
self.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.SetLut(self.lut_partial_update)
self.send_command(0x37)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x40)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x00)
self.send_data(0x00)
self.send_command(0x3C) # BorderWavefrom
self.send_data(0x80)
self.send_command(0x22)
self.send_data(0xC0)
self.send_command(0x20)
self.ReadBusy()
self.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
# for j in range(0, self.height):
# for i in range(0, linewidth):
# self.send_data(image[i + j * linewidth])
self.send_data2(image)
self.TurnOnDisplayPart()
@ -334,7 +313,6 @@ class EPD:
parameter:
image : Image data
'''
def displayPartBaseImage(self, image):
self.send_command(0x24)
self.send_data2(image)
@ -347,7 +325,6 @@ class EPD:
function : Clear screen
parameter:
'''
def Clear(self, color=0xFF):
if self.width%8 == 0:
linewidth = int(self.width/8)
@ -363,7 +340,6 @@ class EPD:
function : Enter sleep mode
parameter:
'''
def sleep(self):
self.send_command(0x10) #enter deep sleep
self.send_data(0x01)

View File

@ -235,7 +235,7 @@ if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'):
elif os.path.exists('/sys/bus/platform/drivers/gpio-x3'):
implementation = SunriseX3()
else:
implementation = RaspberryPi()
implementation = JetsonNano()
for func in [x for x in dir(implementation) if not x.startswith('_')]:
setattr(sys.modules[__name__], func, getattr(implementation, func))

View File

@ -29,7 +29,7 @@
import logging
from .. import epdconfig
from . import epdconfig
# Display resolution
EPD_WIDTH = 800

View File

@ -1,675 +0,0 @@
# *****************************************************************************
# * | File : epd5in79.py
# * | Author : Waveshare team
# * | Function : Electronic paper driver
# * | Info :
# *----------------
# * | This version: V1.0
# * | Date : 2024-03-05
# # | 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 = 792
EPD_HEIGHT = 272
GRAY1 = 0xff # white
GRAY2 = 0xC0
GRAY3 = 0x80 # gray
GRAY4 = 0x00 # Blackest
logger = logging.getLogger(__name__)
class EPD:
def __init__(self):
self.reset_pin = epdconfig.RST_PIN
self.dc_pin = epdconfig.DC_PIN
self.busy_pin = epdconfig.BUSY_PIN
self.cs_pin = epdconfig.CS_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
self.GRAY1 = GRAY1 # white
self.GRAY2 = GRAY2
self.GRAY3 = GRAY3 # gray
self.GRAY4 = GRAY4 # Blackest
self.LUT_DATA_4Gray = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x4A, 0x00, 0x00, 0x00, 0x01, 0x00,
0x01, 0x82, 0x42, 0x00, 0x00, 0x10, 0x00,
0x01, 0x8A, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x41, 0x00, 0x00, 0x00, 0x01, 0x00,
0x01, 0x82, 0x42, 0x00, 0x00, 0x10, 0x00,
0x01, 0x81, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x81, 0x00, 0x00, 0x00, 0x01, 0x00,
0x01, 0x82, 0x42, 0x00, 0x00, 0x10, 0x00,
0x01, 0x41, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x8A, 0x00, 0x00, 0x00, 0x01, 0x00,
0x01, 0x82, 0x42, 0x00, 0x00, 0x10, 0x00,
0x01, 0x4A, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00,
0x22, 0x17, 0x41, 0xA8, 0x32, 0x40, ]
# Hardware reset
def reset(self):
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
epdconfig.digital_write(self.reset_pin, 0)
epdconfig.delay_ms(1)
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
def send_command(self, command):
epdconfig.digital_write(self.dc_pin, 0)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([command])
epdconfig.digital_write(self.cs_pin, 1)
def send_data(self, data):
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([data])
epdconfig.digital_write(self.cs_pin, 1)
# send a lot of data
def send_data2(self, data):
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte2(data)
epdconfig.digital_write(self.cs_pin, 1)
def ReadBusy(self):
logger.debug("e-Paper busy")
while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy
epdconfig.delay_ms(200)
logger.debug("e-Paper busy release")
def TurnOnDisplay(self):
self.send_command(0x22)
self.send_data(0xF7)
self.send_command(0x20) # DISPLAY REFRESH
epdconfig.delay_ms(100) # The delay here is necessary, 200uS at least!!!
self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
def TurnOnDisplay_Fast(self):
self.send_command(0x22)
self.send_data(0xC7)
self.send_command(0x20) # DISPLAY REFRESH
epdconfig.delay_ms(100) # The delay here is necessary, 200uS at least!!!
self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
def TurnOnDisplay_Partial(self):
self.send_command(0x22)
self.send_data(0xFF)
self.send_command(0x20) # DISPLAY REFRESH
epdconfig.delay_ms(100) # The delay here is necessary, 200uS at least!!!
self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
def TurnOnDisplay_4GRAY(self):
self.send_command(0x22)
self.send_data(0xCF)
self.send_command(0x20) # DISPLAY REFRESH
epdconfig.delay_ms(100) # The delay here is necessary, 200uS at least!!!
self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
def EPD_5in79_Lut(self):
self.send_command(0x32)
self.send_data2(self.LUT_DATA_4Gray[:227])
self.send_command(0x3f)
self.send_data(self.LUT_DATA_4Gray[227])
self.send_command(0x03)
self.send_data(self.LUT_DATA_4Gray[228])
self.send_command(0x04)
self.send_data(self.LUT_DATA_4Gray[229])
self.send_data(self.LUT_DATA_4Gray[230])
self.send_data(self.LUT_DATA_4Gray[231])
self.send_command(0x2C)
self.send_data(self.LUT_DATA_4Gray[232])
def init(self):
if (epdconfig.module_init() != 0):
return -1
self.reset()
self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
self.send_command(0x12) # POWER ON
self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
self.send_command(0x11)
self.send_data(0x01)
self.send_command(0x44) # Set Ram X- address Start / End position
self.send_data(0x00) # XStart, POR = 00h
self.send_data(0x31) # 400/8-1
self.send_command(0x45) # Set Ram Y- address Start / End position
self.send_data(0x0f)
self.send_data(0x01) # 300-1
self.send_data(0x00) # YEnd L
self.send_data(0x00) # YEnd H
self.send_command(0x4e)
self.send_data(0x00)
self.send_command(0x4f)
self.send_data(0x0f)
self.send_data(0x01)
self.ReadBusy()
self.send_command(0x91)
self.send_data(0x00)
self.send_command(0xC4) # Set Ram X- address Start / End position
self.send_data(0x31) # XStart, POR = 00h
self.send_data(0x00) # 400/8-1
self.send_command(0xC5) # Set Ram Y- address Start / End position
self.send_data(0x0f)
self.send_data(0x01) # 300-1
self.send_data(0x00) # YEnd L
self.send_data(0x00) # YEnd H
self.send_command(0xCE)
self.send_data(0x31)
self.send_command(0xCF)
self.send_data(0x0f)
self.send_data(0x01)
self.ReadBusy()
return 0
def init_Fast(self):
if (epdconfig.module_init() != 0):
return -1
self.reset()
self.ReadBusy()
self.send_command(0x12)
self.ReadBusy()
self.send_command(0x18)
self.send_data(0x80)
self.send_command(0x22)
self.send_data(0xB1)
self.send_command(0x20)
self.ReadBusy()
self.send_command(0x1A)
self.send_data(0x64)
self.send_data(0x00)
self.send_command(0x22)
self.send_data(0x91)
self.send_command(0x20)
self.ReadBusy()
self.send_command(0x11)
self.send_data(0x01)
self.send_command(0x44)
self.send_data(0x00)
self.send_data(0x31)
self.send_command(0x45)
self.send_data(0x0f)
self.send_data(0x01)
self.send_data(0x00)
self.send_data(0x00)
self.send_command(0x4e)
self.send_data(0x00)
self.send_command(0x4f)
self.send_data(0x0f)
self.send_data(0x01)
self.ReadBusy()
self.send_command(0x91)
self.send_data(0x00)
self.send_command(0xC4)
self.send_data(0x31)
self.send_data(0x00)
self.send_command(0xC5)
self.send_data(0x0f)
self.send_data(0x01)
self.send_data(0x00)
self.send_data(0x00)
self.send_command(0xCe)
self.send_data(0x31)
self.send_command(0xCf)
self.send_data(0x0f)
self.send_data(0x01)
self.ReadBusy()
return 0
def init_Partial(self):
if (epdconfig.module_init() != 0):
return -1
self.reset()
self.ReadBusy()
self.send_command(0x12)
self.ReadBusy()
self.send_command(0x3C)
self.send_data(0x80)
return 0
def init_4Gray(self):
if (epdconfig.module_init() != 0):
return -1
self.reset()
self.ReadBusy()
self.send_command(0x12)
self.ReadBusy()
self.send_command(0x0C)
self.send_data(0x8B)
self.send_data(0x9C)
self.send_data(0xA6)
self.send_data(0x0F)
self.send_command(0x3C)
self.send_data(0x81)
self.ReadBusy()
self.send_command(0x11)
self.send_data(0x01)
self.send_command(0x44)
self.send_data(0x00)
self.send_data(0x31)
self.send_command(0x45)
self.send_data(0x0f)
self.send_data(0x01)
self.send_data(0x00)
self.send_data(0x00)
self.send_command(0x4e)
self.send_data(0x00)
self.send_command(0x4f)
self.send_data(0x0f)
self.send_data(0x01)
self.ReadBusy()
self.send_command(0x91)
self.send_data(0x00)
self.send_command(0xC4)
self.send_data(0x31)
self.send_data(0x00)
self.send_command(0xC5)
self.send_data(0x0f)
self.send_data(0x01)
self.send_data(0x00)
self.send_data(0x00)
self.send_command(0xCe)
self.send_data(0x31)
self.send_command(0xCf)
self.send_data(0x0f)
self.send_data(0x01)
self.EPD_5in79_Lut()
return 0
def getbuffer(self, image):
# logger.debug("bufsiz = ",int(self.width/8) * self.height)
buf = [0xFF] * (int(self.width / 8) * self.height)
image_monocolor = image.convert('1')
imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load()
# logger.debug("imwidth = %d, imheight = %d",imwidth,imheight)
if imwidth == self.width and imheight == self.height:
logger.debug("Horizontal")
for y in range(imheight):
for x in range(imwidth):
# Set the bits for the column of pixels at the current position.
if pixels[x, y] == 0:
buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
elif imwidth == self.height and imheight == self.width:
logger.debug("Vertical")
for y in range(imheight):
for x in range(imwidth):
newx = y
newy = self.height - x - 1
if pixels[x, y] == 0:
buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8))
return buf
def getbuffer_4Gray(self, image):
# logger.debug("bufsiz = ",int(self.width/8) * self.height)
buf = [0xFF] * (int(self.width / 4) * self.height)
image_monocolor = image.convert('L')
imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load()
i = 0
# logger.debug("imwidth = %d, imheight = %d",imwidth,imheight)
if (imwidth == self.width and imheight == self.height):
logger.debug("Vertical")
for y in range(imheight):
for x in range(imwidth):
# Set the bits for the column of pixels at the current position.
if (pixels[x, y] == 0xC0):
pixels[x, y] = 0x80
elif (pixels[x, y] == 0x80):
pixels[x, y] = 0x40
i = i + 1
if (i % 4 == 0):
buf[int((x + (y * self.width)) / 4)] = (
(pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | (
pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6)
elif (imwidth == self.height and imheight == self.width):
logger.debug("Horizontal")
for x in range(imwidth):
for y in range(imheight):
newx = y
newy = self.height - x - 1
if (pixels[x, y] == 0xC0):
pixels[x, y] = 0x80
elif (pixels[x, y] == 0x80):
pixels[x, y] = 0x40
i = i + 1
if (i % 4 == 0):
buf[int((newx + (newy * self.width)) / 4)] = (
(pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | (
pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6)
return buf
def display(self, imageblack):
Width = int(self.width / 16) + 1
Width1 = int(self.width / 8)
self.send_command(0x24)
for i in range(self.height):
self.send_data2(imageblack[i * Width1: i * Width1 + Width])
self.send_command(0X26)
self.send_data2([0x00] * 13600)
self.send_command(0xA4)
for i in range(self.height):
self.send_data2(imageblack[i * Width1 + Width - 1: i * Width1 + Width * 2 - 1])
self.send_command(0xA6)
self.send_data2([0x00] * 13600)
self.TurnOnDisplay()
def display_Base(self, imageblack):
Width = int(self.width / 16) + 1
Width1 = int(self.width / 8)
self.send_command(0x24)
for i in range(self.height):
self.send_data2(imageblack[i * Width1: i * Width1 + Width])
self.send_command(0X26)
self.send_data2([0x00] * 13600)
self.send_command(0xA4)
for i in range(self.height):
self.send_data2(imageblack[i * Width1 + Width - 1: i * Width1 + Width * 2 - 1])
self.send_command(0xA6)
self.send_data2([0x00] * 13600)
self.TurnOnDisplay()
self.send_command(0x26)
for i in range(self.height):
self.send_data2(imageblack[i * Width1: i * Width1 + Width])
self.send_command(0xA6)
for i in range(self.height):
self.send_data2(imageblack[i * Width1 + Width - 1: i * Width1 + Width * 2 - 1])
def display_Base_color(self, color):
Width = int(self.width / 16) + 1
Width1 = int(self.width / 8)
self.send_command(0x24)
self.send_data2([color] * 13600)
self.send_command(0X26)
self.send_data2([0x00] * 13600)
self.send_command(0xA4)
self.send_data2([color] * 13600)
self.send_command(0xA6)
self.send_data2([0x00] * 13600)
self.TurnOnDisplay()
self.send_command(0x26)
self.send_data2([color] * 13600)
self.send_command(0xA6)
self.send_data2([color] * 13600)
def display_Fast(self, imageblack):
Width = int(self.width / 16) + 1
Width1 = int(self.width / 8)
self.send_command(0x24)
for i in range(self.height):
self.send_data2(imageblack[i * Width1: i * Width1 + Width])
self.send_command(0X26)
self.send_data2([0x00] * 13600)
self.send_command(0xA4)
for i in range(self.height):
self.send_data2(imageblack[i * Width1 + Width - 1: i * Width1 + Width * 2 - 1])
self.send_command(0xA6)
self.send_data2([0x00] * 13600)
self.TurnOnDisplay_Fast()
def display_Partial(self, Image):
Width = int(self.width / 16) + 1
Width1 = int(self.width / 8)
Height = self.height
self.send_command(0x44)
self.send_data(0x00)
self.send_data(0x31)
self.send_command(0x45)
self.send_data(0x0f)
self.send_data(0x01) # 300-1
self.send_data(0x00) # YEnd L
self.send_data(0x00) # YEnd H
self.send_command(0x4e)
self.send_data(0x00)
self.send_command(0x4f)
self.send_data(0x0f)
self.send_data(0x01)
self.send_command(0x24)
for i in range(Height):
self.send_data2(Image[i * Width1: i * Width1 + Width])
self.send_command(0xC4) # Set Ram X- address Start / End position
self.send_data(0x31) # XStart, POR = 00h
self.send_data(0x00) # 400/8-1
self.send_command(0xC5) # Set Ram Y- address Start / End position
self.send_data(0x0f)
self.send_data(0x01) # 300-1
self.send_data(0x00) # YEnd L
self.send_data(0x00) # YEnd H
self.send_command(0xCe)
self.send_data(0x31)
self.send_command(0xCf)
self.send_data(0x0f)
self.send_data(0x01)
self.send_command(0xA4)
for i in range(Height):
self.send_data2(Image[i * Width1 + Width - 1: i * Width1 + Width * 2 - 1])
self.TurnOnDisplay_Partial()
def display_4Gray(self, image):
Width = int(self.width / 16) + 1
Width1 = int(self.width / 8)
self.send_command(0x24)
for j in range(self.height):
for i in range(Width):
temp3 = 0
for o in range(0, 2):
temp1 = image[(j * Width1 + i) * 2 + o]
for k in range(0, 4):
temp2 = temp1 & 0xC0
if (temp2 == 0xC0):
temp3 |= 0x01
elif (temp2 == 0x00):
temp3 |= 0x00
elif (temp2 == 0x80):
temp3 |= 0x00
else: # 0x40
temp3 |= 0x01
if (o != 1 or k != 3):
temp3 <<= 1
temp1 <<= 2
self.send_data(temp3)
self.send_command(0x26)
for j in range(self.height):
for i in range(Width):
temp3 = 0
for o in range(0, 2):
temp1 = image[(j * Width1 + i) * 2 + o]
for k in range(0, 4):
temp2 = temp1 & 0xC0
if (temp2 == 0xC0):
temp3 |= 0x01
elif (temp2 == 0x00):
temp3 |= 0x00
elif (temp2 == 0x80):
temp3 |= 0x01
else: # 0x40
temp3 |= 0x00
if (o != 1 or k != 3):
temp3 <<= 1
temp1 <<= 2
self.send_data(temp3)
self.send_command(0xA4)
for j in range(self.height):
for i in range(Width):
temp3 = 0
for o in range(0, 2):
temp1 = image[(j * Width1 + i + Width - 1) * 2 + o]
for k in range(0, 4):
temp2 = temp1 & 0xC0
if (temp2 == 0xC0):
temp3 |= 0x01
elif (temp2 == 0x00):
temp3 |= 0x00
elif (temp2 == 0x80):
temp3 |= 0x00
else: # 0x40
temp3 |= 0x01
if (o != 1 or k != 3):
temp3 <<= 1
temp1 <<= 2
self.send_data(temp3)
self.send_command(0xA6)
for j in range(self.height):
for i in range(Width):
temp3 = 0
for o in range(0, 2):
temp1 = image[(j * Width1 + i + Width - 1) * 2 + o]
for k in range(0, 4):
temp2 = temp1 & 0xC0
if (temp2 == 0xC0):
temp3 |= 0x01
elif (temp2 == 0x00):
temp3 |= 0x00
elif (temp2 == 0x80):
temp3 |= 0x01
else: # 0x40
temp3 |= 0x00
if (o != 1 or k != 3):
temp3 <<= 1
temp1 <<= 2
self.send_data(temp3)
self.TurnOnDisplay_4GRAY()
def Clear(self):
self.send_command(0x24)
self.send_data2([0xFF] * 13600)
self.send_command(0X26)
self.send_data2([0x00] * 13600)
self.send_command(0xA4)
self.send_data2([0xFF] * 13600)
self.send_command(0xA6)
self.send_data2([0x00] * 13600)
self.TurnOnDisplay()
def sleep(self):
self.send_command(0X10) # deep sleep
self.send_data(0x03)
epdconfig.delay_ms(2000)
epdconfig.module_exit()
### END OF FILE ###

View File

@ -1,205 +0,0 @@
# *****************************************************************************
# * | File : epd5in79b.py
# * | Author : Waveshare team
# * | Function : Electronic paper driver
# * | Info :
# *----------------
# * | This version: V1.0
# * | Date : 2024-03-05
# # | 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 = 792
EPD_HEIGHT = 272
logger = logging.getLogger(__name__)
class EPD:
def __init__(self):
self.reset_pin = epdconfig.RST_PIN
self.dc_pin = epdconfig.DC_PIN
self.busy_pin = epdconfig.BUSY_PIN
self.cs_pin = epdconfig.CS_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
# Hardware reset
def reset(self):
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
epdconfig.digital_write(self.reset_pin, 0)
epdconfig.delay_ms(1)
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
def send_command(self, command):
epdconfig.digital_write(self.dc_pin, 0)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([command])
epdconfig.digital_write(self.cs_pin, 1)
def send_data(self, data):
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([data])
epdconfig.digital_write(self.cs_pin, 1)
# send a lot of data
def send_data2(self, data):
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte2(data)
epdconfig.digital_write(self.cs_pin, 1)
def ReadBusy(self):
logger.debug("e-Paper busy")
while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy
epdconfig.delay_ms(200)
logger.debug("e-Paper busy release")
def TurnOnDisplay(self):
self.send_command(0x22)
self.send_data(0xF7) # 24s # 0xD7 16s Probability refresh bad, probability damage ink screen
self.send_command(0x20) # DISPLAY REFRESH
epdconfig.delay_ms(100) # The delay here is necessary, 200uS at least!!!
self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
def init(self):
if (epdconfig.module_init() != 0):
return -1
self.reset()
self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
self.send_command(0x12) # POWER ON
self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
self.send_command(0x11)
self.send_data(0x01)
self.send_command(0x44) # Set Ram X- address Start / End position
self.send_data(0x00) # XStart, POR = 00h
self.send_data(0x31) # 400/8-1
self.send_command(0x45) # Set Ram Y- address Start / End position
self.send_data(0x0f)
self.send_data(0x01) # 300-1
self.send_data(0x00) # YEnd L
self.send_data(0x00) # YEnd H
self.send_command(0x4e)
self.send_data(0x00)
self.send_command(0x4f)
self.send_data(0x0f)
self.send_data(0x01)
self.send_command(0x91)
self.send_data(0x00)
self.send_command(0xC4) # Set Ram X- address Start / End position
self.send_data(0x31) # XStart, POR = 00h
self.send_data(0x00) # 400/8-1
self.send_command(0xC5) # Set Ram Y- address Start / End position
self.send_data(0x0f)
self.send_data(0x01) # 300-1
self.send_data(0x00) # YEnd L
self.send_data(0x00) # YEnd H
self.send_command(0xCe)
self.send_data(0x31)
self.send_command(0xCf)
self.send_data(0x0f)
self.send_data(0x01)
return 0
def getbuffer(self, image):
# logger.debug("bufsiz = ",int(self.width/8) * self.height)
buf = [0xFF] * (int(self.width / 8) * self.height)
image_monocolor = image.convert('1')
imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load()
# logger.debug("imwidth = %d, imheight = %d",imwidth,imheight)
if (imwidth == self.width and imheight == self.height):
logger.debug("Vertical")
for y in range(imheight):
for x in range(imwidth):
# Set the bits for the column of pixels at the current position.
if pixels[x, y] == 0:
buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
elif (imwidth == self.height and imheight == self.width):
logger.debug("Horizontal")
for y in range(imheight):
for x in range(imwidth):
newx = y
newy = self.height - x - 1
if pixels[x, y] == 0:
buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8))
return buf
def display(self, imageblack, imagered):
buf = [0x00] * int(self.width * self.height / 8)
for i in range(0, int(self.width * self.height / 8)):
buf[i] = ~imagered[i]
Width = int(self.width / 16) + 1
Width1 = int(self.width / 8)
Height = self.height
self.send_command(0x24)
for i in range(Height):
self.send_data2(imageblack[i * Width1: i * Width1 + Width])
self.send_command(0X26)
for i in range(self.height):
self.send_data2(buf[i * Width1: i * Width1 + Width])
self.send_command(0xA4)
for i in range(self.height):
self.send_data2(imageblack[i * Width1 + Width - 1: i * Width1 + Width * 2 - 1])
self.send_command(0xA6)
for i in range(self.height):
self.send_data2(buf[i * Width1 + Width - 1: i * Width1 + Width * 2 - 1])
self.TurnOnDisplay()
def Clear(self):
self.send_command(0x24)
self.send_data2([0xFF] * 13600)
self.send_command(0X26)
self.send_data2([0x00] * 13600)
self.send_command(0xA4)
self.send_data2([0xFF] * 13600)
self.send_command(0xA6)
self.send_data2([0x00] * 13600)
self.TurnOnDisplay()
def sleep(self):
self.send_command(0X10) # deep sleep
self.send_data(0x03)
epdconfig.delay_ms(2000)
epdconfig.module_exit()
### END OF FILE ###

View File

@ -29,7 +29,7 @@
import logging
from .. import epdconfig
from . import epdconfig
# Display resolution
EPD_WIDTH = 800

View File

@ -1,55 +0,0 @@
# board GPIO:
# A: GPIO5
# B: GPIO6
# X: GPIO16
# Y: GPIO20 / GPIO24 (refer to the pimoroni site or pinout.xyz)
# I2S pins for the DAC: GPIO 18 GPIO19 GPIO21
#
# HW datasheet: https://shop.pimoroni.com/products/pirate-audio-line-out?variant=31189750546515
# pinout xyz: https://pinout.xyz/pinout/pirate_audio_line_out
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class PirateAudio(DisplayImpl):
def __init__(self, config):
super(PirateAudio, self).__init__(config, 'pirateaudio')
self._display = None
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)
self._layout['width'] = 240
self._layout['height'] = 240
self._layout['face'] = (0, 40)
self._layout['name'] = (5, 20)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (175, 0)
self._layout['line1'] = [0, 14, 240, 14]
self._layout['line2'] = [0, 108, 240, 108]
self._layout['friend_face'] = (0, 92)
self._layout['friend_name'] = (40, 94)
self._layout['shakes'] = (0, 109)
self._layout['mode'] = (215, 109)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
return self._layout
def initialize(self):
logging.info("Initializing PirateAudio - display only")
from pwnagotchi.ui.hw.libs.pimoroni.pirateaudio.ST7789 import ST7789
self._display = ST7789(0,1,9,13)
def render(self, canvas):
self._display.display(canvas.rotate(90))
def clear(self):
self._display.clear()

View File

@ -1,62 +0,0 @@
# board GPIO:
# Key1 (for2,8"): GPIO17
# Key2 (for2,8"): GPIO22
# Key3 (for2,8"): GPIO23
# Key4 (for2,8"): GPIO27
# Key5 (for2,4"): GPIO27
#
# Key1 (for2,4"): GPIO16
# Key2 (for2,4"): GPIO13
# Key3 (for2,4"): GPIO12
# Key4 (for2,4"): GPIO6
# Key5 (for2,4"): GPIO5
#
# Touch chipset: STMPE610
# HW datasheet 2,8": https://learn.adafruit.com/adafruit-pitft-28-inch-resistive-touchscreen-display-raspberry-pi/overview
# HW datasheet 2,4": https://learn.adafruit.com/adafruit-2-4-pitft-hat-with-resistive-touchscreen-mini-kit/overview
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
import os,time
class Pitft(DisplayImpl):
def __init__(self, config):
super(Pitft, self).__init__(config, 'pitft')
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'] = (0, 36)
self._layout['name'] = (150, 36)
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': (150, 48),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
return self._layout
def initialize(self):
logging.info("Initializing adafruit pitft 320x240 screen")
from pwnagotchi.ui.hw.libs.adafruit.pitft.ILI9341 import ILI9341
self._display = ILI9341(0,0,25,18)
def render(self, canvas):
self._display.display(canvas)
def clear(self):
self._display.clear()

View File

@ -1,54 +0,0 @@
# board GPIO:
# UP: GPIO17
# DOWN: GPIO22
# LEFT: GPIO27
# RIGHT: GPIO23
# CENTER: GPIO4
# A: GPIO5
# B: GPIO6
#
# HW datasheet: https://learn.adafruit.com/adafruit-1-3-color-tft-bonnet-for-raspberry-pi/overview
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class TftBonnet(DisplayImpl):
def __init__(self, config):
super(TftBonnet, self).__init__(config, 'tftbonnet')
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)
self._layout['width'] = 240
self._layout['height'] = 240
self._layout['face'] = (0, 40)
self._layout['name'] = (5, 20)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (175, 0)
self._layout['line1'] = [0, 14, 240, 14]
self._layout['line2'] = [0, 108, 240, 108]
self._layout['friend_face'] = (0, 92)
self._layout['friend_name'] = (40, 94)
self._layout['shakes'] = (0, 109)
self._layout['mode'] = (215, 109)
self._layout['status'] = {
'pos': (125, 20),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
return self._layout
def initialize(self):
logging.info("initializing Adafruit Tft Bonnet")
from pwnagotchi.ui.hw.libs.adafruit.tftbonnet.ST7789 import ST7789
self._display = ST7789(0,0,25,26)
def render(self, canvas):
self._display.display(canvas)
def clear(self):
self._display.clear()

View File

@ -34,7 +34,9 @@ class Waveshare154(DisplayImpl):
logging.info("initializing waveshare v1in54 display")
from pwnagotchi.ui.hw.libs.waveshare.v1in54.epd1in54 import EPD
self._display = EPD()
self._display.init(self._display.lut_partial_update)
self._display.init(0)
self._display.Clear()
self._display.init(1)
self._display.Clear()
def render(self, canvas):

View File

@ -9,7 +9,8 @@ class WaveshareV1(DisplayImpl):
super(WaveshareV1, self).__init__(config, 'waveshare_1')
def layout(self):
fonts.setup(10, 8, 10, 35, 25, 9)
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)
@ -28,19 +29,61 @@ class WaveshareV1(DisplayImpl):
'font': fonts.status_font(fonts.Medium),
'max': 20
}
else:
fonts.setup(10, 8, 10, 25, 25, 9)
self._layout['width'] = 212
self._layout['height'] = 104
self._layout['face'] = (0, 26)
self._layout['name'] = (5, 15)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (28, 0)
self._layout['uptime'] = (147, 0)
self._layout['line1'] = [0, 12, 212, 12]
self._layout['line2'] = [0, 92, 212, 92]
self._layout['friend_face'] = (0, 76)
self._layout['friend_name'] = (40, 78)
self._layout['shakes'] = (0, 93)
self._layout['mode'] = (187, 93)
self._layout['status'] = {
'pos': (91, 15),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
return self._layout
def initialize(self):
if self.config['color'] == 'black':
logging.info("initializing waveshare v2in13_V1 display in monochromatic mode")
from pwnagotchi.ui.hw.libs.waveshare.v2in13_V1.epd2in13 import EPD
self._display = EPD()
self._display.init(self._display.lut_full_update)
self._display.Clear(0xFF)
self._display.init(self._display.lut_partial_update)
elif self.config['color'] == 'fastAndFurious':
logging.info("initializing waveshare v2in13_V1 3-color display in FAST MODE")
logging.info("THIS MAY BE POTENTIALLY DANGEROUS. NO WARRANTY IS PROVIDED")
logging.info("USE THIS DISPLAY IN THIS MODE AT YOUR OWN RISK")
from pwnagotchi.ui.hw.libs.waveshare.v2in13_V1.epd2in13bcFAST import EPD
self._display = EPD()
self._display.init()
self._display.Clear()
else:
logging.info("initializing waveshare v2in13_V1 display 3-color mode")
from pwnagotchi.ui.hw.libs.waveshare.v2in13_V1.epd2in13bc import EPD
self._display = EPD()
self._display.init()
self._display.Clear()
def render(self, canvas):
if self.config['color'] == 'black':
buf = self._display.getbuffer(canvas)
self._display.display(buf)
elif self.config['color'] == 'fastAndFurious':
buf_black = self._display.getbuffer(canvas)
self._display.DisplayPartial(buf_black)
else:
buf_black = self._display.getbuffer(canvas)
self._display.displayBlack(buf_black)
def clear(self):
self._display.Clear(0xff)

View File

@ -9,7 +9,8 @@ class WaveshareV2(DisplayImpl):
super(WaveshareV2, self).__init__(config, 'waveshare_2')
def layout(self):
fonts.setup(10, 8, 10, 35, 25, 9)
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)
@ -28,6 +29,27 @@ class WaveshareV2(DisplayImpl):
'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):

View File

@ -10,6 +10,7 @@ class Waveshare2in13bV3(DisplayImpl):
super(Waveshare2in13bV3, self).__init__(config, 'waveshare2in13b_v3')
def layout(self):
if self.config['color'] == 'black':
fonts.setup(10, 9, 10, 35, 25, 9)
self._layout['width'] = 250
self._layout['height'] = 122
@ -29,6 +30,27 @@ class Waveshare2in13bV3(DisplayImpl):
'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):

View File

@ -10,6 +10,7 @@ class Waveshare213bV4(DisplayImpl):
super(Waveshare213bV4, self).__init__(config, 'waveshare2in13b_v4')
def layout(self):
if self.config['color'] == 'black':
fonts.setup(10, 9, 10, 35, 25, 9)
self._layout['width'] = 250
self._layout['height'] = 122
@ -29,6 +30,27 @@ class Waveshare213bV4(DisplayImpl):
'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):

View File

@ -35,6 +35,8 @@ class Waveshare27inchV2(DisplayImpl):
from pwnagotchi.ui.hw.libs.waveshare.v2in7_v2.epd2in7_V2 import EPD
self._display = EPD()
self._display.init()
# this must have changed by waveshare
# remove the 0xFF(Clear(0xFF)) other wise it errors. can't pass oxff and self
self._display.Clear()
def render(self, canvas):

View File

@ -9,22 +9,22 @@ class Waveshare3in52(DisplayImpl):
super(Waveshare3in52, self).__init__(config, 'waveshare3in52')
def layout(self):
fonts.setup(16, 14, 16, 50, 31, 15)
fonts.setup(16, 14, 16, 100, 31, 15)
self._layout['width'] = 360
self._layout['height'] = 240
self._layout['face'] = (0, 85)
self._layout['name'] = (10, 30)
self._layout['channel'] = (1, 4)
self._layout['aps'] = (45, 4)
self._layout['uptime'] = (250, 4)
self._layout['face'] = (0, 40)
self._layout['name'] = (0, 0)
self._layout['channel'] = (300, 0)
self._layout['aps'] = (0, 220)
self._layout['uptime'] = (120, 0)
self._layout['line1'] = [0, 24, 360, 24]
self._layout['line2'] = [0, 220, 360, 220]
self._layout['friend_face'] = (0, 180)
self._layout['friend_name'] = (0, 170)
self._layout['shakes'] = (1, 223)
self._layout['mode'] = (320, 222)
self._layout['friend_face'] = (0, 195)
self._layout['friend_name'] = (0, 185)
self._layout['shakes'] = (100, 220)
self._layout['mode'] = (0,200)
self._layout['status'] = {
'pos': (185, 50),
'pos': (3, 170),
'font': fonts.status_font(fonts.Small),
'max': 100
}
@ -42,5 +42,6 @@ class Waveshare3in52(DisplayImpl):
self._display.display(buf)
self._display.refresh()
def clear(self):
self._display.Clear()

View File

@ -1,45 +0,0 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare5in79(DisplayImpl):
def __init__(self, config):
super(Waveshare5in79, self).__init__(config, 'waveshare5in79')
def layout(self):
fonts.setup(10, 8, 10, 18, 25, 9)
self._layout['width'] = 792
self._layout['height'] = 272
self._layout['face'] = (0, 43)
self._layout['name'] = (0, 14)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (0, 71)
self._layout['uptime'] = (0, 25)
self._layout['line1'] = [0, 12, 600, 12]
self._layout['line2'] = [0, 116, 600, 116]
self._layout['friend_face'] = (12, 88)
self._layout['friend_name'] = (1, 103)
self._layout['shakes'] = (26, 117)
self._layout['mode'] = (0, 117)
self._layout['status'] = {
'pos': (65, 26),
'font': fonts.status_font(fonts.Small),
'max': 12
}
return self._layout
def initialize(self):
logging.info("initializing waveshare 5.79 inch lcd display")
from pwnagotchi.ui.hw.libs.waveshare.v5in79.epd5in79 import EPD
self._display = EPD()
self._display.init()
self._display.Clear()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display_Partial(buf)
def clear(self):
self._display.Clear()

View File

@ -1,45 +0,0 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshare5in79b(DisplayImpl):
def __init__(self, config):
super(Waveshare5in79b, self).__init__(config, 'waveshare5in79b')
def layout(self):
fonts.setup(10, 8, 10, 18, 25, 9)
self._layout['width'] = 792
self._layout['height'] = 272
self._layout['face'] = (0, 43)
self._layout['name'] = (0, 14)
self._layout['channel'] = (0, 0)
self._layout['aps'] = (0, 71)
self._layout['uptime'] = (0, 25)
self._layout['line1'] = [0, 12, 600, 12]
self._layout['line2'] = [0, 116, 600, 116]
self._layout['friend_face'] = (12, 88)
self._layout['friend_name'] = (1, 103)
self._layout['shakes'] = (26, 117)
self._layout['mode'] = (0, 117)
self._layout['status'] = {
'pos': (65, 26),
'font': fonts.status_font(fonts.Small),
'max': 12
}
return self._layout
def initialize(self):
logging.info("initializing waveshare 5.79b inch lcd display")
from pwnagotchi.ui.hw.libs.waveshare.v5in79b.epd5in79b import EPD
self._display = EPD()
self._display.init()
self._display.Clear()
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.display(buf, None)
def clear(self):
self._display.Clear()

View File

@ -1,59 +0,0 @@
# workinprogress based on the displayhatmini driver
# LCD support OK
# OLED support ongoing
# board GPIO:
# Key1: GPIO4 / pin7
# Key2: GPIO17 / pin11
# Key3: GPIO23 / pin16
# Key4: GPIO24 / pin18
# OLED SDA: GPIO2 / pin3
# OLED SCL: GPIO3 / pin5
# OLED info:
# driver: SSD1315 (I2C)
# resolution: 128x64
# I2C address: 0x3C 0x3D
# HW datasheet: https://www.waveshare.com/wiki/OLED/LCD_HAT_(A)
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class Waveshareoledlcd(DisplayImpl):
def __init__(self, config):
super(Waveshareoledlcd, self).__init__(config, 'waveshareoledlcd')
def layout(self):
fonts.setup(12, 10, 12, 70, 25, 9)
self._layout['width'] = 320
self._layout['height'] = 240
self._layout['face'] = (0, 36)
self._layout['name'] = (150, 36)
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': (150, 48),
'font': fonts.status_font(fonts.Medium),
'max': 20
}
return self._layout
def initialize(self):
logging.info("initializing Waveshare OLED/LCD hat")
from pwnagotchi.ui.hw.libs.waveshare.oledlcd.ST7789 import ST7789
self._display = ST7789(0,0,22,18)
def render(self, canvas):
self._display.display(canvas)
def clear(self):
self._display.clear()

View File

@ -1,47 +0,0 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class WeAct2in9(DisplayImpl):
def __init__(self, config):
super(WeAct2in9, self).__init__(config, 'weact2in9')
def layout(self):
fonts.setup(10, 8, 10, 35, 25, 9)
self._layout['width'] = 296
self._layout['height'] = 128
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 WeAct 2.9 inch display")
from pwnagotchi.ui.hw.libs.weact.v2in9.epd2in9 import EPD
self._display = EPD()
self._display.init()
self._display.Clear(0xFF)
def render(self, canvas):
buf = self._display.getbuffer(canvas)
self._display.displayPartial(buf)
def clear(self):
#pass
self._display.Clear(0xFF)

View File

@ -23,21 +23,7 @@ ROOT = None
class View(object):
def __init__(self, config, impl, state=None):
global ROOT, BLACK, WHITE
self.invert = 0
self._black = 0xFF
self._white = 0x00
if 'invert' in config['ui'] and config['ui']['invert'] == True:
logging.debug("INVERT BLACK/WHITES:" + str(config['ui']['invert']))
self.invert = 1
BLACK = 0x00
WHITE - 0xFF
self._black = 0x00
self._white = 0xFF
global ROOT
# setup faces from the configuration in case the user customized them
faces.load_from_config(config['ui']['faces'])
@ -71,7 +57,7 @@ class View(object):
'face': Text(value=faces.SLEEP, position=(config['ui']['faces']['position_x'], config['ui']['faces']['position_y']), color=BLACK, font=fonts.Huge, png=config['ui']['faces']['png']),
# 'friend_face': Text(value=None, position=self._layout['friend_face'], font=fonts.Bold, color=BLACK),
'friend_name': Text(value=None, position=self._layout['friend_face'], font=fonts.BoldSmall, color=BLACK),
'friend_name': Text(value=None, position=self._layout['friend_name'], font=fonts.BoldSmall, color=BLACK),
'name': Text(value='%s>' % 'pwnagotchi', position=self._layout['name'], color=BLACK, font=fonts.Bold),
@ -112,11 +98,6 @@ class View(object):
self._state.has_element(key)
def add_element(self, key, elem):
if self.invert is 1 and elem.color:
if elem.color == 0xff:
elem.color = 0x00
elif elem.color == 0x00:
elem.color = 0xff
self._state.add_element(key, elem)
def remove_element(self, key):
@ -390,7 +371,7 @@ class View(object):
state = self._state
changes = state.changes(ignore=self._ignore_changes)
if force or len(changes):
self._canvas = Image.new('1', (self._width, self._height), self._white)
self._canvas = Image.new('1', (self._width, self._height), WHITE)
drawer = ImageDraw.Draw(self._canvas)
plugins.on('ui_update', self)

View File

@ -47,6 +47,6 @@ class Server:
formatServerIpAddress = '[::]' if self._address == '::' else self._address
logging.info("web ui available at http://%s:%d/" % (formatServerIpAddress, self._port))
app.run(host=self._address, port=self._port)
app.run(host=self._address, port=self._port, debug=False)
else:
logging.info("could not get ip of usb0, video server not starting")

View File

@ -238,19 +238,47 @@ def load_config(args):
config = merge_config(additional_config, config)
# the very first step is to normalize the display name, so we don't need dozens of if/elif around
# NON E-INK DISPLAYS---------------------------------------------------------------
if config['ui']['display']['type'] in ('inky', 'inkyphat'):
config['ui']['display']['type'] = 'inky'
elif config['ui']['display']['type'] in ('dummy', 'dummydisplay'):
config['ui']['display']['type'] = 'dummydisplay'
elif config['ui']['display']['type'] in ('papirus', 'papi'):
config['ui']['display']['type'] = 'papirus'
elif config['ui']['display']['type'] in 'oledhat':
config['ui']['display']['type'] = 'oledhat'
elif config['ui']['display']['type'] in ('ws_1', 'ws1', 'waveshare_1', 'waveshare1'):
config['ui']['display']['type'] = 'waveshare_1'
elif config['ui']['display']['type'] in ('ws_2', 'ws2', 'waveshare_2', 'waveshare2'):
config['ui']['display']['type'] = 'waveshare_2'
elif config['ui']['display']['type'] in ('ws_3', 'ws3', 'waveshare_3', 'waveshare3'):
config['ui']['display']['type'] = 'waveshare_3'
elif config['ui']['display']['type'] in ('ws_4', 'ws4', 'waveshare_4', 'waveshare4'):
config['ui']['display']['type'] = 'waveshare_4'
elif config['ui']['display']['type'] in ('ws_27inch', 'ws27inch', 'waveshare2in7', 'waveshare_27inch', 'waveshare27inch'):
config['ui']['display']['type'] = 'waveshare2in7'
elif config['ui']['display']['type'] in ('ws_27inchv2', 'waveshare2in7_v2', 'ws27inchv2', 'waveshare_27inchv2', 'waveshare27inchv2'):
config['ui']['display']['type'] = 'waveshare2in7_v2'
elif config['ui']['display']['type'] in ('ws_27inchbv2', 'waveshare2in7b_v2', 'ws27inchbv2', 'waveshare_27inchbv2', 'waveshare27inchbv2'):
config['ui']['display']['type'] = 'waveshare2in7b_v2'
elif config['ui']['display']['type'] in ('ws_29inch', 'waveshare2in9', 'ws29inch', 'waveshare_29inch', 'waveshare29inch'):
config['ui']['display']['type'] = 'waveshare2in9'
elif config['ui']['display']['type'] in ('ws_29inchv2', 'waveshare2in9_v2', 'ws29inchv2', 'waveshare_29inchv2', 'waveshare29inchv2'):
config['ui']['display']['type'] = 'waveshare2in9_v2'
elif config['ui']['display']['type'] in ('ws_29inchbv3', 'waveshare2in9b_v3', 'ws29inchbv3', 'waveshare_29inchbv3', 'waveshare29inchbv3'):
config['ui']['display']['type'] = 'waveshare2in9b_v3'
elif config['ui']['display']['type'] in ('ws_29inchbv4', 'waveshare2in9b_v4', 'ws29inchbv4', 'waveshare_29inchbv4', 'waveshare29inchbv4'):
config['ui']['display']['type'] = 'waveshare2in9b_v4'
elif config['ui']['display']['type'] in 'lcdhat':
config['ui']['display']['type'] = 'lcdhat'
@ -260,80 +288,20 @@ def load_config(args):
elif config['ui']['display']['type'] in ('dfrobot_2', 'df2'):
config['ui']['display']['type'] = 'dfrobot_2'
elif config['ui']['display']['type'] in ('waveshare144lcd', 'ws_144', 'ws144', 'waveshare_144', 'waveshare144'):
config['ui']['display']['type'] = 'waveshare144lcd'
elif config['ui']['display']['type'] in ('spotpear24inch'):
config['ui']['display']['type'] = 'spotpear24inch'
elif config['ui']['display']['type'] in ('displayhatmini'):
config['ui']['display']['type'] = 'displayhatmini'
elif config['ui']['display']['type'] in ('pirateaudio'):
config['ui']['display']['type'] = 'pirateaudio'
elif config['ui']['display']['type'] in ('pitft'):
config['ui']['display']['type'] = 'pitft'
elif config['ui']['display']['type'] in ('tftbonnet'):
config['ui']['display']['type'] = 'tftbonnet'
elif config['ui']['display']['type'] in ('waveshareoledlcd'):
config['ui']['display']['type'] = 'waveshareoledlcd'
elif config['ui']['display']['type'] in ('waveshare35lcd'):
config['ui']['display']['type'] = 'waveshare35lcd'
# E-INK DISPLAYS ------------------------------------------------------------------------
# Adafruit
elif config['ui']['display']['type'] in ('adafruit2in13v3', 'af213v3', 'adafruit_213v3', 'adafruit213inv3'):
config['ui']['display']['type'] = 'adafruit2in13_v3'
# Waveshare
elif config['ui']['display']['type'] in ('waveshare1in02', 'ws1in02', 'ws102', 'waveshare_102', 'waveshare_1in02'):
config['ui']['display']['type'] = 'waveshare1in02'
elif config['ui']['display']['type'] in ('ws_154inch', 'waveshare1in54', 'ws154inch', 'waveshare_154', 'waveshare154'):
elif config['ui']['display']['type'] in ('ws_154inch', 'waveshare1in54', 'ws154inch', 'waveshare_154inch', 'waveshare154inch'):
config['ui']['display']['type'] = 'waveshare1in54'
elif config['ui']['display']['type'] in ('ws_154inchb', 'waveshare1in54b', 'ws154inchb', 'waveshare_154b', 'waveshare154b'):
elif config['ui']['display']['type'] in ('ws_154inchb', 'waveshare1in54b', 'ws154inchb', 'waveshare_154inchb', 'waveshare154inchb'):
config['ui']['display']['type'] = 'waveshare1in54b'
elif config['ui']['display']['type'] in ('waveshare1in54c', 'ws1in54c', 'ws154c', 'waveshare_154c', 'waveshare_1in54c'):
config['ui']['display']['type'] = 'waveshare1in54c'
elif config['ui']['display']['type'] in ('ws_154inchbv2', 'waveshare1in54bv2', 'waveshare1in54b_v2', 'ws154inchbv2', 'waveshare_154bv2', 'waveshare154bv2'):
elif config['ui']['display']['type'] in ('ws_154inchbv2', 'waveshare1in54bv2', 'ws154inchbv2', 'waveshare_154inchbv2', 'waveshare154inchbv2'):
config['ui']['display']['type'] = 'waveshare1in54b_v2'
elif config['ui']['display']['type'] in ('ws_154inchv2', 'waveshare1in54v2', 'ws154inchv2', 'waveshare_154inchv2', 'waveshare154v2', "waveshare1in54_v2"):
elif config['ui']['display']['type'] in ('ws_154inchv2', 'waveshare1in54v2', 'ws154inchv2', 'waveshare_154inchv2', 'waveshare154inchv2', "waveshare1in54_v2"):
config['ui']['display']['type'] = 'waveshare1in54_v2'
elif config['ui']['display']['type'] in ('waveshare1in64g', 'ws1in64g', 'ws164g', 'waveshare_164g', 'waveshare_1in64g'):
config['ui']['display']['type'] = 'waveshare1in64g'
elif config['ui']['display']['type'] in ('ws_1', 'ws1', 'waveshare_1', 'waveshare1', 'waveshare2in13'):
config['ui']['display']['type'] = 'waveshare_1'
elif config['ui']['display']['type'] in ('ws_2', 'ws2', 'waveshare_2', 'waveshare2', 'waveshare2in13v2'):
config['ui']['display']['type'] = 'waveshare_2'
elif config['ui']['display']['type'] in ('ws_3', 'ws3', 'waveshare_3', 'waveshare3', 'waveshare2in13v3'):
config['ui']['display']['type'] = 'waveshare_3'
elif config['ui']['display']['type'] in ('ws_4', 'ws4', 'waveshare_4', 'waveshare4', 'waveshare2in13v4'):
config['ui']['display']['type'] = 'waveshare_4'
elif config['ui']['display']['type'] in ('waveshare2in13b_v3', 'waveshare2in13b_v3', 'ws213bv3', 'waveshare_213bv3', 'waveshare213inb_v3'):
config['ui']['display']['type'] = 'waveshare2in13b_v3'
elif config['ui']['display']['type'] in ('ws_213bv4', 'waveshare2in13b_v4', 'ws213bv4', 'waveshare_213bv4', 'waveshare213inb_v4'):
config['ui']['display']['type'] = 'waveshare2in13b_v4'
elif config['ui']['display']['type'] in ('ws_213bc', 'ws213bc', 'waveshare2in13bc', 'waveshare_213bc', 'waveshare213bc'):
config['ui']['display']['type'] = 'waveshare2in13bc'
elif config['ui']['display']['type'] in ('waveshare144lcd', 'ws_144inch', 'ws144inch', 'waveshare_144inch', 'waveshare144inch'):
config['ui']['display']['type'] = 'waveshare144lcd'
elif config['ui']['display']['type'] in ('ws_213d', 'ws213d', 'waveshare2in13d', 'waveshare_213d', 'waveshare213d'):
config['ui']['display']['type'] = 'waveshare2in13d'
@ -341,130 +309,126 @@ def load_config(args):
elif config['ui']['display']['type'] in ('ws_213g', 'waveshare2in13g', 'waveshare213g', 'ws213g', 'waveshare_213g'):
config['ui']['display']['type'] = 'waveshare2in13g'
elif config['ui']['display']['type'] in ('ws_2in36g', 'waveshare2in36g', 'waveshare236g', 'ws236g', 'waveshare_236g'):
config['ui']['display']['type'] = 'waveshare2in36g'
elif config['ui']['display']['type'] in ('ws_213bc', 'ws213bc', 'waveshare2in13bc', 'waveshare_213bc', 'waveshare213bc'):
config['ui']['display']['type'] = 'waveshare2in13bc'
elif config['ui']['display']['type'] in ('ws_2in66', 'waveshare2in66', 'waveshare266', 'ws266', 'waveshare_266'):
config['ui']['display']['type'] = 'waveshare2in66'
elif config['ui']['display']['type'] in ('ws_213bv4', 'waveshare2in13b_v4', 'ws213bv4', 'waveshare_213bv4', 'waveshare213inb_v4'):
config['ui']['display']['type'] = 'waveshare2in13b_v4'
elif config['ui']['display']['type'] in ('ws_2in66b', 'waveshare2in66b', 'waveshare266b', 'ws266b', 'waveshare_266b'):
config['ui']['display']['type'] = 'waveshare2in66b'
elif config['ui']['display']['type'] in 'spotpear24inch':
config['ui']['display']['type'] = 'spotpear24inch'
elif config['ui']['display']['type'] in ('ws_2in66g', 'waveshare2in66g', 'waveshare266g', 'ws266g', 'waveshare_266g'):
config['ui']['display']['type'] = 'waveshare2in66g'
elif config['ui']['display']['type'] in 'displayhatmini':
config['ui']['display']['type'] = 'displayhatmini'
elif config['ui']['display']['type'] in ('ws_27inch', 'ws27inch', 'waveshare2in7', 'waveshare_27inch', 'waveshare27'):
config['ui']['display']['type'] = 'waveshare2in7'
elif config['ui']['display']['type'] in 'waveshare35lcd':
config['ui']['display']['type'] = 'waveshare35lcd'
elif config['ui']['display']['type'] in ('ws_2in7v2', 'waveshare2in7_v2', 'waveshare2in7v2', 'ws27inchv2', 'waveshare_27v2', 'waveshare27v2'):
config['ui']['display']['type'] = 'waveshare2in7_v2'
elif config['ui']['display']['type'] in 'waveshare1in54c':
config['ui']['display']['type'] = 'waveshare1in54c'
elif config['ui']['display']['type'] in ('ws_2in7bv2', 'waveshare2in7b_v2', 'waveshare2in7bv2', 'ws27inchbv2', 'waveshare_27bv2', 'waveshare27bv2'):
config['ui']['display']['type'] = 'waveshare2in7b_v2'
elif config['ui']['display']['type'] in 'waveshare1in64g':
config['ui']['display']['type'] = 'waveshare1in64g'
elif config['ui']['display']['type'] in ('ws_2in9', 'waveshare2in9', 'ws29inch', 'waveshare_29inch', 'waveshare29inch'):
config['ui']['display']['type'] = 'waveshare2in9'
elif config['ui']['display']['type'] in 'waveshare1in02':
config['ui']['display']['type'] = 'waveshare1in02'
elif config['ui']['display']['type'] in ('ws_2in9bc', 'waveshare2in9bc', 'ws2in9bc', 'ws29bc', 'waveshare_29bc', 'waveshare_2in9bc'):
elif config['ui']['display']['type'] in 'waveshare2in9bc':
config['ui']['display']['type'] = 'waveshare2in9bc'
elif config['ui']['display']['type'] in ('ws_2in9d', 'waveshare2in9d', 'ws2in9d', 'ws29d', 'waveshare_29d', 'waveshare_2in9d'):
elif config['ui']['display']['type'] in 'waveshare2in9d':
config['ui']['display']['type'] = 'waveshare2in9d'
elif config['ui']['display']['type'] in ('ws_2in9v2', 'waveshare2in9_v2', 'waveshare2in9v2', 'ws2in9v2', 'waveshare_29v2', 'waveshare29v2'):
config['ui']['display']['type'] = 'waveshare2in9_v2'
elif config['ui']['display']['type'] in 'waveshare2in13b_v3':
config['ui']['display']['type'] = 'waveshare2in13b_v3'
elif config['ui']['display']['type'] in ('ws_2in9bv3', 'waveshare2in9b_v3', 'waveshare2in9bv3', 'ws2in9bv3', 'waveshare_29bv3', 'waveshare29bv3'):
config['ui']['display']['type'] = 'waveshare2in9b_v3'
elif config['ui']['display']['type'] in 'waveshare2in36g':
config['ui']['display']['type'] = 'waveshare2in36g'
elif config['ui']['display']['type'] in ('ws_2in9bv4', 'waveshare2in9b_v4', 'waveshare2in9bv4', 'ws2in9bv4', 'waveshare_29bv4', 'waveshare29bv4'):
config['ui']['display']['type'] = 'waveshare2in9b_v4'
elif config['ui']['display']['type'] in 'waveshare2in66':
config['ui']['display']['type'] = 'waveshare2in66'
elif config['ui']['display']['type'] in ('ws_3in0g', 'waveshare3in0g', 'ws3in0g', 'waveshare_30g', 'waveshare30g'):
elif config['ui']['display']['type'] in 'waveshare2in66b':
config['ui']['display']['type'] = 'waveshare2in66b'
elif config['ui']['display']['type'] in 'waveshare2in66g':
config['ui']['display']['type'] = 'waveshare2in66g'
elif config['ui']['display']['type'] in 'waveshare3in0g':
config['ui']['display']['type'] = 'waveshare3in0g'
elif config['ui']['display']['type'] in ('ws_3in7', 'waveshare3in7', 'ws3in7', 'waveshare_37', 'waveshare37'):
elif config['ui']['display']['type'] in 'waveshare3in7':
config['ui']['display']['type'] = 'waveshare3in7'
elif config['ui']['display']['type'] in ('ws_3in52', 'waveshare3in52', 'ws3in52', 'waveshare_352', 'waveshare352'):
elif config['ui']['display']['type'] in 'waveshare3in52':
config['ui']['display']['type'] = 'waveshare3in52'
elif config['ui']['display']['type'] in ('ws_4in01f', 'waveshare4in01f', 'ws4in01f', 'waveshare_401f', 'waveshare401f'):
elif config['ui']['display']['type'] in 'waveshare4in01f':
config['ui']['display']['type'] = 'waveshare4in01f'
elif config['ui']['display']['type'] in ('ws_4in2', 'waveshare4in2', 'ws4in2', 'waveshare_42', 'waveshare42'):
elif config['ui']['display']['type'] in 'waveshare4in2':
config['ui']['display']['type'] = 'waveshare4in2'
elif config['ui']['display']['type'] in ('ws_4in2v2', 'waveshare4in2v2', 'ws4in2v2', 'waveshare_42v2', 'waveshare42v2'):
elif config['ui']['display']['type'] in 'waveshare4in2_v2':
config['ui']['display']['type'] = 'waveshare4in2_v2'
elif config['ui']['display']['type'] in ('ws_4in2bv2', 'waveshare4in2bv2', 'ws4in2bv2', 'waveshare_42bv2', 'waveshare42bv2'):
elif config['ui']['display']['type'] in 'waveshare4in2b_v2':
config['ui']['display']['type'] = 'waveshare4in2b_v2'
elif config['ui']['display']['type'] in ('ws_4in2bc', 'waveshare4in2bc', 'ws4in2bc', 'waveshare_42bc', 'waveshare42bc'):
elif config['ui']['display']['type'] in 'waveshare4in2bc':
config['ui']['display']['type'] = 'waveshare4in2bc'
elif config['ui']['display']['type'] in ('ws_4in26', 'waveshare4in26', 'ws4in26', 'waveshare_426', 'waveshare426'):
elif config['ui']['display']['type'] in 'waveshare4in26':
config['ui']['display']['type'] = 'waveshare4in26'
elif config['ui']['display']['type'] in ('ws_4in37g', 'waveshare4in37g', 'ws4in37g', 'waveshare_37g', 'waveshare437g'):
elif config['ui']['display']['type'] in 'waveshare4in37g':
config['ui']['display']['type'] = 'waveshare4in37g'
elif config['ui']['display']['type'] in ('ws_5in65f', 'waveshare5in65f', 'ws5in65f', 'waveshare_565f', 'waveshare565f'):
elif config['ui']['display']['type'] in 'waveshare5in65f':
config['ui']['display']['type'] = 'waveshare5in65f'
elif config['ui']['display']['type'] in ('ws_5in79', 'waveshare5in79', 'ws5in79', 'waveshare_579', 'waveshare579'):
config['ui']['display']['type'] = 'waveshare5in79'
elif config['ui']['display']['type'] in ('ws_5in79b', 'waveshare5in79b', 'ws5in79b', 'waveshare_579b', 'waveshare579b'):
config['ui']['display']['type'] = 'waveshare5in79b'
elif config['ui']['display']['type'] in ('ws_5in83', 'waveshare5in83', 'ws5in83', 'waveshare_583', 'waveshare583'):
elif config['ui']['display']['type'] in 'waveshare5in83':
config['ui']['display']['type'] = 'waveshare5in83'
elif config['ui']['display']['type'] in ('ws_5in83v2', 'waveshare5in83v2', 'ws5in83v2', 'waveshare_583v2', 'waveshare583v2'):
elif config['ui']['display']['type'] in 'waveshare5in83_v2':
config['ui']['display']['type'] = 'waveshare5in83_v2'
elif config['ui']['display']['type'] in ('ws_5in83bv2', 'waveshare5in83bv2', 'ws5in83bv2', 'waveshare_583bv2', 'waveshare583bv2'):
elif config['ui']['display']['type'] in 'waveshare5in83b_v2':
config['ui']['display']['type'] = 'waveshare5in83b_v2'
elif config['ui']['display']['type'] in ('ws_5in83bc', 'waveshare5in83bc', 'ws5in83bc', 'waveshare_583bc', 'waveshare583bc'):
elif config['ui']['display']['type'] in 'waveshare5in83bc':
config['ui']['display']['type'] = 'waveshare5in83bc'
elif config['ui']['display']['type'] in ('ws_7in3f', 'waveshare7in3f', 'ws7in3f', 'waveshare_73f', 'waveshare73f'):
elif config['ui']['display']['type'] in 'waveshare7in3f':
config['ui']['display']['type'] = 'waveshare7in3f'
elif config['ui']['display']['type'] in ('ws_7in3g', 'waveshare7in3g', 'ws7in3g', 'waveshare_73g', 'waveshare73g'):
elif config['ui']['display']['type'] in 'waveshare7in3g':
config['ui']['display']['type'] = 'waveshare7in3g'
elif config['ui']['display']['type'] in ('ws_7in5', 'waveshare7in5', 'ws7in5', 'waveshare_75', 'waveshare75'):
elif config['ui']['display']['type'] in 'waveshare7in5':
config['ui']['display']['type'] = 'waveshare7in5'
elif config['ui']['display']['type'] in ('ws_7in5hd', 'waveshare7in5hd', 'ws7in5hd', 'waveshare_75hd', 'waveshare75hd'):
elif config['ui']['display']['type'] in 'waveshare7in5_HD':
config['ui']['display']['type'] = 'waveshare7in5_HD'
elif config['ui']['display']['type'] in ('ws_7in5v2', 'waveshare7in5v2', 'ws7in5v2', 'waveshare_75v2', 'waveshare75v2'):
elif config['ui']['display']['type'] in 'waveshare7in5_v2':
config['ui']['display']['type'] = 'waveshare7in5_v2'
elif config['ui']['display']['type'] in ('ws_7in5bhd', 'waveshare7in5bhd', 'ws7in5bhd', 'waveshare_75bhd', 'waveshare75bhd'):
elif config['ui']['display']['type'] in 'waveshare7in5b_HD':
config['ui']['display']['type'] = 'waveshare7in5b_HD'
elif config['ui']['display']['type'] in ('ws_7in5bv2', 'waveshare7in5bv2', 'ws7in5bv2', 'waveshare_75bv2', 'waveshare75bv2'):
elif config['ui']['display']['type'] in 'waveshare7in5b_v2':
config['ui']['display']['type'] = 'waveshare7in5b_v2'
elif config['ui']['display']['type'] in ('ws_7in5bc', 'waveshare7in5bc', 'ws7in5bc', 'waveshare_75bc', 'waveshare75bc'):
elif config['ui']['display']['type'] in 'waveshare7in5bc':
config['ui']['display']['type'] = 'waveshare7in5bc'
elif config['ui']['display']['type'] in ('ws_13in3k', 'waveshare13in3k', 'ws13in3k', 'waveshare_133k', 'waveshare133k'):
elif config['ui']['display']['type'] in 'waveshare13in3k':
config['ui']['display']['type'] = 'waveshare13in3k'
# WeAct e-ink
elif config['ui']['display']['type'] in ('weact2in9', 'weact29in'):
config['ui']['display']['type'] = 'weact2in9'
else:
logging.debug("using dummy display, as your display type is unsupported")
config['ui']['display']['type'] = 'dummydisplay'
print("unsupported display type %s" % config['ui']['display']['type'])
sys.exit(1)
return config

View File

@ -1,62 +0,0 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[project]
name = "pwnagotchi"
dynamic = ["version"]
dependencies = [
"Pillow",
"PyYAML",
"RPi.GPIO",
"dbus-python",
"file-read-backwards",
"flask",
"flask-cors",
"flask-wtf",
"gast",
"gpiozero",
"gym",
"inky",
"pycryptodome",
"pydrive2",
"python-dateutil",
"requests",
"rpi_hardware_pwm",
"scapy",
"shimmy",
"smbus2",
"spidev",
"stable_baselines3",
"toml",
"torch",
"torchvision",
"tweepy",
"websockets",
]
requires-python = ">=3.9"
authors = [
{name = "Evilsocket", email = "evilsocket@gmail.com"},
{name = "Jayofelony", email = "oudshoorn.jeroen@gmail.com"}
]
maintainers = [
{name = "Jayofelony", email = "oudshoorn.jeroen@gmail.com"}
]
description = "(⌐■_■) - Deep Reinforcement Learning instrumenting bettercap for WiFI pwning."
readme = "README.md"
license = {file = "LICENSE.md"}
classifiers = [
'Programming Language :: Python :: 3',
'Development Status :: 5 - Production/Stable',
'License :: OSI Approved :: GNU General Public License (GPL)',
'Environment :: Console',
]
[project.urls]
Homepage = "https://pwnagotchi.org/"
Documentation = "https://pwnagotchi.org/"
Repository = "https://github.com/jayofelony/pwnagotchi.git"
Issues = "https://github.com/jayofelony/pwnagotchi/issues"
[project.scripts]
pwnagotchi_cli = "bin.pwnagotchi:pwnagotchi_cli"

View File

@ -1,5 +1,5 @@
gym
shimmy
shimmy; platform_machine!="armv6l"
pycryptodome
requests
PyYAML
@ -18,9 +18,17 @@ dbus-python
toml
python-dateutil
websockets
torch
torchvision
stable_baselines3
RPi.GPIO
rpi_hardware_pwm
torch; platform_machine=="aarch64"
torch>=2.0.1; platform_machine!="aarch64"
torchvision; platform_machine=="aarch64"
torchvision>=0.15.2; platform_machine!="aarch64"
stable_baselines3==1.8.0; platform_machine=="armv6l"
stable_baselines3; platform_machine!="armv6l"
RPi.GPIO; platform_release!="6.1.31-sun50iw9"
OPi.GPIO; platform_release=="6.1.31-sun50iw9"
rpi_hardware_pwm; platform_release!="6.1.31-sun50iw9"
pydrive2

View File

@ -16,10 +16,7 @@ log = logging.getLogger(__name__)
def install_file(source_filename, dest_filename):
# do not overwrite network configuration if it exists already
# https://github.com/evilsocket/pwnagotchi/issues/483
if dest_filename.startswith('/etc/network/interfaces.d/') and os.path.exists(dest_filename):
log.info(f"{dest_filename} exists, skipping ...")
return
elif dest_filename.startswith('/root/') and os.path.exists(dest_filename):
if dest_filename.startswith('/etc/network/interfaces.d/') and dest_filename.startswith('/root/') and os.path.exists(dest_filename):
log.info(f"{dest_filename} exists, skipping ...")
return
@ -34,11 +31,6 @@ def install_file(source_filename, dest_filename):
def install_system_files():
f = open("apt_packages.txt", "r")
for x in f:
os.system(f"apt-get install -y {x}")
f.close()
setup_path = os.path.dirname(__file__)
if platform.machine().startswith('arm'):
data_path = os.path.join(setup_path, "builder/data/32bit")
@ -106,7 +98,7 @@ setup(name='pwnagotchi',
"install": CustomInstall,
},
scripts=['bin/pwnagotchi'],
package_data={'pwnagotchi': ['defaults.toml', 'pwnagotchi/defaults.toml', 'locale/*/LC_MESSAGES/*.mo']},
package_data={'pwnagotchi': ['defaults.yml', 'pwnagotchi/defaults.yml', 'locale/*/LC_MESSAGES/*.mo']},
include_package_data=True,
packages=find_packages(),
classifiers=[