Compare commits

...

21 Commits

Author SHA1 Message Date
7dfb2f2ff6 Update build for Pi 5 users 2024-01-23 14:56:34 +01:00
cf46ab3da9 Removed hashie interval and deprecated functions of hcxpcapngtool. 2024-01-23 14:47:06 +01:00
6824e541a4 Removed hashie interval and deprecated functions of hcxpcapngtool. 2024-01-23 14:40:41 +01:00
1c4e8fa750 Removed hashie interval and deprecated functions of hcxpcapngtool. 2024-01-23 14:36:46 +01:00
a50ec4a65c Removed hashie interval and deprecated functions of hcxpcapngtool. 2024-01-23 14:35:12 +01:00
5414fdf21d Removed hashie interval and deprecated functions of hcxpcapngtool. 2024-01-23 13:29:31 +01:00
c9883bb4f9 Add hcxtools to apt_packages.txt 2024-01-23 12:27:59 +01:00
045f5853bc Test 2024-01-23 12:26:33 +01:00
6dfcaf05d5 Hashie enabled by default 2024-01-23 12:26:00 +01:00
e2f8119d2c Install apt-packages while installing pwnagotchi, if needed 2024-01-23 12:22:30 +01:00
d675b3d0dd Added hcxtools as installed package. 2024-01-23 11:46:50 +01:00
30850b6530 Added hcxtools as installed package. 2024-01-23 11:42:47 +01:00
1e9c9bae42 Added hcxtools as installed package. 2024-01-23 11:38:14 +01:00
64241515ad Added hashie to default plugins, to convert pcaps to a crackable format from the start. 2024-01-23 11:37:34 +01:00
c9f232dae2 Added hashie to default plugins, to convert pcaps to a crackable format from the start. 2024-01-23 11:37:23 +01:00
4f97bcaa83 Moved aircrackonly to default plugins, is also enabled by default. To immediately delete empty handshakes. 2024-01-23 11:25:04 +01:00
7d2a03c79f Changing behaviour when mon_max_blind_epochs is hit, from full reboot to simple restart to save uptime. 2024-01-23 11:13:23 +01:00
f89ba85f73 Couple updated displays from Waveshare, not 213g.
166c012cde
2024-01-23 10:59:25 +01:00
57f03f4359 Removed paw-gps.py, as it is no longer supported. 2024-01-22 23:56:13 +01:00
9bc266f9ff Make wigle, wpa-sec, onlinehashcrack and grid plugins check against main.whitelist instead of their own whitelist. Possibly reduces possibility of errors in config.
https://github.com/jayofelony/pwnagotchi-bookworm/issues/24
2024-01-22 23:29:43 +01:00
b0db0285bc Version 2.7.7 2024-01-22 23:12:59 +01:00
23 changed files with 831 additions and 274 deletions

1
apt_packages.txt Normal file
View File

@ -0,0 +1 @@
hcxtools

View File

@ -7,6 +7,7 @@
kernel: kernel:
min: "6.1" min: "6.1"
full: "6.1.0-rpi7-rpi-v8" full: "6.1.0-rpi7-rpi-v8"
full_pi5: "6.1.0-rpi7-rpi-2712"
pwnagotchi: pwnagotchi:
hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}" hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi-torch', true) }}" version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi-torch', true) }}"
@ -80,6 +81,7 @@
- gawk - gawk
- gcc-arm-none-eabi - gcc-arm-none-eabi
- git - git
- hcxtools
- libatlas-base-dev - libatlas-base-dev
- libavcodec59 - libavcodec59
- libavformat59 - libavformat59
@ -342,11 +344,19 @@
- name: backup original driver - 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" 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: 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"
- name: copy modified driver - name: copy modified driver
copy: copy:
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko" 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" dest: "/usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
- name: copy modified driver, RPi5
copy:
src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko"
dest: "/usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko"
- name : load brcmfmac drivers - name : load brcmfmac drivers
command: "/sbin/depmod -a" command: "/sbin/depmod -a"

View File

@ -1 +1 @@
__version__ = '2.7.6' __version__ = '2.7.7'

View File

@ -277,6 +277,10 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
self._save_recovery_data() self._save_recovery_data()
pwnagotchi.reboot() pwnagotchi.reboot()
def _restart(self):
self._save_recovery_data()
pwnagotchi.restart("AUTO")
def _save_recovery_data(self): def _save_recovery_data(self):
logging.warning("writing recovery data to %s ...", RECOVERY_DATA_FILE) logging.warning("writing recovery data to %s ...", RECOVERY_DATA_FILE)
with open(RECOVERY_DATA_FILE, 'w') as fp: with open(RECOVERY_DATA_FILE, 'w') as fp:

View File

@ -138,6 +138,6 @@ class Automata(object):
plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data()) plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data())
if self._epoch.blind_for >= self._config['main']['mon_max_blind_epochs']: if self._epoch.blind_for >= self._config['main']['mon_max_blind_epochs']:
logging.critical("%d epochs without visible access points -> rebooting ...", self._epoch.blind_for) logging.critical("%d epochs without visible access points -> restarting ...", self._epoch.blind_for)
self._reboot() self._restart()
self._epoch.blind_for = 0 self._epoch.blind_for = 0

View File

