Arduino interrupt based single, double en long click

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Ik heb het idee dat ik iets over het hoofd zie.
Momenteel ben ik bezig om zoiets zelf te maken: http://flic.io/
Hard- en software zijn gereed, maar ik heb een fout in het registreren van de verschillende functies van de button. Het vermoeden bestaat dat dit komt door het bouncen van de microswitch. Ik kan daar helaas geen hardwarematige oplossing voor implementeren in verband met de fysieke ruimte in mijn behuizing.

Ik werk met een Arduino (Pro Mini), 433mhz transmitter en twee CR2032 batterijtjes. Om de knop zo lang mogelijk mee te laten gaan wil ik het systeem in principe altijd in power down hebben. Pas als de knop ingedrukt wordt dan moet alles beginnen, en daarna zo snel mogelijk weer wakker worden. Het is wat betreft energieverbruik dus ook geen mogelijkheid om niet met interrupts te werken, maar bijvoorbeeld met elke keer 100ms deep sleep en dan checken of er iets is veranderd.

Het probleem is dat soms drie clicks achter elkaar als twee double clicks wordt gezien en dat bij elke double en long click óók de single click wordt gedetecteerd. Dit kan dus komen door het debouncen, maar elke standaard oplossing die ik daarvoor implementeer (millis() bijhouden en < x ms gewoon afkappen of iets dergelijks, bijvoorbeeld debounce oplossing van Arduino documentatie) zorgt er voor dat er óf niets veranderd aan het probleem, óf dat geen enkele click wordt geregistreerd.

Dit is mijn code waarbij het probleem optreedt:
C:
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <Ports.h>
#include <RF12.h>
#include <avr/sleep.h>
#include <PinChangeInt.h>
#include <VirtualWire.h>

ISR(WDT_vect) { Sleepy::watchdogEvent(); }

char *controller;

const int buttonPin = 3;

bool stateSingle = false;
bool stateDouble = false;
bool stateLong = false;

