Hoe maak ik een submenu?

algemene C code
Berichten: 9
Geregistreerd: 05 Jan 2017, 15:05

Hoe maak ik een submenu?

Berichtdoor Bassie123 » 07 Mrt 2017, 21:27

Hallo allemaal ik ben voor mijn profielwerkstuk en meesterproef bezig met het maken van een Rotary encoder menu met Arduino waarmee ik een aantal dingen kan meten en instellen. Het is mij ondertussen gelukt om met de Rotary encoder een menu te maken en om daar metingen in te laten zien. Het lukt mij alleen niet om een submenu te maken waarin ik dingen kan instellen met diezelfde Rotary encoder. Ik heb in onderstaande code een DisplayFanSpeed dit is een fan controller die ik gemaakt heb ik wil met een Rotary encoder de snelheid in kunnen stellen binnen dit menu. zijn er mensen die mij hiermee kunnen helpen? ander suggesties om mijn code te verbeteren zijn natuurlijk ook altijd welkom.

cpp code
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <HX711.h>

LiquidCrystal_I2C lcd(0x27,20,4);

enum EncoderDir
{
Anticlockwise = 0,
Clockwise = 1,
};
typedef enum EncoderDir EncodedirStatus;
EncodedirStatus EncoderRoateDir;

#define EncoderPin1 3
#define EncoderPin2 4

unsigned char EncoderFlag = 0;
signed char LCDPage = 6;
int motorPin = 11;
int motorValue = 0;
signed char motorSpeed = 0;

float gewicht = 0;
const float calibration_factor = 401.1;

#define DOUT A2
#define CLK A3

HX711 scale(DOUT, CLK);

int buttonPin1 = 7;

volatile float time = 0 ;
volatile float time_last = 0;
volatile int rpm_array[5] = {0,0,0,0,0};

char buffer[30];

void EncoderRotate()
{
if(digitalRead(EncoderPin1) == 1) {
delay(10);
if(digitalRead(EncoderPin1) == 1) {
if(EncoderFlag == 0) {
EncoderFlag = 1;
if(digitalRead(EncoderPin2) == 1) {
EncoderRoateDir = Clockwise;
} else {
EncoderRoateDir = Anticlockwise;
}
}
} else {
}
} else {
}
}

void setup()
{
Wire.begin();
lcd.init();
lcd.backlight();
lcd.clear();

pinMode(EncoderPin1,INPUT);
pinMode(EncoderPin2,INPUT);
attachInterrupt(1,EncoderRotate,RISING);

long zero_factor = scale.read_average(5);

scale.set_scale(calibration_factor);
scale.set_offset(zero_factor);
scale.tare();

attachInterrupt(0, fan_interrupt, FALLING);

}

void DisplayWeight()
{
lcd.setCursor(1,0);
lcd.print(gewicht,0);
lcd.print(" gr");
delay(1000);

gewicht = scale.get_units();
Serial.print(gewicht,0);
Serial.print(" gr");
}

void DisplayRPM()
{
int rpm = 0;

while(1){
delay(400);

lcd.setCursor(0,1);
lcd.print(" ");

lcd.setCursor(0,1);
lcd.print(rpm);

if(time > 0)
{
rpm_array[0] = rpm_array[1];
rpm_array[1] = rpm_array[2];
rpm_array[2] = rpm_array[3];
rpm_array[3] = rpm_array[4];
rpm_array[4] = 60*(1000000/(time*20));

rpm = (rpm_array[0] + rpm_array[1] + rpm_array[2] + rpm_array[3] + rpm_array[4]) / 5;

}
}
}

void fan_interrupt()
{
time = (micros() - time_last);
time_last = micros();
}

void DisplayFanSpeed()
{
sprintf(buffer,"Fan speed");
lcd.setCursor(1,4);
lcd.print(buffer);
Serial.print(buffer);

if(digitalRead(buttonPin1 = HIGH) && LCDPage == 3){

motorValue = map(motorSpeed, 0, 24, 0, 255);

if (EncoderFlag == 1) {
delay(100);
EncoderFlag = 0;
switch(EncoderRoateDir) {
case Clockwise:
motorSpeed++;
break;
case Anticlockwise:
motorSpeed--;
break;
default:
break;
}
if (motorSpeed > 24){
motorSpeed = 24;
}
if (motorSpeed < 0) {
motorSpeed = 0;
}
lcd.setCursor(1,3);
lcd.println(motorValue);
analogWrite(motorPin, motorValue);
Serial.print("t motor = ");
Serial.println(motorValue);
delay(2);
}
}
}

