Merge branch 'jayofelony:master' into master

This commit is contained in:
Rai
2024-06-12 20:40:08 +10:00
committed by GitHub
51 changed files with 2345 additions and 1083 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

19
.idea/deployment.xml generated
View File

@ -1,23 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PublishConfigData" serverName="pwnagotchi" filePermissions="493" folderPermissions="493" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false">
<component name="PublishConfigData" filePermissions="493" folderPermissions="493" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false">
<option name="confirmBeforeUploading" value="false" />
<serverData>
<paths name="pwnagotchi">
<serverdata>
<mappings>
<mapping deploy="/usr/local/lib/python3.11/dist-packages/pwnagotchi" local="$PROJECT_DIR$/pwnagotchi" web="/" />
<mapping deploy="/usr/local/bin" local="$PROJECT_DIR$/bin" />
<mapping local="" />
</mappings>
<excludedPaths>
<excludedPath local="true" path="$PROJECT_DIR$/venv" />
<excludedPath local="true" path="$PROJECT_DIR$/pwnagotchi.egg-info" />
<excludedPath local="true" path="$PROJECT_DIR$/dist" />
<excludedPath local="true" path="$PROJECT_DIR$/builder/packer-builder-arm" />
</excludedPaths>
</serverdata>
</paths>
</serverData>
</component>
</project>

View File

