Arduino robotprogrammering

C, C++, Pascal, Assembly, Raspberry, Java, Matlab, Python, BASIC, SQL, PHP, etc.
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Arduino robotprogrammering

Inlägg av Magnus_K »

Är det någon som har tid att kika på den här kodsnutten?
I det stora hela så försöker jag få till en "grundfunktion" för att hantera knapptryckningarna. Dels så en knapptryckning blir en one-shot samt debounce.
Just nu försöker jag debugga vart det går fel genom att skriva ut text seriellt vilken knapp jag trycker på och det enda jag får ut är Left button pressed! som rullar om och om igen, utan att jag ens tryckt någon knapp.

Kod: Markera allt

/*****************************************CONSTANTS****************************************************/
const uint8_t buttonLeft = 3;
const uint8_t buttonRight = 4;
const uint8_t buttonFwd = 5;
const uint8_t buttonX = 6;
/*****************************************************************************************************/

/**************************************GLOBAL VARIABLES***********************************************/
uint32_t debounceDelay = 50;
uint32_t lastDebounceChk = 0;
uint8_t debounceReg_Left = 0;
uint8_t debounceReg_Right = 0;
uint8_t debounceReg_Fwd = 0;
uint8_t debounceReg_X = 0;
uint8_t buttonLeft_Pressed = 0;
uint8_t buttonRight_Pressed = 0;
uint8_t buttonFwd_Pressed = 0;
uint8_t buttonX_Pressed = 0;
uint8_t Left_Press_Done = 0;
uint8_t Right_Press_Done = 0;
uint8_t Fwd_Press_Done = 0;
uint8_t X_Press_Done = 0;
uint8_t buttonCounter = 0;

enum Directions : uint8_t {LEFT, RIGHT, FWD, X, NONE};
enum Directions key;
enum StatusOfButtons : uint8_t {WAIT_FOR_KEYPRESS, WAIT_FOR_KEYRELEASE};
enum StatusOfButtons Buttonstatus;

/*****************************************************************************************************/

void pushButtonChk() {
    if(millis() >= (lastDebounceChk + debounceDelay)) {        
    
    lastDebounceChk = millis();
    
    debounceReg_Left <<= 1;                                        // 
    debounceReg_Right <<= 1;
    debounceReg_Fwd <<= 1;
    debounceReg_X <<= 1;
    debounceReg_Left |= buttonLeft;
    debounceReg_Right |= buttonRight;
    debounceReg_Fwd |= buttonFwd;
    debounceReg_X |= buttonX;

    if(debounceReg_Left == 0xFF) {
      Directions key = LEFT;
    }
    else if(debounceReg_Left == 0x00) {
      Directions key = NONE;
    }
    if(debounceReg_Right == 0xFF) {
      Directions key = RIGHT;
    }
    else if(debounceReg_Right == 0x00) {
      Directions key = NONE;
    }
    if(debounceReg_Fwd == 0xFF) {
      Directions key = FWD;
    }
    else if(debounceReg_Fwd == 0x00) {
      Directions key = NONE;
    }
    if(debounceReg_X == 0xFF) {
      Directions key = X;
    }
    else if(debounceReg_X == 0x00) {
      Directions key = NONE;
    }
  }
}


void setup() {

  Serial.begin(9600);
  pinMode(buttonLeft, INPUT);
  pinMode(buttonRight, INPUT);
  pinMode(buttonFwd, INPUT);
  pinMode(buttonX, INPUT);
  
}

