Archive for 2 september, 2020

Miniprojekt: Metronom i klockan

02/09/2020

Jag har köpt en del småsaker, främst elektronik, från Banggood som är ett kinesiskt företag. Hittills har beställningarna kommit ungefär enligt beräknad tid men COVID-19 ställer till med problem. Tidigare beställningar

 
  • Visningar
  • Besökare

leder till att Banggood via facebook synbarligen aktiverar reklam aktuell för mig.

Jag råkade stöta på reklam för en ny aktivitetsklocka Lilygo T-Watch-2020. Klockan är baserad på en ESP32 mikroprocessor med två kärnor. Klockan innehåller WiFi funktionalitet med bl.a. en webbserver och också Bluetooth funktionalitet. Det speciella med den här klockan är att den från början är avsedd att hackas d.v.s. källkoden finns på github och den programmeras direkt över USB-anslutningen.

Då man får en ny programmeringsleksak så uppstår naturligtvis genast problemet med vad man skall göra med apparaten.  Färdiga funktioner är bl. a.

  • Väderapp
  • Stoppklocka
  • Kryptovaluta
  • Navigering via kart-API

Då jag sysslar med folkmusik som hobby så slog det mig att det kunde vara praktiskt att ha en enkel metronom i klockan som innan jag börjar spela ett stycke slår t.ex. tio slag med hjälp av klockans vibrationsmotor. Tanken är alltså inte att jag skulle spela hela stycken med klockan som metronom utan klockan skulle endast upplysa mig om ungefär vilket tempot är, att mentalt tänka sig ett tempo kan ibland leda fel rejält eftersom hjärnan inte i alla sammanhang går i realtid …

Operativsystem

Klockan kör ett kompakt realtidsoperativsystem FreeRTOS som stöder multitasking d.v.s. jag kan köra flera applikationer parallellt. Det är också i princip möjligt att ladda in nya program medan klockan är i gång eller stänga av obehövliga program. Den senare funktionaliteten används inte utan gör jag förändringar så kompilerar jag om rubbet och laddar upp allt som ett paket till klockan.

Man hittar FreeRTOS dokumentation på nätet.

Metronom

Jag plockade hem en klockversion från Github. Då jag ögnade igenom applikationerna som fanns färdigt så såg jag att det fanns en exempel app som inte gjorde någonting men som visade vilken struktur en app har.

Jag beslöt att försöka göra en extremt enkel applikation där användaren använder sig av en slider d.v.s. en funktion där man drar en indikator i sidled för att ställa in tempot. Jag tänkte mig att tempot kan ställas mellan 50 och 200 slag per minut. Det finns inga tekniska problem med att använda lägre tempon men jag uppfattar 50 slg/minut som väldigt långsamt. Då klockan startar är det förinställda värdet 100 slag/minut och klockan kommer ihåg det senast valda värdet mellan olika körningar av appen.

I princip borde applikationen ha varit väldigt enkel att realisera men det visade sig att indikatorhuvudet som samtidigt användes för att visa valt tempo var alltför litet d.v.s. det var svårt att se det valda tempot. Jag försökte kringgå problemet genom att lägga till ett separat textfält som skulle innehålla det valda värdet. Det nya textfältet förorsakade en omedelbar crasch.

Efter att jag två dagar hade slagit huvudet i väggen beslöt jag att byta ut slidern mot en spinbox där man i mitten har ett textfält och på vardera sidan tryckknappar som sänker/höjer det valda värdet. Det var tydligen något problem med slidern för nu fungerade val av tempo som det skulle.

Jag modifierade setupdelen i exempelapplikationen så att man väljer tempot med spinboxen och då man går ur tempofunktionen så gör klockan tio slag i det valda tempot varefter det sker retur till appens huvudnivå.

Call back funktioner

static void exit_example_app_setup_event_cb( lv_obj_t * obj, lv_event_t event );
static void example_app_metr_spinbox_event_cb(lv_obj_t * obj, lv_event_t event );
static void lv_spinbox_increment_event_cb(lv_obj_t * obj, lv_event_t event );
static void lv_spinbox_decrement_event_cb(lv_obj_t * obj, lv_event_t event );

Då användaren trycker på olika funktioner måste användargränssnittet kunna ”skicka signaler” till användarens kod så att den kan köras. För detta ändamål behöver vi såkallade callback funktioner. Om jag t.ex. trycker på knappen ”-” så stegas det valda tempot ner med ett steg (systemet anropar lv_spinbox_decrement_event_cb). Trycker jag på ”+” så stegas värdet upp med ett steg( lv_spinbox_increment_event_cb anropas).

Då jag trycker på återgång till appens huvudnivå så anropas exit_example_app_setup_event_cb. Denna funktion anropar funktionen execute_taps() som vibrerar telefonen tio gånger i önskat tempo.

// Let the metronome do ten taps
static void execute_taps(int taps, int tempo){
  int i;
  int taplen=0;
  int fixed = 100;
  float ms=60000;
  taplen = ms/tempo; 
  // Buzzer pin output BUZZER=4
  for(i=0; i< taps; i++){ 
    pinMode(BUZZER,OUTPUT);
    digitalWrite(BUZZER,ON);
    delay(fixed);
    digitalWrite(BUZZER,OFF);
    delay(taplen-fixed);
  }
}

Spinnboxen

Då klockan programmeras så har man tillgång till ett enkelt grafiskt användargränssnitt lvgl vilket i ytterst hög grad förenklar programmeringen. Problemet är dock att det finns rätt många olika grafiska gränssnitt och det kräver alltid en del läsande att börja använda ett nytt gränssnitt … så även denna gång.

Koden för att sätta upp en spinbox är:

