mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
Compare commits
39 Commits
Author | SHA1 | Date | |
---|---|---|---|
357ffc67e5 | |||
ea8fb5e533 | |||
31afd6d0ba | |||
b38274e652 | |||
518e8c219c | |||
4e07fbf1aa | |||
b3e81a95c8 | |||
050966215b | |||
fef442edbb | |||
1064936503 | |||
a762a7f763 | |||
8fd0e358a9 | |||
9a941c1d46 | |||
ead5a3baac | |||
ac2973889d | |||
8991dd6811 | |||
415e5cd551 | |||
47705ba1a2 | |||
7c7bbc770b | |||
ab83de4905 | |||
8f7741cd9e | |||
835886e6f6 | |||
6b2039a8e6 | |||
d53d0c7841 | |||
6c40998b51 | |||
a71a90ba3d | |||
b03f6f747b | |||
a8ba88c9cc | |||
f597bd6d29 | |||
a3f103ac06 | |||
e0a068e51d | |||
dc1b3c7635 | |||
3657859a73 | |||
33ff5a0bf8 | |||
666f65c640 | |||
84d45a0d45 | |||
06a4491008 | |||
5485f83ca6 | |||
ab541458fa |
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@ -1,4 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: jayofelony
|
||||
custom: https://tikkie.me/pay/dubcto94hnskg539kar0
|
||||
github: jayofelony
|
@ -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")
|
||||
|
@ -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
|
@ -32,6 +32,7 @@ start_monitor_interface() {
|
||||
ifconfig wlan0 down
|
||||
ifconfig wlan0mon up
|
||||
iw dev wlan0mon set power_save off
|
||||
iw dev wlan0 del
|
||||
}
|
||||
|
||||
# stops mon0
|
||||
|
@ -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
|
@ -26,6 +26,7 @@ start_monitor_interface() {
|
||||
ifconfig wlan0 down
|
||||
ifconfig wlan0mon up
|
||||
iw dev wlan0mon set power_save off
|
||||
iw dev wlan0 del
|
||||
}
|
||||
|
||||
# stops mon0
|
||||
|
@ -62,7 +62,7 @@
|
||||
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:
|
||||
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:
|
||||
@ -123,6 +123,7 @@
|
||||
- libc-ares-dev
|
||||
- libc6-dev
|
||||
- libcpuinfo-dev
|
||||
- libcurl4-openssl-dev
|
||||
- libdbus-1-dev
|
||||
- libdbus-glib-1-dev
|
||||
- libeigen3-dev
|
||||
@ -151,6 +152,7 @@
|
||||
- libsleef-dev
|
||||
- libsqlite3-dev
|
||||
- libssl-dev
|
||||
- libssl-ocaml-dev
|
||||
- libswscale5
|
||||
- libtiff5
|
||||
- libtool
|
||||
@ -206,6 +208,30 @@
|
||||
state: present
|
||||
when: hostname.changed
|
||||
|
||||
- name: setup /boot/config.txt
|
||||
blockinfile:
|
||||
path: /boot/config.txt
|
||||
insertafter: EOF
|
||||
block: |
|
||||
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
|
||||
|
||||
- name: Create custom plugin directory
|
||||
file:
|
||||
path: '{{ pwnagotchi.custom_plugin_dir }}'
|
||||
@ -264,6 +290,24 @@
|
||||
dest: /usr/local/lib/libpcap.so.0.8
|
||||
state: link
|
||||
|
||||
# install latest hcxtools
|
||||
|
||||
#- name: clone hcxtools
|
||||
# git:
|
||||
# repo: https://github.com/ZerBea/hcxtools.git
|
||||
# dest: /usr/local/src/hcxtools
|
||||
|
||||
#- 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
|
||||
|
||||
###############################################################
|
||||
# Install nexmon to fix wireless scanning (takes 2.5G of space)
|
||||
###############################################################
|
||||
@ -374,21 +418,36 @@
|
||||
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 }}"
|
||||
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:
|
||||
@ -559,4 +618,4 @@
|
||||
handlers:
|
||||
- name: reload systemd services
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
daemon_reload: yes
|
||||
|
@ -211,6 +211,34 @@
|
||||
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
|
||||
|
||||
[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
|
||||
|
@ -1 +1 @@
|
||||
__version__ = '2.8.9'
|
||||
__version__ = '2.9.1'
|
||||
|
Binary file not shown.
@ -1,260 +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 <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-17 15:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Foxy <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: Portuguese (Brazil)\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 "Olá, Eu sou Pwnagotchi! Iniciando ..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Um novo dia, Uma nova caça e novos pwns!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Burle o Planeta!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "IA pronta."
|
||||
|
||||
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 ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Ei, canal {channel} está livre! Seu AP vai agradecer."
|
||||
|
||||
msgid "Reading last session logs ..."
|
||||
msgstr "Lendo os logs da ultima sessão"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Read {lines_so_far} log lines so far ..."
|
||||
msgstr "Leia {lines_so_far} linha de logs até agora ..."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Eu estou entediado ..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Vamos ir numa caminhada!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Esse é o melhor dia da minha vida!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Dia ruim :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Eu estou extremamente entediado ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Eu estou muito triste ..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Eu estou triste"
|
||||
|
||||
msgid "Leave me alone ..."
|
||||
msgstr "Me deixe em paz ..."
|
||||
|
||||
msgid "I'm mad at you!"
|
||||
msgstr "Eu estou bravo com você!"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Eu estou vivendo a vida!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Eu pwn então Eu sou."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Tantas redes!!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Eu estou tendo muita diversão"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Meu crime é de curiosidade ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you."
|
||||
msgstr "Olá {name}! É bom em conhecê-lo"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {name}! Sup?"
|
||||
msgstr "Ei {name}! Como vai?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {name} how are you doing?"
|
||||
msgstr "Ei {name} como você está indo?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name} errou!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Errei!"
|
||||
|
||||
msgid "Good friends are a blessing!"
|
||||
msgstr "Bom amigos são uma bensão!"
|
||||
|
||||
msgid "I love my friends!"
|
||||
msgstr "Eu amo meus amigos!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Ninguém quer brincar comigo ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Estou me sentindo sozinho"
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Onde está todo mundo?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Tirando uma soneca por {secs}s ..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "ZzzZzzz ({secs}s)"
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "Boa noite."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr "Zzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Esperando por {secs}s ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Olhando por volta ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Ei {what} vamos ser amigos!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Associando para {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "Ei {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Apenas decidindo que {mac} não precisa de WiFi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "Desautenticando {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "Banindo {mac}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Legal, conseguimos {num} novos handshake{plural}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "Você tem {count} novas messagem{plural}!"
|
||||
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Oops, algo deu errado ... Reiniciando ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uploading data to {to} ..."
|
||||
msgstr "Enviando dados para {to} ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Downloading from {name} ..."
|
||||
msgstr "Instalando para {name} ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Expulsei {num} estações\n"
|
||||
|
||||
msgid "Made >999 new friends\n"
|
||||
msgstr "Fiz >999 novos amigos\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Fiz {num} novos amigos\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Peguei {num} handshakes\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Encontrei 1 pessoa"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Encontrei {num} pessoas"
|
||||
|
||||
#, 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 ""
|
||||
"Estou navegando há {duration} e expulsei {deauthed} clientes! Também conheci "
|
||||
"{associamos} novos amigos e comi {handshakes} handshakes! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "horas"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "minutos"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "segundos"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "hora"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "minuto"
|
||||
|
||||
msgid "second"
|
||||
# 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: 2024-03-25 22:30+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Fabiano F O <fabfernandes@hotmail.com>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: Brazilian Portuguese\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 "Olá, sou Pwnagotchi! Iniciando ..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Novo dia, Nova caçada, Novos pwns!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hackeie o Planeta!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "IA pronta."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "A rede neural está pronta."
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Gerando chaves, não desligue ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Ei, o canal {channel} está livre! Seu AP vai agradecer."
|
||||
|
||||
msgid "Reading last session logs ..."
|
||||
msgstr "Lendo os logs da última sessão ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Read {lines_so_far} log lines so far ..."
|
||||
msgstr "Li {lines_so_far} linhas de logs até agora ..."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Estou entediado ..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Vamos dar um passeio!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Este é o melhor dia da minha vida!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Que dia ruim :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Estou extremamente entediado ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Estou muito triste ..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Estou triste"
|
||||
|
||||
msgid "Leave me alone ..."
|
||||
msgstr "Me deixe em paz ..."
|
||||
|
||||
msgid "I'm mad at you!"
|
||||
msgstr "Estou bravo com você!"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Estou aproveitando a vida!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Eu pwn, logo existo."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Uau! Quantas redes!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Estou me divertindo muito!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Meu crime é a curiosidade ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you."
|
||||
msgstr "Olá {name}! Prazer em conhecê-lo."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {name}! Sup?"
|
||||
msgstr "Ei {name}! Como vai?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {name} how are you doing?"
|
||||
msgstr "Ei {name}, como você está?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr "A unidade {name} está próxima!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Hmm ... tchau {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} desapareceu ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Oops ... {name} desapareceu."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "Perdi {name}!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Perdi!"
|
||||
|
||||
msgid "Good friends are a blessing!"
|
||||
msgstr "Bons amigos são uma bênção!"
|
||||
|
||||
msgid "I love my friends!"
|
||||
msgstr "Eu amo meus amigos!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Ninguém quer brincar comigo ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Me sinto tão sozinho ..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Onde está todo mundo?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Tirando uma soneca por {secs}s ..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "ZzzZzzz ({secs}s)"
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "Boa noite."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr "Zzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Aguardando {secs}s ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Olhando em volta ... ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Ei {what}, vamos ser amigos!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Associando a {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "Olá {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Acabei de decidir que {mac} não precisa de WiFi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "Desautenticando {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "Banindo {mac}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Legal, conseguimos {num} novo{plural} handshake{plural}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "Você tem {count} nova{plural} messagem{plural}!"
|
||||
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Oops, algo deu errado ... Reiniciando ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uploading data to {to} ..."
|
||||
msgstr "Enviando dados para {to} ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Downloading from {name} ..."
|
||||
msgstr "Baixando de {name} ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Expulsei {num} estações\n"
|
||||
|
||||
msgid "Made >999 new friends\n"
|
||||
msgstr "Fiz >999 novos amigos\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Fiz {num} novos amigos\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Peguei {num} handshakes\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Conheci 1 peer"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Conheci {num} peers"
|
||||
|
||||
#, 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 ""
|
||||
"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"
|
||||
msgstr "horas"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "minutos"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "segundos"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "hora"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "minuto"
|
||||
|
||||
msgid "second"
|
||||
msgstr "segundo"
|
Binary file not shown.
@ -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 "遇到了 1 個同好"
|
||||
|
||||
#, 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 "秒"
|
||||
|
@ -35,6 +35,7 @@ class FixServices(plugins.Plugin):
|
||||
self.pattern4 = re.compile(r'error 400: could not find interface wlan0mon')
|
||||
self.pattern5 = re.compile(r'fatal error: concurrent map iteration and map write')
|
||||
self.pattern6 = re.compile(r'panic: runtime error')
|
||||
self.pattern7 = re.compile(r'ieee80211 phy0: _brcmf_set_multicast_list: Setting allmulti failed, -110')
|
||||
self.isReloadingMon = False
|
||||
self.connection = None
|
||||
self.LASTTRY = 0
|
||||
@ -111,7 +112,7 @@ class FixServices(plugins.Plugin):
|
||||
logging.debug("[Fix_Services]**** epoch")
|
||||
if time.time() - self.LASTTRY > 180:
|
||||
# get last 10 lines
|
||||
display = None
|
||||
display = agent.view()
|
||||
|
||||
logging.debug("[Fix_Services]**** checking")
|
||||
|
||||
@ -119,7 +120,6 @@ class FixServices(plugins.Plugin):
|
||||
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 = agent.view()
|
||||
display.set('status', 'Blind-Bug detected. Restarting.')
|
||||
display.update(force=True)
|
||||
logging.debug('[Fix_Services] Blind-Bug detected. Restarting.')
|
||||
@ -132,7 +132,6 @@ class FixServices(plugins.Plugin):
|
||||
elif 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 = agent.view()
|
||||
display.set('status', 'Wifi channel stuck. Restarting recon.')
|
||||
display.update(force=True)
|
||||
logging.debug('[Fix_Services] Wifi channel stuck. Restarting recon.')
|
||||
@ -156,7 +155,6 @@ class FixServices(plugins.Plugin):
|
||||
elif len(self.pattern3.findall(other_last_lines)) >= 1:
|
||||
logging.debug("[Fix_Services] Firmware has halted or crashed. Restarting wlan0mon.")
|
||||
if hasattr(agent, 'view'):
|
||||
display = agent.view()
|
||||
display.set('status', 'Firmware has halted or crashed. Restarting wlan0mon.')
|
||||
display.update(force=True)
|
||||
try:
|
||||
@ -170,7 +168,6 @@ class FixServices(plugins.Plugin):
|
||||
elif len(self.pattern4.findall(other_other_last_lines)) >= 3:
|
||||
logging.debug("[Fix_Services] wlan0 is down!")
|
||||
if hasattr(agent, 'view'):
|
||||
display = agent.view()
|
||||
display.set('status', 'Restarting wlan0 now!')
|
||||
display.update(force=True)
|
||||
try:
|
||||
@ -184,7 +181,6 @@ class FixServices(plugins.Plugin):
|
||||
elif len(self.pattern5.findall(other_other_last_lines)) >= 1:
|
||||
logging.debug("[Fix_Services] Bettercap has crashed!")
|
||||
if hasattr(agent, 'view'):
|
||||
display = agent.view()
|
||||
display.set('status', 'Restarting pwnagotchi!')
|
||||
display.update(force=True)
|
||||
os.system("systemctl restart bettercap")
|
||||
@ -194,11 +190,28 @@ class FixServices(plugins.Plugin):
|
||||
elif len(self.pattern6.findall(other_other_last_lines)) >= 1:
|
||||
logging.debug("[Fix_Services] Bettercap has crashed!")
|
||||
if hasattr(agent, 'view'):
|
||||
display = agent.view()
|
||||
display.set('status', 'Restarting pwnagotchi!')
|
||||
display.update(force=True)
|
||||
os.system("systemctl restart bettercap")
|
||||
pwnagotchi.restart("AUTO")
|
||||
|
||||
# Look for pattern 7
|
||||
elif len(self.pattern7.findall(other_other_last_lines)) >= 1:
|
||||
logging.debug("[Fix_Services] Monitor mode failed!")
|
||||
try:
|
||||
result = agent.run("wifi.recon off; wifi.recon on")
|
||||
if result["success"]:
|
||||
logging.debug("[Fix_Services] wifi.recon flip: success!")
|
||||
if display:
|
||||
display.update(force=True, new_data={"status": "Wifi recon flipped!",
|
||||
"face": faces.COOL})
|
||||
else:
|
||||
print("Wifi recon flipped\nthat was easy!")
|
||||
else:
|
||||
logging.warning("[Fix_Services] wifi.recon flip: FAILED: %s" % repr(result))
|
||||
|
||||
except Exception as err:
|
||||
logging.error("[Fix_Services wifi.recon flip] %s" % repr(err))
|
||||
else:
|
||||
print("logs look good")
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -264,6 +264,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'
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
67
pwnagotchi/ui/hw/i2coled.py
Normal file
67
pwnagotchi/ui/hw/i2coled.py
Normal 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()
|
312
pwnagotchi/ui/hw/libs/i2coled/SSD1306.py
Normal file
312
pwnagotchi/ui/hw/libs/i2coled/SSD1306.py
Normal 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
|
34
pwnagotchi/ui/hw/libs/i2coled/epd.py
Normal file
34
pwnagotchi/ui/hw/libs/i2coled/epd.py
Normal 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()
|
@ -31,7 +31,6 @@ import os
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -113,6 +112,7 @@ class RaspberryPi:
|
||||
|
||||
self.GPIO_RST_PIN.off()
|
||||
self.GPIO_DC_PIN.off()
|
||||
self.GPIO_CS_PIN.off()
|
||||
self.GPIO_PWR_PIN.off()
|
||||
logger.debug("close 5V, Module enters 0 power consumption ...")
|
||||
|
||||
|
294
pwnagotchi/ui/hw/libs/waveshare/oled/oledlcd/SSD1306.py
Normal file
294
pwnagotchi/ui/hw/libs/waveshare/oled/oledlcd/SSD1306.py
Normal 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
|
352
pwnagotchi/ui/hw/libs/waveshare/oled/oledlcd/ST7789vert.py
Normal file
352
pwnagotchi/ui/hw/libs/waveshare/oled/oledlcd/ST7789vert.py
Normal 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()
|
23
pwnagotchi/ui/hw/libs/waveshare/oled/oledlcd/epd.py
Normal file
23
pwnagotchi/ui/hw/libs/waveshare/oled/oledlcd/epd.py
Normal 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()
|
59
pwnagotchi/ui/hw/waveshareoledlcdvert.py
Normal file
59
pwnagotchi/ui/hw/waveshareoledlcdvert.py
Normal 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()
|
@ -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'])
|
||||
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user