void setup() {
  pinMode(13, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(5, OUTPUT);

  Serial.begin(9600);

  PCintPort::attachInterrupt(buttonPin, wakeUp, HIGH);
}

void wakeUp() {
}

void loop() {
    int i = 0;
    while (digitalRead(buttonPin) == HIGH) { // Wait until button is LOW, or has been high for more than 600ms
      Sleepy::loseSomeTime(50);
      if (i > 12)
        break;
      i++;
    }

    if (digitalRead(buttonPin) == HIGH)
      longTapAction();
    else {
      i = 0;
      while (digitalRead(buttonPin) == LOW) { // Wait for possible double press
        Sleepy::loseSomeTime(50);
        if (i > 8)
          break;
        i++;
      }

      if (digitalRead(buttonPin) == HIGH) {
          doubleTapAction();

          while (digitalRead(buttonPin) == HIGH)
            Sleepy::loseSomeTime(50);
      } else
        singleTapAction();
    }
}

void singleTapAction() {
  stateSingle = !stateSingle;
  digitalWrite(5, stateSingle ? HIGH : LOW);

  Sleepy::powerDown();
}

void doubleTapAction() {
  stateDouble = !stateDouble;
  digitalWrite(6, stateDouble ? HIGH : LOW);

  Sleepy::powerDown();
}

void longTapAction() {
  stateLong = !stateLong;
  digitalWrite(7, stateLong ? HIGH : LOW);

  Sleepy::powerDown();
}


Dit is een versimpelde versie die ik gebruik om de verschillende clicks te testen.
Schema is simpel:
GND -- 10k -- pin 3 -- switch -- Vcc
Sorry, ik heb geen kunde om zoiets netjes te tekenen.

Inmiddels heb ik het idee dat ik de interrupts gewoon niet goed begrijp. Als een interrupt wordt getriggerd, moet daar niets gebeuren verder en moet loop() verder gaan. Bij een tussenkomende interrupt zou niets moeten gebeuren en moet het systeem simpelweg doorgaan met het uitvoeren waar die mee bezig was. Althans, zo heb ik het in mijn gedachte als logisch zitten.

Na twee dagen dit weekend ben ik ten einde raad. Pak ik het nu echt zo verkeerd aan? _/-\o_

Acties:
  • 0 Henk 'm!

  • MrDoodoo
  • Registratie: Juli 2000
  • Laatst online: 18-11-2024
Ik weet niet of het iets oplevert maar de pinmode van attachInterrupt zou RISING, FALLING of CHANGE moeten zijn. Waar triggert de interrupt nu op?

En ik moet toegeven dat mijn C skills iet wat roestig zijn, maar moet dit

code:
1
PCintPort::attachInterrupt(buttonPin, wakeUp, HIGH);


niet dit zijn:

code:
1
PCintPort::attachInterrupt(buttonPin, &wakeUp, RISING);


Wat ik mij nog afvraag: in de loop zet je de interrupts uit, maar als ik het goed lees is loseSomeTime afhankelijk van de watchdogtimer (interrupt). Geeft dat geen problemen?

[ Voor 62% gewijzigd door MrDoodoo op 28-09-2015 21:49 . Reden: toevoeging ]


Acties:
  • 0 Henk 'm!

  • Pizza_Boom
  • Registratie: Juli 2012
  • Laatst online: 23-07 00:15
Zo te zien op een hoog:
PCintPort::attachInterrupt(buttonPin, wakeUp, HIGH);

Ik vraag mij af waarom je iedere keer de interrupts disabled en reenabled. Ik ben te eigenwijs om de standaard libraries te gebruiken en, alhoewel ik een andere chip gebruik, zet nergens voor een ISR mijn interrupt disabled en na de ISR weer enabled. Ik werk met een interrupt op basis van een timer en een interrupt op de pinchange en de timer interrupt blijft gewoon doorwerken terwijl ik de pin change gebruik.

Acties:
  • 0 Henk 'm!

  • Sendy
  • Registratie: September 2001
  • Niet online
Ik kan maar weinig van deze code bakken. Heb je https://www.arduino.cc/en/Reference/AttachInterrupt gelezen? En zie je dan dat daar dat blink() het feitelijke werk doet om de input te registreren en dat loop() alleen de resultaten daarvan verwerkt?

Jouw code heeft geeneens de isr (wakeUp()), en loop() doet al het werk.

Acties:
  • 0 Henk 'm!

  • MrDoodoo
  • Registratie: Juli 2000
  • Laatst online: 18-11-2024
Sendy schreef op maandag 28 september 2015 @ 21:49:
Ik kan maar weinig van deze code bakken. Heb je https://www.arduino.cc/en/Reference/AttachInterrupt gelezen? En zie je dan dat daar dat blink() het feitelijke werk doet om de input te registreren en dat loop() alleen de resultaten daarvan verwerkt?

Jouw code heeft geeneens de isr (wakeUp()), en loop() doet al het werk.
De interrupt moet de atmega uit deepsleep halen. Het lijkt mij geen goed idee om een routine die meer dan 600ms kan duren in een isr te zetten.

Acties:
  • 0 Henk 'm!

  • Pizza_Boom
  • Registratie: Juli 2012
  • Laatst online: 23-07 00:15
Je kan hem wel aanroepen vanuit die ISR... En dan niet je interrupt disablen, dan blijft de rest gewoon doordraaien.

Acties:
  • 0 Henk 'm!

  • Sendy
  • Registratie: September 2001
  • Niet online
MrDoodoo schreef op maandag 28 september 2015 @ 21:54:
[...]
De interrupt moet de atmega uit deepsleep halen. Het lijkt mij geen goed idee om een routine die meer dan 600ms kan duren in een isr te zetten.
Wat ik lees is dat zo'n arduino geen 'deepsleep' heeft. loop() draait als een dolle rondjes. En de isr doet geen 600ms over een vlaggetje zetten dat die loop() daarna afhandeld.

Acties:
  • 0 Henk 'm!

  • MrDoodoo
  • Registratie: Juli 2000
  • Laatst online: 18-11-2024
Sendy schreef op maandag 28 september 2015 @ 22:21:
[...]

Wat ik lees is dat zo'n arduino geen 'deepsleep' heeft. loop() draait als een dolle rondjes. En de isr doet geen 600ms over een vlaggetje zetten dat die loop() daarna afhandeld.
Je vergeet Sleepy::PowerDown()
http://jeelabs.net/pub/docs/jeelib/classSleepy.html
Met wat meer google zie je dat mensen hiermee behoorlijk zuinig met de arduino om kunnen gaan.
En inderdaad, een vlaggetje zetten kost geen 600ms. Dacht dat je bedoelde de inhoud van loop naar de isr te verhuizen.

[ Voor 0% gewijzigd door MrDoodoo op 28-09-2015 23:24 . Reden: typo ]


Acties:
  • 0 Henk 'm!

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
@Sendy: het werk gebeurt niet in de ISR, maar de interrupt is alleen bedoeld om de ATMega uit z'n powerDown() te halen. Bovendien zie je dat er veel meer moet gebeuren dan alleen een vlaggetje zetten, dus daarom is alles naar loop verplaatst. Die wordt elke keer maximaal één keer uitgevoerd, omdat na elke cycle de ATMega in power down gaat.

@Pizza_Boom: Het interrupts disablen en enablen heb ik totaal niet over nagedacht, maar net als alles wat ik probeer lijkt dit totaal geen verschil te maken..!

@MrDoodoo: HIGH, RISING of CHANGE, alles heeft het zelfde effect..!

Acties:
  • 0 Henk 'm!

  • Pizza_Boom
  • Registratie: Juli 2012
  • Laatst online: 23-07 00:15
Even een gedachtenspinsel: Wek je niet alleen de Arduino uit zijn slaap met de pin change interrupt en omdat je verder een loop hebt staan, telt ie niet door? En zou dat niet over gaan als je gelijktijdig een interrupt flag zet om wat te doen?

Acties:
  • 0 Henk 'm!

  • MrDoodoo
  • Registratie: Juli 2000
  • Laatst online: 18-11-2024
@MrDoodoo: HIGH, RISING of CHANGE, alles heeft het zelfde effect..!
En als je nu zowel op een rising als falling edge een isr aanhaakt en de tijd opneemt tussen rising en falling. Misschien nog wat filtering er op dat minder dan x ms verschil niet telt en de loop wacht 600ms en controleert dan hoeveel keypresses er zijn geweest.

Acties:
  • 0 Henk 'm!

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
@Pizza_Boom: ik volg je niet helemaal. In mijn veronderstelling gaat het zo: Arduino slaapt en doet niets, interrupt, Arduino komt uit slaap, ISR (empty), loop doet iets, ergens anders wordt weer powerDown() aangeroepen en gaat ie dus weer slapen.

@MrDoodoo: ik zie dit nog niet helemaal voor me.
Ik ben een paar dagen geleden begonnen met twee ISR, één voor rising en één voor falling, respectievelijk dus keyDown en keyUp. Helaas kwam ik daar niet echt uit, omdat timer0 (millis()) niet doortelt in ISRs.
De loop lekker door laten lopen en eigenlijk niets laten doen die 600ms is denk ik niet echt een optie, omdat ik echt op m'n verbruik moet letten. Stel tien tot twintig keer per dag 600ms "verspillen op vol vermogen", wil zeggen dat ik binnen no time (< een maand) door mijn 400mAh heen ben.

Acties:
  • 0 Henk 'm!

  • MrDoodoo
  • Registratie: Juli 2000
  • Laatst online: 18-11-2024
@MrDoodoo: ik zie dit nog niet helemaal voor me.
Ik ben een paar dagen geleden begonnen met twee ISR, één voor rising en één voor falling, respectievelijk dus keyDown en keyUp. Helaas kwam ik daar niet echt uit, omdat timer0 (millis()) niet doortelt in ISRs.
De loop lekker door laten lopen en eigenlijk niets laten doen die 600ms is denk ik niet echt een optie, omdat ik echt op m'n verbruik moet letten. Stel tien tot twintig keer per dag 600ms "verspillen op vol vermogen", wil zeggen dat ik binnen no time (< een maand) door mijn 400mAh heen ben.
Ik bedoelde niet de loop vol 600ms laten lopen, maar laten wachten met loseSomeTime zoals je nu ook doet. loseSomeTime geeft een 0 terug indien onderbroken door een interrupt en in dat geval kan je kijken met millis hoeveel tijd er nu verstreken is. Met vlaggetjes vanuit de isr kan je zien of (/hoeveel) rising/falling er is geweest en dan een filter toepassen om te bepalen of je een geldige keypress hebt of niet.
Ik vraag mij alleen af of millis nog wel juist werkt aangezien je in de mainloop de interrupts hebt uitgezet met cli(). Zodra dit langer duurt dan 1ms en de timer een (of meerdere) overflow interrupt(s) genereert wordt deze in de wacht gezet waardoor je drift kan krijgen.
Waarom gebruik je eigenlijk de cli() en sei() ?

Acties:
  • 0 Henk 'm!

  • Sendy
  • Registratie: September 2001
  • Niet online
MrDoodoo schreef op maandag 28 september 2015 @ 23:24:
[...]
Je vergeet Sleepy::PowerDown()
http://jeelabs.net/pub/docs/jeelib/classSleepy.html
Met wat meer google zie je dat mensen hiermee behoorlijk zuinig met de arduino om kunnen gaan.
En inderdaad, een vlaggetje zetten kost geen 600ms. Dacht dat je bedoelde de inhoud van loop naar de isr te verhuizen.
Dat heb ik inderdaad niet gezien, en nu begrijp ik ook beter wat de bedoeling was :)

Acties:
  • 0 Henk 'm!

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
MrDoodoo schreef op dinsdag 29 september 2015 @ 11:16:
[...]

Ik bedoelde niet de loop vol 600ms laten lopen, maar laten wachten met loseSomeTime zoals je nu ook doet. loseSomeTime geeft een 0 terug indien onderbroken door een interrupt en in dat geval kan je kijken met millis hoeveel tijd er nu verstreken is. Met vlaggetjes vanuit de isr kan je zien of (/hoeveel) rising/falling er is geweest en dan een filter toepassen om te bepalen of je een geldige keypress hebt of niet.
Ik vraag mij alleen af of millis nog wel juist werkt aangezien je in de mainloop de interrupts hebt uitgezet met cli(). Zodra dit langer duurt dan 1ms en de timer een (of meerdere) overflow interrupt(s) genereert wordt deze in de wacht gezet waardoor je drift kan krijgen.
Waarom gebruik je eigenlijk de cli() en sei() ?
Ah okee. Ik ga je bericht straks nog een keer doornemen (aandachtig) en kijken of ik kan implementeren wat je bedoelt.
cli() en sei() stonden er nog in, maar zoals ik al zei, helpt het weghalen ook niet. Inmiddels zijn dus weg en kun je er van uit gaan dat ze niet meer terug komen. Was destijds gewoon iets om uit te proberen.

Edit:
Even een korte vraag. Als ik de Arduino ergens, willekeurig in powerDown() gooi en daarna een interrupt ontvangt, ISR uitvoert, waar gaat ie dan daarna verder? Wordt de code na de power down call dan uitgevoerd?

[ Voor 8% gewijzigd door GWTommy op 29-09-2015 14:59 ]


Acties:
  • 0 Henk 'm!

  • MrDoodoo
  • Registratie: Juli 2000
  • Laatst online: 18-11-2024
Even een korte vraag. Als ik de Arduino ergens, willekeurig in powerDown() gooi en daarna een interrupt ontvangt, ISR uitvoert, waar gaat ie dan daarna verder? Wordt de code na de power down call dan uitgevoerd?
Ik zie niet waarom het programma ergens anders zou starten dan waar je gebleven bent.
Dan doet dit denk ik niet wat je zou willen:
code:
1
2
3
4
5
6
if (digitalRead(buttonPin) == HIGH) {
          doubleTapAction();

          while (digitalRead(buttonPin) == HIGH) //<---------
            Sleepy::loseSomeTime(50);//<----------
      }

Acties:
  • 0 Henk 'm!

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Die link van gammon.com.eu heeft veel nuttige info! Ga ik eens goed doornemen. Thnx.

MrDoodoo, die had ik er inderdaad al uit gehaald. Echter, weinig resultaat.

In het filmpje hieronder kun je zien wat precies het probleem is. Eerst doe ik een single click, dat gaat goed. Dan bij een double click wordt ook weer een single click aangeroepen. Dan bij een long click het zelfde verhaal. Vervolgens zie je dat het geheel in een staat komt dat meerdere malen klikken zorgt voor bij elke klik double click uitgevoerd.
YouTube: Probleem met debouncing

Ledje 1 is single click.
Ledje 2 is double click.
Ledje 3 is long click.
Van links naar rechts in de video.

Acties:
  • 0 Henk 'm!

  • lordprimoz
  • Registratie: November 2013
  • Laatst online: 25-07 21:36
Al geprobeert iets uit te laten voeren in de wakeUp() methode?

Het kan namelijk best dat de compiler deze code eruit haalt omdat er eigenlijk niks gebeurd en dus eigenlijk overbodig is

Acties:
  • 0 Henk 'm!

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Ik heb niet het vermoeden dat dat het geval is. De interrupt zorgt er wel voor dat de Arduino uit z'n sleep komt, en dat is in principe voldoende.

Acties:
  • 0 Henk 'm!

  • lordprimoz
  • Registratie: November 2013
  • Laatst online: 25-07 21:36
Sorry ik dacht dat daar de issues bij waren, had verhaal niet helemaal gelezen.

Werkt de debounce van hier niet?:https://www.arduino.cc/en/Tutorial/Debounce

Zier er in ieder geval een heel stuk minder gaar uit.
Die while loops gevolgd door een if statement waar je weer controlleerd of de pin high is gaat natuurlijk niet werken, omdat de while loop niet locked. Als je dus de knop op het verkeerde moment debounced en hij uit de while loop slaat, kan hij dus weer als high ingelezen worden, en krijg je een longpush die je helemaal niet wilt hebben. Zelfde geld dus voor je andere while -if

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Ik vrees dat het principe van met while loops wachten of de knop nogmaals wordt ingedrukt, of af uitgedrukt is, nooit zal gaan werken. Ik had bij het schrijven van deze code namelijk geen benul van het bestaan van bouncing. Dat ondermijnt heel het idee van die loops natuurlijk. Dat laat zien dat ik toch nog weinig ervaring heb met hardware interactie.

Daarom aan jullie de vraag hoe ik dit anders aan kan pakken, met in het achterhoofd het zo veel mogelijk laten slapen van de microcontroller. In principe kunnen jullie deze opzet dus als vergeten beschouwen.
Het tellen van keydown en keyup zie ik jog niet helemaal voor me. Ik kan me niet inbeelden hoe dit er uit zou zien, omdat ik volgens mijn dan extensief gebruik moet maken van millis(), die niet meer door telt als alles in powerdown is.
Iemand die een (pseudo) codevoorbeeld zou kunnen uitwerken om mij verder te helpen?

  • Pizza_Boom
  • Registratie: Juli 2012
  • Laatst online: 23-07 00:15
Hoe stel jij je je clicks precies voor, want dat is het enige dat voor mij niet echt duidelijk is. Welke soorten kliks heb je allemaal?

  • lordprimoz
  • Registratie: November 2013
  • Laatst online: 25-07 21:36
http://forums.parallax.co...ounce-multi-part-question

Dan heb je geen last meer van het afhandelen van de debouncing in je code :p

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
@Pizza_Boom:
Zoiets:
code:
1
2
3
4
5
Single: ____|``````|_____________

Double: ____|``````|____|`````|__

Long:   ____|```````````````|____

Met het volgende in het achterhoofd. Long is elke keer high > 600 ms (bijv.). Double is elke high < 600 ms gevolgd door een low van x ms gevolgd door een high, waarvan de duur niet uit maakt. Single is elke high < 600 ms gevolgd door een low van > x ms zodat het geen double click is.
Wanneer mogelijk zou ik eventueel ook bijv. triple click willen doen, maar dat is geen must.
Edit: dit heb ik min of meer zo letterlijk mogelijk in mijn code vertaald, ook in die volgorde. Maar dat blijkt dus niks.. :'(

@lordprimoz: dus jij zegt gewoon hardwarematig debouncen met een 100uF cap? Is dat alles wat nodig is? Hoe klein zijn die condensators bij de gemiddelde elektronicazaak te krijgen? Ik zit namelijk behoorlijk met de beschikbare ruimte. Die is nihil.

[ Voor 7% gewijzigd door GWTommy op 30-09-2015 09:58 ]


  • Pizza_Boom
  • Registratie: Juli 2012
  • Laatst online: 23-07 00:15
Een 100 uF keramisch capje is echt klein. Formaatje kwartwatt weerstand of een fractie groter.

Over dat drukken: Is een lang hoog echt langer dan de kort hoog + laag?

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Ok dan ga ik die zo eens scoren en kijken of ik die er tussen krijg.

De schematische weergave is niet gebaseerd op duur van hoog of laag.

  • Pizza_Boom
  • Registratie: Juli 2012
  • Laatst online: 23-07 00:15
als je echt back to basic gaat en de periode dat er gedrukt wordt dat ding aan houdt (lijkt mij wel handig), is dat wel iets belangrijkers...

  • lordprimoz
  • Registratie: November 2013
  • Laatst online: 25-07 21:36
Bij die link hebben ze het over het gebruik van eem schmitt trigger, maar het zou inderdaad ook zonder kunnen.

Die schmitt trigger wordt gebruikt om pas bij een bepaalde spanning pas triggered, deze spanning wordt door je filter langzaam opgebouwd.

Werk je zonder schmitt trigger, dan ben je afhankelijk van wat je ingang ziet als hoog of laag.

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
@Pizza_Boom: wat bedoel je dan precies?

En dan de vraag: mijn huidige code zou eventueel correct wel kunnen werken met hardwarematige debounce, toch?

  • Pizza_Boom
  • Registratie: Juli 2012
  • Laatst online: 23-07 00:15
Jij wil hem tussen het drukken door ook nog in sleepmodus gooien. Snap ik, maar ik vraag mij af of je het jezelf daarmee niet te moeilijk maakt. Wat ik zou doen (even een snel gedachtespinseltje) is een setje hulpvariabele maken en op en pin change interrupt gaan werken. Een van die hulpvariabelen laat je doortellen in een apart whileloopje buiten je ISR, zo lang je knop hoog is. Wordt de knop laag, dan komt er weer een interrupt en laat je de getelde waarde vergelijken met de hulpvariabelen. Is deze korter dan je 600 mS die je net noemt, dan kan je dus ook de laag gaan tellen. Is ie hoger, dan weet je dat je een lang hebt.