void loop() {

  pushButtonChk();

  switch(Buttonstatus) {
 
    case WAIT_FOR_KEYPRESS :
      buttonCounter = 0;

      switch(key) {

        case LEFT :
          if(!Left_Press_Done) {
            Left_Press_Done = 1;
            Serial.println("Left button pressed!");
          }       
          Buttonstatus = WAIT_FOR_KEYRELEASE;
          break;

        case RIGHT :
          if(!Right_Press_Done) {
            Right_Press_Done = 1;
            Serial.println("Right button pressed!");
          }
          Buttonstatus = WAIT_FOR_KEYRELEASE;
          break;

        case FWD :
          if(!Fwd_Press_Done) {
            Fwd_Press_Done = 1;
            Serial.println("Fwd button pressed!");
          }
          Buttonstatus = WAIT_FOR_KEYRELEASE;
          break;

        case X :
          if(!X_Press_Done) {
            X_Press_Done = 1;
            Serial.println("X button pressed!");
          }
          Buttonstatus = WAIT_FOR_KEYRELEASE;
          break;

        case NONE :
          Buttonstatus = WAIT_FOR_KEYPRESS;
          break;
        
      }
      
      break;
    
    case WAIT_FOR_KEYRELEASE :

      switch(key) {

        case LEFT :
          Left_Press_Done = 0;
          Buttonstatus = WAIT_FOR_KEYPRESS;
          break;

        case RIGHT :
          Right_Press_Done = 0;
          Buttonstatus = WAIT_FOR_KEYPRESS;
          break;

        case FWD :
          Fwd_Press_Done = 0;
          Buttonstatus = WAIT_FOR_KEYPRESS;
          break;

        case X :
          X_Press_Done = 0;
          Buttonstatus = WAIT_FOR_KEYPRESS;
          break;
      }
      
      break;
    
    default :
  
      break;
  }
}
Glattnos
Inlägg: 2972
Blev medlem: 29 oktober 2009, 20:01:18

Re: Arduino robotprogrammering

Inlägg av Glattnos »

Är det inte enklare att göra en rutin som körs med jämna mellanrum där du kollar status på knapparna?
Om du kollar status på knapparna 20ggr i sekunden så har du automatiskt debounce på 50 ms. Om du i rutinen bara accepterar en knapptryckning som giltig om den inte var aktiverad förra gången rutinen kördes så kommer ett långt tryck bara registreras som ett tryck.

Edit: Hmmm...jag ser att det är typ så du gjort. Men det ser lite väl komplicerat ut.
Användarvisningsbild
Icecap
Inlägg: 26106
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Re: Arduino robotprogrammering

Inlägg av Icecap »

Jag brukar läsa knapptryckningar (få knappar) via en port in till ett bitmönster. Där vill jag sedan ha två identiska avläsningar för att det ska vara giltigt (läser i en timer-interupt, 20-50Hz).

Sedan masker jag ut "gamla" tryck så att de räknas som gamla om de var godkända förra avläsningen.

Till slut sätter jag knapparnas indikator i enligthet med detta - eller lägger knapptryckningarna i en buffer som sedan läsas ut.

När en knapp har blivit "använd" rensas indikatorn bort av rutinen som använder den - men är det en buffer som läsas sker det ju automatiskt.

Det hela låter komplicerat men är egentligen mest några bitmässiga AND och OR men viktigast av allt är timer-interupten.
svanted
Inlägg: 5082
Blev medlem: 30 augusti 2010, 21:20:38
Ort: Umeå

Re: Arduino robotprogrammering

Inlägg av svanted »

du gör det alldeles för komplicerat direkt, då blir felsökning svårt...
bryt ner det i små bitar som du funktionskollar alltefterssom...
lägg in Serial.println("vadsomhest?");
här och var som bekräftelse på att koden gör som du tänkt.
Användarvisningsbild
Krille Krokodil
Inlägg: 4062
Blev medlem: 9 december 2005, 22:33:11
Ort: Helsingborg

Re: Arduino robotprogrammering

Inlägg av Krille Krokodil »

Enklare & renare skulle det kunna se ut något likt nedan. One delay och one delay only i huvudloopen som kan vara ner till 5 ms eller så för att debouncen ska fungera.

Kod: Markera allt

int LEFT_BUTTON = 1;
int RIGHT_BUTTON = 2;  
int UP_BUTTON = 4;    

int LEFT_BUTTON_PIN = 12;
int RIGHT_BUTTON_PIN = 9;

void ReadAndDebounceButtons(){
 if(digitalRead(LEFT_BUTTON_PIN))
   ButtonRead = ButtonRead || LEFT_BUTTON;
 else 
   ButtonRead = ButtonRead && !LEFT_BUTTON;

 key = ButtonRead && PrevButtonRead; 
 PrevButtonRead = ButtonRead; 
}