void DisplayMenu4()
{
sprintf(buffer,"Menu 4");
lcd.setCursor(1,4);
lcd.print(buffer);


}


void DisplayMenu0()
{
sprintf(buffer,"Menu 0");
lcd.setCursor(1,4);
lcd.print(buffer);

}
void DisplayMenu5()
{
sprintf(buffer,"Menu 5");
lcd.setCursor(1,4);
lcd.print(buffer);
}




void loop()
{

if (EncoderFlag == 1) {
delay(100);
EncoderFlag = 0;
switch(EncoderRoateDir) {
case Clockwise:
LCDPage++;
break;
case Anticlockwise:
LCDPage--;
break;
default:
break;
}
if (LCDPage > 5) {
LCDPage = 0;
}
if (LCDPage < 0) {
LCDPage = 5;
}
lcd.clear();
}

switch (LCDPage) {
case 0:
DisplayMenu0();
break;
case 1:
DisplayRPM();
break;
case 2:
DisplayFanSpeed();
break;
case 3:
DisplayWeight();
break;
case 4:
DisplayMenu4();
break;
case 5:
DisplayMenu5();
break;
}

}

Advertisement

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: Hoe maak ik een submenu?

Berichtdoor Koepel » 07 Mrt 2017, 22:39

Helaas kan ik je niet helpen met het menu, op dit moment springen er teveel andere dingen in mijn oog.

Zou je eens met mij door de code willen lopen ?

1 ) Een 'Flag' kan een 'boolean' zijn. Die is 'true' of 'false'.
2 ) Zodra een tijd met millis() of micros() te maken heeft, dan is het 'unsigned long'. Dus 'time' en 'time_last' horen 'unsigned long' te zijn.
3 ) De 'time_last' wordt alleen in de interrupt gebruikt, en niet in de loop(). Dus die hoeft in principe niet 'volatile' te zijn.
4 ) Je gebruikt de 'time' in de loop() en in een interrupt. Die 'time' is 4 bytes. Dus als je het in de loop() gebruikt dan kan tijdens het lezen van die 4 bytes een interrupt komen die de 'time' wijzigt. Daarom is het nodig om in de loop() de interrupts uit te zetten zodra je 'time' gebruikt. Meestal worden de interrupts even uitgezet en wordt er een kopie van gemaakt. Met die kopie wordt vervolgens gewerkt in de loop().
5 ) Een digitalRead() kan 'HIGH' of 'LOW' terug geven. Zou je die daarom willen vergelijken met 'HIGH' of 'LOW' ? en niet met '1'.
6 ) Tegenwoordig wordt geadviseerd om altijd de "digitalPinToInterrupt(pin)" te gebruiken bij attachInterrupt(), zie: https://www.arduino.cc/en/Reference/attachInterrupt.
7 ) In je interrupt routine 'EncoderRotate()' zit een delay van 10ms. Dat is een zeer zware belasting. In een interrupt routine hoort geen delay. Kun je het debouncen op een andere manier doen ?
8 ) In DisplayFanSpeed() staat dit "if (digitalRead(buttonPin1 = HIGH)", dat is een bug.
9 ) Volgens mij wordt 'rpm_array[]' niet in een interrupt gebruikt, dus die hoeft niet 'volatile' te zijn.
10 ) De 'LCDPage' en 'motorSpeed' mogen gerust integers zijn. Het geeft niets, maar gewoon 'int' is gemakkelijker.
11 ) Je 'enum' declaratie en de variabele heeft iets drie-dubbel-op-achtigs. De taal 'c' is gemoderniseerd, en vele 'typedef' zijn niet meer nodig. De 'EncoderDir' is het type, dus dan heb je alleen nog maar één regel nodig met: EncoderDir EncoderRoateDir;