Met googlen op millis en hun resetmogelijkheden (na verloop van tijd heeft ie een overflow en daar kan jij dan net even in de problemen mee komen), kwam ik hierop uit. Ook iemand met een sleepcyclus: http://forum.arduino.cc/index.php?topic=49629.0

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Dat zou betekenen dat ik hoe dan ook alle tijd die tussen de eerste keer hoog worden zit en tot ik voldoende weet om een klik te registreren (welke dat ook is), die Arduino op vol vermogen draait? Tegen 13mA verbruik en laten we zeggen 15 click per dag, gaat dat aardig snel volgens mij..

Dat millis() te resetten is en dat de overflow gecorrigeerd kan worden weet ik, maar ik weet zo 123 niet waar dat nodig is.

Edit: en de EMPTY_INTERRUPT hier vandaan http://www.gammon.com.au/forum/?id=11488 doet bij mij niets. Vreemd..

[ Voor 30% gewijzigd door GWTommy op 30-09-2015 10:34 ]


  • clogie886
  • Registratie: Juli 2005
  • Laatst online: 31-05 16:20
Misschien dat deze tutorial je kan helpen: YouTube: Tutorial 02 for Arduino: Buttons, PWM, and Functions

De overheid is niet de oplossing maar de oorzaak van veel problemen.


  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Ik heb er inmiddels een 110µF condensator tussen gezet. Dit haalt niets uit. Deze code laat zien dat de counter per click aardig op loopt (stuk of 6 interrupts per click):
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
volatile int counter = 0;
int last = 0;

void setup() {
  pinMode(3, INPUT);
  Serial.begin(9600);
  
  PCintPort::attachInterrupt(buttonPin, wakeUp, RISING);
}

void wakeUp() {
  counter++;
}

void loop() {
  delay(200);
  if (last != counter) {
    Serial.println(counter);
    last = counter;
  }
}


Mijn schema is nou zo:
Afbeeldingslocatie: http://3.bp.blogspot.com/-wAYF14jJWx0/T2Fq_yhoBnI/AAAAAAAABDk/UmTVve9tS70/s1600/cap+button.PNG

  • MrDoodoo
  • Registratie: Juli 2000
  • Laatst online: 18-11-2024
Staat die condensator daar wel goed? Moet die niet parallel aan de weerstand na de switch ? Je laad hem dan op zodra de knop wordt ingedrukt en zodra de knop wordt losgelaten ontlaad de condensator over de weerstand waardoor je het signaal uitsmeert. Maar ik ben met electronica ook een beginner, dus ik kan het helemaal verkeerd hebben...

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Als ik 'm zet zoals jij beschrijft of helemaal weg haal dan loopt die counter nóg sneller op..

  • MrDoodoo
  • Registratie: Juli 2000
  • Laatst online: 18-11-2024
Dat is wel vreemd.
Het lijkt er dan op dat de interrupt niet op rising edge triggert maar op high.
De RC in deze opstelling is 0.001 sec wat een enkele druk op de knop voor 1ms het signaal hoog houdt.
Heb je een oscilloscope om te kijken wat er gebeurt?

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Zowel RISING als HIGH geven het zelfde resultaat.
Helaas geen oscilloscoop beschikbaar.

  • MrDoodoo
  • Registratie: Juli 2000
  • Laatst online: 18-11-2024
Als ik het voorbeeld op arduino.cc bekijk zie ik daar twee punten:
- Men zet de pinMode op Output (wat ik in jou geval NIET zou doen ivm low-impedance state)
- Triggermode HIGH wordt alleen ondersteund op een Arduino Due

In het voorbeeld op gammon wordt wel gebruikt van pinMode Input, maar wordt de pull-up weerstand aangezet met een DigitalWrite(pinnum,HIGH)

Wat mogelijk interessant is:
This also means however, that pins configured as pinMode(pin, INPUT) with nothing connected to them, or with wires connected to them that are not connected to other circuits, will report seemingly random changes in pin state, picking up electrical noise from the environment, or capacitively coupling the state of a nearby pin.
-edit- wat dus niet opgaat aangezien de pin verbonden is met ground via de 10k weerstand

[ Voor 5% gewijzigd door MrDoodoo op 30-09-2015 17:39 . Reden: -edit- ]


  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Als ik 'm op OUTPUT zet ontvang ik geen interrupts.
Ik zal onthouden dat ik slechts RISING moet gebruiken.

PinMode heb ik op input gezet dus, en een digitalWrite(3, HIGH);. Heeft ook geen effect.

  • MrDoodoo
  • Registratie: Juli 2000
  • Laatst online: 18-11-2024
Ik ga vanavond eens kijken of ik nog een drukknop heb liggen.
Wil het nou zelf ook eens gaan proberen want volgens alle voorbeelden zou dit gewoon moeten werken :)

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Ik vind het ook enorm vreemd.
Voor de knop heb ik de eerste de beste microswitch genomen die ik kon vinden.

  • itcouldbeanyone
  • Registratie: Augustus 2014
  • Laatst online: 25-07 07:29
int button;
int lastbutton =0;
int Pressed =0;
int pin = 9 // plaat pin nummer hier!
int led1 =13; //led op pin 13
int led2 =12; //led op pin 12

button = digitalRead(pin);

if(button!=lastbutton&&button==HIGH){
Pressed++;
}
if(Pressed > 2){Pressed =0;}
lastbutton = button;

switch Pressed{
case 0:
digitalWrite (led1, LOW);
digitalWrite (led2, LOW);
break;
case 1:
digitalWrite (led1, HIGH);
digitalWrite (led2, LOW);
break;
case 2:
digitalWrite (led2, HIGH);
digitalWrite (led1, LOW);
break;
}