// Create a spinbox center box with value
lv_obj_t *example_app_metr_spinbox_cont = lv_obj_create(example_app_setup_tile,NULL);
lv_obj_set_pos(example_app_metr_spinbox_cont,90,100);
example_app_metr_spinbox = lv_spinbox_create(example_app_metr_spinbox_cont, NULL ); 
lv_spinbox_set_range(example_app_metr_spinbox,50,200);
lv_spinbox_set_digit_format(example_app_metr_spinbox,3,0);
lv_spinbox_set_value(example_app_metr_spinbox,100);
lv_obj_set_event_cb(example_app_metr_spinbox,example_app_metr_spinbox_event_cb);
// Create increment button
lv_coord_t h = lv_obj_get_height(example_app_metr_spinbox);
lv_obj_t * btn = lv_btn_create(example_app_setup_tile, NULL);
lv_obj_set_size(btn, h, h);
lv_theme_apply(btn, LV_THEME_SPINBOX_BTN);
lv_obj_set_style_local_value_str(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_SYMBOL_PLUS);
lv_obj_align(btn, example_app_metr_spinbox, LV_ALIGN_OUT_RIGHT_MID, -20, 0);
lv_theme_apply(btn, LV_THEME_SPINBOX_BTN);
lv_obj_set_style_local_value_str(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_SYMBOL_PLUS);
lv_obj_set_event_cb(btn, lv_spinbox_increment_event_cb);
// Create decrement button
btn = lv_btn_create(example_app_setup_tile, btn);
lv_obj_align(btn, example_app_metr_spinbox, LV_ALIGN_OUT_LEFT_MID, -30, 0);
lv_obj_set_event_cb(btn, lv_spinbox_decrement_event_cb);
lv_obj_set_style_local_value_str(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_SYMBOL_MINUS);

Klockans skärm har upplösningen 240×240 pixlar. Först skapas ett kontext för spinboxen i filen example_app_setup.cpp utgående från specifikationer i appens huvudfönster. Därefter placeras spinnboxen på önskat ställe och programmet ställer in hurudant värdeområde (50-200 slag/minut) som är tillåtna. Programmet ställer också in vilket standardvärde (100 slag/minut) jag vill ha. Därefter skapar jag två tangenter/knappar som stegar spinboxens varde uppåt respektive nedåt.

Grafiska änvändargränssnitt fungerar så att skapade vidgetar själv detekterar om det händer något. I ovanstående fall så kommer tangenterna upp/ner att skicka en ”signal” som säger att en knapptryckning har detekterats. För att programmet skall kunna reagera på knapptryckningen så måste vi definiera såkallade callback funktioner d.v.s. funktioner som användargränssnittet anropar då aktivitet detekteras. I vårt fall är funktionen enkel:

static void lv_spinbox_increment_event_cb(lv_obj_t * obj, lv_event_t event ){
  if(event == LV_EVENT_SHORT_CLICKED || event == LV_EVENT_LONG_PRESSED_REPEAT) {
    lv_spinbox_increment(example_app_metr_spinbox);
  }
}

static void lv_spinbox_decrement_event_cb(lv_obj_t * obj, lv_event_t event ){
  if(event == LV_EVENT_SHORT_CLICKED || event == LV_EVENT_LONG_PRESSED_REPEAT) {
    lv_spinbox_decrement(example_app_metr_spinbox);
  }
}

När spinnboxens kontrollknappar detekterar aktivitet så anropas ifrågavarande funktioner ovan som i sin tur stegar upp spinboxens värde eller stegar ner det.

Aktivera metronomen

Jag valde att aktivera metronomen då man går ut ur inställningen av tempo tillbaka till appens huvudsida. Alternativet skulle ha varit att definiera en separat knapp för att aktivera metronomen. Jag har valt att begränsa antalet slag till tio helt enkelt för att jag uppfattar att jag då har hunnit uppfatta tempot tillräckligt bra.

static void exit_example_app_setup_event_cb( lv_obj_t * obj, lv_event_t event ) {
  switch( event ) {
    case( LV_EVENT_CLICKED ): 
      delay(2000);
      execute_taps(10,lv_spinbox_get_value(example_app_metr_spinbox));
      mainbar_jump_to_tilenumber( example_app_get_app_main_tile_num(), LV_ANIM_ON );
      break;
  }
}

Koden för execute_taps() finns litad tidigare i texten.

Att använda appen

IMGP7532

Fig. 1  Metronomappen syns till höger om väderappen nedanför tiden 20:57.

IMGP7533

Fig. 2  Applikationen metronom (Metr) huvudsida efter att man har valt applikationen genom att trycka på Metr i föregående bild. Kugghjulet leder till val av tempo. I framtiden lägger jag antagligen till t.ex. fyra knappar med fördefinierade tempon på denna sida.

IMGP7534

Fig. 3  Inställning av tempot med en spinbox. Det valda tempot stannar i minnet. Då man väljer retur (trycker på symbolen överst till vänster) så vibrerar telefonen tio gånger i det angivna tempot.

 


Pointman's

A lagrange point in life

THE HOCKEY SCHTICK

Lars Silén: Reflex och Spegling

NoTricksZone

Lars Silén: Reflex och Spegling

Big Picture News, Informed Analysis

This blog is written by Canadian journalist Donna Laframboise. Posts appear Monday & Wednesday.

JoNova

Lars Silén: Reflex och Spegling

Climate Audit

by Steve McIntyre

Musings from the Chiefio

Techno bits and mind pleasers

Bishop Hill

Lars Silén: Reflex och Spegling

Watts Up With That?

The world's most viewed site on global warming and climate change

TED Blog

The TED Blog shares news about TED Talks and TED Conferences.

Larsil2009's Blog

Lars Silén: Reflex och Spegling

%d bloggare gillar detta: