Software voor de Pro Mini

In een voorgaande knutselaerij wordt het het ineen knutselen van een bij fischertechnik te gebruiken controller met een Arduino Pro Mini Atmega328P beschreven.

Zodra de hardware als prototype in elkaar gezet was, kon het experimenteren met het programmeren van de Zauberling beginnen. Een van de uitgangspunten was bijvoorbeeld veel functionaliteit over te nemen van andere fishertechnik modules.

Gaandeweg ontstonden ook ideeën ter uitbreiding en/of verbetering van deze functionaliteit. In dit artikel ga ik daar wat nader op in.

Een succesvol magiër moet een goede goochelstaf, betoverende kleding en wonderlijke tovervoorwerpen hebben. De 'hardware' moet in orde zijn. Maar voor het verrichten van de ware wonderen, is de leergierigheid op het gebied van toverspreuken en bezweringen doorslaggevend. De betovering is compleet met de juiste magische formule, en het is juist de combinatie van toverstaf en toverspreuken waarin de ware magie schuilt. Laten eens te kijken naar het grote experimentele goochelboek: de software van de Zauberling.

Zauberling_Logo

Het prettige van de Arduino Pro Mini Atmega328P is dat deze zich, zodra de brug naar de TTL-interface van het kleine printje geslagen is, eenvoudig vanuit de Arduino IDE laat programmeren. Op die manier kan de software in principe altijd in ontwikkeling blijven en eenvoudig worden uitgebreid of aangepast. Nadat ik enkele testprogramma’s had gemaakt voor het controleren van de aansturing van de motor- en servo-uitgangen, startte ik met het nabouwen van de algemene functionaliteit van de E-Tec en Elektronics modules. Doordat de software van de Zauberling zo eenvoudig aan te passen is, paste ik het ‘Basisprogramma’ meteen op enkele punten aan. In deel 3 van dit artikel wordt daarmee een toepassing aangestuurd. In dit deel leest u welke andere trucs de Zauberling op dit moment al onder de knie heeft.

Programma’s en functies

Op het front van de Zauberling is met DIP-schuifschakelaars instelbaar welk ‘programma’ of stuurfunctie moet worden uitgevoerd. Hierdoor is het, net als met de E-Tec module, mogelijk snel te wisselen tussen diverse programma’s. Vooral voor een programmeerbare module als de Zauberling is dat tijdens de ontwikkeling van software erg handig. Terwijl bijvoorbeeld aan een verbeterde versie van bepaalde functionaliteit wordt gewerkt, kan een eerdere stabiele versie van deze functionaliteit ook gewoon nog ‘onder de knop’ worden gehouden.
 
De Zauberling is nog een eerste prototype en de programmatuur daardoor nog in ontwikkeling. Onderstaande tabel toont wat reeds, in verschillende stadia van perfectie, is gerealiseerd.

zauberling_prototype

De functies en programma's

Het ‘Basisprogramma’ is bekend van o.a. de E-Tec module en vereist drie schakelaars op de ingangen. Indien schakelaar 1 wordt gedrukt, begint de motor linksom te draaien. Indien schakelaar 2 wordt gedrukt, begint de motor rechtsom te draaien. Bij drukken op schakelaar 3 stopt de motor. Een kleine wijziging die ik maakte, is dat door nogmaals drukken op schakelaar 3 de motor de laatste draairichting na de stop hervat.

Prog DIP1 DIP2 DIP3 DIP4 I1 Logica I2 Logica I3 Logica Funktie Potentiometer
0 - - - - Negatief Negatief Negatief Basisprogramma met gelijkmatig toerentalverloop Tijd 0~2500ms
1 - - - ON Positief Positief Positief Basisprogramma met gelijkmatig toerentalverloop Tijd 0~2500ms
2 - - ON - Auto Auto Auto AND/NAND−Poort Drempelwaarde Sensoren
3 - - ON ON Auto Auto Auto OR/NOR−Poort Drempelwaarde Sensoren
4 - ON - - Auto Auto Auto XOR/XNOR−Poort Drempelwaarde Sensoren
5 - ON - ON Auto Auto Auto SR-Flip Flop - SET=IN1, CLK=IN2, RESET=IN3 (R is dominant) Drempelwaarde Sensoren
6 - ON ON - Auto Auto Auto JK-Flip Flop - J=IN1, CLK=IN2, K=IN3 Drempelwaarde Sensoren
7 - ON ON ON Auto Auto Auto D-Flip Flop met Reset Drempelwaarde Sensoren
8 ON - - - Auto Auto Auto Monoflop met Reset (tijd met Potentiometer instelbaar) Pulstijd 10~2500ms
9 ON - - ON Auto Auto Auto Zwaailicht, snelheid instelbaar Frequentie
10 ON - ON - Auto Auto Auto Flitslicht, snelheid instelbaar Frequentie
11 ON - ON ON - - - Toekomstige projecten :-) -
12 ON ON - - - - - Toekomstige projecten :-) -
13 ON ON - ON - - - Toekomstige projecten :-) -
14 ON ON ON - - - - Demo: Motor draairichtingingen -
15 ON ON ON ON - - - Demo: Servobewegingen -
  bit3 bit2 bit1 bit0          

 

  • Positieve logica - LOW-->HIGH-overgang is binair 1 (Dit zijn actieve sensoren en/of signalen: DIP-schakelaar ingang OFF)
  • Negatieve logica  - HIGH-->LOW-overgang is binair 1 (Voor passieve sensoren ingang DIP-schakelaar ON, voor actieve sensoren OFF)
  • Auto - Toestand bij inschakelen is de inactieve stand, verandering is de schakel-/detectie-toestand. Drempel en hysterese zijn met de potentiometer op het front instelbaar