Ik hoop dat ik niet heb afgeschrikt. Dit is wat er gebeurt als ik je code zie :geek:

Berichten: 9
Geregistreerd: 05 Jan 2017, 15:05

Re: Hoe maak ik een submenu?

Berichtdoor Bassie123 » 08 Mrt 2017, 10:38

koepel,


bedankt voor je reactie om eerlijk te zijn heb ik gewoon een aantal codes van internet samengevoegd. hierdoor heb ik maar gewoon aangenomen dat al die dingen die u nu zegt gewoon zo horen.

1.Die ‘Flag’ heb ik ook gewoon aangenomen dat dit zo is dus weet eigenlijk niet wat hiermee bedoeld wordt.
2.Dit is inderdaad vrij logisch dit ga ik aanpassen.
3.Dit ga ik ook aanpassen
4.Ik snap niet wat u hiermee bedoeld ik ben namelijk nog niet zo vaardig met interrupts
5.Ik snap niet helemaal wat u hiermee bedoeld bedoeld u dat ik net als bij die encoder wel moet vergelijken met 1 of 0 of dat ik juist wel HIGH of LOW moet gebruiken?
6.Dit zal ik gaan aanpassen hier had ik nog niet naar gekeken
7.Ik heb deze code van een vriend en ik heb deze code dus gewoon gebruikt en niet naar het debouncen gekeken ik nam aan dat die delay nodig was. Ik weet niet heel veel van debouncen het werkte dus vond ik het goed. Maar zijn hier andere oplossingen voor.
8.Deze regel had ik eruit moeten halen dit hoorde niet meer in mijn code. Ik heb hiermee geprobeerd een submenu te maken maar dat werkte dus niet.
9.Dit komt omdat ik deze losse code in het menu heb proberen te verwerken. Dus als ik het goed begrijp kan ik hier gewoon een int van maken?
10.Dit is inderdaad een goede tip daar had ik nog niet aan gedacht.
11.Dat is vrij logisch had dit gewoon laten staan omdat het zo in die code van een vriend van mij stond

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: Hoe maak ik een submenu?

Berichtdoor Koepel » 09 Mrt 2017, 10:12

1 ) wanneer iemand naar je EncoderFlag variabele wijst en vraagt: "Waarom heb je daar geen 'boolean' variabele van gemaakt ?", dan zou het handig zijn als je een antwoord had ;)

4 ) Welk Arduino board gebruik je ? Een Uno/Nano/Leoardo/Micro/Mega 2560/Pro Mini ? Die hebben een 8-bit microcontroller. Stel dat er een variabele in het geheugen staat die 4 byte is. Die kan niet met één instructie uit het geheugen gehaald worden, dat gaat in meerdere stappen. Terwijl dat gebeurt kan er een interrupt komen die die variabele gaat wijzigen. Dat zit je vervolgens met een variabele die voor een deel nog de oude waarde heeft en voor een deel de nieuwe waarde.

De code in de functie DisplayRPM() kan dus beter zo iets zijn:
Code: Alles selecteren
    unsigned long time_copy;

    noInterrupts();
    time_copy = time;
    interrupts();

    if (time_copy > 0)
    {
      rpm_array[0] = rpm_array[1];
      rpm_array[1] = rpm_array[2];
      rpm_array[2] = rpm_array[3];
      rpm_array[3] = rpm_array[4];
      rpm_array[4] = 60 * (1000000 / (time_copy * 20));

      rpm = (rpm_array[0] + rpm_array[1] + rpm_array[2] + rpm_array[3] + rpm_array[4]) / 5;
    }
De kans dat het inderdaad mis gaat is uiterst klein. Maar waarom zou je iets verkeerd doen als het ook goed kan :geek:

5 ) Dit is de referentie van digitalRead() : https://www.arduino.cc/en/Reference/DigitalRead
Daar staat: "Returns HIGH or LOW". Dus kun je het alleen maar met HIGH of LOW vergelijken.
Code: Alles selecteren
  if (digitalRead(EncoderPin) == HIGH)
In jouw code vergelijk je het met '1', maar dat staat niet in de referentie.

