
Идея устройства возникла у меня давно. Захотелось аппаратного менеджера паролей. Надоело перепечатывать 20-символьные пароли из телефонного KeyPass, а на каждый компьютер, где нужно что‑то вводить, устанешь устанавливать. Посмотрел, что предлагают, посмотрел, что делают. В основном, не понравились размеры. Иногда цена. С другой стороны, телефон с паролями всегда с собой. А тут ещё бесплатный DeepSeek.
TL;DR
Просто история мучений. В конце прошивка для контроллера + Android‑приложение, работает. Хотя и на стадии «концепт».
Однажды я вспомнил, что есть такие ESP32, которые умеют работать с USB напрямую и эмулировать HID-устройства. У них есть bluetooth, то есть можно воткнуть в компьютер, прикинуться клавиатурой, и с телефона отправить текст. Размер контроллера совсем маленький, повесил на ключи, — и он всегда с тобой.
Купил у китайцев ESP32-C3. Пока оно ехало, узнал, что USB HID там нет. Купил ESP32-S3 zero mini micro nano (их по-разному называют) — это для тех, кто решит повторить мой путь.
В общем, из железа это всё. Дальше прошивка и приложение.
Вайб-кодинг это называется, когда сказать можешь, а программировать — нет. Удобно, что не пришлось изучать контроллер, BLE (я с ним до этого не сталкивался), Kotlin — ни разу не писал приложений для телефона, и до сих пор не умею. Я не настоящий программист.
Я почему‑то решил, что написав в чат DeepSeek «Напиши программу для ESP32-S3 для эмуляции USB HID клавиатуры. ESP32-S3 получает текст через BLE и отправляет в USB. Напиши Android-приложение, которое отправляет текст по BLE из буфера обмена» получу всё и сразу. Сразу не заработало ничего :(
Пока ехал контроллер, я занялся приложением для телефона.

Вот перечень некоторых ошибок:
Unresolved reference ‘R'
unable to instantiate activity componentinfo didnt find class mainactivivty
resource style/AppTheme not found
Attempt to invoke virtual method 'java.lang.String android.provider.MiuiSettings$SettingsCloudData$CloudData.getString(java.lang.String, java.lang.String)' on a null object reference
По большей части, я просто копировал текст ошибки из IDE в чат DeepSeek. Сначала добавлял «исправь ошибку» или «найди решение», потом забил даже на это. Нейросеть рассказывала, как исправить ошибки, которые она же и сделала. Странное ощущение. В итоге приложение заработало, я добавил тёмную тему и передвинул кнопочку:
Скрытый текст

MainActivity.kt
package com.mc.bleapp
import android.annotation.SuppressLint
import android.bluetooth.*
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.content.ClipboardManager
import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import java.util.*
class MainActivity : AppCompatActivity() {
private lateinit var statusText: TextView
private lateinit var logText: TextView
private lateinit var sendButton: Button
private lateinit var reconnectButton: Button
private val DEVICE_NAME = "Secure BLE Keyboard"
private val SERVICE_UUID = UUID.fromString("4fafc201-1fb5-459e-8fcc-c5c9c331914b")
private val INPUT_CHAR_UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361b26a8")
private var bluetoothAdapter: BluetoothAdapter? = null
private var bluetoothGatt: BluetoothGatt? = null
private var inputCharacteristic: BluetoothGattCharacteristic? = null
private var isConnected = false
private val handler = Handler(Looper.getMainLooper())
private val PERMISSIONS = arrayOf(
android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.BLUETOOTH,
android.Manifest.permission.BLUETOOTH_ADMIN,
android.Manifest.permission.BLUETOOTH_CONNECT,
android.Manifest.permission.BLUETOOTH_SCAN
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
statusText = findViewById(R.id.statusText)
logText = findViewById(R.id.logText)
sendButton = findViewById(R.id.sendButton)
reconnectButton = findViewById(R.id.reconnectButton)
reconnectButton.setOnClickListener { reconnect() }
sendButton.setOnClickListener { sendClipboardContent() }
checkPermissions()
}
private fun checkPermissions() {
if (PERMISSIONS.any { ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED }) {
ActivityCompat.requestPermissions(this, PERMISSIONS, 0)
} else {
initializeBluetooth()
}
}
@SuppressLint("MissingPermission")
private fun initializeBluetooth() {
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter().takeIf { it.isEnabled }
?: run {
Toast.makeText(this, "Enable Bluetooth", Toast.LENGTH_LONG).show()
return
}
startDeviceScan()
}
@SuppressLint("MissingPermission")
private fun startDeviceScan() {
statusText.text = "Scanning..."
bluetoothAdapter?.bluetoothLeScanner?.startScan(scanCallback)
handler.postDelayed({ stopScan() }, 10000)
}
private fun stopScan() {
bluetoothAdapter?.bluetoothLeScanner?.stopScan(scanCallback)
}
private val scanCallback = object : ScanCallback() {
@SuppressLint("MissingPermission")
override fun onScanResult(callbackType: Int, result: ScanResult) {
if (result.device.name == DEVICE_NAME) {
stopScan()
connectToDevice(result.device)
}
}
}
@SuppressLint("MissingPermission")
private fun connectToDevice(device: BluetoothDevice) {
statusText.text = "Connecting..."
bluetoothGatt = device.connectGatt(this, false, gattCallback)
}
private val gattCallback = object : BluetoothGattCallback() {
@SuppressLint("MissingPermission")
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
when (newState) {
BluetoothProfile.STATE_CONNECTED -> {
isConnected = true
handler.post {
statusText.text = "Connected"
log("Connected")
}
gatt.discoverServices()
}
BluetoothProfile.STATE_DISCONNECTED -> {
isConnected = false
handler.post {
statusText.text = "Disconnected"
log("Connection lost")
}
}
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
if (status == BluetoothGatt.GATT_SUCCESS) {
inputCharacteristic = gatt.getService(SERVICE_UUID)
?.getCharacteristic(INPUT_CHAR_UUID)
handler.post {
if (inputCharacteristic != null) {
log("Service found")
sendButton.isEnabled = true
} else {
log("Service not found")
}
}
}
}
}
private fun sendClipboardContent() {
if (!isConnected) {
Toast.makeText(this, "Not connected", Toast.LENGTH_SHORT).show()
return
}
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.primaryClip?.getItemAt(0)?.text?.toString()?.let { text ->
if (text.isNotEmpty()) sendData(text)
} ?: Toast.makeText(this, "Clipboard empty", Toast.LENGTH_SHORT).show()
}
@SuppressLint("MissingPermission")
private fun sendData(text: String) {
inputCharacteristic?.let {
it.value = text.toByteArray()
bluetoothGatt?.writeCharacteristic(it)
log("Sent: ${text.take(20)}...")
} ?: log("Characteristic null")
}
private fun log(message: String) {
logText.append("\n$message")
}
@SuppressLint("MissingPermission")
private fun reconnect() {
log("Reconnect...")
bluetoothGatt?.disconnect()
bluetoothGatt?.close()
bluetoothGatt = null
inputCharacteristic = null
sendButton.isEnabled = false
startDeviceScan()
}
override fun onDestroy() {
super.onDestroy()
bluetoothGatt?.disconnect()
bluetoothGatt?.close()
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/statusText"
android:layout_marginTop="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Status: Disconnected"
android:textSize="18sp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Log:"
android:textSize="16sp"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<TextView
android:id="@+id/logText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"/>
</ScrollView>
<Button
android:id="@+id/reconnectButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Переподключиться"/>
<Button
android:id="@+id/sendButton"
android:layout_width="match_parent"
android:layout_height="87dp"
android:enabled="false"
android:text="Отправить"/>
</LinearLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mc.bleapp">
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<application
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="BLE Keyboard">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Дальше приехал контроллер, и вот с ним я бился две недели.
После исправления ошибок компиляции оно прошилось, но не работало.
Первая ошибка -BT_GATT: gatts_write_attr_perm_check - GATT_INSUF_AUTHENTICATION,handle 002a, perm 0020
Она сыпалась в лог при попытке отправить текст с телефона. Я решил, что раз про аутентификацию — нужно добавить пин‑код при bluetooth‑спаривании. К этому моменту я уже понял, что, хоть нейросеть и понимает контекст, лучше начинать новый чат для новой идеи.

Дальше были ошибки и решения:
error: lvalue required as unary '&' operand -> Заменили #define PIN_CODE на константную переменную const uint32_t PIN_CODE
error: conversion from 'String' to non-scalar type 'std::string' {aka 'std::_cxx11::basic_string'} requested -> std::string value = pCharacteristic->getValue().c_str();
И ещё штук пяток.
Причём, эти ошибки повторяются из итерации в итерацию. Исправил одну — вернул другую. Исправил другую, новая итерация — опа, вернулась первая. Я сначала копировал только текст ошибки, затем перешёл к варианту текст ошибки + строка с ошибкой — ответы значительно улучшились. В любом кодинге помогает опыт =)
В итоге, когда спаривание с пин-кодом случилось, я перешёл к имитации HID-клавиатуры. Тут вроде как проблем не случилось. Ну это только сначала.
error: 'BLEProperties' has not been declared BLEProperties::PROPERTY_NOTIFY -> Ошибка возникает из-за устаревшего синтаксиса. Нужно использовать BLECharacteristic::PROPERTY* вместо BLEProperties::.
Так зачем ты, собака, предлагаешь устаревший синтаксис???
error: 'class BLEAdvertising' has no member named ‘setMinSecurity' -> Удалена строка pAdvertising->setMinSecurity() - эта функция не существует в текущей версии библиотеки
Ну и так далее. Почему сразу не писать без ошибок? Это что, имитация человека?
К этому моменту я прочитал статью про то, что можно в запрос прикладывать файл с кодом. Сохранил работающий код, в котором был только запрос пин-кода, и мы начали сначала:

Ну и снова заново:
error: conversion from 'String' to non-scalar type ‘std::string'
Временами нейросеть что-то придумывала. Например, метод asciiToHID
— ей так удобнее конвертировать текст в HID-символы. Оказалось, что «в классе SecurityCallbacks не реализованы все чисто виртуальные методы из базового класса BLESecurityCallbacks». И много ещё всякого, через что мы с ней прошли.
В итоге, я не добился чего хотел в чистом виде. В некоторых вариантах ядро ESP падало в панике, часто после внесения изменений не запрашивало пин-код и не соединялось соответственно. У меня была программа, которая получает из телефона текст и выводит в терминал, но никак не хочет в USB HID. Я взял код из примера для имитации клавиатуры от DeepSeek и попросил дублировать вывод терминала при помощи Keyboard.press:
Пример
#include <USB.h>
#include <USBHIDKeyboard.h>
USBHIDKeyboard Keyboard;
void setup() {
USB.begin();
Keyboard.begin();
delay(2000); // Дать время для подключения USB
Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press(KEY_ALT);
Keyboard.press('t');
Keyboard.releaseAll(); // Отправка Ctrl+Alt+T (открыть терминал в Linux)
}
void loop() {}

Тут заработало с первого раза. Но только латиница, цифры и спецсимволы. Впрочем, мне этого достаточно.
worked_hid_keyboard.ino
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
#include <USB.h>
#include <USBHIDKeyboard.h>
USBHIDKeyboard Keyboard;
#define DEVICE_NAME "Secure BLE Keyboard"
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
const uint32_t PIN_CODE = 123456;
bool deviceConnected = false;
class CharacteristicCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) override {
String arduinoString = pCharacteristic->getValue().c_str();
std::string value(arduinoString.c_str());
if (!value.empty()) {
Serial.print("Received: ");
Serial.println(value.c_str());
// Отправка текста через USB HID
for (char c : value) {
Keyboard.press(c); // Нажать клавишу
delay(10); // Короткая задержка
Keyboard.release(c); // Отпустить клавишу
}
}
}
};
class ServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) override {
deviceConnected = true;
Serial.println("Device connected");
};
void onDisconnect(BLEServer* pServer) override {
deviceConnected = false;
Serial.println("Device disconnected");
pServer->startAdvertising();
}
};
class SecurityCallbacks: public BLESecurityCallbacks {
bool onConfirmPIN(uint32_t pass_key) override {
Serial.print("Confirm PIN: ");
Serial.println(pass_key);
return (pass_key == PIN_CODE);
}
void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl) override {
if(cmpl.success) {
Serial.println("Authentication success");
} else {
Serial.println("Authentication failed");
}
}
uint32_t onPassKeyRequest() override {
return PIN_CODE;
}
void onPassKeyNotify(uint32_t pass_key) override {
Serial.print("PassKey Notify: ");
Serial.println(pass_key);
}
bool onSecurityRequest() override {
return true;
}
};
void setup() {
Serial.begin(115200);
USB.begin();
Keyboard.begin();
delay(2000); // Дать время для подключения USB
BLEDevice::init(DEVICE_NAME);
BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT);
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks());
BLESecurity *pSecurity = new BLESecurity();
pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND);
pSecurity->setCapability(ESP_IO_CAP_OUT);
pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
BLEDevice::setSecurityCallbacks(new SecurityCallbacks());
esp_ble_gap_set_security_param(
ESP_BLE_SM_SET_STATIC_PASSKEY,
const_cast<uint32_t*>(reinterpret_cast<const uint32_t*>(&PIN_CODE)),
sizeof(PIN_CODE)
);
BLEService *pService = pServer->createService(SERVICE_UUID);
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
// Добавлен обработчик входящих данных
pCharacteristic->setCallbacks(new CharacteristicCallbacks());
pCharacteristic->setValue("Hello World");
pCharacteristic->addDescriptor(new BLE2902());
pService->start();
BLEAdvertising *pAdvertising = pServer->getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->start();
}
void loop() {
delay(1000);
}
Осталось только добавить мигание светодиодом и нарисовать корпус (я и проектировщик не настоящий, умею только в SketchUp).
Итого:
вайб‑программисту всё равно пришлось побыть программистом. Правил код, добавлял нужное, убирал лишнее. Если б я был условным электронщиком, и DeepSeek бы делал ошибки в рисовании схем — я бы не смог исправить;
действительно, лучше разрабатывать шажочками, а не приложение целиком. Один шажочек — один запрос;
сохранять работающий код в файл и прикладывать к запросу — это прям здорово, для меня было прорывом;
с Android‑приложением получилось хорошо, я в этом совсем ноль был. Теперь умею кнопку по экрану двигать.
На весь процесс ушло 2 недели между делом. Чистого времени часа 2–3. Это, конечно, быстрее, нежели я бы читал доки и писал код самостоятельно.