@ -1,5 +1,11 @@
main.name = "pwnagotchi" main.name = "pwnagotchi"
main.lang = "en" main.lang = "en"
main.whitelist = [
"EXAMPLE_NETWORK",
"ANOTHER_EXAMPLE_NETWORK",
"fo:od:ba:be:fo:od",
"fo:od:ba"
]
main.confd = "/etc/pwnagotchi/conf.d/" main.confd = "/etc/pwnagotchi/conf.d/"
main.custom_plugin_repos = [ main.custom_plugin_repos = [
"https://github.com/jayofelony/pwnagotchi-torch-plugins/archive/master.zip", "https://github.com/jayofelony/pwnagotchi-torch-plugins/archive/master.zip",
@ -12,6 +18,10 @@ main.custom_plugin_repos = [
main.custom_plugins = "/usr/local/share/pwnagotchi/custom-plugins/" main.custom_plugins = "/usr/local/share/pwnagotchi/custom-plugins/"
main.plugins.aircrackonly.enabled = true
main.plugins.hashie.enabled = true
main.plugins.auto-update.enabled = true main.plugins.auto-update.enabled = true
main.plugins.auto-update.install = true main.plugins.auto-update.install = true
main.plugins.auto-update.interval = 1 main.plugins.auto-update.interval = 1
@ -55,9 +65,6 @@ main.plugins.gps.device = "/dev/ttyUSB0" # for GPSD: "localhost:2947"
main.plugins.grid.enabled = true main.plugins.grid.enabled = true
main.plugins.grid.report = true main.plugins.grid.report = true
main.plugins.grid.exclude = [
"YourHomeNetworkHere"
]
main.plugins.logtail.enabled = false main.plugins.logtail.enabled = false
main.plugins.logtail.max-lines = 10000 main.plugins.logtail.max-lines = 10000
@ -73,10 +80,6 @@ main.plugins.onlinehashcrack.enabled = false
main.plugins.onlinehashcrack.email = "" main.plugins.onlinehashcrack.email = ""
main.plugins.onlinehashcrack.dashboard = "" main.plugins.onlinehashcrack.dashboard = ""
main.plugins.onlinehashcrack.single_files = false main.plugins.onlinehashcrack.single_files = false
main.plugins.onlinehashcrack.whitelist = []
main.plugins.paw-gps.enabled = false
main.plugins.paw-gps.ip = "192.168.44.1:8080"
main.plugins.pisugar2.enabled = false main.plugins.pisugar2.enabled = false
main.plugins.pisugar2.shutdown = 5 main.plugins.pisugar2.shutdown = 5
@ -100,26 +103,19 @@ main.plugins.webgpsmap.enabled = false
main.plugins.wigle.enabled = false main.plugins.wigle.enabled = false
main.plugins.wigle.api_key = "" main.plugins.wigle.api_key = ""
main.plugins.wigle.whitelist = [] main.plugins.wigle.donate = false
main.plugins.wigle.donate = true
main.plugins.wpa-sec.enabled = false main.plugins.wpa-sec.enabled = false
main.plugins.wpa-sec.api_key = "" main.plugins.wpa-sec.api_key = ""
main.plugins.wpa-sec.api_url = "https://wpa-sec.stanev.org" main.plugins.wpa-sec.api_url = "https://wpa-sec.stanev.org"
main.plugins.wpa-sec.download_results = false main.plugins.wpa-sec.download_results = false
main.plugins.wpa-sec.whitelist = []
main.iface = "wlan0mon" main.iface = "wlan0mon"
main.mon_start_cmd = "/usr/bin/monstart" main.mon_start_cmd = "/usr/bin/monstart"
main.mon_stop_cmd = "/usr/bin/monstop" main.mon_stop_cmd = "/usr/bin/monstop"
main.mon_max_blind_epochs = 50 main.mon_max_blind_epochs = 50
main.no_restart = false main.no_restart = false
main.whitelist = [
"EXAMPLE_NETWORK",
"ANOTHER_EXAMPLE_NETWORK",
"fo:od:ba:be:fo:od",
"fo:od:ba"
]
main.filter = "" main.filter = ""
main.log.path = "/home/pi/logs/pwnagotchi.log" main.log.path = "/home/pi/logs/pwnagotchi.log"

View File

@ -0,0 +1,69 @@
import pwnagotchi.plugins as plugins
import logging
import subprocess
import string
import os
'''
Aircrack-ng needed, to install:
> apt-get install aircrack-ng
'''
class AircrackOnly(plugins.Plugin):
__author__ = 'pwnagotchi [at] rossmarks [dot] uk'
__version__ = '1.0.1'
__license__ = 'GPL3'
__description__ = 'confirm pcap contains handshake/PMKID or delete it'
def __init__(self):
self.text_to_set = ""
def on_loaded(self):
logging.info("aircrackonly plugin loaded")
if 'face' not in self.options:
self.options['face'] = '(>.<)'
check = subprocess.run(
'/usr/bin/dpkg -l aircrack-ng | grep aircrack-ng | awk \'{print $2, $3}\'', shell=True,
stdout=subprocess.PIPE)
check = check.stdout.decode('utf-8').strip()
if check != "aircrack-ng <none>":
logging.info("aircrackonly: Found " + check)
else:
logging.warning("aircrack-ng is not installed!")
def on_handshake(self, agent, filename, access_point, client_station):
display = agent.view()
to_delete = 0
handshake_found = 0
result = subprocess.run(('/usr/bin/aircrack-ng ' + filename + ' | grep "1 handshake" | awk \'{print $2}\''),
shell=True, stdout=subprocess.PIPE)
result = result.stdout.decode('utf-8').translate({ord(c): None for c in string.whitespace})
if result:
handshake_found = 1
logging.info("[AircrackOnly] contains handshake")
if handshake_found == 0:
result = subprocess.run(('/usr/bin/aircrack-ng ' + filename + ' | grep "PMKID" | awk \'{print $2}\''),
shell=True, stdout=subprocess.PIPE)
result = result.stdout.decode('utf-8').translate({ord(c): None for c in string.whitespace})
if result:
logging.info("[AircrackOnly] contains PMKID")
else:
to_delete = 1
if to_delete == 1:
os.remove(filename)
self.text_to_set = "Removed an uncrackable pcap"
logging.warning("Removed uncrackable pcap " + filename)
display.update(force=True)
def on_ui_update(self, ui):
if self.text_to_set:
ui.set('face', self.options['face'])
ui.set('status', self.text_to_set)
self.text_to_set = ""

View File

@ -58,8 +58,9 @@ class Grid(plugins.Plugin):
self.total_messages = 0 self.total_messages = 0
self.lock = Lock() self.lock = Lock()
def is_excluded(self, what): def is_excluded(self, what, agent):
for skip in self.options['exclude']: config = agent.config()
for skip in config['main']['whitelist']:
skip = skip.lower() skip = skip.lower()
what = what.lower() what = what.lower()
if skip in what or skip.replace(':', '') in what: if skip in what or skip.replace(':', '') in what:
@ -87,6 +88,7 @@ class Grid(plugins.Plugin):
def check_handshakes(self, agent): def check_handshakes(self, agent):
logging.debug("checking pcaps") logging.debug("checking pcaps")
config = agent.config()
pcap_files = glob.glob(os.path.join(agent.config()['bettercap']['handshakes'], "*.pcap")) pcap_files = glob.glob(os.path.join(agent.config()['bettercap']['handshakes'], "*.pcap"))
num_networks = len(pcap_files) num_networks = len(pcap_files)
@ -98,19 +100,19 @@ class Grid(plugins.Plugin):
if self.options['report']: if self.options['report']:
logging.info("grid: %d new networks to report" % num_new) logging.info("grid: %d new networks to report" % num_new)
logging.debug("self.options: %s" % self.options) logging.debug("self.options: %s" % self.options)
logging.debug(" exclude: %s" % self.options['exclude']) logging.debug(" exclude: %s" % config['main']['whitelist'])
for pcap_file in pcap_files: for pcap_file in pcap_files:
net_id = os.path.basename(pcap_file).replace('.pcap', '') net_id = os.path.basename(pcap_file).replace('.pcap', '')
if net_id not in reported: if net_id not in reported:
if self.is_excluded(net_id): if self.is_excluded(net_id, agent):
logging.debug("skipping %s due to exclusion filter" % pcap_file) logging.debug("skipping %s due to exclusion filter" % pcap_file)
self.set_reported(reported, net_id) self.set_reported(reported, net_id)
continue continue
essid, bssid = parse_pcap(pcap_file) essid, bssid = parse_pcap(pcap_file)
if bssid: if bssid:
if self.is_excluded(essid) or self.is_excluded(bssid): if self.is_excluded(essid, agent) or self.is_excluded(bssid, agent):
logging.debug("not reporting %s due to exclusion filter" % pcap_file) logging.debug("not reporting %s due to exclusion filter" % pcap_file)
self.set_reported(reported, net_id) self.set_reported(reported, net_id)
else: else:

View File

@ -0,0 +1,179 @@
import logging
import subprocess
import os
import json
import pwnagotchi.plugins as plugins
from threading import Lock
'''
hcxpcapngtool needed, to install:
> git clone https://github.com/ZerBea/hcxtools.git
> cd hcxtools
> apt-get install libcurl4-openssl-dev libssl-dev zlib1g-dev
> make
> sudo make install
'''
class Hashie(plugins.Plugin):
__author__ = 'Jayofelony'
__version__ = '1.0.4'
__license__ = 'GPL3'
__description__ = '''
Attempt to automatically convert pcaps to a crackable format.
If successful, the files containing the hashes will be saved
in the same folder as the handshakes.
The files are saved in their respective Hashcat format:
- EAPOL hashes are saved as *.22000
- PMKID hashes are saved as *.16800
All PCAP files without enough information to create a hash are
stored in a file that can be read by the webgpsmap plugin.
Why use it?:
- Automatically convert handshakes to crackable formats!
We dont all upload our hashes online ;)
- Repair PMKID handshakes that hcxpcapngtool misses
- If running at time of handshake capture, on_handshake can
be used to improve the chance of the repair succeeding
- Be a completionist! Not enough packets captured to crack a network?
This generates an output file for the webgpsmap plugin, use the
location data to revisit networks you need more packets for!
Additional information:
- Currently requires hcxpcapngtool compiled and installed
- Attempts to repair PMKID hashes when hcxpcapngtool cant find the SSID
- hcxpcapngtool sometimes has trouble extracting the SSID, so we
use the raw 16800 output and attempt to retrieve the SSID via tcpdump
- When access_point data is available (on_handshake), we leverage
the reported AP name and MAC to complete the hash
- The repair is very basic and could certainly be improved!
Todo:
Make it so users dont need hcxpcapngtool (unless it gets added to the base image)
Phase 1: Extract/construct 22000/16800 hashes through tcpdump commands
Phase 2: Extract/construct 22000/16800 hashes entirely in python
Improve the code, a lot
'''
def __init__(self):
self.lock = Lock()
self.options = dict()
def on_loaded(self):
logging.info("[Hashie] Plugin loaded")
def on_unloaded(self):
logging.info("[Hashie] Plugin unloaded")
# called when everything is ready and the main loop is about to start
def on_ready(self, agent):
config = agent.config()
handshake_dir = config['bettercap']['handshakes']
logging.info('[Hashie] Starting batch conversion of pcap files')
with self.lock:
self._process_stale_pcaps(handshake_dir)
def on_handshake(self, agent, filename, access_point, client_station):
with self.lock:
handshake_status = []
fullpathNoExt = filename.split('.')[0]
name = filename.split('/')[-1:][0].split('.')[0]
if os.path.isfile(fullpathNoExt + '.22000'):
handshake_status.append('Already have {}.22000 (EAPOL)'.format(name))
elif self._writeEAPOL(filename):
handshake_status.append('Created {}.22000 (EAPOL) from pcap'.format(name))
if os.path.isfile(fullpathNoExt + '.16800'):
handshake_status.append('Already have {}.16800 (PMKID)'.format(name))
elif self._writePMKID(filename):
handshake_status.append('Created {}.16800 (PMKID) from pcap'.format(name))
if handshake_status:
logging.info('[Hashie] Good news:\n\t' + '\n\t'.join(handshake_status))
def _writeEAPOL(self, fullpath):
fullpathNoExt = fullpath.split('.')[0]
filename = fullpath.split('/')[-1:][0].split('.')[0]
subprocess.getoutput('hcxpcapngtool -o {}.22000 {} >/dev/null 2>&1'.format(fullpathNoExt, fullpath))
if os.path.isfile(fullpathNoExt + '.22000'):
logging.debug('[Hashie] [+] EAPOL Success: {}.22000 created'.format(filename))
return True
return False
def _writePMKID(self, fullpath):
fullpathNoExt = fullpath.split('.')[0]
filename = fullpath.split('/')[-1:][0].split('.')[0]
subprocess.getoutput('hcxpcapngtool -o {}.16800 {} >/dev/null 2>&1'.format(fullpathNoExt, fullpath))
if os.path.isfile(fullpathNoExt + '.16800'):
logging.debug('[Hashie] [+] PMKID Success: {}.16800 created'.format(filename))
return True
return False
def _process_stale_pcaps(self, handshake_dir):
handshakes_list = [os.path.join(handshake_dir, filename) for filename in os.listdir(handshake_dir) if filename.endswith('.pcap')]
failed_jobs = []
successful_jobs = []
lonely_pcaps = []
for num, handshake in enumerate(handshakes_list):
fullpathNoExt = handshake.split('.')[0]
pcapFileName = handshake.split('/')[-1:][0]
if not os.path.isfile(fullpathNoExt + '.22000'): # if no 22000, try
if self._writeEAPOL(handshake):
successful_jobs.append('22000: ' + pcapFileName)
else:
failed_jobs.append('22000: ' + pcapFileName)
if not os.path.isfile(fullpathNoExt + '.16800'): # if no 16800, try
if self._writePMKID(handshake):
successful_jobs.append('16800: ' + pcapFileName)
else:
failed_jobs.append('16800: ' + pcapFileName)
if not os.path.isfile(fullpathNoExt + '.22000'): # if no 16800 AND no 22000
lonely_pcaps.append(handshake)
logging.debug('[hashie] Batch job: added {} to lonely list'.format(pcapFileName))
if ((num + 1) % 50 == 0) or (num + 1 == len(handshakes_list)): # report progress every 50, or when done
logging.info('[Hashie] Batch job: {}/{} done ({} fails)'.format(num + 1, len(handshakes_list), len(lonely_pcaps)))
if successful_jobs:
logging.info('[Hashie] Batch job: {} new handshake files created'.format(len(successful_jobs)))
if lonely_pcaps:
logging.info('[Hashie] Batch job: {} networks without enough packets to create a hash'.format(len(lonely_pcaps)))
self._getLocations(lonely_pcaps)
def _getLocations(self, lonely_pcaps):
# export a file for webgpsmap to load
with open('/root/.incompletePcaps', 'w') as isIncomplete:
count = 0
for pcapFile in lonely_pcaps:
filename = pcapFile.split('/')[-1:][0] # keep extension
fullpathNoExt = pcapFile.split('.')[0]
isIncomplete.write(filename + '\n')
if os.path.isfile(fullpathNoExt + '.gps.json') or os.path.isfile(fullpathNoExt + '.geo.json'):
count += 1
if count != 0:
logging.info('[Hashie] Used {} GPS/GEO files to find lonely networks, '
'go check webgpsmap! ;)'.format(str(count)))
else:
logging.info('[Hashie] Could not find any GPS/GEO files '
'for the lonely networks'.format(str(count)))
def _getLocationsCSV(self, lonely_pcaps):
# in case we need this later, export locations manually to CSV file, needs try/catch format/etc.
locations = []
for pcapFile in lonely_pcaps:
filename = pcapFile.split('/')[-1:][0].split('.')[0]
fullpathNoExt = pcapFile.split('.')[0]
if os.path.isfile(fullpathNoExt + '.gps.json'):
with open(fullpathNoExt + '.gps.json', 'r') as tempFileA:
data = json.load(tempFileA)
locations.append(filename + ',' + str(data['Latitude']) + ',' + str(data['Longitude']) + ',50')
elif os.path.isfile(fullpathNoExt + '.geo.json'):
with open(fullpathNoExt + '.geo.json', 'r') as tempFileB:
data = json.load(tempFileB)
locations.append(
filename + ',' + str(data['location']['lat']) + ',' + str(data['location']['lng']) + ',' + str(data['accuracy']))
if locations:
with open('/root/locations.csv', 'w') as tempFileD:
for loc in locations:
tempFileD.write(loc + '\n')
logging.info('[Hashie] Used {} GPS/GEO files to find lonely networks, '
'load /root/locations.csv into a mapping app and go say hi!'.format(len(locations)))

View File

@ -25,6 +25,7 @@ class OnlineHashCrack(plugins.Plugin):
self.report = StatusFile('/root/.ohc_uploads', data_format='json') self.report = StatusFile('/root/.ohc_uploads', data_format='json')
self.skip = list() self.skip = list()
self.lock = Lock() self.lock = Lock()
self.options = dict()
def on_loaded(self): def on_loaded(self):
""" """
@ -34,13 +35,9 @@ class OnlineHashCrack(plugins.Plugin):
logging.error("OHC: Email isn't set. Can't upload to onlinehashcrack.com") logging.error("OHC: Email isn't set. Can't upload to onlinehashcrack.com")
return return
if 'whitelist' not in self.options:
self.options['whitelist'] = list()
self.ready = True self.ready = True
logging.info("OHC: OnlineHashCrack plugin loaded.") logging.info("OHC: OnlineHashCrack plugin loaded.")
def _upload_to_ohc(self, path, timeout=30): def _upload_to_ohc(self, path, timeout=30):
""" """
Uploads the file to onlinehashcrack.com Uploads the file to onlinehashcrack.com
@ -78,7 +75,6 @@ class OnlineHashCrack(plugins.Plugin):
except OSError as os_e: except OSError as os_e:
raise os_e raise os_e
def on_webhook(self, path, request): def on_webhook(self, path, request):
import requests import requests
from flask import redirect from flask import redirect
@ -87,7 +83,6 @@ class OnlineHashCrack(plugins.Plugin):
r = s.post('https://www.onlinehashcrack.com/dashboard', data={'emailTasks': self.options['email'], 'submit': ''}) r = s.post('https://www.onlinehashcrack.com/dashboard', data={'emailTasks': self.options['email'], 'submit': ''})
return redirect(r.url, code=302) return redirect(r.url, code=302)
def on_internet_available(self, agent): def on_internet_available(self, agent):
""" """
Called in manual mode when there's internet connectivity Called in manual mode when there's internet connectivity
@ -105,7 +100,7 @@ class OnlineHashCrack(plugins.Plugin):
handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if
filename.endswith('.pcap')] filename.endswith('.pcap')]
# pull out whitelisted APs # pull out whitelisted APs
handshake_paths = remove_whitelisted(handshake_paths, self.options['whitelist']) handshake_paths = remove_whitelisted(handshake_paths, config['main']['whitelist'])
handshake_new = set(handshake_paths) - set(reported) - set(self.skip) handshake_new = set(handshake_paths) - set(reported) - set(self.skip)
if handshake_new: if handshake_new:
logging.info("OHC: Internet connectivity detected. Uploading new handshakes to onlinehashcrack.com") logging.info("OHC: Internet connectivity detected. Uploading new handshakes to onlinehashcrack.com")

View File

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

View File

@ -121,9 +121,6 @@ class Wigle(plugins.Plugin):
logging.debug("WIGLE: api_key isn't set. Can't upload to wigle.net") logging.debug("WIGLE: api_key isn't set. Can't upload to wigle.net")
return return
if not 'whitelist' in self.options:
self.options['whitelist'] = list()
if not 'donate' in self.options: if not 'donate' in self.options:
self.options['donate'] = True self.options['donate'] = True
@ -148,7 +145,7 @@ class Wigle(plugins.Plugin):
for filename in all_files for filename in all_files
if filename.endswith('.gps.json') or filename.endswith('.paw-gps.json') or filename.endswith('.geo.json')] if filename.endswith('.gps.json') or filename.endswith('.paw-gps.json') or filename.endswith('.geo.json')]
all_gps_files = remove_whitelisted(all_gps_files, self.options['whitelist']) all_gps_files = remove_whitelisted(all_gps_files, config['main']['whitelist'])
new_gps_files = set(all_gps_files) - set(reported) - set(self.skip) new_gps_files = set(all_gps_files) - set(reported) - set(self.skip)
if new_gps_files: if new_gps_files:
logging.info("WIGLE: Internet connectivity detected. Uploading new handshakes to wigle.net") logging.info("WIGLE: Internet connectivity detected. Uploading new handshakes to wigle.net")