Positieve en negatieve logica

Het is doorgaans belangrijk te weten of een schakelaar of sensor aan de ingang in ruststand open of gesloten is en daarmee hoe het detecterende gedrag moet worden geïnterpreteerd. Voor een programma als het ‘Basisprogramma’ in de E-Tec module is dit bijvoorbeeld zinvol, omdat één van de schakelaars bij het inschakelen van de module al kan zijn ingedrukt en de motor dan dus direct in de juiste richting moet gaan lopen om deze weer vrij te spelen.
 
Anderzijds is het prettig aan de ingangen zowel spanningsdalingen als spanningsstijging als logische detectieniveaus te kunnen gebruiken. Voorbeelden van sensoren die een spanningsstijging bewerkstelligen als ze moeten melden, zijn bijvoorbeeld de foto-transistoren uit een lichtkast of een schakelaar die in ruststand aan massa is gelegd. Sommige sensoren reageren echter net andersom, zoals bijvoorbeeld het uitgangssignaal van actieve IR-obstacle sensor in rust 5 volt is en dit bij detectie van een obstakel juist naar nul volt daalt. Je zou dit ‘negatieve logica’ kunnen noemen.
 
Om deze reden had ik mijzelf de uitdaging gesteld de verwerking van de ingangssignalen van de Zauberling zo uniform mogelijk te houden. De module kan flexibeler gebruikt worden als (bijna) alle soorten sensoren en logica aan de ingangen door elkaar kunnen worden gebruikt. Meestal is immers slechts signalering van de afwijkende uitgangsstand voldoende als schakelende impuls.

Treshold en hysterese

Het is natuurlijk mogelijk bij het inschakelen (of na een reset) van de Zauberling, de gemeten ingangswaarden op te slaan als referentiewaarden. Met de potentiometer op het front kan een drempelwaarde worden ingesteld. Een ingang wordt ‘actief’ zodra daarna de overeenkomstige referentiewaarde met meer dan de drempelwaarde wordt overschreden. Het maakt hierbij niet uit of de waarde naar boven, of naar onder, van de referentiewaarde afwijkt. De ingestelde drempelwaarde dient bovendien als maat voor de hysterese voor sensoren die na detectie slechts geleidelijk terugkeren op hun uitgangswaarde en hierbij door het gebied rond de drempelwaarde gaan. Hierdoor gaat de ingang niet ‘klapperen’ wanneer de uitganswaarde van de sensor zich in het gebied rond de detectiedrempel beweegt. Het detectiegedrag van een langzaam naar de referentiewaarde terugkerende sensor wordt op deze manier getemperd.
 
De software zal nog wel even in ontwikkeling zal blijven. Het code-fragment hiernaast toont (sterk vereenvoudigd) de meting van de uitgangswaarden en hoe met de treshold en hysterese wordt omgegaan.

Door deze truc zijn de ingangen van de Zauberling met een grote verscheidenheid aan sensoren te gebruiken. Actieve sensoren zoals de IR-obstacle sensor, de PIR-bewegingssensor of een hall-effect sensor kunnen hierdoor ook worden gebruikt. De enige voorwaarde is dat de referentiewaarde bij aanvang kan worden gemeten met een ‘ongeactiveerde’ sensor. Aangezien de meeste regelingen de sensoren voornamelijk als detector gebruiken, wordt aan deze eis in de praktijk vaak voldaan.
 