void loop() {

 ReadAndDebounceButtons()

 switch(State) {
 case WAIT_FOR_KEYPRESS :
  buttonCounter = 0;
  switch(key) {
   case LEFT_BUTTON:
    State = LEFT_KEY;
    break;  
   }

  case LEFT_KEY:
   Serial.println("Left button pressed!");
   State = WAIT_FOR_KEYRELEASE;
   break; 

  case RIGHT_KEY:
    if(ButtonCounter == 0)
      Serial.println("Right button pressed!");
    if(ButtonCounter > 20){
      Serial.println("Right button hold for 1 second!");
      DoSomethingOneTimeWhenRightButtonHoldFor1Second();
      State = WAIT_FOR_KEYRELEASE;
     }
    ButtonCounter++;
    break; 

  case WAIT_FOR_KEYRELEASE :
    if(key == 0) 
    State = WAIT_FOR_KEYPRESS; 
  }

  UpdateDisplay();
  DoSomeOtherStuff();
  delay(50);
 }

Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Arduino robotprogrammering

Inlägg av Magnus_K »

Vill inte vara otacksam men det blir så himla svårt att helt plötsligt tänka om när man jobbat med den koden så länge som jag gjort.
Jag valde att testa det svanted tipsade om och testade en funktion i taget samt strösslade med debug-prints.

Ett av dom dom största felen var att jag inte använde mig av digitalRead() när jag läste av en portpinne. Utan vänsterskiftade och sedan fyllde på med pin-numret. Ni ser felet i den tidigare koden i "debounce-funktionen".
Klurade också länge på varflr enbart X-knappen fungerade och ingen annan. Det visade sig att jag skrivit rutinen fel så jag avslutade alltid med att "nolla" vilken knapp som var tryckt. Ni kan se även det felet i den tidigare koden.
Gjort lite andra småändringar men så här ser det ut nu.
Nu gäller det bara att kunna slå ihop den här koden med den jag gjort som ritar pilar på displayen... In i dimman...

Tack för hjälpen så här långt! :tumupp:

Kod: Markera allt

/*****************************************CONSTANTS****************************************************/
const uint8_t buttonLeft = 9;
const uint8_t buttonRight = 10;
const uint8_t buttonFwd = 11;
const uint8_t buttonX = 12;
/*****************************************************************************************************/

/**************************************GLOBAL VARIABLES***********************************************/
uint32_t debounceDelay = 30;
uint32_t lastDebounceChk = 0;
uint8_t debounceReg_Left = 0xFF;
uint8_t debounceReg_Right = 0xFF;
uint8_t debounceReg_Fwd = 0xFF;
uint8_t debounceReg_X = 0xFF;
uint8_t buttonLeft_Pressed = 0;
uint8_t buttonRight_Pressed = 0;
uint8_t buttonFwd_Pressed = 0;
uint8_t buttonX_Pressed = 0;
uint8_t Left_Press_Done = 0;
uint8_t Right_Press_Done = 0;
uint8_t Fwd_Press_Done = 0;
uint8_t X_Press_Done = 0;
uint8_t buttonCounter = 0;

enum Directions : uint8_t {NONE, LEFT, RIGHT, FWD, X};
enum Directions key;
enum StatusOfButtons : uint8_t {WAIT_FOR_KEYPRESS, WAIT_FOR_KEYRELEASE};
enum StatusOfButtons Buttonstatus;


/*****************************************************************************************************/

void pushButtonChk() {
  if(millis() >= (lastDebounceChk + debounceDelay)) {        
    
    lastDebounceChk = millis();
    
    debounceReg_Left <<= 1;                                         // Every time the function is called
    debounceReg_Right <<= 1;                                        // the debounce registers is being shifted
    debounceReg_Fwd <<= 1;                                          // left and then the current button status
    debounceReg_X <<= 1;                                            // is read and added
    debounceReg_Left |= digitalRead(buttonLeft);
    debounceReg_Right |= digitalRead(buttonRight);
    debounceReg_Fwd |= digitalRead(buttonFwd);
    debounceReg_X |= digitalRead(buttonX);

    if(debounceReg_Left == 0x00) {                                  // As soon as the debounce register is full
      key = LEFT;                                                   // then the system reads the button as pushed
    }
    if(debounceReg_Right == 0x00) {
      key = RIGHT;
    }
    if(debounceReg_Fwd == 0x00) {
      key = FWD;
    }
    if(debounceReg_X == 0x00) {
      key = X;
    }
    else if((debounceReg_X & debounceReg_Fwd & debounceReg_Right & debounceReg_Left) == 0xFF) {
      key = NONE;
    }
  }
}