Ссылка на Google Drive с исходниками, apk и stl для тех, кто хочет так же, а программировать лень.
В написании статьи нейросети участия не принимали, всё сам =)
Комментарии (56)
xSVPx
25.05.2025 21:48Найти бы тоже самое, но с хранением паролей внутри esp, как, к примеру мультипасс делает. Прям хоть пили... Ещеб ТОТР туда же...
PeterFukuyama
25.05.2025 21:48Вроде такого?
xSVPx
25.05.2025 21:48Вероятно. Тонкости в деталях. Я много готовых девайсов поразглядывал, но нормальный подход нашелся только у mooltopass. Но они нифига их не производят и имеют проблемы с аппаратной частью :(( (из-за европейских ограничений у них там никелевые аккумуляторы).
niarbrnd
25.05.2025 21:48https://ozon.ru/t/NAOiOp9
xSVPx
25.05.2025 21:48Описание неплохое, но нет, пароли по подписке это через край. И подтверждение входа с смартфона тоже. Что то stand alone хотелось.
niarbrnd
25.05.2025 21:48Standalone, это значит ее надо носить с собой, подключать/отключать каждый раз, мы же про пароли, а это же вроде как безопасность. Но если про безопасность не беспокоиться, то можно и standalone делать, но зачем если тогда и пароля 123 достаточно.
xSVPx
25.05.2025 21:48Безопасность может обеспечить только девайс, пароли из которого попадают куда-то наружу только после вашего подтверждения(физически, на нем, кнопкой). И да, они должны храниться внутри, быть зашифрованы, требовать вводить пин при подключении.
А то, что проходит через телефон по нажатию в нем клавиш имеет меньшую безопасность чем написанные на листочке пароли. А хочется большую.
В этой штуке хотелось бы хранить пароли от брокеров где семизначные счета, к примеру... От файрволлов корпоративных итд итп. Не от озонов и вайлдбериз... Госуслуги с ТОТР итд итп...
niarbrnd
25.05.2025 21:48У каждого свои требования к безопасности, если для пароля нужен пин, то все сводится к получению пин, который даже по названию обычно цифровой, пином безопасность уже снижается, всегда будет какой-то компромисс, каждый выбирает себе тот который его устраивает.
Ваш сценарий тоже можно реализовать.
В предложенном варианте пароль и ключ разделены, удобство пользования телефона сложно переоценить, физическое устройство сложно удаленно сломать, из пк это устройство не сломать через HID.
xSVPx
25.05.2025 21:48Конечно у каждого свой. Мне нужен не хуже, чем у записанного на бумагу пароля...
Пин позволяет получить пароль если у вас есть физический доступ к токену. Если нет, то пин вам не поможет. Посмотрите как мультипасс сделан, там всё довольно нормально. База паролей зашифрована смарт картой, сама смарт картаа позволяет до окирпичивания ввести три неверных пароля, сами пароли будучи расшифрованными набираются только после нажатия на устройстве физической клавиши, т.е. вы видите на мониторе устройства пароль к какому ресурсу сейчас будете набирать.
В предложенном вами варианте сломают ваш телефон и сольют все ваши пароли.
niarbrnd
25.05.2025 21:48Где они возьмут ключ для расшифровывания пароля? Он только в устройстве. И нет функции для его извлечения, при каждой смене пароля ключ генерируется заново.
xSVPx
25.05.2025 21:48Не очень понятно как это всё работает, но где пароль хранится совершенно неважно если он оттуда набирается по какой-то внешней команде. Инициируют команду, сграббят со стороны куда оно набирается. Возможно придется инфицировать два устройства (и это неплохо).
Тут ничего нового не придумать. Если устройство хранения паролей без монитора - невозможно проверить что сейчас происходит. Если без кнопки - все пароли сопрут как только скомпромитируют внешнюю обвязку.
niarbrnd
25.05.2025 21:48Т.е. вариант, когда надо нажать кпопку, потом в течении 2-5секунд, отправить шифрованный пароль и только в таком сочетании вводить данные в пк, вы считаете улучшенным?
xSVPx
25.05.2025 21:48Я не очень понял что вы имеете ввиду.
Хорошее устройство показывает на экране что оно собирается вводить (пароль от какого логина) и вводит его после того, как вы на этом устройстве нажимаете кнопку подтверждения. Какие-то дополнительные таймауты обычно отсутствуют. При этом это устройство - это не сотовый телефон. Оно не имеет сетевого доступа и к нему никакого доступа без подтверждения той же кнопкой на нём нету.
Sergey_Morozov Автор
25.05.2025 21:48Такое тоже есть. Мне не понравились размер и необходимость вручную забивать туда пароли при помощи энкодера: https://github.com/seawarrior181/PasswordPump_II
xSVPx
25.05.2025 21:48В нормальном случае оно их само генерит и набирает...
Для мультипасса есть редактор, который позволяет их набивать с компьютера. Надо только кнопку подтверждения нажимать и по-моему переспрашивает пин в начале и конце редактирования. (Т.е. можно все отредактировать дважды введя пин)
xSVPx
25.05.2025 21:48Гугл mooltipass. В первых версиях была проблема с колесом, в следующих с аккумулятором.
sergyk2
25.05.2025 21:48я для есп начал с примеров которые идут вместе с библиотеками. но не все из них компилируюца, другие прошиваются но не работают.
granv1
25.05.2025 21:48я тебе больше скажу. куча готовых библиотек от "маститых" контор типа адафрут с кучей багов. всё приходится перепроверять и, порой, переписывать
AndyKorg
25.05.2025 21:48Минус такого подхода в том, что в результате не остается глубокого понимания работы компонентов. :(
Sergey_Morozov Автор
25.05.2025 21:48Абсолютно согласен. Но если эти знания нужны условно раз в жизни, - круто, что через три часа можно получить работающее устройство, а не изучать java ради одного приложения.
xSVPx
25.05.2025 21:48Всё-таки макет работающего устройства.
С потенциально суровыми и непредсказуемыми багами, и не всегда работающий как надо. Для прототипирования пойдет. Для паролей.... вы очень смелый человек :).
kenomimi
25.05.2025 21:48Делал такое на Lilygo токене. Все бы хорошо, но аппаратные косяки умножают идею на ноль. Оно банально перегревается, и помирает дерьмовая паленая spi-флешка - зачем мне менеджер паролей, который в случайный момент перестает работать, или, еще хуже того, втихаря глючит на посыпавшемся флеше... И перепаять чтобы, надо, во-первых, экранчик отклеить - что не очень просто из-за его хрупкости, и, во-вторых, найти в рознице не поддельную industrial-grade флешку, что в наше время тот еще квест.
xSVPx
25.05.2025 21:48О, да, отличная заготовка под нормальный автономный девайс. А заранее залить каким-нибудь термоинтерфейсом жидким не вариант ? Или алюминиевый корпус ей сделать?
Прям очень перспективно выглядит именно железо. Корпус, конечно дешево не сделать, но его скорее всего всё равно менять... т.е. делать придется в любом случае, зато хоть начинка недорогая.
kenomimi
25.05.2025 21:48Я заливал несколько штук эпоксидкой в металлическом корпусе - не помогает, все сдохли где-то к 50-60 перепрошивке. Флешку там под перепай однозначно, на высокотемпературную. А так да, очень вкеусный девайс.
lv333
25.05.2025 21:48Надо тогда брать самум примитивную ESP32-S3 Zero(или вообще ESP32-C3 Zero, хотя там уже разница в цене не такая уж прям большая что бы экономить, тем более если нужны возможности S3), вон как автор этой статьи, там на борту по сути ничего кроме одного чипа и нет, а если надо больше ПЗУ, просто припаять отдельно модуль под микросд по spi, а потом уже получившийся бутерброд заливать в эпоксидку, ну или не заливать :) Это будет раз в 5 дешевле, а главное, скорее всего не будет иметь тех проблем про которые вы говорите...
Хотя у S3 4МБ ПЗУ, под любую базу паролей этого точно хватит...
kenomimi
25.05.2025 21:48Ну, после провала с Lilygo я для себя сделал прототитп на LuckFox Pico - тоже не без косяков, но они исправимы, так как материнка под SoM моя. Выходит чуть дороже esp32, но там и сенсорный экран навешивается без проблем, и ридер отпечатков, и mass storage там быстрый - можно запароленную флешку сделать... И самое главное, что там линукс, в котором вся железная часть уже реализована и протестирована сообществом - пишешь простой приклад и вперед. А корпус делается на фотополимернике, работы на полчаса-час во фрикаде...
lv333
25.05.2025 21:48Интересная платка, надо будет прочитать... Можно было ещё наверное вообще rpi zero 2W взять, но это уже явный оверхед:) Хотя она тоже может спокойно питаться от любого юсб порта - проверено.
xSVPx
25.05.2025 21:48Теплопроводность эпоксидки под вопросом. Они точно перегреваются ? Если через прокладку к радиатору прислонить - тоже ? Или флеш такой, что десятки перепрошивок и "всё"?
kenomimi
25.05.2025 21:48Всё в комплексе. Там флешка нонейм или подделка под winbond - уже ресурс мизерный. А ее еще и нагревают до 80 градусов в работе - типовая температура S3... Понятно дело, что она кончается моментально.
Ее бы просто отодвинуть на 1см от чипа и всё было бы хорошо, но нет.
xSVPx
25.05.2025 21:48Можно прислонить к алюминиевому корпусу через термоинтерфейс(или даже напрямую) и греться не будет.
Но, похоже надо просто заказать своё из нормальных комплектующих. Надо подумать...
lv333
25.05.2025 21:48Такие платки есть, но и габариты у них побольше, а вообще хз зачем контроллеру все эти объемы ОЗУ и ПЗУ, там где это реально нужно и правда проще уже микрокопьютер с линуксом поставить. А для таких вот поделок, хватит того флеша и ОЗУ что есть уже в чипе S3.
xSVPx
25.05.2025 21:48Тут применение такое, что лучше микрокомпьютеры не использовать. Неизвестно что с линуксом получишь в плане функциональности (что-нибудь закешируется некстати, к примеру)...
Надо что-то достаточно минималистичное но при этом с монитором и кнопкой...
NutsUnderline
25.05.2025 21:48прикинуться клавиатурой, и с телефона отправить текст.
хакеры очень любят такую технологию что бы отправлять весьма зловредные тексты (нажатия клавиш). можно было не мучиться с gpt а взять готовый код.
PTM
25.05.2025 21:48а пастильда не нравится?
xSVPx
25.05.2025 21:48Mooltipass гораздо лучше, но с аппаратными проблемами. С пастилдой рано или поздно наберешь невовремя мастер-пароль и он попадет в ПК. Ну и в целом "такое себе", все эти попытки сделать управление от клавиатуры только безмерно всё усложняют. Совершенно не везде будет работать итд итп.
Sergey_Morozov Автор
25.05.2025 21:48Она мне встречалась. Во-первых, нужно ползать отключать-подключать клавиатуру, когда понадобится пароль. Она обычно сзади системника, системник в ногах. Во-вторых, я не понял, как это будет работать с ноутбуком.
Firelander
25.05.2025 21:48Наверное каждый должен написать свой менеджер паролей :) Потому что доверять продукту какой-то компании в этом деле не хочется. Есть конечно опенсорс решения но судя по отзывам в комментариях они имеют свои недостатки. Может дойдут руки сварганить что-нибудь на black pill и ble модуле. Раньше останавливала необходимость писать под андроид но с теперешними нейронками действительно минимальное приложение сварганить должно быть проще
Albert_latariya
25.05.2025 21:48Тоже недавно загорелся такой штукой.
Но за основу взял esp32 2432s028. Там esp32, сенсорный цветной дисплей и слот sd карты на одной плате. Плюс из мелочей: фоторезистор, rgb светодион и пара распаянных коннекторов.
Идея в том чтобы хранить шифрованный файл с паролями на флешке (так легче с ним работать, чем каждый раз шить плату). При включении расшифровывать файл вводом пин кода на экране, выбирать необходимый пароль и отправлять по блютусу.
lv333
25.05.2025 21:48Сам смотрел на эти платки не раз, но мне тут люди говорят, что они говно редкое, это все перегревается и "расклеивается" очень быстро. Короче прикольно компоновка плотная, маленькая платка, но... Работать нормально не может...
При включении расшифровывать файл вводом пин кода на экране, выбирать необходимый пароль и отправлять по блютусу.
Это все можно реализовать на смартфоне, лишнее устройство как скрипач - ненужно! :)
xSVPx
25.05.2025 21:48На смартфоне можно конечно. Если ты согласен отдать все бабки когда этот смартфон взломают, то тебе всё это вообще не нужно. Можно пароль использовать везде один 111111, его легко запомнить и быстро набирать.
lv333
25.05.2025 21:48Ну штош, делай тогда на китайской платке, которая в любой момент сгорит, а главное нигде больше кроме нее его не храни! Вдруг сломают!
xSVPx
25.05.2025 21:48Спасибо за совет. Однако если хочешь я тебе базу моих паролей пришлю за 1000руб. Сможешь расшифровать - всё твое.
А мастер-пароль да, нигде больше не хранится в электронном виде. Заперт в двух сейфах напечатанный на бумажке.
ЗЫ. Иногда лучше жевать ей богу...
sergyk2
25.05.2025 21:48я бы смотрел в сторону более компактного, хтя ввод пароля с экрана будет затруднён
https://m.media-amazon.com/images/I/61xCcUlX35L._AC_UF1000,1000_QL80_.jpg
lv333
Телефон может прикидывается блютуз клавиатурой... Можно обойтись без лишнего звена. https://play.google.com/store/apps/details?id=io.appground.blek
С рут правами можно и юсб клаву/мыш/флешку эмулировать https://habr.com/ru/companies/ruvds/articles/816595/
Sergey_Morozov Автор
Как говорится, "был бы у меня такой кот...", я бы носил на ключах BT-свисток, а не велосипедил ESP. Почему-то не знал, что в таких программах есть поле, куда можно скопировать и отправить текст.
kenomimi
Это не очень жизнеспособно. Донгл требует прав админа на установку, может быть запрещен к подключению, требует дрова, требует постоянного прохождения квеста с подключением (была не одна блютус-клава, и задрало то, что она регулярно не хочет автоматически подключаться)... HID же работает везде и сразу.
Эта идея лучше, но рутованный телефон требует изрядных приседаний, чтобы сохранить работоспособность всех приложений...
lv333
Как и левая "клавиатура".
Если прикинуть, то: если система вам не подконтрольная и не доверенная, то вводить там пароли любым способом небезопасно, так как их очень легко перехватить, как с родной клавы руками, так и с любой эмулируемой. Если же наоборот - то проще просто установить менеджер паролей и не городить весь этот колхоз. Альтернатива по сути только 2-х факторная авторизация...
Этот же сетап не решает никаких вопросов по факту.
Firelander
Ну например чтобы на неподконтрольной системе залогиниться на какой-нибудь специфический сайт скачать специфический файл. Если этот конкретный пароль утечет ничего страшного не произойдет, но на ней очень не хочется открывать свой менеджер паролей и вводить мастер пароль
lv333
Если пароль на специфических сайт который не жалко потерять, то его можно и несложным сделать для набивания на клавиатуре и это будет быстрее чем выполнить весь этот ритуал, ну имхо конечно.
Firelander
Его всё равно нужно где-то хранить, значит вбивать например с экрана телефона, потому что если он нужен раз в год он просто вылетит из головы, проходили уже. Да и как правило заранее не знаешь где и когда может понадобиться тот или иной пароль, не хочется специально делить пароли на простые и безопасные, плюс на сайтах обычно есть свои требования к паролям, минимальная длина символы итд
Sergey_Morozov Автор
Это прям мои мысли. У меня пароли в телефоне в KeePass. И я перепечатывал их оттуда.