Maar er kunnen natuurlijk specifieke uitzonderingen zijn. Voor sommige regelingen (zoals bijvoorbeeld het ‘Basisprogramma’ is het beter als het type sensor en het gedrag vast kan worden gekozen. De software is zodanig opgezet, dat voor het ‘Basisprogramma’ deze keuze ook expliciet moet worden gemaakt. Er zijn twee varianten opgenomen zodat het zowel met de klassieke sensoren met positive logica (zoals de vertrouwde lichtkast met foto-transistor), als met sensoren met negatieve logica kan worden ingezet.

void setup() {
  …
  in1Default = analogRead(IN1);
  in2Default = analogRead(IN2);
  in3Default = analogRead(IN3);
  …
  …
}

void loop() {
  …
  …
  potValue = analogRead(POTMETER);
  sensorTreshold = map(potValue, MINREG, MAXREG, 1, TRESHOLD_MAX);
  …
  …
  // Read inputs ,take hysteresis into account
  in1Value = analogRead(IN1);
  if (abs(in1Value-in1Default) > sensorTreshold) {
    in1ValueTrigger = in1Value;
    in1Active = true;
  } else {
    if (abs(in1Value-in1ValueTrigger) > sensorTreshold/2) { // Hysteresis...
      in1Active = false;
      in1TriggerUsed = false;
    }
  }
  in2Value = analogRead(IN2);
  if (abs(in2Value-in2Default) > sensorTreshold) {
    in2ValueTrigger = in2Value;
    in2Active = true;
  } else {
    if (abs(in2Value-in2ValueTrigger) > sensorTreshold/2) { // Hysteresis...
      in2Active = false;
      in2TriggerUsed = false;
    }
  }
  in3Value = analogRead(IN3);
  if (abs(in3Value-in3Default) > sensorTreshold) {
    in3ValueTrigger = in3Value;
    in3Active = true;
  } else {
    if (abs(in3Value-in3ValueTrigger) > sensorTreshold/2) { // Hysteresis...
      in3Active = false; 
      in3TriggerUsed = false;
    }
  }
  …
  …
}
zauberling_prototype

Logica check

In beide varianten van het ‘Basisprogramma’ wordt gecheckt of de sensoren bij aanvang voldoen aan hun karakteristiek (dus bijvoorbeeld een lage spanning aan de betreffende ingang wordt gemeten als het ‘Basisprogramma’ in positieve logica wordt gebruikt). Er mag slechts één sensor/ingang afwijken waarvan wordt verondersteld dat deze bij aanvang reeds meldt en niet in de ongeactiveerde ruststand begint. Zodra de ingangsniveau’s van de sensor wijzigen wordt de sensor dan alsnog tijdens de loop van het programma opnieuw geïnitialiseerd waarbij de referentiewaarde wordt gemeten en opgeslagen.
 
Mocht er bij gebruik van het ‘Basisprogramma’ meer dan één sensor bij aanvang afwijken, dan wordt een storingsmelding gegeven door de drie LEDs op het front gelijktijdig te laten knipperen. In dit geval dient de bedrading te worden gecheckt. In de meeste gevallen volstaat het om de mechanische schakelaar 3 (start/stop) van ‘normaal gesloten’ naar ‘normaal geopend’ (of vice versa) om te pluggen en de module te resetten.

Schakelruis

Wat in software kan, hoeft niet in hardware. De oplettende elektronicus had bij het bestuderen van het schakelschema in deel 1 vast al opgemerkt dat er geen ontstoringscondensatoren aan de ingangen zijn opgenomen. Per ingang wordt in de software een semafoor inxTriggerUsed gereset (zie codefragment hierboven) die in de verwerkende routine kan worden geset om te signaleren dat de triggerende flank van het signaal is ‘verwerkt’. Hierdoor worden de eventuele valse extra schakelimpulsen of ruis rond het mechanische schakelpunt van bijvoorbeeld een schakelaar genegeerd.

Geleidelijk toerentalverloop

Een van de kleine ergernissen van het standaard ‘Basisprogramma’ was voor mij dat de motor niet geleidelijk op gang komt bij inschakelen, of een rustige uitloop heeft bij het uitschakelen. En vooral op de punten waar de draairichting abrupt wijzigt, zou een geleidelijk verloop prettiger zijn om slijtage aan de motor en onnodige krachten op het model te voorkomen.
 
In plaats van een harde ompoling van de draairichting als in de eerste grafiek, zou een geleidelijk verloop volgens de middelste grafiek wenselijker zijn.

Inmiddels is de Zauberling hiertoe in staat en het ‘Basisprogramma’ hierop aangepast. Het koppel van de motor bij de lage toerentallen en rond stilstand te blijkt echter in de praktijk zo laag dat de grafiek toch niet precies kan worden gevolgd. Daarom kon de curve zonder problemen door een rechte lijn, zoals in de laatste grafiek, vervangen worden. Dit maakte de software vanzelfsprekend nog eenvoudiger.

speed_graph_blockspeed_graph_bezierspeed_graph_ramp

Verloop geprogrammeerd

In het (vereenvoudigde) codefragment hiernaast wordt eerst het aantal stappen bepaald waarin het toerental en/of de draairichting moet verlopen. Dit gebeurt op basis van de waarde die met de potentiometer op het front kan worden ingesteld. Indien deze geheel naar links (naar nul) wordt gedraaid is het gedrag zoals bekend is van de E-Tec module. Naarmate de potentiometer meer naar rechts wordt gedraaid worden de snelheids- en draairichtingveranderingen geleidelijker. De tijd hiervoor is instelbaar van 0 tot 2,5 seconden. In het filmpje dat ik maakte is te zien hoe boterzacht de motor uitdraait en weer op gang komt.
 
Hoewel dit talent bij een magiër niet zou misstaan, is het helaas nog niet gelukt de Zauberling de toekomst te laten voorspellen. Bedenk dus dat deze motoruitloop ná de sensor-detectie plaatsvindt. De bewegende constructie moet dit vanzelfsprekend faciliteren en niet vastlopen of botsen. In de praktijk is hier echter bij de keuze van de positie van de sensoren eenvoudig rekening mee te houden. Een bijkomstig voordeel zou zelfs kunnen zijn dat deze eigenschap gebruikt worden om, zonder de sensoren te verplaatsen, de maximale uitslag van een bewegende slede af te stellen.

De Zauberling kan nog niet overweg met stappenmotoren, dus de afgelegde weg is nu nog slechts op basis van de ingestelde tijd bepaald. Hierdoor zal deze bij verschillende overbrengingen van de motor variëren. Mocht een volgende incarnatie van de Zauberling meer ingangen krijgen, dan zou het mogelijk zijn die te gebruiken voor de terugkoppeling van een encodermotor. Voor nu vond ik dat allemaal echter nog niet nodig.

void setMotorSpeed(bool rotateDir, int motorSpeed, bool smooth) {
  // rotateDir = true means O1/O2 CW rotation
  // rotateDir = false is O1/O2 CCW, output O3/O4 is reversed
  // motorSpeed is a value between 0 and 512
  // smooth = potmeter setting is value of gracefully descend and ascend
  int tempSpeed;
  int speedStep;

  if (rotateDir!=currentMotorDir || motorSpeed!=currentMotorSpeed) {  
    int smoothDelay = timeDelay/25;
    if (currentMotorDir == rotateDir) { // No directional changes...
      speedStep = (motorSpeed-currentMotorSpeed)/SPEED_STEPS;
      tempSpeed = currentMotorSpeed;
      do {
        tempSpeed += speedStep;
        if (abs(motorSpeed-tempSpeed) <= abs(speedStep)) tempSpeed = motorSpeed; // Last step...       
        delay(smoothDelay);
        if (rotateDir) {
          Out1.drive(tempSpeed);
          Out2.drive(-tempSpeed);
        } else {
          Out1.drive(-tempSpeed);
          Out2.drive(tempSpeed);    
        }
      } while (abs(motorSpeed-tempSpeed) > abs(speedStep));
    } else { // Towards and through zero...
      for (tempSpeed=currentMotorSpeed; tempSpeed>0; tempSpeed-=(currentMotorSpeed/SPEED_STEPS)) { 
        // Slow down to zero...
        delay(smoothDelay);
        if (currentMotorDir) {
          Out1.drive(tempSpeed);
          Out2.drive(-tempSpeed);
        } else {
          Out1.drive(-tempSpeed);
          Out2.drive(tempSpeed);    
        }         
      }
      speedStep = motorSpeed/SPEED_STEPS; // Always positive...
      for (tempSpeed=0; tempSpeed<=motorSpeed; tempSpeed += (motorSpeed/SPEED_STEPS)) { 
        // Build up to motorSpeed again...
        if (abs(motorSpeed-tempSpeed) <= speedStep) tempSpeed = motorSpeed; // Last step...
        delay(smoothDelay);
        if (rotateDir) {
          Out1.drive(tempSpeed);
          Out2.drive(-tempSpeed);
        } else {
          Out1.drive(-tempSpeed);
          Out2.drive(tempSpeed);    
        }         
      }       
    }
    currentMotorDir = rotateDir;
    currentMotorSpeed = motorSpeed;
  }
}

Conclusie

De software voor de Zauberling zal de komende tijd vast nog verder worden uitgebreid en verfijnd. Schroomt u niet mij te mailen of te benaderen op het forum als u suggesties, opmerkingen, vragen of ideeën voor toekomstige functionaliteit voor de Zauberling heeft. Voor een echte tovenaar is het schijnbaar onmogelijke immers de ultieme uitdaging... 😄