void setup() {                                                      // Using internal pullup for buttons

  Serial.begin(9600);
  pinMode(buttonLeft, INPUT);
  pinMode(buttonRight, INPUT);
  pinMode(buttonFwd, INPUT);
  pinMode(buttonX, INPUT);
  digitalWrite(buttonLeft, HIGH);
  digitalWrite(buttonRight, HIGH);
  digitalWrite(buttonFwd, HIGH);
  digitalWrite(buttonX, HIGH);
  
}

void loop() {

  pushButtonChk();

  switch(Buttonstatus) { 

    case WAIT_FOR_KEYPRESS :
    
      buttonCounter = 0;

      switch(key) {

        case LEFT :
          if(!Left_Press_Done) {
            Left_Press_Done = 1;
            Serial.println("Left button pressed!");
          }       
          Buttonstatus = WAIT_FOR_KEYRELEASE;
          break;

        case RIGHT :
          if(!Right_Press_Done) {
            Right_Press_Done = 1;
            Serial.println("Right button pressed!");
          }
          Buttonstatus = WAIT_FOR_KEYRELEASE;
          break;

        case FWD :
          if(!Fwd_Press_Done) {
            Fwd_Press_Done = 1;
            Serial.println("Fwd button pressed!");
          }
          Buttonstatus = WAIT_FOR_KEYRELEASE;
          break;

        case X :
          if(!X_Press_Done) {
            X_Press_Done = 1;
            Serial.println("X button pressed!");
          }
          Buttonstatus = WAIT_FOR_KEYRELEASE;
          break;

        case NONE :
          Buttonstatus = WAIT_FOR_KEYPRESS;
          break;
        
      }

      break;
    
    case WAIT_FOR_KEYRELEASE :

      switch(key) {

          case NONE :
          Left_Press_Done = 0;
          Right_Press_Done = 0;
          Fwd_Press_Done = 0;
          X_Press_Done = 0;
          Buttonstatus = WAIT_FOR_KEYPRESS;

          break;
      }

      break;
      
  }
}
Användarvisningsbild
Icecap
Inlägg: 26106
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Re: Arduino robotprogrammering

Inlägg av Icecap »

Om du använder en byte till att representerar knappavläsningen anser jag att det blir enklare.

Deklarerar lite bytes:
Now, Then, Came, Used. Initieras till 0.

I main-loop ska man (i Arduino-fallet) exekvera Read_Button() var 50ms. I en riktig µC sätter man en timer-interrupt ISR till att köra det med samma hastighet.

Read_Button() består av:
Steg 1:
Läs in alla 4 knappar till en byte. Bit 0 representerar ena knappen, bit 1 andra osv. Byten kallas "Now".
Använde man en verklig µC med riktiga portnamn ville jag placera knapparna på samma port och använda det bitmönster.
Jag har dock mycket nyligt gjort så att varje bit läsas från valfri portpinne och placeras sedan i ett känd bitmönster så det är en lösning som kan fungera även om att den inte är snygg.

Steg 2:
Om Now != Then ska det hoppas till Steg 4.
I annat fall fortsätter en del av rutinen då det ju finns två identiska avläsningar efter varandra = debounced.
Tänk:
if(Now == Then)
{
Steg 3
}

Steg 3:
Used &= Now;
Came = Now & ~Used;

Steg 4: (köras ALLTID och sist)
Then = Now;

Nu ska alla funktioner som känner av en knapptryckning (i Came, bitmönster) ÄVEN - när knapptryckningen är använd - setta samma bit i Used.