@ -1,4 +1,4 @@
PACKER_VERSION := 1.10.1
PACKER_VERSION := 1.11.0
PWN_HOSTNAME := pwnagotchi
PWN_VERSION := $(shell cut -d"'" -f2 < pwnagotchi/_version.py)
@ -26,7 +26,7 @@ UNSHARE := $(UNSHARE) --uts
endif
# sudo apt-get install qemu-user-static qemu-utils
all: clean packer image
all: packer image
update_langs:
@for lang in pwnagotchi/locale/*/; do\
@ -40,22 +40,23 @@ compile_langs:
./scripts/language.sh compile $$(basename $$lang); \
done
packer: clean
packer:
curl https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_amd64.zip -o /tmp/packer.zip
unzip /tmp/packer.zip -d /tmp
unzip -o /tmp/packer.zip -d /tmp
sudo mv /tmp/packer /usr/bin/packer
image: clean packer
image: 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
32bit: 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 raspberrypi32.json.pkr.hcl && QEMU_CPU=arm1176 sudo -E $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" raspberrypi32.json.pkr.hcl
bookworm: clean packer
64bit: 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
clean:
- rm -rf /tmp/packer*
- rm -rf /tmp/LICENSE.txt

View File

@ -5,6 +5,8 @@ This is the main source for all forks:
[GH Sponsor](https://github.com/sponsors/jayofelony)
**Proudly partnering with [PiSugar](https://www.pisugar.com)!!**
---
[Pwnagotchi](https://pwnagotchi.ai/) is an [A2C](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752)-based "AI" leveraging [bettercap](https://www.bettercap.org/) that learns from its surrounding Wi-Fi environment to maximize the crackable WPA key material it captures (either passively, or by performing authentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/),

View File

@ -60,7 +60,7 @@ def pwnagotchi_cli():
channels = agent.get_access_points_by_channel()
# for each channel
for ch, aps in channels:
time.sleep(0.2)
time.sleep(0.5)
agent.set_channel(ch)
if not agent.is_stale() and agent.any_activity():
@ -237,7 +237,7 @@ def pwnagotchi_cli():
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"
"https://github.com/jayofelony/pwnagotchi/blob/master/pwnagotchi/utils.py#L240-L501\n\n"
"Display type: ")
if pwn_display_type != "":
f.write(f"ui.display.type = \"{pwn_display_type}\"\n")

View File

@ -25,7 +25,7 @@ source "arm" "rpi64-pwnagotchi" {
file_checksum_type = "sha256"
file_target_extension = "xz"
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
image_path = "../pwnagotchi-64bit.img"
image_path = "../../../pwnagotchi-64bit.img"
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P"
image_build_method = "resize"
@ -50,12 +50,12 @@ 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_lite_armhf/images/raspios_lite_armhf-2024-03-15/2024-03-15-raspios-bookworm-armhf-lite.img.xz.sha256"
file_urls = ["https://downloads.raspberrypi.com/raspios_lite_armhf/images/raspios_lite_armhf-2024-03-15/2024-03-15-raspios-bookworm-armhf-lite.img.xz"]
file_checksum_type = "sha256"
file_target_extension = "xz"
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
image_path = "../pwnagotchi-32bit.img"
image_path = "../../../pwnagotchi-32bit.img"
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
image_build_method = "resize"
@ -67,7 +67,7 @@ source "arm" "rpi32-pwnagotchi" {
start_sector = "8192"
filesystem = "fat"
size = "256M"
mountpoint = "/boot"
mountpoint = "/boot/firmware"
}
image_partitions {
name = "root"
@ -101,6 +101,13 @@ build {
provisioner "shell" {
inline = ["chmod +x /usr/bin/*"]
}
provisioner "shell" {
inline = ["mkdir -p /usr/local/src/pwnagotchi"]
}
provisioner "file" {
destination = "/usr/local/src/pwnagotchi/"
source = "../"
}
provisioner "file" {
destination = "/etc/systemd/system/"
@ -142,10 +149,16 @@ build {
"data/32bit/usr/bin/pwnlib",
]
}
provisioner "shell" {
inline = ["mkdir -p /usr/local/src/pwnagotchi"]
}
provisioner "file" {
destination = "/usr/local/src/pwnagotchi/"
source = "../"
}
provisioner "shell" {
inline = ["chmod +x /usr/bin/*"]
}
provisioner "file" {
destination = "/etc/systemd/system/"
sources = [
@ -167,7 +180,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_dir = "extras/"
playbook_file = "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

@ -0,0 +1,5 @@
[main]
plugins=keyfile,ifupdown
[ifupdown]
managed=true

View File

@ -1,62 +0,0 @@
# A sample configuration for dhcpcd.
# See dhcpcd.conf(5) for details.
# Allow users of this group to interact with dhcpcd via the control socket.
#controlgroup wheel
# Inform the DHCP server of our hostname for DDNS.
hostname
# Use the hardware address of the interface for the Client ID.
clientid
# or
# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361.
# Some non-RFC compliant DHCP servers do not reply with this set.
# In this case, comment out duid and enable clientid above.
#duid
# Persist interface configuration when dhcpcd exits.
persistent
# Rapid commit support.
# Safe to enable by default because it requires the equivalent option set
# on the server to actually work.
option rapid_commit
# A list of options to request from the DHCP server.
option domain_name_servers, domain_name, domain_search, host_name
option classless_static_routes
# Respect the network MTU. This is applied to DHCP routes.
option interface_mtu
# Most distributions have NTP support.
#option ntp_servers
# A ServerID is required by RFC2131.
require dhcp_server_identifier
# Generate SLAAC address using the Hardware Address of the interface
#slaac hwaddr
# OR generate Stable Private IPv6 Addresses based from the DUID
slaac private
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# !! DO NOT EDIT THESE LINES BELOW PLEASE !!
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# static IP configuration:
denyinterfaces wlan0
interface eth0
static domain_name_servers=8.8.8.8 1.1.1.1
metric 201
interface usb0
static ip_address=10.0.0.2/24
static routers=10.0.0.1
static domain_name_servers=10.0.0.1 8.8.8.8 1.1.1.1
metric 202
interface bnep0
static domain_name_servers=8.8.8.8 1.1.1.1
metric 203

View File

@ -0,0 +1,2 @@
allow-hotplug eth0
iface eth0 inet dhcp

View File

@ -0,0 +1,2 @@
auto lo
iface lo inet loopback

View File

@ -0,0 +1,8 @@
allow-hotplug usb0
iface usb0 inet static
address 10.0.0.2
netmask 255.255.255.0
network 10.0.0.0
broadcast 10.0.0.255
gateway 10.0.0.1
metric 101

View File

@ -0,0 +1,2 @@
allow-hotplug wlan0
iface wlan0 inet static

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

@ -8,7 +8,6 @@ After=pwngrid-peer.service
Type=simple
WorkingDirectory=~
ExecStart=/usr/bin/pwnagotchi-launcher
ExecStopPost=/usr/bin/bash -c "if egrep -qi 'personality.clear_on_exit[ =]*true' /etc/pwnagotchi/config.toml ; then /usr/local/bin/pwnagotchi --clear; fi"
Restart=always
RestartSec=30
TasksMax=infinity

View File

@ -1,40 +0,0 @@
# Install nexmon to fix wireless scanning (takes 2.5G of space)
- name: clone nexmon repository
git:
repo: https://github.com/DrSchottky/nexmon.git
dest: /usr/local/src/nexmon
- name: make firmware
shell: "source ./setup_env.sh && make"
args:
executable: /bin/bash
chdir: /usr/local/src/nexmon/
- name: "make firmware patch ({{ item.name }})"
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/{{ item.patch }}/nexmon/ && make"
args:
executable: /bin/bash
chdir: /usr/local/src/nexmon/
environment:
QEMU_UNAME: "{{ item.kernel }}"
ARCHFLAGS: "{{ item.arch_flags }}"
- name: "install new firmware ({{ item.name }})"
copy:
src: "/usr/local/src/nexmon/patches/{{ item.patch }}/nexmon/{{ item.firmware }}"
dest: "/usr/lib/firmware/brcm/{{ item.firmware }}"
follow: true
environment:
QEMU_UNAME: "{{ item.kernel }}"
ARCHFLAGS: "{{ item.arch_flags }}"
- name: backup original driver
command: "mv /usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
- name: copy modified driver
copy:
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_6.1.y-nexmon/brcmfmac.ko"
dest: "/usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
- name : load brcmfmac drivers
command: "/sbin/depmod -a {{ item.kernel }}"

View File

@ -1,8 +1,8 @@
packer {
required_plugins {
arm = {
version = "1.0.0"
source = "github.com/cdecoux/builder-arm"
version = ">=1.0.0"
source = "github.com/michalfita/cross"
}
ansible = {
source = "github.com/hashicorp/ansible"
@ -20,12 +20,12 @@ 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_lite_armhf/images/raspios_lite_armhf-2024-03-15/2024-03-15-raspios-bookworm-armhf-lite.img.xz.sha256"
file_urls = ["https://downloads.raspberrypi.com/raspios_lite_armhf/images/raspios_lite_armhf-2024-03-15/2024-03-15-raspios-bookworm-armhf-lite.img.xz"]
file_checksum_type = "sha256"
file_target_extension = "xz"
file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"]
image_path = "../../pwnagotchi-32bit.img"
image_path = "../../../pwnagotchi-32bit.img"
qemu_binary_source_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/arm-binfmt-P"
image_build_method = "resize"
@ -37,7 +37,7 @@ source "arm" "rpi32-pwnagotchi" {
start_sector = "8192"
filesystem = "fat"
size = "256M"
mountpoint = "/boot"
mountpoint = "/boot/firmware"
}
image_partitions {
name = "root"
@ -51,6 +51,9 @@ source "arm" "rpi32-pwnagotchi" {
build {
name = "Raspberry Pi 32 Pwnagotchi"
sources = ["source.arm.rpi32-pwnagotchi"]
provisioner "shell" {
inline = ["uname -m"]
}
provisioner "file" {
destination = "/usr/bin/"
sources = [
@ -66,7 +69,13 @@ build {
provisioner "shell" {
inline = ["chmod +x /usr/bin/*"]
}
provisioner "shell" {
inline = ["mkdir -p /usr/local/src/pwnagotchi"]
}
provisioner "file" {
destination = "/usr/local/src/pwnagotchi/"
source = "../"
}
provisioner "file" {
destination = "/etc/systemd/system/"
sources = [
@ -88,7 +97,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_dir = "extras/"
playbook_file = "raspberrypi32.yml"
}
}

View File

@ -4,46 +4,15 @@
gather_facts: true
become: true
vars:
boards:
- {
kernel: "6.1.21+",
name: "PiZeroW",
firmware: "brcmfmac43430-sdio.bin",
patch: "bcm43430a1/7_45_41_46",
cpu: arm1176,
arch_flags: "-arch armv6l"
}
- {
kernel: "6.1.21-v7+",
name: "PiZero2W",
firmware: "brcmfmac43436-sdio.bin",
patch: "bcm43436b0/9_88_4_65",
cpu: any, #cortex-a53
arch_flags: "-arch armv7l"
}
- {
kernel: "6.1.21-v7l+",
name: "Pi4b_32",
firmware: "brcmfmac43455-sdio.bin",
patch: "bcm43455c0/7_45_206",
cpu: any, #cortex-a72
arch_flags: "-arch armv7l"
}
kernel:
min: "6.1"
full: "6.1.21+"
full_2w: "6.1.21-v7+"
full_4b: "6.1.21-v7l+"
arch: "v6l"
min: "6.6"
full: "6.6.31+rpt-rpi-v6"
pwnagotchi:
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"
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi', true) }}"
services:
enable:
- bettercap.service
- bluetooth.service
- dphys-swapfile.service
- fstrim.timer
- pwnagotchi.service
- pwngrid-peer.service
@ -52,25 +21,24 @@
- apt-daily-upgrade.timer
- apt-daily.service
- apt-daily.timer
- bluetooth.service
- ifup@wlan0.service
- triggerhappy.service
- wpa_supplicant.service
packages:
caplets:
source: "https://github.com/jayofelony/caplets.git"
branch: "lite" # or master
bettercap:
source: "https://github.com/jayofelony/bettercap.git"
url: "https://github.com/jayofelony/bettercap/releases/download/2.32.2/bettercap-2.32.2-armhf.zip"
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
opwngrid:
branch: "lite" # or master
pwngrid:
source: "https://github.com/jayofelony/pwngrid.git"
url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.7/pwngrid-1.10.7-armhf.zip"
torch:
wheel: "torch-2.1.0a0+gitunknown-cp39-cp39-linux_armv6l.whl"
url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/v1.0.0/torch-2.1.0a0+gitunknown-cp39-cp39-linux_armv6l.whl"
wheel: "torch-2.1.0a0+gita8e7c98-cp311-cp311-linux_armv6l.whl"
url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/py0torch-bookworm-2024-05/torch-2.1.0a0+gita8e7c98-cp311-cp311-linux_armv6l.whl"
torchvision:
wheel: "torchvision-0.16.0a0-cp39-cp39-linux_armv6l.whl"
url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/v1.0.0/torchvision-0.16.0a0-cp39-cp39-linux_armv6l.whl"
wheel: "torchvision-0.16.0+fbb4cc5-cp311-cp311-linux_armv6l.whl"
url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/py0torch-bookworm-2024-05/torchvision-0.16.0+fbb4cc5-cp311-cp311-linux_armv6l.whl"
apt:
downgrade:
- libpcap-dev_1.9.1-4_armhf.deb
@ -85,109 +53,145 @@
- firmware-realtek
- libpcap-dev
- libpcap0.8
- libpcap0.8-dev
- libpcap0.8-dbg
- libpcap0.8-dev
remove:
- avahi-daemon
- nfs-common
- triggerhappy
- wpasupplicant
install:
- aircrack-ng
- autoconf
- bc
- bison
- bluez
- bluez-tools
- build-essential
- curl
- dkms
- dphys-swapfile
- espeak-ng
- evtest
- fbi
- firmware-atheros
- firmware-brcm80211
- firmware-libertas
- firmware-misc-nonfree
- firmware-realtek
- flex
- fonts-dejavu
- fonts-dejavu-core
- fonts-dejavu-extra
- fonts-freefont-ttf
- g++
- gawk
- gcc-arm-none-eabi
- git
- libatlas-base-dev
- libavcodec58
- libavformat58
- libblas-dev
- libbluetooth-dev
- libbz2-dev
- libc-ares-dev
- libc6-dev
- libcpuinfo-dev
- libcurl-ocaml-dev
- libdbus-1-dev
- libdbus-glib-1-dev
- libeigen3-dev
- libelf-dev
- libffi-dev
- libfl-dev
- libfuse-dev
- libgdbm-dev
- libgl1-mesa-glx
- libgmp3-dev
- libgstreamer1.0-0
- libhdf5-dev
- liblapack-dev
- libncursesw5-dev
- libnetfilter-queue-dev
- libopenblas-dev
- libopenblas-dev # https://stackoverflow.com/questions/14570011/explain-why-numpy-should-not-be-imported-from-source-directory
- libopenjp2-7
- libopenmpi-dev
- libopenmpi3
- libpcap-dev
- libprotobuf-dev
- libraspberrypi-bin
- libraspberrypi-dev
- libraspberrypi-doc
- libraspberrypi0
- libsleef-dev
- libsqlite3-dev
- libssl-dev
- libswscale5
- libtiff5
- libssl-ocaml-dev
- libtool
- libts-bin
- libusb-1.0-0-dev
- lsof
- make
- ntp
- python3-dbus
- python3-flask
- python3-flask-cors
- python3-flaskext.wtf
- python3-pil
- pkg-config
- python3-dev
- python3-pip
- python3-protobuf
- python3-smbus
- python3-setuptools
- qpdf
- raspberrypi-kernel-headers
- rsync
- screen
- tcpdump
- texinfo
- time
- tk-dev
- unzip
- vim
- wget
- wl
- xxd
- zlib1g-dev
environment:
ARCHFLAGS: "-arch armv6l"
tasks:
# First we install packages
- name: install packages
apt:
name: "{{ packages.apt.install }}"
state: latest
update_cache: yes
install_recommends: no
- 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
- name: install 32bit torch
shell: "python3 -m pip install {{ packages.torch.url }} {{ packages.torchvision.url }} --break-system-packages"
args:
executable: /bin/bash
environment:
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch armv6l"
# Now we set up /boot/firmware
- name: Create pi user
copy:
dest: /boot/userconf
dest: /boot/firmware/userconf
content: |
pi:$6$3jNr0GA9KIyt4hmM$efeVIopdMQ8DGgEPCWWlbx3mJJNAYci1lEXGdlky0xPyjqwKNbwTL5SrCcpb4144C4IvzWjn7Iv.QjqmU7iyT/
pi:$5$733Efsksay$SEFUKemv8FaNAu6X4GUfxdSzSDh6PbpOcdtNe5b7Nt0
- name: enable ssh on boot
file:
path: /boot/firmware/ssh
state: touch
- name: remove current rc.local
file:
path: /etc/rc.local
state: absent
- name: change root partition
replace:
dest: /boot/firmware/cmdline.txt
backup: no
regexp: "root=PARTUUID=[a-zA-Z0-9\\-]+"
replace: "root=/dev/mmcblk0p2"
- name: configure /boot/firmware/cmdline.txt
lineinfile:
path: /boot/firmware/cmdline.txt
backrefs: True
state: present
backup: no
regexp: '(.*)$'
line: '\1 modules-load=dwc2,g_ether'
- name: setup /boot/firmware/config.txt
blockinfile:
path: /boot/firmware/config.txt
insertafter: EOF
block: |
dtparam=i2c1=on
dtparam=i2c_arm=on
dtparam=spi=on
gpu_mem=1
dtoverlay=dwc2
#dtoverlay=disable-wifi
enable_uart=1
[pi0]
dtoverlay=spi0-0cs
#dtoverlay=disable-wifi
- name: change hostname
lineinfile:
@ -206,31 +210,14 @@
state: present
when: hostname.changed
- name: Create custom plugin directory
file:
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
- name: install packages
apt:
name: "{{ packages.apt.install }}"
# Now we disable sap and a2dp, we don't use them on rpi
- name: disable sap plugin for bluetooth.service
lineinfile:
dest: /lib/systemd/system/bluetooth.service
regexp: '^ExecStart=/usr/libexec/bluetooth/bluetoothd$'
line: 'ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp'
state: present
- name: update pip3, setuptools, wheel
shell: "python3 -m pip install --upgrade pip setuptools wheel"
args:
executable: /bin/bash
chdir: /usr/local/src
###########################################
#
# libpcap v1.9 - build from source
@ -264,17 +251,61 @@
dest: /usr/local/lib/libpcap.so.0.8
state: link
###############################################################
# Install nexmon to fix wireless scanning (takes 2.5G of space)
###############################################################
# install latest hcxtools
- name: clone hcxtools
git:
repo: https://github.com/ZerBea/hcxtools.git
dest: /usr/local/src/hcxtools
# Install nexmon for all boards
- name: build and install nexmon as needed
include_tasks: nexmon.yml
loop: "{{ boards }}"
- name: install hcxtools
shell: "make && make install"
args:
executable: /bin/bash
chdir: /usr/local/src/hcxtools
- name: remove hcxtools directory
file:
state: absent
path: /usr/local/src/hcxtools
# Installing nexmon
- name: clone nexmon repository
git:
repo: https://github.com/DrSchottky/nexmon.git
dest: /usr/local/src/nexmon
- name: make firmware
shell: "source ./setup_env.sh && make"
args:
executable: /bin/bash
chdir: /usr/local/src/nexmon/
environment:
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch armv6l"
- name: make firmware patch (bcm43430a1)
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/ && make"
args:
executable: /bin/bash
chdir: /usr/local/src/nexmon/
environment:
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch armv6l"
- name: install new firmware (bcm43430a1)
copy:
src: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/brcmfmac43430-sdio.bin
dest: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
follow: true
- name: copy modified driver
copy:
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
dest: "/usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
environment:
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch armv6l"
# some pizero2w have the pizeroW wifi chip
# could this be a link instead of a copy? and force, only if not a link?
- name: copy 43430-sdio as 43436s-sdio for the special 43430/1 /2
copy:
src: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
@ -289,9 +320,14 @@
loop:
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.clm_blob
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,model-zero-w.clm_blob
- /usr/lib/firmware/brcm/brcmfmac43430b0-sdio.raspberrypi,model-zero-2-w.clm_blob
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.raspberrypi,model-zero-2-w.clm_blob
- /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,3-model-b.clm_blob
- name: backup original driver
command: "mv /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
- name: load brcmfmac drivers
command: "/sbin/depmod {{ kernel.full }}"
environment:
QEMU_UNAME: "{{ kernel.full }}"
# To shrink the final image, remove the nexmon directory (takes 2.5G of space) post build and installation
- name: Delete nexmon content & directory
@ -299,58 +335,36 @@
state: absent
path: /usr/local/src/nexmon/
- name: clone pwnagotchi repository
git:
repo: https://github.com/jayofelony/pwnagotchi.git
dest: /usr/local/src/pwnagotchi
register: pwnagotchigit
- name: Create custom config directory
file:
path: /etc/pwnagotchi/conf.d/
state: directory
#- name: clone pwnagotchi repository
# git:
# repo: https://github.com/jayofelony/pwnagotchi.git
# dest: /usr/local/src/pwnagotchi
# is this even necessary? Can't we just link from /home/pi/pwnagotchi to /usr/local/{bin,lib,etc}
# then just git update in the home dir and encourage hacking?
# make owned by pi.pi, and custom plugins.
- name: build pwnagotchi wheel
command: "python3 setup.py sdist bdist_wheel"
command: "pip3 install . --no-cache-dir --break-system-packages"
args:
chdir: /usr/local/src/pwnagotchi
when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version)
- name: download torch whl
get_url:
url: "{{ packages.torch.url }}"
dest: /usr/local/src/
- name: download torchvision whl
get_url:
url: "{{ packages.torchvision.url }}"
dest: /usr/local/src/
- name: install 32-bit pwnagotchi wheel and dependencies with 32-bit torch wheels
pip:
name:
- "{{ lookup('fileglob', '/usr/local/src/pwnagotchi/dist/pwnagotchi*.whl') }}"
- "{{ packages.torch.url }}"
- "{{ packages.torchvision.url }}"
extra_args: "--no-cache-dir"
environment:
QEMU_CPU: arm1176
QEMU_UNAME: "{{ kernel.full }}"
when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version)
- name: create /usr/local/share/pwnagotchi/ folder
file:
path: /usr/local/share/pwnagotchi/
state: directory
- name: Create custom plugin directory
file:
path: /usr/local/share/pwnagotchi/custom-plugins/
state: directory
- name: remove pwnagotchi folder
file:
state: absent
path: /usr/local/src/pwnagotchi
- name: remove torch whl
file:
state: absent
path: "{{ lookup('fileglob', '/usr/local/src/torch*.whl') }}"
##########################################
#
# pwngrid, bettercap
@ -359,14 +373,14 @@
- name: Install go-1.21
unarchive:
src: https://go.dev/dl/go1.21.6.linux-armv6l.tar.gz
src: https://go.dev/dl/go1.22.3.linux-armv6l.tar.gz
dest: /usr/local
remote_src: yes
register: golang
- name: Update .bashrc for go-1.21
blockinfile:
dest: /home/pi/.bashrc
dest: /etc/profile
state: present
block: |
export GOPATH=$HOME/go
@ -374,25 +388,42 @@
when: golang.changed
- name: download pwngrid
unarchive:
remote_src: yes
src: "{{ packages.opwngrid.url }}"
dest: /usr/local/bin/
mode: 0755
git:
repo: "{{ packages.pwngrid.source }}"
dest: /usr/local/src/pwngrid
- name: download and install bettercap
unarchive:
src: "{{ packages.bettercap.url }}"
dest: /usr/local/bin
remote_src: yes
exclude:
- README.md
- LICENSE.md
mode: 0755
- name: install pwngrid
shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install"
args:
executable: /bin/bash
chdir: /usr/local/src/pwngrid
- name: remove pwngrid folder
file:
state: absent
path: /usr/local/src/pwngrid
- name: download bettercap
git:
repo: "{{ packages.bettercap.source }}"
version: "{{ packages.bettercap.branch }} "
dest: /usr/local/src/bettercap
- name: install bettercap 2.32.4
shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install"
args:
executable: /bin/bash
chdir: /usr/local/src/bettercap
- name: remove bettercap folder
file:
state: absent
path: /usr/local/src/bettercap
- name: clone bettercap caplets
git:
repo: "{{ packages.caplets.source }}"
version: "{{ packages.caplets.branch }}"
dest: /tmp/caplets
register: capletsgit
@ -402,31 +433,11 @@
target: install
when: capletsgit.changed
- name: download and install bettercap ui
unarchive:
src: "{{ packages.bettercap.ui }}"
dest: /usr/local/share/bettercap/
remote_src: yes
mode: 0755
# to always have the bettercap webui available (because why not?)
- name: copy pwnagotchi-manual over pwnagotchi-auto caplet
ansible.builtin.copy:
src: /usr/local/share/bettercap/caplets/pwnagotchi-manual.cap
dest: /usr/local/share/bettercap/caplets/pwnagotchi-auto.cap
force: true
ignore_errors: true
- name: create /etc/pwnagotchi folder
file:
path: /etc/pwnagotchi
state: directory
- name: create log folder
file:
path: /home/pi/logs
state: directory
- name: check if user configuration exists
stat:
path: /etc/pwnagotchi/config.toml
@ -442,32 +453,16 @@
# ui.display.type = "waveshare_4"
when: not user_config.stat.exists
- name: Delete motd
file:
state: absent
path: /etc/motd
- name: Delete motd 10-uname
file:
state: absent
path: /etc/update-motd.d/10-uname
- name: enable ssh on boot
file:
path: /boot/ssh
state: touch
- name: change root partition
replace:
dest: /boot/cmdline.txt
backup: no
regexp: "root=PARTUUID=[a-zA-Z0-9\\-]+"
replace: "root=/dev/mmcblk0p2"
- name: configure /boot/cmdline.txt
lineinfile:
path: /boot/cmdline.txt
backrefs: True
state: present
backup: no
regexp: '(.*)$'
line: '\1 modules-load=dwc2,g_ether'
- name: add firmware packages to hold
dpkg_selections:
name: "{{ item }}"
@ -487,16 +482,7 @@
enabled: true
state: stopped
with_items: "{{ services.enable }}"
#- name: remove golang build libraries
# file:
# state: absent
# path: /root/go
#- name: remove golang
# file:
# state: absent
# path: /usr/local/go
register: enabled
- name: make /root readable, becauase that's where all the files are
file:
@ -510,39 +496,25 @@
group: pi
recurse: true
- name: remove unnecessary apt packages
apt:
name: "{{ packages.apt.remove }}"
state: absent
purge: yes
- name: remove dependencies that are no longer required
apt:
autoremove: yes
- name: clean apt cache
apt:
autoclean: true
- name: remove golang build libraries
file:
state: absent
path: /root/go
- name: remove pre-collected packages zip
file:
path: /root/go_pkgs.tgz
state: absent
- name: remove golang
- name: remove /root/go folder
file:
state: absent
path: /root/go
- name: remove /usr/local/go folder
file:
state: absent
path: /usr/local/go
- name: remove /root/.cache (pip cache)
- name: remove pip cache
file:
state: absent
path: /root/.cache
path: /root/.cache/pip
- name: remove ssh keys
file:
@ -556,7 +528,26 @@
args:
executable: /bin/bash
# Now we remove packages
- name: remove unnecessary apt packages
apt:
name: "{{ packages.apt.remove }}"
state: absent
purge: yes
register: removed
- name: remove dependencies that are no longer required
apt:
autoremove: yes
when: removed.changed
- name: clean apt cache
apt:
autoclean: true
when: removed.changed
handlers:
- name: reload systemd services
systemd:
daemon_reload: yes
when: enabled.changed

View File

@ -1,8 +1,8 @@
packer {
required_plugins {
arm = {
version = "1.0.0"
source = "github.com/cdecoux/builder-arm"
version = ">=1.0.0"
source = "github.com/michalfita/cross"
}
ansible = {
source = "github.com/hashicorp/ansible"
@ -73,7 +73,13 @@ build {
provisioner "shell" {
inline = ["chmod +x /usr/bin/*"]
}
provisioner "shell" {
inline = ["mkdir -p /usr/local/src/pwnagotchi"]
}
provisioner "file" {
destination = "/usr/local/src/pwnagotchi/"
source = "../"
}
provisioner "file" {
destination = "/etc/systemd/system/"
sources = [

View File

@ -6,8 +6,8 @@
vars:
kernel:
min: "6.6"
full: "6.6.20+rpt-rpi-v8"
full_pi5: "6.6.20+rpt-rpi-2712"
full: "6.6.31+rpt-rpi-v8"
full_pi5: "6.6.31+rpt-rpi-2712"
pwnagotchi:
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi', true) }}"
@ -27,10 +27,11 @@
packages:
caplets:
source: "https://github.com/jayofelony/caplets.git"
branch: "lite" # or master
bettercap:
source: "https://github.com/jayofelony/bettercap.git"
url: "https://github.com/jayofelony/bettercap/releases/download/2.32.4/bettercap-2.32.4.zip"
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
branch: "lite" # or master
pwngrid:
source: "https://github.com/jayofelony/pwngrid.git"
url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.5/pwngrid-1.10.5-aarch64.zip"
@ -51,23 +52,18 @@
- libpcap0.8-dbg
- libpcap0.8-dev
remove:
- avahi-daemon
- dhpys-swapfile
- libcurl-ocaml-dev
- libssl-ocaml-dev
- nfs-common
- triggerhappy
- wpasupplicant
install:
- aircrack-ng
- autoconf
- bc
- bison
- bluez
- bluez-tools
- build-essential
- curl
- dkms
- dphys-swapfile
- fbi
- firmware-atheros
@ -76,86 +72,38 @@
- firmware-misc-nonfree
- firmware-realtek
- flex
- fonts-dejavu
- fonts-dejavu-core
- fonts-dejavu-extra
- fonts-freefont-ttf
- g++
- gawk
- gcc-arm-none-eabi
- git
- hcxtools
- libatlas-base-dev
- libavcodec59
- libavformat59
- libblas-dev
- libbluetooth-dev
- libbz2-dev
- libc-ares-dev
- libc6-dev
- libcap-dev
- libcurl-ocaml-dev
- libdbus-1-dev
- libdbus-glib-1-dev
- libeigen3-dev
- libelf-dev
- libffi-dev
- libfl-dev
- libfuse-dev
- libgdbm-dev
- libgl1-mesa-glx
- libgmp3-dev
- libgstreamer1.0-0
- libhdf5-dev
- liblapack-dev
- libncursesw5-dev
- libnetfilter-queue-dev
- libopenblas-dev
- libopenjp2-7
- libopenmpi-dev
- libopenmpi3
- libpcap-dev
- libraspberrypi-bin
- libraspberrypi-dev
- libraspberrypi-doc
- libraspberrypi0
- libsqlite3-dev
- libssl-dev
- libssl-ocaml-dev
- libswscale5
- libtiff6
- libtool
- libusb-1.0-0-dev
- lsof
- make
- ntp
- python3-dbus
- python3-flask
- python3-flask-cors
- python3-flaskext.wtf
- python3-gast
- python3-pil
- pkg-config
- python3-dev
- python3-pip
- python3-pycryptodome
- python3-requests
- python3-scapy
- python3-setuptools
- python3-smbus
- python3-smbus2
- python3-spidev
- python3-tweepy
- python3-werkzeug
- python3-yaml
- qpdf
- raspberrypi-kernel-headers
- rsync
- screen
- tcpdump
- texinfo
- time
- tk-dev
- unzip
- vim
- wget
- wl
- xxd
@ -168,9 +116,9 @@
- name: install packages
apt:
name: "{{ packages.apt.install }}"
state: present
state: latest
update_cache: yes
install_recommends: false
install_recommends: no
- name: update pip3, setuptools, wheel
shell: "python3 -m pip install --upgrade pip setuptools wheel --break-system-packages"
@ -178,12 +126,22 @@
executable: /bin/bash
chdir: /usr/local/src
- name: build pwnagotchi wheel
command: "pip3 install . --no-cache-dir --break-system-packages"
args:
chdir: /usr/local/src/pwnagotchi
- name: remove pwnagotchi folder
file:
state: absent
path: /usr/local/src/pwnagotchi
# Now we set up /boot/firmware
- name: Create pi user
copy:
dest: /boot/firmware/userconf
content: |
pi:$6$3jNr0GA9KIyt4hmM$efeVIopdMQ8DGgEPCWWlbx3mJJNAYci1lEXGdlky0xPyjqwKNbwTL5SrCcpb4144C4IvzWjn7Iv.QjqmU7iyT/
pi:$5$733Efsksay$SEFUKemv8FaNAu6X4GUfxdSzSDh6PbpOcdtNe5b7Nt0
- name: enable ssh on boot
file:
@ -211,6 +169,35 @@
regexp: '(.*)$'
line: '\1 modules-load=dwc2,g_ether'
- name: setup /boot/firmware/config.txt
blockinfile:
path: /boot/firmware/config.txt
insertafter: EOF
block: |
dtparam=i2c1=on
dtparam=i2c_arm=on
dtparam=spi=on
gpu_mem=1
dtoverlay=dwc2
#dtoverlay=disable-wifi
enable_uart=1
[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
- name: change hostname
lineinfile:
dest: /etc/hostname
@ -270,7 +257,6 @@
state: link
# install latest hcxtools
- name: clone hcxtools
git:
repo: https://github.com/ZerBea/hcxtools.git
@ -287,13 +273,13 @@
state: absent
path: /usr/local/src/hcxtools
# Installing nexmon
- name: clone nexmon repository
git:
repo: https://github.com/DrSchottky/nexmon.git
dest: /usr/local/src/nexmon
# FIRST WE BUILD DRIVER FOR RPi5
- name: make firmware, RPi5
shell: "source ./setup_env.sh && make"
args:
@ -320,11 +306,6 @@
QEMU_UNAME: "{{ kernel.full_pi5 }}"
ARCHFLAGS: "-arch aarch64"
- name: Delete the modified driver, RPi5
file:
state: absent
path: '/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko'
- name: backup original driver, RPi5
command: "mv /usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
@ -339,7 +320,6 @@
path: /usr/local/src/nexmon/
# NOW WE BUILD DRIVERS FOR RPi4, RPizero2w and RPi3
- name: clone nexmon repository
git:
repo: https://github.com/DrSchottky/nexmon.git
@ -370,7 +350,6 @@
follow: true
# NOW WE BUILD DRIVERS FOR RPiZero2W, RPi 3
- name: make firmware patch (bcm43436b0)
shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/ && make"
args:
@ -395,6 +374,12 @@
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch aarch64"
- name: install new firmware (bcm43430a1)
copy:
src: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/brcmfmac43430-sdio.bin
dest: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
follow: true
- name: copy modified driver, RPi4
copy:
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
@ -403,12 +388,6 @@
QEMU_UNAME: "{{ kernel.full }}"
ARCHFLAGS: "-arch aarch64"
- name: install new firmware (bcm43430a1)
copy:
src: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/brcmfmac43430-sdio.bin
dest: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
follow: true
- name: copy 43430-sdio as 43436s-sdio for the special 43430/1 /2
copy:
src: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin
@ -428,6 +407,7 @@
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.clm_blob
- /usr/lib/firmware/brcm/brcmfmac43436-sdio.raspberrypi,model-zero-2-w.clm_blob
- /usr/lib/firmware/brcm/brcmfmac43455-sdio.clm_blob
- /usr/lib/firmware/brcm/BCM43430A1.raspberrypi,model-zero-2-w.hcd
- name: backup original driver
command: "mv /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig"
@ -443,39 +423,24 @@
state: absent
path: /usr/local/src/nexmon/
- name: Create custom plugin directory
file:
path: /usr/local/share/pwnagotchi/custom-plugins/
state: directory
- name: Create custom config directory
file:
path: /etc/pwnagotchi/conf.d/
state: directory
- name: clone pwnagotchi repository
git:
repo: https://github.com/jayofelony/pwnagotchi.git
dest: /usr/local/src/pwnagotchi
- name: build pwnagotchi wheel
command: "pip3 install . --no-cache-dir --break-system-packages"
args:
chdir: /usr/local/src/pwnagotchi
- name: remove pwnagotchi folder
file:
state: absent
path: /usr/local/src/pwnagotchi
- name: create /usr/local/share/pwnagotchi/ folder
file:
path: /usr/local/share/pwnagotchi/
state: directory
- name: Create custom plugin directory
file:
path: /usr/local/share/pwnagotchi/custom-plugins/
state: directory
- name: Install go-1.21
unarchive:
src: https://go.dev/dl/go1.21.5.linux-arm64.tar.gz
src: https://go.dev/dl/go1.22.3.linux-arm64.tar.gz
dest: /usr/local
remote_src: yes
register: golang
@ -508,6 +473,7 @@
- name: download bettercap
git:
repo: "{{ packages.bettercap.source }}"
version: "{{ packages.bettercap.branch }}"
dest: /usr/local/src/bettercap
- name: install bettercap 2.32.4
@ -534,6 +500,7 @@
- name: clone bettercap caplets
git:
repo: "{{ packages.caplets.source }}"
version: "{{ packages.caplets.branch }}"
dest: /tmp/caplets
register: capletsgit
@ -543,21 +510,6 @@
target: install
when: capletsgit.changed
- name: download and install bettercap ui
unarchive:
src: "{{ packages.bettercap.ui }}"
dest: /usr/local/share/bettercap/
remote_src: yes
mode: 0755
# to always have the bettercap webui available (because why not?)
- name: copy pwnagotchi-manual over pwnagotchi-auto caplet
ansible.builtin.copy:
src: /usr/local/share/bettercap/caplets/pwnagotchi-manual.cap
dest: /usr/local/share/bettercap/caplets/pwnagotchi-auto.cap
force: true
ignore_errors: true
- name: create /etc/pwnagotchi folder
file:
path: /etc/pwnagotchi

View File

@ -1 +1 @@
__version__ = '2.8.9'
__version__ = '2.9.2'

View File

@ -150,8 +150,6 @@ personality.bond_encounters_factor = 20000
personality.throttle_a = 0.4
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

View File

@ -1,18 +1,18 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# pwnagotchi Brazilian Portuguese translation file.
# Copyright (C) 2024
# This file is distributed under the same license as the pwnagotchi package.
# Fabiano F O <fabfernandes@hotmail.com>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-17 15:46+0100\n"
"POT-Creation-Date: 2024-03-25 22:30+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Foxy <EMAIL@ADDRESS>\n"
"Last-Translator: Fabiano F O <fabfernandes@hotmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: Portuguese (Brazil)\n"
"Language: Brazilian Portuguese\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
@ -21,13 +21,13 @@ msgid "ZzzzZZzzzzZzzz"
msgstr "ZzzzZZzzzzZzzz"
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Olá, Eu sou Pwnagotchi! Iniciando ..."
msgstr "Olá, sou Pwnagotchi! Iniciando ..."
msgid "New day, new hunt, new pwns!"
msgstr "Um novo dia, Uma nova caça e novos pwns!"
msgstr "Novo dia, Nova caçada, Novos pwns!"
msgid "Hack the Planet!"
msgstr "Burle o Planeta!"
msgstr "Hackeie o Planeta!"
msgid "AI ready."
msgstr "IA pronta."
@ -36,64 +36,64 @@ msgid "The neural network is ready."
msgstr "A rede neural está pronta."
msgid "Generating keys, do not turn off ..."
msgstr "Criando chaves, não desligue o sistema ..."
msgstr "Gerando chaves, não desligue ..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Ei, canal {channel} está livre! Seu AP vai agradecer."
msgstr "Ei, o canal {channel} está livre! Seu AP vai agradecer."
msgid "Reading last session logs ..."
msgstr "Lendo os logs da ultima sessão"
msgstr "Lendo os logs da última sessão ..."
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr "Leia {lines_so_far} linha de logs até agora ..."
msgstr "Li {lines_so_far} linhas de logs até agora ..."
msgid "I'm bored ..."
msgstr "Eu estou entediado ..."
msgstr "Estou entediado ..."
msgid "Let's go for a walk!"
msgstr "Vamos ir numa caminhada!"
msgstr "Vamos dar um passeio!"
msgid "This is the best day of my life!"
msgstr "Esse é o melhor dia da minha vida!"
msgstr "Este é o melhor dia da minha vida!"
msgid "Shitty day :/"
msgstr "Dia ruim :/"
msgstr "Que dia ruim :/"
msgid "I'm extremely bored ..."
msgstr "Eu estou extremamente entediado ..."
msgstr "Estou extremamente entediado ..."
msgid "I'm very sad ..."
msgstr "Eu estou muito triste ..."
msgstr "Estou muito triste ..."
msgid "I'm sad"
msgstr "Eu estou triste"
msgstr "Estou triste"
msgid "Leave me alone ..."
msgstr "Me deixe em paz ..."
msgid "I'm mad at you!"
msgstr "Eu estou bravo com você!"
msgstr "Estou bravo com você!"
msgid "I'm living the life!"
msgstr "Eu estou vivendo a vida!"
msgstr "Estou aproveitando a vida!"
msgid "I pwn therefore I am."
msgstr "Eu pwn então Eu sou."
msgstr "Eu pwn, logo existo."
msgid "So many networks!!!"
msgstr "Tantas redes!!!"
msgstr "Uau! Quantas redes!!"
msgid "I'm having so much fun!"
msgstr "Eu estou tendo muita diversão"
msgstr "Estou me divertindo muito!"
msgid "My crime is that of curiosity ..."
msgstr "Meu crime é de curiosidade ..."
msgstr "Meu crime é a curiosidade ..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr "Olá {name}! É bom em conhecê-lo"
msgstr "Olá {name}! Prazer em conhecê-lo."
#, python-brace-format
msgid "Yo {name}! Sup?"
@ -101,33 +101,33 @@ msgstr "Ei {name}! Como vai?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr "Ei {name} como você está indo?"
msgstr "Ei {name}, como você está?"
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr ""
msgstr "A unidade {name} está próxima!"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr ""
msgstr "Hmm ... tchau {name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr ""
msgstr "{name} desapareceu ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr ""
msgstr "Oops ... {name} desapareceu."
#, python-brace-format
msgid "{name} missed!"
msgstr "{name} errou!"
msgstr "Perdi {name}!"
msgid "Missed!"
msgstr "Errei!"
msgstr "Perdi!"
msgid "Good friends are a blessing!"
msgstr "Bom amigos são uma bensão!"
msgstr "Bons amigos são uma bênção!"
msgid "I love my friends!"
msgstr "Eu amo meus amigos!"
@ -136,7 +136,7 @@ msgid "Nobody wants to play with me ..."
msgstr "Ninguém quer brincar comigo ..."
msgid "I feel so alone ..."
msgstr "Estou me sentindo sozinho"
msgstr "Me sinto tão sozinho ..."
msgid "Where's everybody?!"
msgstr "Onde está todo mundo?!"
@ -160,27 +160,27 @@ msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Esperando por {secs}s ..."
msgstr "Aguardando {secs}s ..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Olhando por volta ({secs}s)"
msgstr "Olhando em volta ... ({secs}s)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Ei {what} vamos ser amigos!"
msgstr "Ei {what}, vamos ser amigos!"
#, python-brace-format
msgid "Associating to {what}"
msgstr "Associando para {what}"
msgstr "Associando a {what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr "Ei {what}!"
msgstr "Olá {what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Apenas decidindo que {mac} não precisa de WiFi!"
msgstr "Acabei de decidir que {mac} não precisa de WiFi!"
#, python-brace-format
msgid "Deauthenticating {mac}"
@ -192,11 +192,11 @@ msgstr "Banindo {mac}!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Legal, conseguimos {num} novos handshake{plural}!"
msgstr "Legal, conseguimos {num} novo{plural} handshake{plural}!"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr "Você tem {count} novas messagem{plural}!"
msgstr "Você tem {count} nova{plural} messagem{plural}!"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr "Oops, algo deu errado ... Reiniciando ..."
@ -207,7 +207,7 @@ msgstr "Enviando dados para {to} ..."
#, python-brace-format
msgid "Downloading from {name} ..."
msgstr "Instalando para {name} ..."
msgstr "Baixando de {name} ..."
#, python-brace-format
msgid "Kicked {num} stations\n"
@ -225,11 +225,11 @@ msgid "Got {num} handshakes\n"
msgstr "Peguei {num} handshakes\n"
msgid "Met 1 peer"
msgstr "Encontrei 1 pessoa"
msgstr "Conheci 1 peer"
#, python-brace-format
msgid "Met {num} peers"
msgstr "Encontrei {num} pessoas"
msgstr "Conheci {num} peers"
#, python-brace-format
msgid ""
@ -237,8 +237,8 @@ msgid ""
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgstr ""
"Estou navegando há {duration} e expulsei {deauthed} clientes! Também conheci "
"{associamos} novos amigos e comi {handshakes} handshakes! #pwnagotchi "
"Estou pwning há {duration} e expulsei {deauthed} clientes! Também conheci "
"{associated} novos amigos e comi {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"

Binary file not shown.

View File

@ -0,0 +1,260 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <dragan.miljkovic29@gmail.com>, 2024.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Srpski prevod v1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-16 15:26-0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: RS <dragan.miljkovic29@gmail.com>\n"
"Language: Serbian\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "ZzzzZZzzzzZzzz"
msgstr "ZzzzZZzzzzZzzz"
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Zdravo, ja sam Pwnagotchi! Započinjem ..."
msgid "New day, new hunt, new pwns!"
msgstr "Novi dan, novi lov, novi pnwovi!"
msgid "Hack the Planet!"
msgstr "Hakuj Planetu!"
msgid "AI ready."
msgstr "AI spreman."
msgid "The neural network is ready."
msgstr "Neuronska mreža je spremna."
msgid "Generating keys, do not turn off ..."
msgstr "Generišem ključeve, ne isključuj me..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Hej, kanal {channel} je slobodan! Tvoj AP će ti se zahvaliti."
msgid "Reading last session logs ..."
msgstr "Čitanje logova poslednje sesije ..."
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr "Pročitao {lines_so_far} log linija do sada ..."
msgid "I'm bored ..."
msgstr "Dosadno mi je ..."
msgid "Let's go for a walk!"
msgstr "Hajmo u šetnju!"
msgid "This is the best day of my life!"
msgstr "Ovo je najbolji dan mog života!"
msgid "Shitty day :/"
msgstr "Sranje dan :/"
msgid "I'm extremely bored ..."
msgstr "Užasno mi je dosadno ..."
msgid "I'm very sad ..."
msgstr "Jako sam tužan ..."
msgid "I'm sad"
msgstr "Tužan sam"
msgid "Leave me alone ..."
msgstr "Ostavi me na miru ..."
msgid "I'm mad at you!"
msgstr "Ljut sam na tebe!"
msgid "I'm living the life!"
msgstr "Živim ga!"
msgid "I pwn therefore I am."
msgstr "Ja pwnujem dakle postojim."
msgid "So many networks!!!"
msgstr "Tako mnogo mreža!!!"
msgid "I'm having so much fun!"
msgstr "Previše se zabavljam!"
msgid "My crime is that of curiosity ..."
msgstr "Moj zločin je radoznalost ..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr "Zdravo {name}! Drago mi je da te upoznam."
#, python-brace-format
msgid "Yo {name}! Sup?"
msgstr "Ej {name}! Šta ima?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr "Hej {name} kako si?"
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr "Jedinica {name} je blizu!"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr "Umm ... doviđenja {name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name} je nestao ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Ups ... {name} je nestao."
#, python-brace-format
msgid "{name} missed!"
msgstr "{name} promašen!"
msgid "Missed!"
msgstr "Promašen!"
msgid "Good friends are a blessing!"
msgstr "Dobri prijatelji su blagoslov!"
msgid "I love my friends!"
msgstr "Volim svoje prijatelje!"
msgid "Nobody wants to play with me ..."
msgstr "Niko ne želi da se igra sa mnom ..."
msgid "I feel so alone ..."
msgstr "Osećam se toliko usamljeno ..."
msgid "Where's everybody?!"
msgstr "Gde su svi?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Dremam {secs}s ..."
msgid "Zzzzz"
msgstr "Zzzzz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr "ZzzZzzz ({secs}s)"
msgid "Good night."
msgstr "Laku noć."
msgid "Zzz"
msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Čekam {secs}s ..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Gledam unaokolo ({secs}s)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Hej {what} hajde da budemo prijatelji!"
#, python-brace-format
msgid "Associating to {what}"
msgstr "Povezujem se sa {what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr "Ej {what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Upravo sam odlučio da {mac} ne zahteva WiFi!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr "Deauthenticating {mac}"
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "Kickbanning {mac}!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Kul, imamo {num} novih handshakeova"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr "Imas {count} novih poruka"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr "Ups, nešto je poslo po zlu ... Restartujem ..."
#, python-brace-format
msgid "Uploading data to {to} ..."
msgstr "Otpremam podatke na {to} ..."
#, python-brace-format
msgid "Downloading from {name} ..."
msgstr "Preuzimam od {name} ..."
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "Kickovano {num} stanica\n"
msgid "Made >999 new friends\n"
msgstr "Stekao sam >999 novih prijatelja\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "Stekao sam {num} novih prijatelja\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "Imam {num} handshakeova\n"
msgid "Met 1 peer"
msgstr "Sreo 1 peera"
#, python-brace-format
msgid "Met {num} peers"
msgstr "Sreo {num} peerova"
#, python-brace-format
msgid ""
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgstr ""
"Pwnujem već {duration} i kickovao sam {deauthed} klijenata! Takođe sam sreo "
"{associated} novih prijatelja i pojeo {handshakes} handshakeova! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr "sati"
msgid "minutes"
msgstr "minuta"
msgid "seconds"
msgstr "sekundi"
msgid "hour"
msgstr "sat"
msgid "minute"
msgstr "minut"
msgid "second"
msgstr "sekunda"

View File

@ -9,8 +9,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-16 21:10+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"PO-Revision-Date: 2024-03-27 18:40+0800\n"
"Last-Translator: AlanLeung <admin@mcnot.pro>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: Twi\n"
"MIME-Version: 1.0\n"
@ -18,218 +18,218 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
msgid "ZzzzZZzzzzZzzz"
msgstr ""
msgstr "ZzzzZZzzzzZzzz"
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr ""
msgstr "HI!我是Pwnagotchi!\n程式啟動..."
msgid "New day, new hunt, new pwns!"
msgstr ""
msgstr "新的一天!\n新的狩獵!新的入侵!"
msgid "Hack the Planet!"
msgstr ""
msgstr "我要駭入\n地球的所有人!"
msgid "AI ready."
msgstr ""
msgstr "人工智慧已啟動。"
msgid "The neural network is ready."
msgstr ""
msgstr "神經網路已啟動。"
msgid "Generating keys, do not turn off ..."
msgstr ""
msgstr "產生金鑰中,\n請勿關閉..."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr ""
msgstr "嘿,{channel}很順暢!\n你的WIFI會感謝你的。"
msgid "Reading last session logs ..."
msgstr ""
msgstr "正在閱讀最後的會話紀錄..."
#, python-brace-format
msgid "Read {lines_so_far} log lines so far ..."
msgstr ""
msgstr "目前已經閱讀了 {lines_so_far} 行的紀錄..."
msgid "I'm bored ..."
msgstr ""
msgstr "我好無聊..."
msgid "Let's go for a walk!"
msgstr ""
msgstr "我們! 散步! 散步散步散步散步"
msgid "This is the best day of my life!"
msgstr ""
msgstr "這是我生命中最棒的一天!"
msgid "Shitty day :/"
msgstr ""
msgstr "糟糕的一天 :/"
msgid "I'm extremely bored ..."
msgstr ""
msgstr "我超無聊的...炒雞 炒雞的那種"
msgid "I'm very sad ..."
msgstr ""
msgstr "我好難過..."
msgid "I'm sad"
msgstr ""
msgstr "嗚嗚嗚...."
msgid "Leave me alone ..."
msgstr ""
msgstr "尼奏凱啦臭臭"
msgid "I'm mad at you!"
msgstr ""
msgstr "喔氣氣氣氣氣ˋ^ˊ"
msgid "I'm living the life!"
msgstr ""
msgstr "真是充實的一生!"
msgid "I pwn therefore I am."
msgstr ""
msgstr "我駭故我在."
msgid "So many networks!!!"
msgstr ""
msgstr "好多網路啊!!!吃! 吃他! 吃光光!!!"
msgid "I'm having so much fun!"
msgstr ""
msgstr "我玩的超級開心!"
msgid "My crime is that of curiosity ..."
msgstr ""
msgstr "我的缺點就是\n太好奇了..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you."
msgstr ""
msgstr "尼豪{name}!\n很高興認識你!!!!"
#, python-brace-format
msgid "Yo {name}! Sup?"
msgstr ""
msgstr "嗨 {name}! 你來攻打我的村莊?"
#, python-brace-format
msgid "Hey {name} how are you doing?"
msgstr ""
msgstr "嗨 {name} 你最近過得如何˙ˇ˙?"
#, python-brace-format
msgid "Unit {name} is nearby!"
msgstr ""
msgstr "{name}\n就在附近!"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr ""
msgstr "哦嗚 ... \n拜拜{name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr ""
msgstr "{name}\n不見了 ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr ""
msgstr "哦歐...\n{name}\n不見了。"
#, python-brace-format
msgid "{name} missed!"
msgstr ""
msgstr "我剛剛錯過了{name}!"
msgid "Missed!"
msgstr ""
msgstr "又錯過了!"
msgid "Good friends are a blessing!"
msgstr ""
msgstr "有個好朋友\n真幸福!"
msgid "I love my friends!"
msgstr ""
msgstr "我喜歡\n我的朋友!"
msgid "Nobody wants to play with me ..."
msgstr ""
msgstr "沒人想跟我玩..."
msgid "I feel so alone ..."
msgstr ""
msgstr "我覺得好孤單..."
msgid "Where's everybody?!"
msgstr ""
msgstr "大家都去哪裡了?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr ""
msgstr "我想瞇{secs}秒一下..."
msgid "Zzzzz"
msgstr ""
msgstr "Zzzzz"
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr ""
msgstr "ZzzZzzz({secs}秒)"
msgid "Good night."
msgstr ""
msgstr "晚安!"
msgid "Zzz"
msgstr ""
msgstr "Zzz"
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr ""
msgstr "等我{secs}秒..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr ""
msgstr "環顧四周({secs}秒)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr ""
msgstr "嗨\n{what}\n讓我們來當朋友吧!"
#, python-brace-format
msgid "Associating to {what}"
msgstr ""
msgstr "正在連接\n{what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr ""
msgstr "喲,\n{what}!"
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr ""
msgstr "我要讓\n{mac}\n斷線!\n他不需要上網!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr ""
msgstr "解除\n{mac}\n的授權中"
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr ""
msgstr "把\n{mac}\n踢出中!"
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr ""
msgstr "酷耶,我們抓到{num}個\n新的握手包{plural}!"
#, python-brace-format
msgid "You have {count} new message{plural}!"
msgstr ""
msgstr "你有{count}個新訊息{plural}!"
msgid "Oops, something went wrong ... Rebooting ..."
msgstr ""
msgstr "哦歐,有些地方出錯了...\n重新啟動中..."
#, python-brace-format
msgid "Uploading data to {to} ..."
msgstr ""
msgstr "正在上傳資料到 {to} ..."
#, python-brace-format
msgid "Downloading from {name} ..."
msgstr ""
msgstr "正在從 {name} 下載資料..."
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr ""
msgstr "踢了 {num} 個設備\n"
msgid "Made >999 new friends\n"
msgstr ""
msgstr "交了 >999 個新朋友\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr ""
msgstr "交了 {num} 個新朋友\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr ""
msgstr "捕獲了 {num} 個握手包\n"
msgid "Met 1 peer"
msgstr ""
msgstr "遇到了 個同好"
#, python-brace-format
msgid "Met {num} peers"
msgstr ""
msgstr "遇到了 {num} 個同好"
#, python-brace-format
msgid ""
@ -237,21 +237,24 @@ msgid ""
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgstr ""
"我花了{duration}的時間\n駭入和踢了{deauthed}好多設備."
"我還交了好多{associated}新朋友,\n而且抓到了{handshakes}握手包!"
"#pwnagotchi#入侵日志 #駭客人生 #入侵整個星球 #天網 #我好棒˙ˇ˙"
msgid "hours"
msgstr ""
msgstr ""
msgid "minutes"
msgstr ""
msgstr ""
msgid "seconds"
msgstr ""
msgstr ""
msgid "hour"
msgstr ""
msgstr ""
msgid "minute"
msgstr ""
msgstr ""
msgid "second"
msgstr ""
msgstr ""

View File

@ -29,7 +29,6 @@ class FixServices(plugins.Plugin):
def __init__(self):
self.options = dict()
self.pattern = re.compile(r'brcmf_cfg80211_nexmon_set_channel.*?Set Channel failed')
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')
@ -55,20 +54,6 @@ class FixServices(plugins.Plugin):
if ",UP," in str(cmd_output):
logging.debug("wlan0mon is up.")
if len(self.pattern.findall(last_lines)) >= 3:
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.')
try:
self._tryTurningItOffAndOnAgain(agent)
except Exception as err:
logging.warning("[Fix_Services turnOffAndOn] %s" % repr(err))
else:
logging.debug("[Fix_Services] Logs look good!")
except Exception as err:
logging.error("[Fix_Services ip link show wlan0mon]: %s" % repr(err))
try:
@ -106,7 +91,7 @@ class FixServices(plugins.Plugin):
other_last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10'],
stdout=subprocess.PIPE).stdout))[-10:])
other_other_last_lines = ''.join(
list(TextIOWrapper(subprocess.Popen(['tail', '-n10', '/var/log/pwnagotchi.log'],
list(TextIOWrapper(subprocess.Popen(['tail', '-n10', '/etc/pwnagotchi/log/pwnagotchi.log'],
stdout=subprocess.PIPE).stdout))[-10:])
# don't check if we ran a reset recently
logging.debug("[Fix_Services]**** epoch")
@ -116,20 +101,8 @@ class FixServices(plugins.Plugin):
logging.debug("[Fix_Services]**** checking")
# Look for pattern 1
if len(self.pattern.findall(last_lines)) >= 3:
logging.debug("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
if hasattr(agent, 'view'):
display.set('status', 'Blind-Bug detected. Restarting.')
display.update(force=True)
logging.debug('[Fix_Services] Blind-Bug detected. Restarting.')
try:
self._tryTurningItOffAndOnAgain(agent)
except Exception as err:
logging.warning("[Fix_Services] TTOAOA: %s" % repr(err))
# Look for pattern 2
elif len(self.pattern2.findall(other_last_lines)) >= 5:
if len(self.pattern2.findall(other_last_lines)) >= 5:
logging.debug("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines)
if hasattr(agent, 'view'):
display.set('status', 'Wifi channel stuck. Restarting recon.')

View File

@ -14,8 +14,9 @@ class GPIOButtons(plugins.Plugin):
self.running = False
self.ports = {}
self.commands = None
self.options = dict()
def runCommand(self, channel):
def runcommand(self, channel):
command = self.ports[channel]
logging.info(f"Button Pressed! Running command: {command}")
process = subprocess.Popen(command, shell=True, stdin=None, stdout=open("/dev/null", "w"), stderr=None,
@ -35,8 +36,8 @@ class GPIOButtons(plugins.Plugin):
gpio = int(gpio)
self.ports[gpio] = command
GPIO.setup(gpio, GPIO.IN, GPIO.PUD_UP)
GPIO.add_event_detect(gpio, GPIO.FALLING, callback=self.runCommand, bouncetime=600)
#set pimoroni display hat mini LED off/dim
GPIO.add_event_detect(gpio, GPIO.FALLING, callback=self.runcommand, bouncetime=600)
# set pimoroni display hat mini LED off/dim
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

View File

@ -47,7 +47,7 @@ class Grid(plugins.Plugin):
__version__ = '1.0.1'
__license__ = 'GPL3'
__description__ = 'This plugin signals the unit cryptographic identity and list of pwned networks and list of pwned ' \
'networks to api.pwnagotchi.ai '
'networks to opwngrid.xyz '
def __init__(self):
self.options = dict()
@ -89,7 +89,7 @@ class Grid(plugins.Plugin):
logging.debug("checking pcap's")
config = agent.config()
pcap_files = glob.glob(os.path.join(agent.config()['bettercap']['handshakes'], "*.pcap"))
pcap_files = glob.glob(os.path.join(config['bettercap']['handshakes'], "*.pcap"))
num_networks = len(pcap_files)
reported = self.report.data_field_or('reported', default=[])
num_reported = len(reported)

View File

@ -130,7 +130,7 @@ class MemTemp(plugins.Plugin):
except Exception:
# Set default position based on screen type
if ui.is_waveshare_v2():
h_pos = (178, 84)
h_pos = (175, 84)
v_pos = (197, 74)
elif ui.is_waveshare_v1():
h_pos = (170, 80)

View File

@ -18,12 +18,14 @@ def systemd_dropin(name, content):
systemctl("daemon-reload")
def systemctl(command, unit=None):
if unit:
os.system("/bin/systemctl %s %s" % (command, unit))
else:
os.system("/bin/systemctl %s" % command)
def run_task(name, options):
task_service_name = "switcher-%s-task.service" % name
# save all the commands to a shell script
@ -57,7 +59,7 @@ def run_task(name, options):
""" % (name, task_service_name, name))
if 'reboot' in options and options['reboot']:
# create a indication file!
# create an indication file!
# if this file is set, we want the switcher-tasks to run
open('/root/.switcher', 'a').close()
@ -98,6 +100,7 @@ def run_task(name, options):
systemctl("daemon-reload")
systemctl("start", task_service_name)
class Switcher(plugins.Plugin):
__author__ = '33197631+dadav@users.noreply.github.com'
__version__ = '0.0.1'

View File

@ -10,13 +10,17 @@ class Widget(object):
def draw(self, canvas, drawer):
raise Exception("not implemented")
# canvas.paste: https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.paste
# takes mask variable, to identify color system. (not used for pwnagotchi yet)
# Pwn should use "1" since its mainly black or white displays.
class Bitmap(Widget):
def __init__(self, path, xy, color=0):
super().__init__(xy, color)
self.image = Image.open(path)
def draw(self, canvas, drawer):
if self.color == 0xFF:
self.image = ImageOps.invert(self.image)
canvas.paste(self.image, self.xy)

View File

@ -265,6 +265,12 @@ class Display(View):
def is_waveshareoledlcd(self):
return self._implementation.name == 'waveshareoledlcd'
def is_waveshareoledlcdvert(self):
return self._implementation.name == 'waveshareoledlcdvert'
def is_i2coled(self):
return self._implementation.name == 'i2coled'
def is_waveshare35lcd(self):
return self._implementation.name == 'waveshare35lcd'

View File

@ -104,6 +104,14 @@ def display_for(config):
from pwnagotchi.ui.hw.waveshareoledlcd import Waveshareoledlcd
return Waveshareoledlcd(config)
elif config['ui']['display']['type'] == 'waveshareoledlcdvert':
from pwnagotchi.ui.hw.waveshareoledlcdvert import Waveshareoledlcdvert
return Waveshareoledlcdvert(config)
elif config['ui']['display']['type'] == 'i2coled':
from pwnagotchi.ui.hw.i2coled import I2COled
return I2COled(config)
elif config['ui']['display']['type'] == 'waveshare1in02':
from pwnagotchi.ui.hw.waveshare1in02 import Waveshare1in02
return Waveshare1in02(config)

View File

@ -34,10 +34,10 @@ class DisplayHatMini(DisplayImpl):
def initialize(self):
logging.info("initializing Display Hat Mini")
from pwnagotchi.ui.hw.libs.pimoroni.displayhatmini.ST7789 import ST7789
self._display = ST7789(0, 1, 9, 13)
self._display = ST7789(0, 1, 9, 13, width=self._layout['width'], height=self._layout['height'], rotation=0)
def render(self, canvas):
self._display.display(canvas)
def clear(self):
self._display.clear()
pass

View File

@ -0,0 +1,67 @@
# Created for the Pwnagotchi project by RasTacsko
# HW libraries are based on the adafruit python SSD1306 repo:
# https://github.com/adafruit/Adafruit_Python_SSD1306
# SMBus parts coming from BLavery's lib_oled96 repo:
# https://github.com/BLavery/lib_oled96
# I2C address, width and height import from config.toml made by NurseJackass
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
#
# Default is 128x64 display on i2c address 0x3C
#
# Configure i2c address and dimensions in config.toml:
#
# ui.display.type = "i2coled"
# ui.display.i2c_addr = 0x3C
# ui.display.width = 128
# ui.display.height = 64
#
class I2COled(DisplayImpl):
def __init__(self, config):
self._config = config['ui']['display']
super(I2COled, self).__init__(config, 'i2coled')
def layout(self):
fonts.setup(8, 8, 8, 10, 10, 8)
self._layout['width'] = self._config['width'] if 'width' in self._config else 128
self._layout['height'] = self._config['height'] if 'height' in self._config else 64
self._layout['face'] = (0, 30)
self._layout['name'] = (0, 10)
self._layout['channel'] = (72, 10)
self._layout['aps'] = (0, 0)
self._layout['uptime'] = (87, 0)
self._layout['line1'] = [0, 9, 128, 9]
self._layout['line2'] = [0, 54, 128, 54]
self._layout['friend_face'] = (0, 41)
self._layout['friend_name'] = (40, 43)
self._layout['shakes'] = (0, 55)
self._layout['mode'] = (107, 10)
self._layout['status'] = {
'pos': (37, 19),
'font': fonts.status_font(fonts.Small),
'max': 18
}
return self._layout
def initialize(self):
i2caddr = self._config['i2c_addr'] if 'i2c_addr' in self._config else 0x3C
width = self._config['width'] if 'width' in self._config else 128
height = self._config['height'] if 'height' in self._config else 64
logging.info("initializing %dx%d I2C Oled Display on address 0x%X" % (width, height, i2caddr))
from pwnagotchi.ui.hw.libs.i2coled.epd import EPD
self._display = EPD(address=i2caddr, width=width, height=height)
self._display.Init()
self._display.Clear()
def render(self, canvas):
self._display.display(canvas)
def clear(self):
self._display.clear()

View File

@ -0,0 +1,312 @@
# 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.
#
# SMBus parts coming from BLavery's lib_oled96 repo:
# https://github.com/BLavery/lib_oled96
#
# Modified for the Pwnagotchi project by RasTacsko
# Using SMBus, spidev RPi.GPIO for I2C communication instead of Adafruit libraries
# spidev maybe not necessary... needs some checking!!!
# ToDo:
# rotation support for vertical layouts
# checking luma oled library for support other chipsets/resolutions
from __future__ import division
import logging
import time
import RPi.GPIO as GPIO
# import spidev
from smbus import SMBus
i2cbus = SMBus(1)
# Constants
SSD1306_I2C_ADDRESS = 0x3C # 011110+SA0+RW - 0x3C or 0x3D
SSD1306_SETCONTRAST = 0x81
SSD1306_DISPLAYALLON_RESUME = 0xA4
SSD1306_DISPLAYALLON = 0xA5
SSD1306_NORMALDISPLAY = 0xA6
SSD1306_INVERTDISPLAY = 0xA7
SSD1306_DISPLAYOFF = 0xAE
SSD1306_DISPLAYON = 0xAF
SSD1306_SETDISPLAYOFFSET = 0xD3
SSD1306_SETCOMPINS = 0xDA
SSD1306_SETVCOMDETECT = 0xDB
SSD1306_SETDISPLAYCLOCKDIV = 0xD5
SSD1306_SETPRECHARGE = 0xD9
SSD1306_SETMULTIPLEX = 0xA8
SSD1306_SETLOWCOLUMN = 0x00
SSD1306_SETHIGHCOLUMN = 0x10
SSD1306_SETSTARTLINE = 0x40
SSD1306_MEMORYMODE = 0x20
SSD1306_COLUMNADDR = 0x21
SSD1306_PAGEADDR = 0x22
SSD1306_COMSCANINC = 0xC0
SSD1306_COMSCANDEC = 0xC8
SSD1306_SEGREMAP = 0xA0
SSD1306_CHARGEPUMP = 0x8D
SSD1306_EXTERNALVCC = 0x1
SSD1306_SWITCHCAPVCC = 0x2
# Scrolling constants
SSD1306_ACTIVATE_SCROLL = 0x2F
SSD1306_DEACTIVATE_SCROLL = 0x2E
SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3
SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26
SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27
SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29
SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A
class SSD1306Base(object):
"""Base class for SSD1306-based OLED displays. Implementors should subclass
and provide an implementation for the _initialize function.
"""
def __init__(self, width, height, address=SSD1306_I2C_ADDRESS, bus=None):
self._log = logging.getLogger('Adafruit_SSD1306.SSD1306Base')
self.cmd_mode = 0x00
self.data_mode = 0x40
self.bus = i2cbus
self.addr = address
self.width = width
self.height = height
self._pages = height//8
self._buffer = [0]*(width*self._pages)
def _initialize(self):
raise NotImplementedError
def command(self, *cmd):
"""Send command byte to display."""
# I2C write.
assert(len(cmd) <= 31)
try:
self.bus.write_i2c_block_data(self.addr, self.cmd_mode, list(cmd))
except Exception as e:
logging.exception(e)
def data(self, data):
"""Send byte of data to display."""
# I2C write.
try:
for i in range(0, len(data), 31):
self.bus.write_i2c_block_data(self.addr, self.data_mode, list(data[i:i+31]))
except Exception as e:
logging.exception(e)
def begin(self, vccstate=SSD1306_SWITCHCAPVCC):
"""Initialize display."""
# Save vcc state.
self._vccstate = vccstate
# Reset and initialize display.
self._initialize()
# Turn on the display.
self.command(SSD1306_DISPLAYON)
def ShowImage(self):
"""
The image on the "canvas" is flushed through to the hardware display.
Takes the 1-bit image and dumps it to the SSD1306 OLED display.
"""
self.command(SSD1306_COLUMNADDR)
self.command(0) # Column start address. (0 = reset)
self.command(self.width-1) # Column end address.
self.command(SSD1306_PAGEADDR)
self.command(0) # Page start address. (0 = reset)
self.command(self._pages-1) # Page end address.
try:
for i in range(0, len(self._buffer), 16):
self.bus.write_i2c_block_data(self.addr, self.data_mode, self._buffer[i:i+16])
except Exception as e:
logging.exception(e)
def getbuffer(self, image):
"""Set buffer to value of Python Imaging Library image. The image should
be in 1 bit mode and a size equal to the display size.
"""
if image.mode != '1':
raise ValueError('Image must be in mode 1.')
imwidth, imheight = image.size
if imwidth != self.width or imheight != self.height:
raise ValueError('Image must be same dimensions as display ({0}x{1}).' \
.format(self.width, self.height))
# Grab all the pixels from the image, faster than getpixel.
pix = image.load()
# Iterate through the memory pages
index = 0
for page in range(self._pages):
# Iterate through all x axis columns.
for x in range(self.width):
# Set the bits for the column of pixels at the current position.
bits = 0
# Don't use range here as it's a bit slow
for bit in [0, 1, 2, 3, 4, 5, 6, 7]:
bits = bits << 1
bits |= 0 if pix[(x, page*8+7-bit)] == 0 else 1
# Update buffer byte and increment to next byte.
self._buffer[index] = bits
index += 1
def clear(self):
"""Clear contents of image buffer."""
self._buffer = [0]*(self.width*self._pages)
def set_contrast(self, contrast):
"""Sets the contrast of the display. Contrast should be a value between
0 and 255."""
if contrast < 0 or contrast > 255:
raise ValueError('Contrast must be a value from 0 to 255 (inclusive).')
self.command(SSD1306_SETCONTRAST)
self.command(contrast)
def dim(self, dim):
"""Adjusts contrast to dim the display if dim is True, otherwise sets the
contrast to normal brightness if dim is False.
"""
# Assume dim display.
contrast = 0
# Adjust contrast based on VCC if not dimming.
if not dim:
if self._vccstate == SSD1306_EXTERNALVCC:
contrast = 0x9F
else:
contrast = 0xCF
self.set_contrast(contrast)
class SSD1306_128_64(SSD1306Base):
def __init__(self, width, height, address=None, bus=None):
# Call base class constructor.
super(SSD1306_128_64, self).__init__(128, 64, address, bus)
def _initialize(self):
# 128x64 pixel specific initialization.
self.command(SSD1306_DISPLAYOFF) # 0xAE
self.command(SSD1306_SETDISPLAYCLOCKDIV) # 0xD5
self.command(0x80) # the suggested ratio 0x80
self.command(SSD1306_SETMULTIPLEX) # 0xA8
self.command(0x3F)
self.command(SSD1306_SETDISPLAYOFFSET) # 0xD3
self.command(0x0) # no offset
self.command(SSD1306_SETSTARTLINE | 0x0) # line #0
self.command(SSD1306_CHARGEPUMP) # 0x8D
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x10)
else:
self.command(0x14)
self.command(SSD1306_MEMORYMODE) # 0x20
self.command(0x00) # 0x0 act like ks0108
self.command(SSD1306_SEGREMAP | 0x1)
self.command(SSD1306_COMSCANDEC)
self.command(SSD1306_SETCOMPINS) # 0xDA
self.command(0x12)
self.command(SSD1306_SETCONTRAST) # 0x81
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x9F)
else:
self.command(0xCF)
self.command(SSD1306_SETPRECHARGE) # 0xd9
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x22)
else:
self.command(0xF1)
self.command(SSD1306_SETVCOMDETECT) # 0xDB
self.command(0x40)
self.command(SSD1306_DISPLAYALLON_RESUME) # 0xA4
self.command(SSD1306_NORMALDISPLAY) # 0xA6
class SSD1306_128_32(SSD1306Base):
def __init__(self, width, height, address=None, bus=None):
# Call base class constructor.
super(SSD1306_128_32, self).__init__(128, 32, address, bus)
def _initialize(self):
# 128x32 pixel specific initialization.
self.command(SSD1306_DISPLAYOFF) # 0xAE
self.command(SSD1306_SETDISPLAYCLOCKDIV) # 0xD5
self.command(0x80) # the suggested ratio 0x80
self.command(SSD1306_SETMULTIPLEX) # 0xA8
self.command(0x1F)
self.command(SSD1306_SETDISPLAYOFFSET) # 0xD3
self.command(0x0) # no offset
self.command(SSD1306_SETSTARTLINE | 0x0) # line #0
self.command(SSD1306_CHARGEPUMP) # 0x8D
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x10)
else:
self.command(0x14)
self.command(SSD1306_MEMORYMODE) # 0x20
self.command(0x00) # 0x0 act like ks0108
self.command(SSD1306_SEGREMAP | 0x1)
self.command(SSD1306_COMSCANDEC)
self.command(SSD1306_SETCOMPINS) # 0xDA
self.command(0x02)
self.command(SSD1306_SETCONTRAST) # 0x81
self.command(0x8F)
self.command(SSD1306_SETPRECHARGE) # 0xd9
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x22)
else:
self.command(0xF1)
self.command(SSD1306_SETVCOMDETECT) # 0xDB
self.command(0x40)
self.command(SSD1306_DISPLAYALLON_RESUME) # 0xA4
self.command(SSD1306_NORMALDISPLAY) # 0xA6
class SSD1306_96_16(SSD1306Base):
def __init__(self, width, height, address=None, bus=None):
# Call base class constructor.
super(SSD1306_96_16, self).__init__(96, 16, address, bus)
def _initialize(self):
# 128x32 pixel specific initialization.
self.command(SSD1306_DISPLAYOFF) # 0xAE
self.command(SSD1306_SETDISPLAYCLOCKDIV) # 0xD5
self.command(0x60) # the suggested ratio 0x60
self.command(SSD1306_SETMULTIPLEX) # 0xA8
self.command(0x0F)
self.command(SSD1306_SETDISPLAYOFFSET) # 0xD3
self.command(0x0) # no offset
self.command(SSD1306_SETSTARTLINE | 0x0) # line #0
self.command(SSD1306_CHARGEPUMP) # 0x8D
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x10)
else:
self.command(0x14)
self.command(SSD1306_MEMORYMODE) # 0x20
self.command(0x00) # 0x0 act like ks0108
self.command(SSD1306_SEGREMAP | 0x1)
self.command(SSD1306_COMSCANDEC)
self.command(SSD1306_SETCOMPINS) # 0xDA
self.command(0x02)
self.command(SSD1306_SETCONTRAST) # 0x81
self.command(0x8F)
self.command(SSD1306_SETPRECHARGE) # 0xd9
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x22)
else:
self.command(0xF1)
self.command(SSD1306_SETVCOMDETECT) # 0xDB
self.command(0x40)
self.command(SSD1306_DISPLAYALLON_RESUME) # 0xA4
self.command(SSD1306_NORMALDISPLAY) # 0xA6

View File

@ -0,0 +1,34 @@
from . import SSD1306
# Display resolution, change if the screen resolution is changed!
EPD_WIDTH = 128
EPD_HEIGHT = 64
# Available screen resolutions:
# disp = SSD1306.SSD1306_128_32(128, 32, address=0x3C)
# disp = SSD1306.SSD1306_96_16(96, 16, address=0x3C)
# If you change for different resolution, you have to modify the layout in pwnagotchi/ui/hw/i2coled.py
class EPD(object):
def __init__(self, address=0x3D, width=EPD_WIDTH, height=EPD_HEIGHT):
self.width = width
self.height = height
# choose subclass based on dimensions
if height == 32:
self.disp = SSD1306.SSD1306_128_32(width, height, address)
elif height == 16:
self.disp = SSD1306.SSD1306_96_16(width, height, address)
else:
self.disp = SSD1306.SSD1306_128_64(width, height, address)
def Init(self):
self.disp.begin()
def Clear(self):
self.disp.clear()
def display(self, image):
self.disp.getbuffer(image)
self.disp.ShowImage()

View File

@ -33,6 +33,8 @@ import sys
import time
import subprocess
from ctypes import *
logger = logging.getLogger(__name__)
@ -43,6 +45,8 @@ class RaspberryPi:
CS_PIN = 8
BUSY_PIN = 24
PWR_PIN = 18
MOSI_PIN = 10
SCLK_PIN = 11
def __init__(self):
import spidev
@ -98,9 +102,41 @@ class RaspberryPi:
def spi_writebyte2(self, data):
self.SPI.writebytes2(data)
def module_init(self):
def DEV_SPI_write(self, data):
self.DEV_SPI.DEV_SPI_SendData(data)
def DEV_SPI_nwrite(self, data):
self.DEV_SPI.DEV_SPI_SendnData(data)
def DEV_SPI_read(self):
return self.DEV_SPI.DEV_SPI_ReadData()
def module_init(self, cleanup=False):
self.GPIO_PWR_PIN.on()
if cleanup:
find_dirs = [
os.path.dirname(os.path.realpath(__file__)),
'/usr/local/lib',
'/usr/lib',
]
self.DEV_SPI = None
for find_dir in find_dirs:
val = int(os.popen('getconf LONG_BIT').read())
logging.debug("System is %d bit" % val)
if val == 64:
so_filename = os.path.join(find_dir, 'DEV_Config_64.so')
else:
so_filename = os.path.join(find_dir, 'DEV_Config_32.so')
if os.path.exists(so_filename):
self.DEV_SPI = CDLL(so_filename)
break
if self.DEV_SPI is None:
RuntimeError('Cannot find DEV_Config.so')
self.DEV_SPI.DEV_Module_Init()
else:
# SPI device, bus = 0, device = 0
self.SPI.open(0, 0)
self.SPI.max_speed_hz = 4000000
@ -128,5 +164,3 @@ 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

@ -446,7 +446,7 @@ class EPD:
def display_4Gray(self, image):
self.send_command(0x24)
for i in range(0, 48000): # 5808*4 46464
for i in range(0, 5808): # 5808*4 46464
temp3 = 0
for j in range(0, 2):
temp1 = image[i * 2 + j]
@ -478,7 +478,7 @@ class EPD:
self.send_data(temp3)
self.send_command(0x26)
for i in range(0, 48000): # 5808*4 46464
for i in range(0, 5808): # 5808*4 46464
temp3 = 0
for j in range(0, 2):
temp1 = image[i * 2 + j]

View File

@ -28,7 +28,7 @@
#
import logging
from pwnagotchi.ui.hw.libs.waveshare.epaper import epdconfig
from .. import epdconfig
# Display resolution
EPD_WIDTH = 400
@ -45,6 +45,10 @@ class EPD:
self.cs_pin = epdconfig.CS_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
self.flag = 0
if (epdconfig.module_init(cleanup=True) != 0):
return -1
# Hardware reset
def reset(self):
@ -58,13 +62,13 @@ class EPD:
def send_command(self, command):
epdconfig.digital_write(self.dc_pin, 0)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([command])
epdconfig.DEV_SPI_write(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.DEV_SPI_write(data)
epdconfig.digital_write(self.cs_pin, 1)
# send a lot of data
@ -76,23 +80,77 @@ class EPD:
def ReadBusy(self):
logger.debug("e-Paper busy")
self.send_command(0x71)
while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy
self.send_command(0x71)
epdconfig.delay_ms(20)
if (self.flag == 1):
while (epdconfig.digital_read(self.busy_pin) == 1):
epdconfig.delay_ms(100)
else:
while (epdconfig.digital_read(self.busy_pin) == 0):
epdconfig.delay_ms(100)
logger.debug("e-Paper busy release")
def TurnOnDisplay(self):
if (self.flag == 1):
self.send_command(0x22)
self.send_data(0xF7)
self.send_command(0x20)
self.ReadBusy()
else:
self.send_command(0x12)
epdconfig.delay_ms(100)
self.ReadBusy()
def init(self):
if (epdconfig.module_init() != 0):
return -1
i = 0x00
self.reset()
self.send_command(0x2F)
epdconfig.delay_ms(100)
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
i = epdconfig.DEV_SPI_read()
epdconfig.digital_write(self.cs_pin, 1)
# print(i)
self.send_command(0x04);
self.ReadBusy();
if (i == 0x01):
self.flag = 1
self.ReadBusy()
self.send_command(0x12)
self.ReadBusy()
self.send_command(0x00);
self.send_data(0x0f);
self.send_command(0x3C)
self.send_data(0x05)
self.send_command(0x18)
self.send_data(0x80)
self.send_command(0x11)
self.send_data(0x03)
self.send_command(0x44)
self.send_data(0x00)
self.send_data(self.width // 8 - 1)
self.send_command(0x45)
self.send_data(0x00)
self.send_data(0x00)
self.send_data((self.height - 1) % 256)
self.send_data((self.height - 1) // 256)
self.send_command(0x4E)
self.send_data(0x00)
self.send_command(0x4F)
self.send_data(0x00)
self.send_data(0x00)
self.ReadBusy()
else:
self.flag = 0
self.send_command(0x04) # POWER_ON
self.ReadBusy()
self.send_command(0x00) # panel setting
self.send_data(0x0f)
return 0
@ -121,39 +179,78 @@ class EPD:
return buf
def display(self, imageblack, imagered):
high = self.height
if (self.width % 8 == 0):
wide = self.width // 8
else:
wide = self.width // 8 + 1
if (self.flag == 1):
self.send_command(0x24)
for j in range(0, high):
for i in range(0, wide):
self.send_data(imageblack[i + j * wide])
self.send_command(0x26)
for j in range(0, high):
for i in range(0, wide):
self.send_data(~imagered[i + j * wide])
else:
self.send_command(0x10)
self.send_data2(imageblack)
for j in range(0, high):
for i in range(0, wide):
self.send_data(imageblack[i + j * wide])
self.send_command(0x13)
self.send_data2(imagered)
for j in range(0, high):
for i in range(0, wide):
self.send_data(imagered[i + j * wide])
self.send_command(0x12)
epdconfig.delay_ms(20)
self.ReadBusy()
self.TurnOnDisplay()
def Clear(self):
if self.width % 8 == 0:
linewidth = int(self.width / 8)
high = self.height
if (self.width % 8 == 0):
wide = self.width // 8
else:
linewidth = int(self.width / 8) + 1
wide = self.width // 8 + 1
if (self.flag == 1):
self.send_command(0x24)
for j in range(0, high):
for i in range(0, wide):
self.send_data(0xff)
self.send_command(0x26)
for j in range(0, high):
for i in range(0, wide):
self.send_data(0x00)
else:
self.send_command(0x10)
self.send_data2([0xff] * int(self.height * linewidth))
for j in range(0, high):
for i in range(0, wide):
self.send_data(0xff)
self.send_command(0x13)
self.send_data2([0xff] * int(self.height * linewidth))
for j in range(0, high):
for i in range(0, wide):
self.send_data(0xff)
self.send_command(0x12)
epdconfig.delay_ms(20)
self.ReadBusy()
self.TurnOnDisplay()
def sleep(self):
self.send_command(0X50)
self.send_data(0xf7) # border floating
if (self.flag == 1):
self.send_command(0X10)
self.send_data(0x03)
self.send_command(0X02) # power off
self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
self.send_command(0X07) # deep sleep
else:
self.send_command(0X50)
self.send_data(0xf7)
self.send_command(0X02)
self.ReadBusy()
self.send_command(0X07)
self.send_data(0xA5)
epdconfig.delay_ms(2000)

View File

@ -0,0 +1,294 @@
# 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.
from __future__ import division
import logging
import time
import RPi.GPIO as GPIO
import spidev
from smbus import SMBus
i2cbus = SMBus(1)
# Constants
SSD1306_I2C_ADDRESS = 0x3C # 011110+SA0+RW - 0x3C or 0x3D
SSD1306_SETCONTRAST = 0x81
SSD1306_DISPLAYALLON_RESUME = 0xA4
SSD1306_DISPLAYALLON = 0xA5
SSD1306_NORMALDISPLAY = 0xA6
SSD1306_INVERTDISPLAY = 0xA7
SSD1306_DISPLAYOFF = 0xAE
SSD1306_DISPLAYON = 0xAF
SSD1306_SETDISPLAYOFFSET = 0xD3
SSD1306_SETCOMPINS = 0xDA
SSD1306_SETVCOMDETECT = 0xDB
SSD1306_SETDISPLAYCLOCKDIV = 0xD5
SSD1306_SETPRECHARGE = 0xD9
SSD1306_SETMULTIPLEX = 0xA8
SSD1306_SETLOWCOLUMN = 0x00
SSD1306_SETHIGHCOLUMN = 0x10
SSD1306_SETSTARTLINE = 0x40
SSD1306_MEMORYMODE = 0x20
SSD1306_COLUMNADDR = 0x21
SSD1306_PAGEADDR = 0x22
SSD1306_COMSCANINC = 0xC0
SSD1306_COMSCANDEC = 0xC8
SSD1306_SEGREMAP = 0xA0
SSD1306_CHARGEPUMP = 0x8D
SSD1306_EXTERNALVCC = 0x1
SSD1306_SWITCHCAPVCC = 0x2
# Scrolling constants
SSD1306_ACTIVATE_SCROLL = 0x2F
SSD1306_DEACTIVATE_SCROLL = 0x2E
SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3
SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26
SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27
SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29
SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A
class SSD1306Base(object):
"""Base class for SSD1306-based OLED displays. Implementors should subclass
and provide an implementation for the _initialize function.
"""
def __init__(self, width, height, address=SSD1306_I2C_ADDRESS, bus=None):
self._log = logging.getLogger('Adafruit_SSD1306.SSD1306Base')
# self._rst = None
self.cmd_mode = 0x00
self.data_mode = 0x40
self.bus = i2cbus
self.addr = address
self.width = width
self.height = height
self._pages = height//8
self._buffer = [0]*(width*self._pages)
def _initialize(self):
raise NotImplementedError
def command(self, *cmd):
"""Send command byte to display."""
# I2C write.
assert(len(cmd) <= 31)
self.bus.write_i2c_block_data(self.addr, self.cmd_mode, list(cmd))
def data(self, data):
"""Send byte of data to display."""
# I2C write.
for i in range(0, len(data), 31):
self.bus.write_i2c_block_data(self.addr, self.data_mode, list(data[i:i+31]))
def begin(self, vccstate=SSD1306_SWITCHCAPVCC):
"""Initialize display."""
# Save vcc state.
self._vccstate = vccstate
# Reset and initialize display.
self._initialize()
# Turn on the display.
self.command(SSD1306_DISPLAYON)
def ShowImage(self):
"""
The image on the "canvas" is flushed through to the hardware display.
Takes the 1-bit image and dumps it to the SSD1306 OLED display.
"""
self.command(SSD1306_COLUMNADDR)
self.command(0) # Column start address. (0 = reset)
self.command(self.width-1) # Column end address.
self.command(SSD1306_PAGEADDR)
self.command(0) # Page start address. (0 = reset)
self.command(self._pages-1) # Page end address.
for i in range(0, len(self._buffer), 16):
self.bus.write_i2c_block_data(self.addr, self.data_mode, self._buffer[i:i+16])
def getbuffer(self, image):
"""Set buffer to value of Python Imaging Library image. The image should
be in 1 bit mode and a size equal to the display size.
"""
if image.mode != '1':
raise ValueError('Image must be in mode 1.')
imwidth, imheight = image.size
if imwidth != self.width or imheight != self.height:
raise ValueError('Image must be same dimensions as display ({0}x{1}).' \
.format(self.width, self.height))
# Grab all the pixels from the image, faster than getpixel.
pix = image.load()
# Iterate through the memory pages
index = 0
for page in range(self._pages):
# Iterate through all x axis columns.
for x in range(self.width):
# Set the bits for the column of pixels at the current position.
bits = 0
# Don't use range here as it's a bit slow
for bit in [0, 1, 2, 3, 4, 5, 6, 7]:
bits = bits << 1
bits |= 0 if pix[(x, page*8+7-bit)] == 0 else 1
# Update buffer byte and increment to next byte.
self._buffer[index] = bits
index += 1
def clear(self):
"""Clear contents of image buffer."""
self._buffer = [0]*(self.width*self._pages)
def set_contrast(self, contrast):
"""Sets the contrast of the display. Contrast should be a value between
0 and 255."""
if contrast < 0 or contrast > 255:
raise ValueError('Contrast must be a value from 0 to 255 (inclusive).')
self.command(SSD1306_SETCONTRAST)
self.command(contrast)
def dim(self, dim):
"""Adjusts contrast to dim the display if dim is True, otherwise sets the
contrast to normal brightness if dim is False.
"""
# Assume dim display.
contrast = 0
# Adjust contrast based on VCC if not dimming.
if not dim:
if self._vccstate == SSD1306_EXTERNALVCC:
contrast = 0x9F
else:
contrast = 0xCF
self.set_contrast(contrast)
class SSD1306_128_64(SSD1306Base):
def __init__(self, width, height, address=None, bus=None):
# Call base class constructor.
super(SSD1306_128_64, self).__init__(128, 64, address, bus)
def _initialize(self):
# 128x64 pixel specific initialization.
self.command(SSD1306_DISPLAYOFF) # 0xAE
self.command(SSD1306_SETDISPLAYCLOCKDIV) # 0xD5
self.command(0x80) # the suggested ratio 0x80
self.command(SSD1306_SETMULTIPLEX) # 0xA8
self.command(0x3F)
self.command(SSD1306_SETDISPLAYOFFSET) # 0xD3
self.command(0x0) # no offset
self.command(SSD1306_SETSTARTLINE | 0x0) # line #0
self.command(SSD1306_CHARGEPUMP) # 0x8D
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x10)
else:
self.command(0x14)
self.command(SSD1306_MEMORYMODE) # 0x20
self.command(0x00) # 0x0 act like ks0108
self.command(SSD1306_SEGREMAP | 0x1)
self.command(SSD1306_COMSCANDEC)
self.command(SSD1306_SETCOMPINS) # 0xDA
self.command(0x12)
self.command(SSD1306_SETCONTRAST) # 0x81
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x9F)
else:
self.command(0xCF)
self.command(SSD1306_SETPRECHARGE) # 0xd9
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x22)
else:
self.command(0xF1)
self.command(SSD1306_SETVCOMDETECT) # 0xDB
self.command(0x40)
self.command(SSD1306_DISPLAYALLON_RESUME) # 0xA4
self.command(SSD1306_NORMALDISPLAY) # 0xA6
class SSD1306_128_32(SSD1306Base):
def __init__(self, width, height, address=None, bus=None):
# Call base class constructor.
super(SSD1306_128_64, self).__init__(128, 64, address, bus)
def _initialize(self):
# 128x32 pixel specific initialization.
self.command(SSD1306_DISPLAYOFF) # 0xAE
self.command(SSD1306_SETDISPLAYCLOCKDIV) # 0xD5
self.command(0x80) # the suggested ratio 0x80
self.command(SSD1306_SETMULTIPLEX) # 0xA8
self.command(0x1F)
self.command(SSD1306_SETDISPLAYOFFSET) # 0xD3
self.command(0x0) # no offset
self.command(SSD1306_SETSTARTLINE | 0x0) # line #0
self.command(SSD1306_CHARGEPUMP) # 0x8D
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x10)
else:
self.command(0x14)
self.command(SSD1306_MEMORYMODE) # 0x20
self.command(0x00) # 0x0 act like ks0108
self.command(SSD1306_SEGREMAP | 0x1)
self.command(SSD1306_COMSCANDEC)
self.command(SSD1306_SETCOMPINS) # 0xDA
self.command(0x02)
self.command(SSD1306_SETCONTRAST) # 0x81
self.command(0x8F)
self.command(SSD1306_SETPRECHARGE) # 0xd9
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x22)
else:
self.command(0xF1)
self.command(SSD1306_SETVCOMDETECT) # 0xDB
self.command(0x40)
self.command(SSD1306_DISPLAYALLON_RESUME) # 0xA4
self.command(SSD1306_NORMALDISPLAY) # 0xA6
class SSD1306_96_16(SSD1306Base):
def __init__(self, width, height, address=None, bus=None):
# Call base class constructor.
super(SSD1306_96_16, self).__init__(96, 16, address, bus)
def _initialize(self):
# 128x32 pixel specific initialization.
self.command(SSD1306_DISPLAYOFF) # 0xAE
self.command(SSD1306_SETDISPLAYCLOCKDIV) # 0xD5
self.command(0x60) # the suggested ratio 0x60
self.command(SSD1306_SETMULTIPLEX) # 0xA8
self.command(0x0F)
self.command(SSD1306_SETDISPLAYOFFSET) # 0xD3
self.command(0x0) # no offset
self.command(SSD1306_SETSTARTLINE | 0x0) # line #0
self.command(SSD1306_CHARGEPUMP) # 0x8D
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x10)
else:
self.command(0x14)
self.command(SSD1306_MEMORYMODE) # 0x20
self.command(0x00) # 0x0 act like ks0108
self.command(SSD1306_SEGREMAP | 0x1)
self.command(SSD1306_COMSCANDEC)
self.command(SSD1306_SETCOMPINS) # 0xDA
self.command(0x02)
self.command(SSD1306_SETCONTRAST) # 0x81
self.command(0x8F)
self.command(SSD1306_SETPRECHARGE) # 0xd9
if self._vccstate == SSD1306_EXTERNALVCC:
self.command(0x22)
else:
self.command(0xF1)
self.command(SSD1306_SETVCOMDETECT) # 0xDB
self.command(0x40)
self.command(SSD1306_DISPLAYALLON_RESUME) # 0xA4
self.command(SSD1306_NORMALDISPLAY) # 0xA6

View File

@ -0,0 +1,352 @@
# 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=270, 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,23 @@
from . import SSD1306
# Display resolution
EPD_WIDTH = 128
EPD_HEIGHT = 64
disp = SSD1306.SSD1306_128_64(128, 64, address=0x3C)
class EPD(object):
def __init__(self):
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
def Init(self):
disp.begin()
def Clear(self):
disp.clear()
def display(self, image):
disp.getbuffer(image)
disp.ShowImage()

View File

@ -0,0 +1,59 @@
# 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 Waveshareoledlcdvert(DisplayImpl):
def __init__(self, config):
super(Waveshareoledlcdvert, self).__init__(config, 'waveshareoledlcdvert')
def layout(self):
fonts.setup(10, 9, 10, 35, 25, 9)
self._layout['width'] = 240
self._layout['height'] = 320
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 Waveshare OLED/LCD hat vertical mode")
from pwnagotchi.ui.hw.libs.waveshare.oled.oledlcd.ST7789vert 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

@ -32,13 +32,10 @@ class View(object):
logging.debug("INVERT BLACK/WHITES:" + str(config['ui']['invert']))
self.invert = 1
BLACK = 0x00
WHITE - 0xFF
WHITE = 0xFF
self._black = 0x00
self._white = 0xFF
# setup faces from the configuration in case the user customized them
faces.load_from_config(config['ui']['faces'])
@ -112,7 +109,7 @@ class View(object):
self._state.has_element(key)
def add_element(self, key, elem):
if self.invert is 1 and elem.color:
if self.invert is 1 and hasattr(elem, 'color'):
if elem.color == 0xff:
elem.color = 0x00
elif elem.color == 0x00:

View File

@ -315,9 +315,15 @@ def load_config(args):
elif config['ui']['display']['type'] in ('waveshareoledlcd'):
config['ui']['display']['type'] = 'waveshareoledlcd'
elif config['ui']['display']['type'] in ('i2coled'):
config['ui']['display']['type'] = 'i2coled'
elif config['ui']['display']['type'] in ('waveshare35lcd'):
config['ui']['display']['type'] = 'waveshare35lcd'
elif config['ui']['display']['type'] in ('waveshareoledlcdvert'):
config['ui']['display']['type'] = 'waveshareoledlcdvert'
# E-INK DISPLAYS ------------------------------------------------------------------------
# Adafruit

View File

@ -8,7 +8,7 @@ dynamic = ["version"]
dependencies = [
"Pillow",
"PyYAML",
"RPi.GPIO",
"rpi.lgpio",
"dbus-python",
"file-read-backwards",
"flask",
@ -29,8 +29,8 @@ dependencies = [
"spidev",
"stable_baselines3",
"toml",
"torch",
"torchvision",
"torch; platform_machine=='aarch64'",
"torchvision; platform_machine=='aarch64'",
"tweepy",
"websockets",
]

View File

@ -18,9 +18,7 @@ dbus-python
toml
python-dateutil
websockets
torch
torchvision
stable_baselines3
RPi.GPIO
rpi-lgpio
rpi_hardware_pwm
pydrive2