Skocz do zawartości

Klawiatura makr z ESP32 v2 MAKaROn


Gieneq

Pomocna odpowiedź

21 godzin temu, Gieneq napisał:

Oczywiście taki układ musi mieć wadę, no i jest - max prąd raptem 250 mA, ale... po co większy? No może do tego bloku:

image.thumb.png.b71ce9995f6a14f403be00173c0b3f80.png

Dlaczego postanowiłeś zasilać LEDy z 3.3V zamiast z napięcia przed stabilizatorem?

  • Pomogłeś! 1
Link do komentarza
Share on other sites

Program

UART działa, ESP32 działa, czyli to co najważniejsze jest. Wgrałem przykład z biblioteką Keypad z kursu Arduino II i klawiatura też działa bez problemów. To co jest istotą działa, więc można zabrać się za Bluetooth. Szybki test z użyciem biblioteki BLE Keyboard, która sprawdziła się bezbłędnie w pierwszej odsłonie klawiatury i ruszyło. Zmieniłem nazwę na coś humorystycznego i pojawiła się niespodzianka.

Brownout detector was triggered

Z googla wynika że jest to błąd sprzętu, ale z drugiej strony coś tam się programuje. Poprawiłem nazwę na starą i działa.

Nie jestem specem w Bluetooth, ale szybkie zapytanie do googla i wychodzi na to, że nazwa urządzenia (Device Name) jest za długa. Znalazłem specyfikację Bluetooth 4.2 i jest tam fragment:

Cytat

The Device Name characteristic shall contain the name of the device as an UTF-8 string (...) The Device Name characteristic value shall be 0 to 248 octets in length. A device shall have only one instance of the Device Name characteristic.

248 bajtów to na pewno nie zapełniłem. Ale po lekturze kilku komentarzy z sieci i specyfikacji BLE 4.2 doszedłem do wniosku, że maksymalna długość to jednak 31 bajty i to byłem w stanie przekroczyć. Patrząc w kod biblioteki:

void BleKeyboard::taskServer(void* pvParameter) {
  BleKeyboard* bleKeyboardInstance = (BleKeyboard *) pvParameter; //static_cast<BleKeyboard *>(pvParameter);
  BLEDevice::init(bleKeyboardInstance->deviceName);
  ...

Idąc dalej – klasa BLEDevice i funkcja:

void BLEDevice::init(std::string deviceName){
  ...
  ::esp_ble_gap_set_device_name(deviceName.c_str());

Dalej:

esp_err_t esp_ble_gap_set_device_name(const char *name)
{
    btc_msg_t msg;
    btc_ble_gap_args_t arg;

    if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
        return ESP_ERR_INVALID_STATE;
    }
    
    if (strlen(name) > ESP_GAP_DEVICE_NAME_MAX) {
        return ESP_ERR_INVALID_ARG;
    }

I ostatecznie z chińskiej dokumentacji:

#define 	ESP_GAP_DEVICE_NAME_MAX   (32)

Czyli zabezpieczenie jest, a ja jednak nie przekroczyłem 32 bajtów. Spróbowałem metody bisekcji w celu ustalenia maksymalnej liczby znaków i wyszło 22. Jak? Nie wiem.

Nie zastanawiając się jednak za długo postanowiłem iść dalej. Z poprzedniego projektu klawiatury nauczyłem się ważnego prawidła, które zna też każdy malarz 🤔 rzeźbiarz też – od ogółu do szczegółu. Postanowiłem więc nie drążyć tego tematu, bo skończyłoby się na artykule o Bluetooth w ESP, a tu jednak jest klawiatura do zbudowania. Jak starczy chęci to do tego wrócę. Także w myśl słów "Jeżeli kod działa to znaczy że jest dobry." napisałem zarys programu (kod się trochę rozciągnął bo VSC jakoś mi przerzuca otwierające nawiasy w nową linię):

#include <Arduino.h>
#include <Keypad.h>
#include <BleKeyboard.h>
#include <FastLED.h>

/*
* CONSTANTS
*/

#define ROWS_COUNT 6
#define COLS_COUNT 6

// MAPPED KEYS OMRON
#define KEY_U1 21
#define KEY_U2 27
#define KEY_U3 33
#define KEY_U4 4
#define KEY_U5 10

#define KEY_DRV_UP 9
#define KEY_DRV_DOWN 3

#define KEY_TOP_L 1
#define KEY_TOP_M 17
#define KEY_TOP_R 23

#define KEY_BOT_L 22
#define KEY_BOT_M 16

#define KEY_LEFT_T 5
#define KEY_LEFT_M 34
#define KEY_LEFT_B 28

#define KEY_RIGHT_T 29
#define KEY_RIGHT_M 35

// MAPPED KEYS CHERRY
#define KEY_ARROW_T 7
#define KEY_ARROW_TR 1
#define KEY_ARROW_R 18
#define KEY_ARROW_BR 36
#define KEY_ARROW_B 6
#define KEY_ARROW_BL 12
#define KEY_ARROW_L 30
#define KEY_ARROW_TL 13
#define KEY_ARROW_M 24

#define KEY_A 25
#define KEY_B 19
#define KEY_C 2
#define KEY_D 31
#define KEY_E 8

// KEYBOARD PINS
#define OUT_1 33
#define OUT_2 25
#define OUT_3 26
#define OUT_4 27
#define OUT_5 13
#define OUT_6 23

#define IN_1 17
#define IN_2 16
#define IN_3 19
#define IN_4 18
#define IN_5 5
#define IN_6 14

//KEYBOARD
byte colPins[ROWS_COUNT] = {OUT_1, OUT_2, OUT_3, OUT_4, OUT_5, OUT_6};
byte rowPins[COLS_COUNT] = {IN_1, IN_2, IN_3, IN_4, IN_5, IN_6};

char matrixNumbers[ROWS_COUNT][ROWS_COUNT] = {
    {36, 1, 2, 3, 4, 5},
    {6, 7, 8, 9, 10, 11},
    {12, 13, 14, 15, 16, 17},
    {18, 19, 20, 21, 22, 23},
    {24, 25, 26, 27, 28, 29},
    {30, 31, 32, 33, 34, 35}};

Keypad matrix(makeKeymap(matrixNumbers), rowPins, colPins, sizeof(rowPins), sizeof(colPins));

// PROGRAMMABLE DIODES
#define STRIP_PIN 4
#define STRIP_COUNT 4
#define MAX_BRIGHTNESS 10

CRGB leds[STRIP_COUNT];

// BLUETOOTH
#define DISCONNECTED 0
#define CONNECTED 1

BleKeyboard bleKeyboard("MAKaROn");
int connectionState = DISCONNECTED;

/*
* FUNCTIONS
*/

void connectionInit()
{
  Serial.println("Connected");
}

void connectionLost()
{
  Serial.println("Disconnected");
}

void connectionPolling()
{
  // TODO jakas ciekawa animacja
  for (int i = 0; i < STRIP_COUNT; i++)
    leds[i] = CRGB::Red;
  FastLED.show();
  delay(500);

  for (int i = 0; i < STRIP_COUNT; i++)
    leds[i] = CRGB::Black;
  FastLED.show();
  delay(500);
}

void update()
{
  char key = matrix.getKey();

  delay(5);
}

/*
* UPDATE KEYBOARD
*/

void keypadEventHandler(KeypadEvent key)
{
  switch (matrix.getState())
  {
    //todo zrob cos zeby nie dublowac press/release, pamietaj ze nie zawsze press wystepuje z relese np jakies dluzsze macro wystukujace sekwencje
  case PRESSED:
    // Serial.print("Pressed: ");
    // Serial.println(key, DEC);
    if (key == KEY_ARROW_T)
      bleKeyboard.press(KEY_UP_ARROW);
    if (key == KEY_ARROW_B)
      bleKeyboard.press(KEY_DOWN_ARROW);
    if (key == KEY_ARROW_L)
      bleKeyboard.press(KEY_LEFT_ARROW);
    if (key == KEY_ARROW_R)
      bleKeyboard.press(KEY_RIGHT_ARROW);
    if (key == KEY_ARROW_M)
      bleKeyboard.press('M');
    break;
  case RELEASED:
    // Serial.print("Released: ");
    // Serial.println(key, DEC);
    if (key == KEY_ARROW_T)
      bleKeyboard.release(KEY_UP_ARROW);
    if (key == KEY_ARROW_B)
      bleKeyboard.release(KEY_DOWN_ARROW);
    if (key == KEY_ARROW_L)
      bleKeyboard.release(KEY_LEFT_ARROW);
    if (key == KEY_ARROW_R)
      bleKeyboard.release(KEY_RIGHT_ARROW);
    if (key == KEY_ARROW_M)
      bleKeyboard.release('M');
    break;

  default:
    break;
  }
}

/*
* SETUP & LOOP
*/

void setup()
{
  Serial.begin(9600);
  Serial.println("Bluetooth Keyboard v2.0 MAKaROn");
  bleKeyboard.begin();
  FastLED.addLeds<NEOPIXEL, STRIP_PIN>(leds, STRIP_COUNT);
  FastLED.setBrightness(MAX_BRIGHTNESS);
  matrix.begin(makeKeymap(matrixNumbers));
  matrix.addEventListener(keypadEventHandler);
  matrix.setHoldTime(500);
}

void loop()
{
  if (bleKeyboard.isConnected())
  {
    if (connectionState == DISCONNECTED)
    {
      connectionInit();
      connectionState = CONNECTED;
    }
    update();
  }
  else
  {
    if (connectionState == CONNECTED)
    {
      connectionState = DISCONNECTED;
      connectionLost();
    }
    connectionPolling();
  }
}

Co tu mamy... Na starcie masa definów. Opisują one klawiaturę, położenia przycisków, piny. Dzięki temu później w kodzie wystarczy, że wpiszę interesującą mnie etykietę, aby odnieść się do konkretnego przycisku. A że nie będą one miały swoich liter, jak to jest w zwykłej klawiaturze, to opisałem je zgodnie z rozmieszczeniem (góra, dół- prawo, góro-prawo górnego. itd).

Dalej powołujemy do życia instancję klasy Keypad, oraz obiekty do obsługi diod programowanych (tym razem z ciekawości biblioteka FastLED) i do obsługi klawiatury Bluetooth to o czym wspominałem.

Keypad matrix(makeKeymap(matrixNumbers), rowPins, colPins, sizeof(rowPins), sizeof(colPins));
...

CRGB leds[STRIP_COUNT];
...

BleKeyboard bleKeyboard("MAKaROn");

Następnie przygotowałem prostą maszynę stanów do obsługi połączenia Bluetooth. Dzięki tak przygotowanym funkcjom można łatwo zaimplementować przejrzysty kod (a w razie potrzeby wydzielenia klasy, można z nich zrobić latwo delegaty). W pętli loop spotkamy logikę w postaci zatrzasku na wspomnianym stanie:

void loop()
{
  if (bleKeyboard.isConnected())
  {
    if (connectionState == DISCONNECTED)
    {
      connectionInit();
      connectionState = CONNECTED;
    }
    update();
  }
  else
  {
    if (connectionState == CONNECTED)
    {
      connectionState = DISCONNECTED;
      connectionLost();
    }
    connectionPolling();
  }
}

Wielkiej filozofii tu nie ma, ale robi co ma robić. Po poprawnym połączeniu wykonywany będzie jedynie fragment dotyczący aktualizacji. I tu anegdotka dotycząca pierwszej wersji i skupiania się na tym co ważne w kodzie... W funkcji connectionPolling zaznaczyłem komentarzem:

void connectionPolling()
{
  // TODO jakas ciekawa animacja
  for (int i = 0; i < STRIP_COUNT; i++)
    leds[i] = CRGB::Red;
  FastLED.show();
  delay(500);

  for (int i = 0; i < STRIP_COUNT; i++)
    leds[i] = CRGB::Black;
  FastLED.show();
  delay(500);
}

Oczyma duszy mojej widzę tu...

Kunsztownie przygotowaną animację szkarłatno-złocistych języków ognia, których to światło subtelnie przenika przez nawpół przezroczysty odlew z barwionego epoksydu uformowanego nierówną, łamaną krawędzią deseczki tworzącej front przyszłej, jeszcze nieistniejącej ale zaplanowanej obudowy, mającej składać się nie tylko z łamanego drewna dopełnionego epoksydowym odlewem, ale także złocistego mosiądzu i ognistej miedzi. 🔥

Także zanim zmarnuję 10 godzin na szykowanie tej animacji, która w praktyce będzie widoczna tylko przez 1 sekundę na etapie łączenia, to lepiej zajmę się tym co w klawiaturze najważniejsze... przyciskami 🙄

I tu napotkałem realny problem do rozwiązania – biblioteka Keypad najwyraźniej nie obsługuje kliknięć kilku przycisków jednocześnie. Tak mi się wydaje, bo zdarzenie pochodzi od obiektu klawiatury, zaś w celu aktualizacji pobieram w pętli loop aktualny przycisk... chyba nie tędy droga, albo nie umiem używać tej biblioteki.

Pomijając to, użycie uchwytu dobrze działą, przyciski się wciskają, rozpoznają, wysyłają na port szeregowy swoje kody i nawet "klikają" przyciskami poprzez Bluetooth. Na razie przetestowałem strzałki, jakiś znak i działa:

  case PRESSED:
    // Serial.print("Pressed: ");
    // Serial.println(key, DEC);
    if (key == KEY_ARROW_T)
      bleKeyboard.press(KEY_UP_ARROW);
    if (key == KEY_ARROW_B)
      bleKeyboard.press(KEY_DOWN_ARROW);
    if (key == KEY_ARROW_L)
      bleKeyboard.press(KEY_LEFT_ARROW);
    if (key == KEY_ARROW_R)
      bleKeyboard.press(KEY_RIGHT_ARROW);
    if (key == KEY_ARROW_M)
      bleKeyboard.press('M');

Widać tu jednak problemy w implementacji. Należy jakoś uporać się z powtórzeniami w bloku naciśnięcia i puszczenia szczególnie, gdy wyklikuję zwykłe klawisze. W przypadku makr albo dłuższych sekwencji sprawa będzie wyglądać inaczej.

Takie podejście choć dość dokładne to nie sprawdza się. W pierwszej odsłonie klawiatury wpadłem w sidła perfekcjonizmu... Napisałem klasę, która miała tablicę 32 wskaźników na funkcje naciśnięcia, długiego naciśnięcia i puszczenia... Wodotryski jak się patrzy, dodajesz obsługę przycisku z delegata! Wow! Ale jak napisałem 12 osobnych funkcji i blok attachów do tych lśniących 3linikowych uchwytów to się dopiero zastanowiłem, czy 24 ify nie załatwiłyby sprawy... Także nie jest to najlepszy sposób. Jaki jest "właściwszy"? Zobaczę 🙂 

  • Lubię! 1
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.