View File

@ -76,9 +76,6 @@ class WpaSec(plugins.Plugin):
logging.error("WPA_SEC: API-URL isn't set. Can't upload, no endpoint configured.") logging.error("WPA_SEC: API-URL isn't set. Can't upload, no endpoint configured.")
return return
if 'whitelist' not in self.options:
self.options['whitelist'] = list()
self.ready = True self.ready = True
logging.info("WPA_SEC: plugin loaded") logging.info("WPA_SEC: plugin loaded")
@ -101,9 +98,8 @@ class WpaSec(plugins.Plugin):
reported = self.report.data_field_or('reported', default=list()) reported = self.report.data_field_or('reported', default=list())
handshake_dir = config['bettercap']['handshakes'] handshake_dir = config['bettercap']['handshakes']
handshake_filenames = os.listdir(handshake_dir) handshake_filenames = os.listdir(handshake_dir)
handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if filename.endswith('.pcap')]
filename.endswith('.pcap')] handshake_paths = remove_whitelisted(handshake_paths, config['main']['whitelist'])
handshake_paths = remove_whitelisted(handshake_paths, self.options['whitelist'])
handshake_new = set(handshake_paths) - set(reported) - set(self.skip) handshake_new = set(handshake_paths) - set(reported) - set(self.skip)
if handshake_new: if handshake_new:

View File

@ -264,11 +264,11 @@ class EPD:
def display(self, image): def display(self, image):
if (image == None): if (image == None):
return return
# Width = (self.width % 8 == 0)? (self.width / 8 ): (self.width / 8 + 1) # Width = (self.width % 8 == 0)? (self.width // 8 ): (self.width // 8 + 1)
if (self.width % 8 == 0): if (self.width % 8 == 0):
Width = self.width / 8 Width = self.width // 8
else: else:
Width = self.width / 8 + 1 Width = self.width // 8 + 1
self.send_command(0x10) self.send_command(0x10)
for j in range(0, self.height): for j in range(0, self.height):
@ -282,11 +282,11 @@ class EPD:
self.TurnOnDisplay() self.TurnOnDisplay()
def Clear(self): def Clear(self):
# Width = (self.width % 8 == 0)? (self.width / 8 ): (self.width / 8 + 1) # Width = (self.width % 8 == 0)? (self.width // 8 ): (self.width // 8 + 1)
if (self.width % 8 == 0): if (self.width % 8 == 0):
Width = self.width / 8 Width = self.width // 8
else: else:
Width = self.width / 8 + 1 Width = self.width // 8 + 1
Height = self.height Height = self.height
@ -313,11 +313,11 @@ class EPD:
self.send_data(127) # y-end self.send_data(127) # y-end
self.send_data(0x00) self.send_data(0x00)
# Width = (self.width % 8 == 0)? (self.width / 8 ): (self.width / 8 + 1) # Width = (self.width % 8 == 0)? (self.width // 8 ): (self.width // 8 + 1)
if (self.width % 8 == 0): if (self.width % 8 == 0):
Width = self.width / 8 Width = self.width // 8
else: else:
Width = self.width / 8 + 1 Width = self.width // 8 + 1
Height = self.height Height = self.height
# send data # send data

View File