7 ) Helaas weet ik geen manier om het bouncen tegen te gaan. Ik weet niet of dat een probleem is bij rotary encoders, dat hangt van de code af denk ik. Ik zou een library gebruiken (https://www.pjrc.com/teensy/td_libs_Encoder.html).

10 ) Volgens mij kan de rpm_array een gewone 'int' zijn.

12 ) Ik zie nu dat 'EncoderRoateDir' en 'EncoderFlag' zowel in de interrupt als in de loop() worden gebruikt. Die horen 'volatile' te zijn. Een 'enum' is een integer, dus een 'int' van twee byte. Dan kun je tegen hetzelfde probleem oplopen als met die variabele van 4 byte. Kun je 'EncoderRoateDir' een 'byte' of 'char' maken in plaats van een 'enum/int' ?

Maar uhm... nu over het menu. Is er een 'enter' of 'ok' toets ? Hoe wil met één enkele rotary encode een menu in gaan en daar dingen wijzigen en dan weer terug naar het hoofdmenu ?
Je kunt beginnen om de code die nu in de loop() staat om de rotary encoder te lezen, in een aparte functie te zetten. Dan wordt het eenvoudiger om dat de ene keer voor de LCDPage te gebruiken en de andere keer voor iets anders.

Berichten: 9
Geregistreerd: 05 Jan 2017, 15:05

Re: Hoe maak ik een submenu?

Berichtdoor Bassie123 » 11 Mrt 2017, 16:16

Er zit een kop op de rotary encoder die wil ik gebruiken om op een menu te klikken en dan op die pagina bijvoorbeeld de snelheid kan instellen. Dan wil ik eigenlijk onder in het scherm een back knop tevoorschijn laten komen en als je daarop drukt kun je weer schakelen tussen de verschillende menu’s. Zo zie ik het voor me. Neem aan dat dit wel mogelijk moet zijn maar hoe weet ik niet. Ik heb de tips trouwens aangepast is allemaal vrij logisch alleen punt 12 kom ik niet aan uit maar dit is ook geen heel groot probleem.

Gebruikers-avatar
Berichten: 5043
Geregistreerd: 13 Mei 2013, 20:57
Woonplaats: Heemskerk

Re: Hoe maak ik een submenu?

Berichtdoor nicoverduin » 11 Mrt 2017, 17:13

Ik zou met dezelfde knop werken.... kort indrukken is selecteren, lang indrukken is terug
Docent HBO Technische Informatica, Embedded ontwikkelaar & elektronicus
http://www.verelec.nl

Berichten: 9
Geregistreerd: 05 Jan 2017, 15:05

Re: Hoe maak ik een submenu?

Berichtdoor Bassie123 » 13 Mrt 2017, 10:51

Dat is een goed idee. Alleen het probleem dat ik heb is dat het me niet lukt om hier een werkende code voor te schrijven.

Berichten: 4064
Geregistreerd: 16 Okt 2013, 14:31
Woonplaats: s hertogenbosch

Re: Hoe maak ik een submenu?

Berichtdoor shooter » 13 Mrt 2017, 19:43

in jouw switch case kun je ook zetten dat je naar een submenu schakelt. dat worden dan bijvoorbeeld case 50 etc.
je kunt dan bijvoorbeeld omschakelen tussen hele switch bomen.
paul deelen
shooter@home.nl

Berichten: 9
Geregistreerd: 05 Jan 2017, 15:05

Re: Hoe maak ik een submenu?

Berichtdoor Bassie123 » 13 Mrt 2017, 19:51

hoe zet ik dit dan in mijn switch case?

Berichten: 4064
Geregistreerd: 16 Okt 2013, 14:31
Woonplaats: s hertogenbosch

Re: Hoe maak ik een submenu?

Berichtdoor shooter » 14 Mrt 2017, 20:08

in je displaymenu5 zet je dan bijvoorbeeld in dat de case 50 is en dan daaronder weer 51 enz.
paul deelen
shooter@home.nl

Volgende

Terug naar C code

Wie is er online?

Gebruikers in dit forum: Geen geregistreerde gebruikers en 7 gasten