Skocz do zawartości
Gieneq

Klawiatura makr z ESP32 v2 MAKaROn

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

Udostępnij ten post


Link to post
Share on other sites

@Harnas jak teraz o tym myślę to jest to dobry pomysł. Na PCB te 4 diody są liściem w drzewie 3,3v więc można w razie co rozwiercić przelotkę i dać brzydki kabelek 😒

Udostępnij ten post


Link to post
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

Udostępnij ten post


Link to post
Share on other sites

Podrzucam trwającą kampanie na Kickstarterze 😉

 

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

@Treker Niestety fizycznie się nie da klawiszy przesuwać na niej, a trochę im się pomyliło gdzie powinna być linijka "zxcv". Poza tym od klawiatur mechanicznych już nie ma powrotu do membran.

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

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...