Ben niet slim, maar wel dom


  • ajakkes
  • Registratie: Maart 2004
  • Laatst online: 16-05 22:32

ajakkes

👑

Je geeft aan dat de Arduino op vol vermogen draait tijdens het registreren van click type. Maar ik neem aan dat je pas na het registreren van click type andere acties gaat uitvoeren?

Dus een button High dan gedurende 900ms controleren of hij Low wordt en na 900ms controleren of hij High is. Als hij dan Low is en High is of is geweest is het dubbel press. Als hij High is single press. En anders dubbel press. Na 900ms kan hij bepalen welke actie moet worden uitgevoerd.

👑


  • MrDoodoo
  • Registratie: Juli 2000
  • Laatst online: 18-11-2024
Chocolade koekjes! Heeft mijn Arduino Nano (clone) een CH340 usb chip en heb ik net Windows 10 x64 geinstalleerd... Er lijkt nog geen driver voor te zijn :(

Acties:
  • 0 Henk 'm!

  • DurkVell
  • Registratie: Augustus 2011
  • Laatst online: 19:20
(Off topic) Werkt dit: http://www.arduined.eu/ch340-windows-8-driver-download/ misschien ook onder Windows 10? (/Off topic)

[ Voor 11% gewijzigd door DurkVell op 01-10-2015 09:57 ]


Acties:
  • 0 Henk 'm!

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Inmiddels heb ik het werkend. Dit is mijn huidige code:
C:
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <Ports.h>
#include <RF12.h>
#include <avr/sleep.h>
#include <PinChangeInt.h>
#include <VirtualWire.h>

ISR(WDT_vect) { Sleepy::watchdogEvent(); }

char *controller;

const int buttonPin = 3;

bool stateSingle = false;
bool stateDouble = false;
bool stateLong = false;

void setup() {
  pinMode(13, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(5, OUTPUT);

  PCintPort::attachInterrupt(buttonPin, wakeUp, RISING);
}

void wakeUp() {
}

void loop() {
  if (digitalRead(buttonPin) == LOW)
    Sleepy::powerDown();
  
  int i = 0;
  while (digitalRead(buttonPin) == HIGH) { // Wait until button is LOW, or has been high for more than 600ms
    Sleepy::loseSomeTime(50);
    if (i > 12)
      break;
    i++;
  }

  if (digitalRead(buttonPin) == HIGH) {
    longTapAction();
    return;
  } else {
    i = 0;
    while (digitalRead(buttonPin) == LOW) { // Wait for possible double press
      Sleepy::loseSomeTime(50);
      if (i > 8)
        break;
      i++;
    }

    if (digitalRead(buttonPin) == HIGH) {
        doubleTapAction();
        return;
    } else {
      singleTapAction();
      return;
    }
  }
}

void singleTapAction() {
  stateSingle = !stateSingle;
  digitalWrite(5, stateSingle ? HIGH : LOW);

  Sleepy::powerDown();
}

void doubleTapAction() {
  stateDouble = !stateDouble;
  digitalWrite(6, stateDouble ? HIGH : LOW);

  Sleepy::powerDown();
}

void longTapAction() {
  stateLong = !stateLong;
  digitalWrite(7, stateLong ? HIGH : LOW);

  Sleepy::powerDown();
}


Echter heb ik een 47uF condensator gebruikt nu. Hiermee werkt het dus wel. Zonder die condensator met de bovenstaande code werkt het dus niet. Ik ben naar een kleinere condensator gegaan nadat ik uit dit artikel haalde dat juist een lagere waarde het gewenste resultaat geeft:
That says τ = -60 ms / ln(0.75) = 210 ms. Given a 10 kΩ pullup, that’s C = 210 ms / 10 Ω = 21 μF.

No problem, right? Let’s just put a 22 μF electrolytic cap across every switch and be done with it!

Well, except for the fact that most pushbutton switches can’t tolerate that much energy through their contacts. Assuming a 100 mΩ resistance and ignoring stray inductance, the initial current will be 5 V / 100 mΩ = 50 A with a time constant of τ = 22 μF × 100 mΩ = 2 μs. At the usual 5 V logic supply, the cap stores 22 μF × (5 V)2 = 550 μJ of energy, so we’re now burning the switch contacts with a 250 W pulse. Some switches have a maximum energy rating to deter exactly this design blunder, but you should not assume the lack of such a rating means the switch can handle anything you throw at it.
Ik heb niks berekend, maar ik vond toevallig dus een 47uF condensator.
Echter is dit verhaal voor mij één grote warboel, ik ben namelijk echt een leek op dit gebied. Betekent dit nou dat er enorm veel stroom loopt op het moment dat de switch open gaat? Heeft dit effect op het hele systeem wat betreft veiligheid? Heeft die effect op mijn accu?

Acties:
  • 0 Henk 'm!

  • itcouldbeanyone
  • Registratie: Augustus 2014
  • Laatst online: 25-07 07:29
GWTommy schreef op zaterdag 03 oktober 2015 @ 20:15:
Inmiddels heb ik het werkend. Dit is mijn huidige code:
C:
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <Ports.h>
#include <RF12.h>
#include <avr/sleep.h>
#include <PinChangeInt.h>
#include <VirtualWire.h>

ISR(WDT_vect) { Sleepy::watchdogEvent(); }

char *controller;

const int buttonPin = 3;

bool stateSingle = false;
bool stateDouble = false;
bool stateLong = false;

void setup() {
  pinMode(13, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(5, OUTPUT);

  PCintPort::attachInterrupt(buttonPin, wakeUp, RISING);
}

void wakeUp() {
}

void loop() {
  if (digitalRead(buttonPin) == LOW)
    Sleepy::powerDown();
  
  int i = 0;
  while (digitalRead(buttonPin) == HIGH) { // Wait until button is LOW, or has been high for more than 600ms
    Sleepy::loseSomeTime(50);
    if (i > 12)
      break;
    i++;
  }

  if (digitalRead(buttonPin) == HIGH) {
    longTapAction();
    return;
  } else {
    i = 0;
    while (digitalRead(buttonPin) == LOW) { // Wait for possible double press
      Sleepy::loseSomeTime(50);
      if (i > 8)
        break;
      i++;
    }

    if (digitalRead(buttonPin) == HIGH) {
        doubleTapAction();
        return;
    } else {
      singleTapAction();
      return;
    }
  }
}

void singleTapAction() {
  stateSingle = !stateSingle;
  digitalWrite(5, stateSingle ? HIGH : LOW);

  Sleepy::powerDown();
}

void doubleTapAction() {
  stateDouble = !stateDouble;
  digitalWrite(6, stateDouble ? HIGH : LOW);

  Sleepy::powerDown();
}

void longTapAction() {
  stateLong = !stateLong;
  digitalWrite(7, stateLong ? HIGH : LOW);

  Sleepy::powerDown();
}


Echter heb ik een 47uF condensator gebruikt nu. Hiermee werkt het dus wel. Zonder die condensator met de bovenstaande code werkt het dus niet. Ik ben naar een kleinere condensator gegaan nadat ik uit dit artikel haalde dat juist een lagere waarde het gewenste resultaat geeft:

[...]

Ik heb niks berekend, maar ik vond toevallig dus een 47uF condensator.
Echter is dit verhaal voor mij één grote warboel, ik ben namelijk echt een leek op dit gebied. Betekent dit nou dat er enorm veel stroom loopt op het moment dat de switch open gaat? Heeft dit effect op het hele systeem wat betreft veiligheid? Heeft die effect op mijn accu?
hang er een weerstand tussen, die limiteerd op +- 75mA
uit eindelijk sloop je niet alleen je schakelcontact, maar ook je MCU, en de zooi er om heen.

wil je het helemaal top doen, plaats je ook een snelle diode over je condensator heen

Ben niet slim, maar wel dom


Acties:
  • 0 Henk 'm!

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Een weerstand waartussen precies? Voor of na de condensator? Of begrijp ik het nou verkeerd?

Wat betreft die diode. Ik zit met zeer beperkte ruimte in mijn behuizing.

Acties:
  • 0 Henk 'm!

  • MrDoodoo
  • Registratie: Juli 2000
  • Laatst online: 18-11-2024
het bleek uiteindelijk een kapotte CH340 te zijn.
Ik heb deze gedesoldeerd en de arduino met een PL2303 serial adapter aangesloten en zowaar, alles werkt weer :)

[ontopic]
Nu eens kijken of het ook softwarematig is op te lossen zonder condensator.
[/ontopic]

Acties:
  • 0 Henk 'm!

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Dat zou helemaal top zijn! Ik begin weer hoop te krijgen.

Acties:
  • 0 Henk 'm!

  • MrDoodoo
  • Registratie: Juli 2000
  • Laatst online: 18-11-2024
Bij mij lijkt dit prima te werken.
Ik heb wel maar 1 ledje aangesloten ipv 3, maar wel met verschillende acties op klik, dubbel klik, lange klik.

code:
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include "PinChangeInt.h"
#include "Ports.h"

#define BUTTON_UP       0xff
#define BUTTON_DOWN     0x00
#define BUTTON_NOCHANGE 0x77

//time to wait for double click
#define MAX_WAIT_DOUBLECLICK_MS  300
//time to wait for long click
#define WAIT_LONGCLICK_MS        600
//time to wait between measures in main loop
#define WAIT_BETWEEN_MEASURES_MS  50
//time to wait between measures while debouncing
#define WAIT_BOUNCE_MEASURE_MS     2
//minimum measures needed to determine stable state
//don't use values larger than 16 for MIN_STABLE_MEASURES
#define MIN_STABLE_MEASURES       12

const byte  pinLed    = 11;
const byte  pinButton = 3;

const uint16_t maskButtonUp   =  (0xffff << MIN_STABLE_MEASURES);
const uint16_t maskButtonDown = ~(0x0001 << MIN_STABLE_MEASURES);
const uint16_t maskDontCare   =  (0xffff << (MIN_STABLE_MEASURES+1));

ISR(WDT_vect) { Sleepy::watchdogEvent(); }

//ISR on button
void onButtonChange(){}

inline void enableButtonInterrupt()
{
  PCintPort::attachInterrupt(pinButton,onButtonChange,CHANGE);
}

inline void disableButtonInterrupt()
{
  PCintPort::detachInterrupt(pinButton);
}

// the setup function runs once when you press reset or power the board
void setup() 
{
  // initialize digital pin 13 as an output.
  pinMode(pinLed, OUTPUT);
  pinMode(pinButton,INPUT);
  enableButtonInterrupt();
}

byte readButtonState(uint16_t *milli)
{
  static byte switchState = LOW;

  for(byte i=0; i<MIN_STABLE_MEASURES; i++)
  {
    Sleepy::loseSomeTime(WAIT_BOUNCE_MEASURE_MS);
    *milli += WAIT_BOUNCE_MEASURE_MS;
    switch(debounceSwitch())
    {
      case BUTTON_UP:
        if (switchState == LOW)
        {
          switchState = HIGH;
        }
        return switchState;
        break;
      case BUTTON_DOWN:
        if (switchState == HIGH)
        {
          switchState = LOW;
        }
        return switchState;
      default:
        break;
    }    
  }

  return switchState;
}

//debounce algorithm based on listing 2 at http://www.embedded.com/electronics-blogs/break-points/4024981/My-favorite-software-debouncers
//original only returned high on rising edge detected. Adjusted to include falling edge and no change
byte debounceSwitch()
{
   byte retVal = BUTTON_NOCHANGE;
   static uint16_t State = 0; // Current debounce status
   State=(State<<1) | !digitalRead(pinButton) | maskDontCare;
   if(State==maskButtonUp  )retVal = BUTTON_UP;
   if(State==maskButtonDown  )retVal = BUTTON_DOWN;
   
   return retVal; 
} 

void onSingleClick()
{
  digitalWrite(pinLed,HIGH);}

void onDoubleClick()
{
  for(int i=0; i < 10; i++)
  {
    digitalWrite(pinLed,HIGH);  
    Sleepy::loseSomeTime(500);
    digitalWrite(pinLed,LOW);    
    Sleepy::loseSomeTime(500);    
  }
}

void onLongClick()
{
  digitalWrite(pinLed,LOW);
}
 
// the loop function runs over and over again forever
void loop() 
{
  disableButtonInterrupt();//don't want interrupt to interfere with loseSomeTime
  uint16_t msWait = 0;

  if (readButtonState(&msWait)==HIGH)
  {
    //wait while button is pressed for a max. of WAIT_LONGCLICK_MS      
    while(readButtonState(&msWait)==HIGH && msWait <= WAIT_LONGCLICK_MS)
    {
      Sleepy::loseSomeTime(WAIT_BETWEEN_MEASURES_MS);
      msWait += WAIT_BETWEEN_MEASURES_MS;
    }
    
    msWait = 0;
    if(readButtonState(&msWait)==LOW)
    {//button isn't pressed; wait for MAX_WAIT_DOUBLECLICK ms to exclude double click
      while(readButtonState(&msWait)==LOW && msWait <= MAX_WAIT_DOUBLECLICK_MS)
      {
        Sleepy::loseSomeTime(WAIT_BETWEEN_MEASURES_MS);
        msWait += WAIT_BETWEEN_MEASURES_MS;
      }     
      if (readButtonState(&msWait)==LOW)
      {
        onSingleClick();
      }
      else
      {
        onDoubleClick();
      }  
    }
    else
    {
      onLongClick();
    }
  }

  enableButtonInterrupt();//reenable interrupt for wakeup event
  Sleepy::powerDown();
}

Acties:
  • 0 Henk 'm!

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Dit project heeft een hele tijd stil gelegen in verband met drukte.

De oplossing van MrDoodoo werkt als een trein. Met wat spelen met de defines kon ik het helemaal afstemmen op de switch die ik gebruikte. Echter na het afronden van het prototype heb ik geprobeerd om alles in de 3D geprintte behuizing te stoppen en een volledig werkende versie te maken. Dit is helaas niet gelukt. De ruimte die ik nodig heb voor én twee CR2032 batterijen én een Arduino Pro Mini + 433Mhz module is te veel. Hiermee past het niet meer in de gewenste buitenmaten.

De afgelopen tijd heb ik dus nagedacht over een ander ontwerp. Ik ben tot de conclusie gekomen dat dit niet gaat lukken met de huidige componenten. De 433Mhz module is te lomp en dik (en dat voor een module van 20mm x 20mm) voor het ontwerp. Om die reden heb ik gekeken naar Bluetooth. De HM-10 module met de CC2541/CC2540 chip van TI is mij in het oog gesprongen. Deze is kleiner dan de Arduino Pro Mini. Echter heb ik het me in m'n kop gehaald dat ik deze module zonder Arduino wil gebruiken. De CC2541 heeft 256kb flash en is een complete MCU met een flink aantal pins die te gebruiken zijn. Bovendien is de CC2541 of HM-10 module perfect te voeden met 3V van de coin cells. Ook is het een enorm zuinige setup wanneer goed geconfigureerd.

Mijn vraag nu: is het te doen om zelf de firmware hierop te laden? Is de standaard firmware open source en dus aan te passen? Welke hardware is nodig om de firmware te laden? Welke compiler kan voor deze MCU/SoC compilen?

Als het goed is krijg ik ergens de komende weken een aantal HM-10 modules binnen. Maar die moeten nog uit China komen.

Acties:
  • 0 Henk 'm!

  • MrDoodoo
  • Registratie: Juli 2000
  • Laatst online: 18-11-2024
Ik heb geen ervaring met de HM-10, maar ik denk dat je met de volgende link al een heel eind verder komt http://www.instructables....Compatible-Bluetooth-40-M

Acties:
  • 0 Henk 'm!

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Dat is een tutorial om via een seriële verbinding AT commando's naar de HM-10 te sturen vanaf een Arduino. Ik wil juist de Arduino helemaal achterwege laten en de CC2541 van de HM-10 als MCU gebruiken. Dat is namelijk een volwaardige MCU. Echter lijkt het compilen van software voor deze chip wel te doen, alleen is de Bluetooth stack closed source en alleen met een dure IAR compiler te compileren.

Kijken naar een andere BT module is ook een optie. Zo draait de Texas Instruments CC2640 MCU op een Cortex M0 en M3 (voor applicaties). Dat is weer geschikt om met een open source / gratis compiler voor te compileren. Althans, dat is wat ik heb begrepen uit mijn onderzoek op internet. Het probleem hiermee is dat er geen betaalbare kant en klare modules voor te vinden zijn. De bedoeling is namelijk om dat soort bordjes onder de 5 euro per stuk te houden. De HM-10 is wat dat betreft dan weer uitstekend, die zijn al voor rond de 3 euro per stuk te krijgen.

Is het misschien beter om hier een apart topic voor aan te maken? Of is dat niet de bedoeling?
Pagina: 1