Om knappen släpps kommer Used att nollas på det bits som släpps.

Kod: Markera allt

Read_Button()
  {
  Steg 1;
  if(Now == Then)
    {
    Steg 3;
    }
  Steg 4;
  }
I main-loop kollas:

Kod: Markera allt

if(Came & Bitmönster_för_knapp)
  {
  Used |= Bitmönster_för_knapp; // Mark as used
  // Do whatever needs to be done for that button
  }
Senast redigerad av Icecap 30 juni 2018, 13:34:06, redigerad totalt 1 gång.
Glattnos
Inlägg: 2972
Blev medlem: 29 oktober 2009, 20:01:18

Re: Arduino robotprogrammering

Inlägg av Glattnos »

Nått jag lärt mig är att man i princip alltid tjänar på att göra om och göra rätt när Icecap, Sodjan och Swech (med flera) säger så. Dom har sparat mig många timmar genom att visa "rätt väg" :)
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Arduino robotprogrammering

Inlägg av Magnus_K »

Jodå, jag brukar vara lyhörd och lyssna på "dom som kan", men i det här fallet, angående hur jag läser och avstudsar knapparna, ser jag ingen mening i att ändra. Alla verkar ju ha sitt egna sätt.

I mitt fall använder jag 8 bitar per knapp men med Icecap's metod så hade jag kunnat använda lite mindre minne men jag ser just nu ingen direkt nytta med att göra det på det viset.
Tycker det fungerar bra (som jag fick lära mig här) att skifta in nuvarande nivå på pinnen i ett register och när det innehåller enbart 1:or eller enbart 0:or så är det avstudsat och klart.
Glattnos
Inlägg: 2972
Blev medlem: 29 oktober 2009, 20:01:18

Re: Arduino robotprogrammering

Inlägg av Glattnos »

Fast jag är lite nyfiken på en sak. Varför vill man avstudsa alls när man bara läser knappen med jämna intervaller, t.ex. 100ms. Den KAN ju inte registrera studs kortare än 100ms då?

Om du kör rutinen med 240ms intervall istället och bara gör digitalRead på alla knappar i rutinen så kan du väl ta bort alla skiftningar och if(nånting == 0x00) och ändå få samma resultat?
Eller vad är det jag missar?

Trycker man in knappen just när rutinen körs så kan det studsa, men det spelar ju ingen roll eftersom det registreras nästa gång rutinen körs. När man släpper upp knappen är det samma, om det studsar just på rutinen så registreras bara trycket som en intervall längre/kortare. Processorn ”ser” aldrig studsarna utan kan göra annat istället.
Rätta gärna om jag har fel :)
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Arduino robotprogrammering

Inlägg av Magnus_K »

Ser ingen nytta med att köra rutinen så sällan heller, mer än att just nu ger det en bra tröghet i knappen medans jag programmerar och debuggar.
När allt är klar kommer delayet minskas till kanske 5 eller så. Kommer inte ihåg vad jag använt tidigare.
Användarvisningsbild
Icecap
Inlägg: 26106
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Re: Arduino robotprogrammering

Inlägg av Icecap »

Mina tester har visat att man inte "kan" trycka på en knapp i kortare tid än 100ms. Så ska man ta med debounce i form av två likadana avläsningar är den maximala hastighet man behöver använda 50ms mellan varje avläsning. Man kan såklart göra det snabbare om det passar bättre av någon anledning.

Sedan har jag minne av att knappars bounce-tid ligger runt 15ms eller så så 50ms bör vara rikligt om inte knappen är mycket usel.

Att jag packar ihop knapptryckningarna in i en byte är för att kunde maska bits och utföra de logiska operationer på ett enkelt sätt.
Även det kan såklart göras på knapp-för-knapp basis men vill spendera mycket programtid på "ingenting". Och i detta projekt är det kanske utan betydelse - men i andra kan det vara viktigt.

Skulle jag ha gjort detta projekt hade jag gjort en FIFO-buffer som varje knapptryckning blev lagt i, då ville varje tryckning komma i sekvens även om det var fördröjningar i att knapptryckningarna blev läst. Lite mer komplicerat kanske - men ack så enkelt att använda.
Glattnos
Inlägg: 2972
Blev medlem: 29 oktober 2009, 20:01:18