@ -100,7 +100,7 @@ class EPD:
def ReadBusy(self): def ReadBusy(self):
logger.debug("e-Paper busy") logger.debug("e-Paper busy")
while epdconfig.digital_read(self.busy_pin) == 1: # 1: idle, 0: busy while (epdconfig.digital_read(self.busy_pin) == 1): # 1: idle, 0: busy
epdconfig.delay_ms(20) epdconfig.delay_ms(20)
logger.debug("e-Paper busy release") logger.debug("e-Paper busy release")
@ -134,7 +134,7 @@ class EPD:
self.send_data(self.LUT_DATA_4Gray[i]) self.send_data(self.LUT_DATA_4Gray[i])
def init(self): def init(self):
if epdconfig.module_init() != 0: if (epdconfig.module_init() != 0):
return -1 return -1
# EPD hardware init start # EPD hardware init start
@ -159,7 +159,7 @@ class EPD:
return 0 return 0
def init_Fast(self): def init_Fast(self):
if epdconfig.module_init() != 0: if (epdconfig.module_init() != 0):
return -1 return -1
# EPD hardware init start # EPD hardware init start
@ -204,12 +204,12 @@ class EPD:
return 0 return 0
def Init_4Gray(self): def Init_4Gray(self):
if epdconfig.module_init() != 0: if (epdconfig.module_init() != 0):
return -1 return -1
self.reset() self.reset()
self.send_command(0x12) # soft reset self.send_command(0x12) # soft reset
self.ReadBusy() self.ReadBusy();
self.send_command(0x74) # set analog block control self.send_command(0x74) # set analog block control
self.send_data(0x54) self.send_data(0x54)
@ -268,14 +268,14 @@ class EPD:
imwidth, imheight = image_monocolor.size imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load() pixels = image_monocolor.load()
# logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight)
if imwidth == self.width and imheight == self.height: if (imwidth == self.width and imheight == self.height):
logger.debug("Vertical") logger.debug("Vertical")
for y in range(imheight): for y in range(imheight):
for x in range(imwidth): for x in range(imwidth):
# Set the bits for the column of pixels at the current position. # Set the bits for the column of pixels at the current position.
if pixels[x, y] == 0: if pixels[x, y] == 0:
buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
elif imwidth == self.height and imheight == self.width: elif (imwidth == self.height and imheight == self.width):
logger.debug("Horizontal") logger.debug("Horizontal")
for y in range(imheight): for y in range(imheight):
for x in range(imwidth): for x in range(imwidth):
@ -293,40 +293,40 @@ class EPD:
pixels = image_monocolor.load() pixels = image_monocolor.load()
i = 0 i = 0
# logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight)
if imwidth == self.width and imheight == self.height: if (imwidth == self.width and imheight == self.height):
logger.debug("Vertical") logger.debug("Vertical")
for y in range(imheight): for y in range(imheight):
for x in range(imwidth): for x in range(imwidth):
# Set the bits for the column of pixels at the current position. # Set the bits for the column of pixels at the current position.
if pixels[x, y] == 0xC0: if (pixels[x, y] == 0xC0):
pixels[x, y] = 0x80 pixels[x, y] = 0x80
elif pixels[x, y] == 0x80: elif (pixels[x, y] == 0x80):
pixels[x, y] = 0x40 pixels[x, y] = 0x40
i = i + 1 i = i + 1
if i % 4 == 0: if (i % 4 == 0):
buf[int((x + (y * self.width)) / 4)] = ( buf[int((x + (y * self.width)) / 4)] = (
(pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | ( (pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | (
pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6)
elif imwidth == self.height and imheight == self.width: elif (imwidth == self.height and imheight == self.width):
logger.debug("Horizontal") logger.debug("Horizontal")
for x in range(imwidth): for x in range(imwidth):
for y in range(imheight): for y in range(imheight):
newx = y newx = y
newy = self.height - x - 1 newy = self.height - x - 1
if pixels[x, y] == 0xC0: if (pixels[x, y] == 0xC0):
pixels[x, y] = 0x80 pixels[x, y] = 0x80
elif pixels[x, y] == 0x80: elif (pixels[x, y] == 0x80):
pixels[x, y] = 0x40 pixels[x, y] = 0x40
i = i + 1 i = i + 1
if i % 4 == 0: if (i % 4 == 0):
buf[int((newx + (newy * self.width)) / 4)] = ( buf[int((newx + (newy * self.width)) / 4)] = (
(pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | ( (pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | (
pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6)
return buf return buf
def Clear(self): def Clear(self):
if self.width % 8 == 0: if (self.width % 8 == 0):
Width = self.width // 8 Width = self.width // 8
else: else:
Width = self.width // 8 + 1 Width = self.width // 8 + 1
@ -338,7 +338,7 @@ class EPD:
self.TurnOnDisplay() self.TurnOnDisplay()
def display(self, image): def display(self, image):
if self.width % 8 == 0: if (self.width % 8 == 0):
Width = self.width // 8 Width = self.width // 8
else: else:
Width = self.width // 8 + 1 Width = self.width // 8 + 1
@ -350,7 +350,7 @@ class EPD:
self.TurnOnDisplay() self.TurnOnDisplay()
def display_Fast(self, image): def display_Fast(self, image):
if self.width % 8 == 0: if (self.width % 8 == 0):
Width = self.width // 8 Width = self.width // 8
else: else:
Width = self.width // 8 + 1 Width = self.width // 8 + 1
@ -362,7 +362,7 @@ class EPD:
self.TurnOnDisplay_Fast() self.TurnOnDisplay_Fast()
def display_Base(self, image): def display_Base(self, image):
if self.width % 8 == 0: if (self.width % 8 == 0):
Width = self.width // 8 Width = self.width // 8
else: else:
Width = self.width // 8 + 1 Width = self.width // 8 + 1
@ -379,7 +379,7 @@ class EPD:
self.TurnOnDisplay() self.TurnOnDisplay()
def display_Base_color(self, color): def display_Base_color(self, color):
if self.width % 8 == 0: if (self.width % 8 == 0):
Width = self.width // 8 Width = self.width // 8
else: else:
Width = self.width // 8 + 1 Width = self.width // 8 + 1
@ -396,7 +396,8 @@ class EPD:
# self.TurnOnDisplay() # self.TurnOnDisplay()
def display_Partial(self, Image, Xstart, Ystart, Xend, Yend): def display_Partial(self, Image, Xstart, Ystart, Xend, Yend):
if (Xstart % 8 + Xend % 8 == 8 & Xstart % 8 > Xend % 8) | Xstart % 8 + Xend % 8 == 0 | (Xend - Xstart) % 8 == 0: if ((Xstart % 8 + Xend % 8 == 8 & Xstart % 8 > Xend % 8) | Xstart % 8 + Xend % 8 == 0 | (
Xend - Xstart) % 8 == 0):
Xstart = Xstart // 8 Xstart = Xstart // 8
Xend = Xend // 8 Xend = Xend // 8
else: else:
@ -439,23 +440,23 @@ class EPD:
self.send_command(0x24) # Write Black and White image to RAM self.send_command(0x24) # Write Black and White image to RAM
for j in range(Height): for j in range(Height):
for i in range(Width): for i in range(Width):
if (j > Ystart - 1) & (j < (Yend + 1)) & (i > Xstart - 1) & (i < (Xend + 1)): if ((j > Ystart - 1) & (j < (Yend + 1)) & (i > Xstart - 1) & (i < (Xend + 1))):
self.send_data(Image[i + j * Width]) self.send_data(Image[i + j * Width])
self.TurnOnDisplay_Partial() self.TurnOnDisplay_Partial()
def display_4Gray(self, image): def display_4Gray(self, image):
self.send_command(0x24) self.send_command(0x24)
for i in range(0, 5808): # 5808*4 46464 for i in range(0, 48000): # 5808*4 46464
temp3 = 0 temp3 = 0
for j in range(0, 2): for j in range(0, 2):
temp1 = image[i * 2 + j] temp1 = image[i * 2 + j]
for k in range(0, 2): for k in range(0, 2):
temp2 = temp1 & 0xC0 temp2 = temp1 & 0xC0
if temp2 == 0xC0: if (temp2 == 0xC0):
temp3 |= 0x00 temp3 |= 0x00
elif temp2 == 0x00: elif (temp2 == 0x00):
temp3 |= 0x01 temp3 |= 0x01
elif temp2 == 0x80: elif (temp2 == 0x80):
temp3 |= 0x01 temp3 |= 0x01
else: # 0x40 else: # 0x40
temp3 |= 0x00 temp3 |= 0x00
@ -463,31 +464,31 @@ class EPD:
temp1 <<= 2 temp1 <<= 2
temp2 = temp1 & 0xC0 temp2 = temp1 & 0xC0
if temp2 == 0xC0: if (temp2 == 0xC0):
temp3 |= 0x00 temp3 |= 0x00
elif temp2 == 0x00: elif (temp2 == 0x00):
temp3 |= 0x01 temp3 |= 0x01
elif temp2 == 0x80: elif (temp2 == 0x80):
temp3 |= 0x01 temp3 |= 0x01
else: # 0x40 else: # 0x40
temp3 |= 0x00 temp3 |= 0x00
if j != 1 or k != 1: if (j != 1 or k != 1):
temp3 <<= 1 temp3 <<= 1
temp1 <<= 2 temp1 <<= 2
self.send_data(temp3) self.send_data(temp3)
self.send_command(0x26) self.send_command(0x26)
for i in range(0, 5808): # 5808*4 46464 for i in range(0, 48000): # 5808*4 46464
temp3 = 0 temp3 = 0
for j in range(0, 2): for j in range(0, 2):
temp1 = image[i * 2 + j] temp1 = image[i * 2 + j]
for k in range(0, 2): for k in range(0, 2):
temp2 = temp1 & 0xC0 temp2 = temp1 & 0xC0
if temp2 == 0xC0: if (temp2 == 0xC0):
temp3 |= 0x00 temp3 |= 0x00
elif temp2 == 0x00: elif (temp2 == 0x00):
temp3 |= 0x01 temp3 |= 0x01
elif temp2 == 0x80: elif (temp2 == 0x80):
temp3 |= 0x00 temp3 |= 0x00
else: # 0x40 else: # 0x40
temp3 |= 0x01 temp3 |= 0x01
@ -495,15 +496,15 @@ class EPD:
temp1 <<= 2 temp1 <<= 2
temp2 = temp1 & 0xC0 temp2 = temp1 & 0xC0
if temp2 == 0xC0: if (temp2 == 0xC0):
temp3 |= 0x00 temp3 |= 0x00
elif temp2 == 0x00: elif (temp2 == 0x00):
temp3 |= 0x01 temp3 |= 0x01
elif temp2 == 0x80: elif (temp2 == 0x80):
temp3 |= 0x00 temp3 |= 0x00
else: # 0x40 else: # 0x40
temp3 |= 0x01 temp3 |= 0x01
if j != 1 or k != 1: if (j != 1 or k != 1):
temp3 <<= 1 temp3 <<= 1
temp1 <<= 2 temp1 <<= 2
self.send_data(temp3) self.send_data(temp3)
@ -516,3 +517,5 @@ class EPD:
epdconfig.delay_ms(2000) epdconfig.delay_ms(2000)
epdconfig.module_exit() epdconfig.module_exit()
### END OF FILE ###

View File

@ -28,7 +28,7 @@
# #
import logging import logging
from .. import epdconfig from . import epdconfig
# Display resolution # Display resolution
EPD_WIDTH = 128 EPD_WIDTH = 128
@ -227,7 +227,7 @@ class EPD:
self.send_data((y >> 8) & 0xFF) self.send_data((y >> 8) & 0xFF)
def init(self): def init(self):
if epdconfig.module_init() != 0: if (epdconfig.module_init() != 0):
return -1 return -1
# EPD hardware init start # EPD hardware init start
self.reset() self.reset()

View File

@ -184,7 +184,7 @@ class EPD:
self.send_command(0x01) # Driver output control self.send_command(0x01) # Driver output control
self.send_data((self.height - 1) % 256) self.send_data((self.height - 1) % 256)
self.send_data((self.height - 1) / 256) self.send_data((self.height - 1) // 256)
self.send_data(0x00) self.send_data(0x00)
self.send_command(0x11) # data entry mode self.send_command(0x11) # data entry mode

View File

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

View File

@ -1,11 +1,11 @@
# ***************************************************************************** # *****************************************************************************
# * | File : epd7in5.py # * | File : epd4in26.py
# * | Author : Waveshare team # * | Author : Waveshare team
# * | Function : Electronic paper driver # * | Function : Electronic paper driver
# * | Info : # * | Info :
# *---------------- # *----------------
# * | This version: V4.0 # * | This version: V1.0
# * | Date : 2019-06-20 # * | Date : 2023-12-20
# # | Info : python demo # # | Info : python demo
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
@ -29,11 +29,16 @@
import logging import logging
from .. import epdconfig from . import epdconfig
# Display resolution # Display resolution
EPD_WIDTH = 880 EPD_WIDTH = 800
EPD_HEIGHT = 528 EPD_HEIGHT = 480
GRAY1 = 0xff # white
GRAY2 = 0xC0
GRAY3 = 0x80 # gray
GRAY4 = 0x00 # Blackest
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -46,15 +51,39 @@ class EPD:
self.cs_pin = epdconfig.CS_PIN self.cs_pin = epdconfig.CS_PIN
self.width = EPD_WIDTH self.width = EPD_WIDTH
self.height = EPD_HEIGHT self.height = EPD_HEIGHT
self.GRAY1 = GRAY1 # white
self.GRAY2 = GRAY2
self.GRAY3 = GRAY3 # gray
self.GRAY4 = GRAY4 # Blackest
LUT_DATA_4Gray = [ # #112bytes
0x80, 0x48, 0x4A, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0A, 0x48, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x88, 0x48, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xA8, 0x48, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x1E, 0x1C, 0x02, 0x00,
0x05, 0x01, 0x05, 0x01, 0x02,
0x08, 0x01, 0x01, 0x04, 0x04,
0x00, 0x02, 0x00, 0x02, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01,
0x22, 0x22, 0x22, 0x22, 0x22,
0x17, 0x41, 0xA8, 0x32, 0x30,
0x00, 0x00]
# Hardware reset # Hardware reset
def reset(self): def reset(self):
epdconfig.digital_write(self.reset_pin, 1) epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200) epdconfig.delay_ms(20)
epdconfig.digital_write(self.reset_pin, 0) epdconfig.digital_write(self.reset_pin, 0)
epdconfig.delay_ms(2) epdconfig.delay_ms(2)
epdconfig.digital_write(self.reset_pin, 1) epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200) epdconfig.delay_ms(20)
def send_command(self, command): def send_command(self, command):
epdconfig.digital_write(self.dc_pin, 0) epdconfig.digital_write(self.dc_pin, 0)
@ -71,7 +100,7 @@ class EPD:
def send_data2(self, data): def send_data2(self, data):
epdconfig.digital_write(self.dc_pin, 1) epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0) epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte2(data) epdconfig.SPI.writebytes2(data)
epdconfig.digital_write(self.cs_pin, 1) epdconfig.digital_write(self.cs_pin, 1)
def ReadBusy(self): def ReadBusy(self):
@ -79,103 +108,409 @@ class EPD:
busy = epdconfig.digital_read(self.busy_pin) busy = epdconfig.digital_read(self.busy_pin)
while (busy == 1): while (busy == 1):
busy = epdconfig.digital_read(self.busy_pin) busy = epdconfig.digital_read(self.busy_pin)
epdconfig.delay_ms(200) epdconfig.delay_ms(20)
epdconfig.delay_ms(20)
logger.debug("e-Paper busy release")
def TurnOnDisplay(self):
self.send_command(0x22) # Display Update Control
self.send_data(0xF7)
self.send_command(0x20) # Activate Display Update Sequence
self.ReadBusy()
def TurnOnDisplay_Fast(self):
self.send_command(0x22) # Display Update Control
self.send_data(0xC7)
self.send_command(0x20) # Activate Display Update Sequence
self.ReadBusy()
def TurnOnDisplay_Part(self):
self.send_command(0x22) # Display Update Control
self.send_data(0xFF)
self.send_command(0x20) # Activate Display Update Sequence
self.ReadBusy()
def TurnOnDisplay_4GRAY(self):
self.send_command(0x22) # Display Update Control
self.send_data(0xC7)
self.send_command(0x20) # Activate Display Update Sequence
self.ReadBusy()
'''
function : Setting the display window
parameter:
xstart : X-axis starting position
ystart : Y-axis starting position
xend : End position of X-axis
yend : End position of Y-axis
'''
def SetWindow(self, x_start, y_start, x_end, y_end):
self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION
self.send_data(x_start & 0xFF)
self.send_data((x_start >> 8) & 0x03)
self.send_data(x_end & 0xFF)
self.send_data((x_end >> 8) & 0x03)
self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION
self.send_data(y_start & 0xFF)
self.send_data((y_start >> 8) & 0xFF)
self.send_data(y_end & 0xFF)
self.send_data((y_end >> 8) & 0xFF)
'''
function : Set Cursor
parameter:
x : X-axis starting position
y : Y-axis starting position
'''
def SetCursor(self, x, y):
self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER
# x point must be the multiple of 8 or the last 3 bits will be ignored
self.send_data(x & 0xFF)
self.send_data((x >> 8) & 0x03)
self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER
self.send_data(y & 0xFF)
self.send_data((y >> 8) & 0xFF)
def init(self): def init(self):
if (epdconfig.module_init() != 0): if (epdconfig.module_init() != 0):
return -1 return -1
# EPD hardware init start # EPD hardware init start
self.reset() self.reset()
self.ReadBusy()
self.ReadBusy(); self.send_command(0x12) # SWRESET
self.send_command(0x12); # SWRESET self.ReadBusy()
self.ReadBusy();
self.send_command(0x46); # Auto Write Red RAM self.send_command(0x18) # use the internal temperature sensor
self.send_data(0xf7); self.send_data(0x80)
self.ReadBusy();
self.send_command(0x47); # Auto Write B/W RAM
self.send_data(0xf7);
self.ReadBusy();
self.send_command(0x0C); # Soft start setting self.send_command(0x0C) # set soft start
self.send_data2([0xAE, 0xC7, 0xC3, 0xC0, 0x40]) self.send_data(0xAE)
self.send_data(0xC7)
self.send_data(0xC3)
self.send_data(0xC0)
self.send_data(0x80)
self.send_command(0x01); # Set MUX as 527 self.send_command(0x01) # drive output control
self.send_data2([0xAF, 0x02, 0x01]) self.send_data((self.height - 1) % 256) # Y
self.send_data((self.height - 1) // 256) # Y
self.send_data(0x02)
self.send_command(0x11); # Data entry mode self.send_command(0x3C) # Border Border setting
self.send_data(0x01); self.send_data(0x01)
self.send_command(0x44); self.send_command(0x11) # data entry mode
self.send_data2([0x00, 0x00, 0x6F, 0x03]) # RAM x address start at 0 self.send_data(0x01) # X-mode x+ y-
self.send_command(0x45);
self.send_data2([0xAF, 0x02, 0x00, 0x00])
self.send_command(0x3C); # VBD self.SetWindow(0, self.height - 1, self.width - 1, 0)
self.send_data(0x05); # LUT1, for white
self.send_command(0x18); self.SetCursor(0, 0)
self.send_data(0X80); self.ReadBusy()
self.send_command(0x22); # EPD hardware init end
self.send_data(0XB1); # Load Temperature and waveform setting. return 0
self.send_command(0x20);
self.ReadBusy();
self.send_command(0x4E); # set RAM x address count to 0; def init_Fast(self):
self.send_data2([0x00, 0x00]) if (epdconfig.module_init() != 0):
self.send_command(0x4F); return -1
self.send_data2([0x00, 0x00]) # EPD hardware init start
self.reset()
self.ReadBusy()
self.send_command(0x12) # SWRESET
self.ReadBusy()
self.send_command(0x18) # use the internal temperature sensor
self.send_data(0x80)
self.send_command(0x0C) # set soft start
self.send_data(0xAE)
self.send_data(0xC7)
self.send_data(0xC3)
self.send_data(0xC0)
self.send_data(0x80)
self.send_command(0x01) # drive output control
self.send_data((self.height - 1) % 256) # Y
self.send_data((self.height - 1) // 256) # Y
self.send_data(0x02)
self.send_command(0x3C) # Border Border setting
self.send_data(0x01)
self.send_command(0x11) # data entry mode
self.send_data(0x01) # X-mode x+ y-
self.SetWindow(0, self.height - 1, self.width - 1, 0)
self.SetCursor(0, 0)
self.ReadBusy()
# TEMP (1.5s)
self.send_command(0x1A)
self.send_data(0x5A)
self.send_command(0x22)
self.send_data(0x91)
self.send_command(0x20)
self.ReadBusy()
# EPD hardware init end
return 0
def Lut(self):
self.send_command(0x32)
for count in range(0, 105):
self.send_data(self.LUT_DATA_4Gray[count])
self.send_command(0x03) # VGH
self.send_data(self.LUT_DATA_4Gray[105])
self.send_command(0x04) #
self.send_data(self.LUT_DATA_4Gray[106]) # VSH1
self.send_data(self.LUT_DATA_4Gray[107]) # VSH2
self.send_data(self.LUT_DATA_4Gray[108]) # VSL
self.send_command(0x2C) # VCOM Voltage
self.send_data(self.LUT_DATA_4Gray[109]) # 0x1C
def init_4GRAY(self):
if (epdconfig.module_init() != 0):
return -1
# EPD hardware init start
self.reset()
self.ReadBusy()
self.send_command(0x12) # SWRESET
self.ReadBusy()
self.send_command(0x18) # use the internal temperature sensor
self.send_data(0x80)
self.send_command(0x0C) # set soft start
self.send_data(0xAE)
self.send_data(0xC7)
self.send_data(0xC3)
self.send_data(0xC0)
self.send_data(0x80)
self.send_command(0x01) # drive output control
self.send_data((self.height - 1) % 256) # Y
self.send_data((self.height - 1) // 256) # Y
self.send_data(0x02)
self.send_command(0x3C) # Border Border setting
self.send_data(0x01)
self.send_command(0x11) # data entry mode
self.send_data(0x01) # X-mode x+ y-
self.SetWindow(0, self.height - 1, self.width - 1, 0)
self.SetCursor(0, 0)
self.ReadBusy()
self.Lut()
# EPD hardware init end # EPD hardware init end
return 0 return 0
def getbuffer(self, image): def getbuffer(self, image):
img = image # logger.debug("bufsiz = ",int(self.width/8) * self.height)
imwidth, imheight = img.size buf = [0xFF] * (int(self.width / 8) * self.height)
if (imwidth == self.width and imheight == self.height): image_monocolor = image.convert('1')
img = img.convert('1') imwidth, imheight = image_monocolor.size
elif (imwidth == self.height and imheight == self.width): pixels = image_monocolor.load()
img = img.rotate(90, expand=True).convert('1') # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight)
else: if imwidth == self.width and imheight == self.height:
logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) logger.debug("Horizontal")
# return a blank buffer for y in range(imheight):
return [0xff] * int(self.width * self.height / 8) for x in range(imwidth):
# Set the bits for the column of pixels at the current position.
if pixels[x, y] == 0:
buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
elif imwidth == self.height and imheight == self.width:
logger.debug("Vertical")
for y in range(imheight):
for x in range(imwidth):
newx = y
newy = self.height - x - 1
if pixels[x, y] == 0:
buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8))
return buf
buf = bytearray(img.tobytes('raw')) def getbuffer_4Gray(self, image):
# logger.debug("bufsiz = ",int(self.width/8) * self.height)
buf = [0xFF] * (int(self.width / 4) * self.height)
image_monocolor = image.convert('L')
imwidth, imheight = image_monocolor.size
pixels = image_monocolor.load()
i = 0
# logger.debug("imwidth = %d, imheight = %d",imwidth,imheight)
if (imwidth == self.width and imheight == self.height):
logger.debug("Vertical")
for y in range(imheight):
for x in range(imwidth):
# Set the bits for the column of pixels at the current position.
if (pixels[x, y] == 0xC0):
pixels[x, y] = 0x80
elif (pixels[x, y] == 0x80):
pixels[x, y] = 0x40
i = i + 1
if (i % 4 == 0):
buf[int((x + (y * self.width)) / 4)] = (
(pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | (
pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6)
elif (imwidth == self.height and imheight == self.width):
logger.debug("Horizontal")
for x in range(imwidth):
for y in range(imheight):
newx = y
newy = self.height - x - 1
if (pixels[x, y] == 0xC0):
pixels[x, y] = 0x80
elif (pixels[x, y] == 0x80):
pixels[x, y] = 0x40
i = i + 1
if (i % 4 == 0):
buf[int((newx + (newy * self.width)) / 4)] = (
(pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | (
pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6)
return buf return buf
def display(self, image): def display(self, image):
self.send_command(0x4F);
self.send_data2([0x00, 0x00])
self.send_command(0x24);
self.send_data2(image)
self.send_command(0x22);
self.send_data(0xF7); # Load LUT from MCU(0x32)
self.send_command(0x20);
epdconfig.delay_ms(10);
self.ReadBusy();
def Clear(self):
buf = [0xff] * int(self.width * self.height / 8)
self.send_command(0x4F);
self.send_data2([0x00, 0x00])
self.send_command(0x24) self.send_command(0x24)
self.send_data2(buf) self.send_data2(image)
self.TurnOnDisplay()
def display_Base(self, image):
self.send_command(0x24)
self.send_data2(image)
self.send_command(0x26) self.send_command(0x26)
self.send_data2(buf) self.send_data2(image)
self.send_command(0x22); self.TurnOnDisplay()
self.send_data(0xF7); # Load LUT from MCU(0x32)
self.send_command(0x20); def display_Fast(self, image):
epdconfig.delay_ms(10); self.send_command(0x24)
self.ReadBusy(); self.send_data2(image)
self.TurnOnDisplay_Fast()
def display_Partial(self, Image):
# Reset
self.reset()
self.send_command(0x18) # BorderWavefrom
self.send_data(0x80)
self.send_command(0x3C) # BorderWavefrom
self.send_data(0x80)
self.send_command(0x01) # drive output control
self.send_data((self.height - 1) % 256) # Y
self.send_data((self.height - 1) // 256) # Y
self.send_command(0x11) # data entry mode
self.send_data(0x01) # X-mode x+ y-
self.SetWindow(0, self.height - 1, self.width - 1, 0)
self.SetCursor(0, 0)
self.send_command(0x24) # Write Black and White image to RAM
self.send_data2(Image)
self.TurnOnDisplay_Part()
def display_4Gray(self, image):
self.send_command(0x24)
for i in range(0, 48000): # 5808*4 46464
temp3 = 0
for j in range(0, 2):
temp1 = image[i * 2 + j]
for k in range(0, 2):
temp2 = temp1 & 0xC0
if (temp2 == 0xC0):
temp3 |= 0x00
elif (temp2 == 0x00):
temp3 |= 0x01
elif (temp2 == 0x80):
temp3 |= 0x01
else: # 0x40
temp3 |= 0x00
temp3 <<= 1
temp1 <<= 2
temp2 = temp1 & 0xC0
if (temp2 == 0xC0):
temp3 |= 0x00
elif (temp2 == 0x00):
temp3 |= 0x01
elif (temp2 == 0x80):
temp3 |= 0x01
else: # 0x40
temp3 |= 0x00
if (j != 1 or k != 1):
temp3 <<= 1
temp1 <<= 2
self.send_data(temp3)
self.send_command(0x26)
for i in range(0, 48000): # 5808*4 46464
temp3 = 0
for j in range(0, 2):
temp1 = image[i * 2 + j]
for k in range(0, 2):
temp2 = temp1 & 0xC0
if (temp2 == 0xC0):
temp3 |= 0x00
elif (temp2 == 0x00):
temp3 |= 0x01
elif (temp2 == 0x80):
temp3 |= 0x00
else: # 0x40
temp3 |= 0x01
temp3 <<= 1
temp1 <<= 2
temp2 = temp1 & 0xC0
if (temp2 == 0xC0):
temp3 |= 0x00
elif (temp2 == 0x00):
temp3 |= 0x01
elif (temp2 == 0x80):
temp3 |= 0x00
else: # 0x40
temp3 |= 0x01
if (j != 1 or k != 1):
temp3 <<= 1
temp1 <<= 2
self.send_data(temp3)
self.TurnOnDisplay_4GRAY()
def Clear(self):
self.send_command(0x24)
self.send_data2([0xFF] * (int(self.width / 8) * self.height))
self.send_command(0x26)
self.send_data2([0xFF] * (int(self.width / 8) * self.height))
self.TurnOnDisplay()
def sleep(self): def sleep(self):
self.send_command(0x10); self.send_command(0x10) # DEEP_SLEEP
self.send_data(0x01); self.send_data(0x01)
epdconfig.delay_ms(2000) epdconfig.delay_ms(2000)
epdconfig.module_exit() epdconfig.module_exit()
### END OF FILE ### ### END OF FILE ###

View File

@ -243,7 +243,7 @@ class EPD:
else: else:
Xend = Xend // 8 * 8 + 1 Xend = Xend // 8 * 8 + 1
Width = (Xend - Xstart) / 8 Width = (Xend - Xstart) // 8
Height = Yend - Ystart Height = Yend - Ystart
self.send_command(0x50) self.send_command(0x50)

View File

@ -484,7 +484,7 @@ class EPD:
else: else:
Xend = Xend // 8 * 8 + 1 Xend = Xend // 8 * 8 + 1
Width = (Xend - Xstart) / 8 Width = (Xend - Xstart) // 8
Height = Yend - Ystart Height = Yend - Ystart
self.send_command(0x50) self.send_command(0x50)

View File

@ -81,59 +81,59 @@ class EPD:
self.reset() self.reset()
self.send_command(0x12); # SWRESET self.send_command(0x12) # SWRESET
self.ReadBusy(); # waiting for the electronic paper IC to release the idle signal self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
self.send_command(0x46); # Auto Write RAM self.send_command(0x46) # Auto Write RAM
self.send_data(0xF7); self.send_data(0xF7)
self.ReadBusy(); # waiting for the electronic paper IC to release the idle signal self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
self.send_command(0x47); # Auto Write RAM self.send_command(0x47) # Auto Write RAM
self.send_data(0xF7); self.send_data(0xF7)
self.ReadBusy(); # waiting for the electronic paper IC to release the idle signal self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
self.send_command(0x0C); # Soft start setting self.send_command(0x0C) # Soft start setting
self.send_data(0xAE); self.send_data(0xAE)
self.send_data(0xC7); self.send_data(0xC7)
self.send_data(0xC3); self.send_data(0xC3)
self.send_data(0xC0); self.send_data(0xC0)
self.send_data(0x40); self.send_data(0x40)
self.send_command(0x01); # Set MUX as 527 self.send_command(0x01) # Set MUX as 527
self.send_data(0xAF); self.send_data(0xAF)
self.send_data(0x02); self.send_data(0x02)
self.send_data(0x01); self.send_data(0x01)
self.send_command(0x11); # Data entry mode self.send_command(0x11) # Data entry mode
self.send_data(0x01); self.send_data(0x01)
self.send_command(0x44); self.send_command(0x44)
self.send_data(0x00); # RAM x address start at 0 self.send_data(0x00) # RAM x address start at 0
self.send_data(0x00); self.send_data(0x00)
self.send_data(0x6F); # RAM x address end at 36Fh -> 879 self.send_data(0x6F) # RAM x address end at 36Fh -> 879
self.send_data(0x03); self.send_data(0x03)
self.send_command(0x45); self.send_command(0x45)
self.send_data(0xAF); # RAM y address start at 20Fh; self.send_data(0xAF) # RAM y address start at 20Fh
self.send_data(0x02); self.send_data(0x02)
self.send_data(0x00); # RAM y address end at 00h; self.send_data(0x00) # RAM y address end at 00h
self.send_data(0x00); self.send_data(0x00)
self.send_command(0x3C); # VBD self.send_command(0x3C) # VBD
self.send_data(0x01); # LUT1, for white self.send_data(0x01) # LUT1, for white
self.send_command(0x18); self.send_command(0x18)
self.send_data(0X80); self.send_data(0X80)
self.send_command(0x22); self.send_command(0x22)
self.send_data(0XB1); # Load Temperature and waveform setting. self.send_data(0XB1) # Load Temperature and waveform setting.
self.send_command(0x20); self.send_command(0x20)
self.ReadBusy(); # waiting for the electronic paper IC to release the idle signal self.ReadBusy() # waiting for the electronic paper IC to release the idle signal
self.send_command(0x4E); self.send_command(0x4E)
self.send_data(0x00); self.send_data(0x00)
self.send_data(0x00); self.send_data(0x00)
self.send_command(0x4F); self.send_command(0x4F)
self.send_data(0xAF); self.send_data(0xAF)
self.send_data(0x02); self.send_data(0x02)
return 0 return 0
@ -162,44 +162,44 @@ class EPD:
return buf return buf
def display(self, imageblack, imagered): def display(self, imageblack, imagered):
self.send_command(0x4F); self.send_command(0x4F)
self.send_data(0xAf); self.send_data(0xAf)
self.send_command(0x24) self.send_command(0x24)
for i in range(0, int(self.width * self.height / 8)): for i in range(0, int(self.width * self.height / 8)):
self.send_data(imageblack[i]); self.send_data(imageblack[i])
self.send_command(0x26) self.send_command(0x26)
for i in range(0, int(self.width * self.height / 8)): for i in range(0, int(self.width * self.height / 8)):
self.send_data(~imagered[i]); self.send_data(~imagered[i])
self.send_command(0x22); self.send_command(0x22)
self.send_data(0xC7); # Load LUT from MCU(0x32) self.send_data(0xC7) # Load LUT from MCU(0x32)
self.send_command(0x20); self.send_command(0x20)
epdconfig.delay_ms(200); # !!!The delay here is necessary, 200uS at least!!! epdconfig.delay_ms(200) # !!!The delay here is necessary, 200uS at least!!!
self.ReadBusy(); self.ReadBusy()
def Clear(self): def Clear(self):
self.send_command(0x4F); self.send_command(0x4F)
self.send_data(0xAf); self.send_data(0xAf)
self.send_command(0x24) self.send_command(0x24)
for i in range(0, int(self.width * self.height / 8)): for i in range(0, int(self.width * self.height / 8)):
self.send_data(0xff); self.send_data(0xff)
self.send_command(0x26) self.send_command(0x26)
for i in range(0, int(self.width * self.height / 8)): for i in range(0, int(self.width * self.height / 8)):
self.send_data(0x00); self.send_data(0x00)
self.send_command(0x22); self.send_command(0x22)
self.send_data(0xC7); # Load LUT from MCU(0x32) self.send_data(0xC7) # Load LUT from MCU(0x32)
self.send_command(0x20); self.send_command(0x20)
epdconfig.delay_ms(200); # !!!The delay here is necessary, 200uS at least!!! epdconfig.delay_ms(200) # !!!The delay here is necessary, 200uS at least!!!
self.ReadBusy(); self.ReadBusy()
def sleep(self): def sleep(self):
self.send_command(0x10); # deep sleep self.send_command(0x10) # deep sleep
self.send_data(0x01); self.send_data(0x01)
epdconfig.delay_ms(2000) epdconfig.delay_ms(2000)
epdconfig.module_exit() epdconfig.module_exit()

View File

@ -30,6 +30,13 @@ def install_file(source_filename, dest_filename):
def install_system_files(): def install_system_files():
f = open("apt_packages.txt", "r")
for x in f:
if x == "":
continue
os.system(f"apt-get install {x}")
f.close()
setup_path = os.path.dirname(__file__) setup_path = os.path.dirname(__file__)
data_path = os.path.join(setup_path, "builder/data") data_path = os.path.join(setup_path, "builder/data")
@ -87,7 +94,9 @@ setup(name='pwnagotchi',
author_email='evilsocket@gmail.com', author_email='evilsocket@gmail.com',
url='https://pwnagotchi.ai/', url='https://pwnagotchi.ai/',
license='GPL', license='GPL',
install_requires=required, install_requires=[
required,
],
cmdclass={ cmdclass={
"install": CustomInstall, "install": CustomInstall,
}, },