Re: Arduino robotprogrammering

Inlägg av Glattnos »

Icecap: Men visst blir det väl så att det inte ens behövs två avläsningar för debounce om man kör rutinen ca 50ms? Processorn "ser" ju bara om den är hög eller låg, om det är ett studs eller inte spelar ingen roll eftersom i de fall det är studs så är det för att man just släppt eller tryckt ner knappen med enda skillnaden att "knapptrycket" blir antingen en intervall kortare eller längre. Onödigt att krångla till det om processorn ändå inte "ser" någon skillnad. Det är ju som sagt få knappar som studsar längre än 50ms :)

Magnus_K: Som din kod är nu så bör du rimligen ha väldigt bra debounce och en tid mellan 210-240 ms från nertryckt till registrerat nertryckt. Det är inte fel, rätt bra egentligen :)
Användarvisningsbild
Icecap
Inlägg: 26106
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Re: Arduino robotprogrammering

Inlägg av Icecap »

Glattnos: på sätt och vis sant - men störningar kan ge glitcher och de tas effektivt bort. Faktisk hade jag ett projekt där jag - av den grund - var tvungen att ha 3 lika avläsningar för att accepteras.

Jag gör så för att det är genomtestat och välfungerande varför jag inte har problem med knappsatser.

Jag ser dock att jag har gjort ett minimalt fel i min beskrivning ovan så det får jag justera.
Användarvisningsbild
Magnus_K
EF Sponsor
Inlägg: 5854
Blev medlem: 4 januari 2010, 17:53:25
Ort: Skogen mellan Uppsala-Gävle

Re: Arduino robotprogrammering

Inlägg av Magnus_K »

Försöker baka ihop programmen nu och tänkte alltså "konvertera" knapptryckningar till symboler på displayen.
Tyckte Krille Krokodil's metod han skrev här var så otroligt lättförståelig och enkel så har nu gjort ett försök att anamma den. Dock är det något som ställer till det, och jag förstår inte vad.

Respektive knapptryckning sparas som ett ASCII-tecken i en array som deklarerats uint8_t commands[24];
Det tycks fungera precis som tänkt för om jag trycker Vä, Vä och Vä så innehåller commands tecknen '767676' (LLL).

Funktionen för att skriva ut 3 st vänsterpilar ser ut som nedan. Pilarna skrivs också ut som tänkt men med ett litet problem; bara hälften av symbolen syns.
Efter lite felsökande så verkar det vara helt avgörande vart man placerar u8g.nextPage());. Det här är alltså funktionen för att skriva andra hälften av symbolen.
Tyvärr verkar det inte vara så enkelt så man bara avslutar varje symbolskrivning med det för "originalkoden" ser ut som nedan. Man måste nyttja en while-loop och det är här det skiter sig med mina kunskaper.
Är det någon som förstår vad jag menar och eventuellt har ett sätt att lösa det på?

Originalkod för att skriva ut en hel symbol:

Kod: Markera allt

void loop(void) {

  u8g.firstPage();  
  do {
    u8g.drawBitmapP( 0, 0, 2, 20, arrow_up);
  } while( u8g.nextPage() );

Kod för att skriva ut flera symboler:

Kod: Markera allt

void screenUpdate() {

    u8g.firstPage();
    
    int X = 0;
    int Y = 0;
      
    for(int i=0; i < commands; i++){
      
      switch(commands[i]){
        
        case 'F':
        u8g.drawBitmapP( X, Y, 2, 20, arrow_up);
        X+=16;
        break;
    
        case 'L':
        u8g.drawBitmapP( X, Y, 2, 20, arrow_left);
        X+=16;
        
        break;
  
        case 'R':
        u8g.drawBitmapP( X, Y, 2, 20, arrow_right);
        X+=16;
        //
        break;
  
      }
  
     // Hantera radbyte
      if(X>160){
        X+=0;
        Y+=21;
      }
    }
    u8g.nextPage());
  } 
}
Skriv svar