DataScale()

algemene C code
Berichten: 163
Geregistreerd: 15 Dec 2012, 21:02
Woonplaats: Delfzijl

DataScale()

Berichtdoor FPCUser » 27 Jun 2018, 16:33

Voor een experimenteel programma voor een breektank heb ik wat functies nodig die akties uitvoeren op verschillende waternivo's.
Dit was de aanleiding om hiervoor eens een klasse te maken.
(en stoeien met DoxyGen..).
Het is kort gezegd een map functie met instelbare drempels, dus universeel toe te passen.

Beide bestanden: DataScale.cpp en DataScale.h zal ik in een volgend bericht posten.
Een zip downloaden kan ook met beide bestanden, een keywords.txt en een pdf met de signaal uitgangen.
https://drive.google.com/drive/folders/1BOs7Ivvr_r-oDSgHnO_3nI84vHX_lerk?usp=sharing

Het eindresultaat is verre van perfekt, maar gelukkig voor mij wel werkbaar.
Daarom zijn verbetersuggesties, zoals gewoonlijk, welkom.


Alvast hieronder een klein test programmatje dat alle uitgangen weergeeft en daarmee de werking van de klasse toont.
cpp code
/*
Test DataScale klasse.
Datum laatste wijziging: 25 juni 2018

Een inangswaarde tussen 0 en 50 wordt geschaald naar 0..100
Onder- en bovendrempel (nivo's) op resp. 20 en 80.
De dode zone voor onderdrempel op 10, voor bovendrempel op 5.
Het laag alarmpunt komt op 5, het hoog alarmpunt op 95.

De ingang varieert van 25 naar 0, daarna van 0 naar 50, en dan van 50 terug
naar 25.

Print de ingangswaarde, uitgangswaarde, of onder- en boven derempels bereikt zijn
en of het laag en hoog alarmpunt bereikt zijn.
*/

#include "DataScale.h"

unsigned char i; // teller variabele
char ol [2]; // onderdrempel bereikt, 'T'(true) of 'F'(false)
char hl [2]; // bovendrempel bereikt, 'T'(true) of 'F'(false)
char tn [2]; // tussen beide nivo's,'T'(true) of 'F'(false)
char on_off [2]; // 'T'(true) bij onderdrempel,'F'(false) bij bovendrempel
char al [2]; // laag alarm, 'T'(true) of 'F'(false)
char ah [2]; // hoog alarm, 'T'(true) of 'F'(false)
DataScale nivometing(0, 50, 0, 100); // DataScale object

void setup() {
Serial.begin(9600);
nivometing.setLimits(20, 10, 80, 5); // setpoints (drempel en hysteresis) voor laag- en hoog nivo
/* kan ook afzonderlijkt: */
// nivometing.setLowerLimit(20, 10);
// nivometing.setUpperLimit(80, 5);
nivometing.setLowerAlarm(5); // setpoint laag alarm
nivometing.setUpperAlarm(95); // setpoint hoog alarm
}

void loop() {
for (i = 25; i > 0; i--) {
test();
}
for (i = 0; i <= 50; i++) {
test();
}
for (i = 49; i >= 25; i--) {
test();
}
Serial.end();
}

void test() {
nivometing.setInputValue(i);
unsigned char n = nivometing.getOutputValue();

laagnivo();
hoognivo();
tussennivo();
onOff();
laagalarm();
hoogalarm();

Serial.print(i);
Serial.print(" -> ");
Serial.print(n);
Serial.print(" Laag nivo: ");
Serial.print(ol[0]);
Serial.print(" Hoog nivo: ");
Serial.print(hl[0]);
Serial.print(" Tussen beide nivo's: ");
Serial.print(tn[0]);
Serial.print(" OnLowOffHigh: ");
Serial.print(on_off[0]);
Serial.print(" Laag alarm: ");
Serial.print(al[0]);
Serial.print(" Hoog alarm: ");
Serial.println(ah[0]);
}

void laagnivo() {
ol[0] = (nivometing.isLimitLow()) ? 'T' : 'F';
}

void hoognivo() {
hl[0] = (nivometing.isLimitHigh()) ? 'T' : 'F';
}

void tussennivo() {
tn[0] = (nivometing.isBetweenLimits()) ? 'T' : 'F';
}

void onOff() {
on_off[0] = (nivometing.isOnLowOffHigh()) ? 'T' : 'F';
}

void laagalarm() {
al[0] = (nivometing.isAlarmLow()) ? 'T' : 'F';
}

void hoogalarm() {
ah[0] = (nivometing.isAlarmHigh()) ? 'T' : 'F';
}

Advertisement

Berichten: 163
Geregistreerd: 15 Dec 2012, 21:02
Woonplaats: Delfzijl

Re: DataScale()

Berichtdoor FPCUser » 27 Jun 2018, 16:36

Beide bestanden:

cpp code
/// @file DataScale.h
/// @brief map functie
/// @author FPCUser
/// @version 0.1
/// @date 27 juni 2018
///
/// @mainpage Een klasse voor een uitgebreide 'map' functie.
/// Een klasse met map functie, d.w.z. een waarde tussen de minimale en maximale ingangswaarde
/// geschaald wordt naar een waarde tussen de minimale en maximale uitgangswaarde.
///
/// \li Instelbare onder- en bovendrempel met voor beide een
/// eveneens instelbare hysteresis (dode zone).
/// \li Instelbare onder- en boven alarmpunt.
///
/// Boolean uitgangen:
///
/// \li Ondergrens schakelpunt: actief als de uitgangswaarde op of beneden onderdrempel komt.
/// Inactief als de waarde weer boven de onderdrempel plus hysteresis komt.
/// \li Bovengrens schakelpunt: actief als de uitgangswaarde op of boven bovendrempel komt.
/// Inactief als de uitgangswaarde weer onder de bovendrempel minus de hysteresis komt..
/// \li TussenLimits: actief als de uitgangswaarde zich tussen onder- en bovendrempel bevind.
/// \li OnLowOffHigh: actief als de uitgangswaarde op of beneden de onderdrempel, en inactief
/// als de waarde op of boven de bovendrempel komt.
/// \li Onderste alarmpunt: actief als de uitgangswaarde op of beneden alarmpunt komt.
/// Inactief als waarde weer boven dit punt komt.
/// \li Bovenste alarmpunt: actief als de uitgangswaarde op of boven alarmpunt komt.
/// Inactief als waarde weer beneden dit punt komt.
///

#ifndef DataScale_h
#define DataScale_h

#include "Arduino.h"

class DataScale
{
public:
DataScale();
DataScale(int32_t irb, int32_t ire, int32_t orb, int32_t ore);
~DataScale();
void setRanges(int32_t irb, int32_t ire, int32_t orb, int32_t ore);
void setLimits(int32_t oll, int32_t ohl, int32_t olh, int32_t ohh);
void setLowerLimit(int32_t oll, int32_t ohl);
void setUpperLimit(int32_t olh, int32_t ohh);
void setAlarms(int32_t oal, int32_t oah);
void setLowerAlarm(int32_t oal);
void setUpperAlarm(int32_t oah);
void setInputValue(int32_t iv);
uint8_t getErrorCode();
int32_t getOutputValue();
bool isLimitLow();
bool isLimitHigh();
bool isBetweenLimits();
bool isOnLowOffHigh();
bool isAlarmLow();
bool isAlarmHigh();

private:
int32_t inputValue;
int32_t inputRangeBegin;
int32_t inputRangeEnd;
int32_t outputValue;
int32_t outputRangeBegin;
int32_t outputRangeEnd;
int32_t outputLimitLow;
int32_t outputLimitHigh;
int32_t outputHysteresisLow;
int32_t outputHysteresisHigh;
int32_t outputAlarmLow;
int32_t outputAlarmHigh;
uint8_t errorCode;
void calcOutputValue();
bool flagLimitLow;
bool flagLimitHigh;
};

#endif




cpp code
/// @file DataScale.cpp
/// @brief map functie
/// @author FPCUser
/// @version 0.1
/// @date 27 juni 2018

#include "DataScale.h"


/**
@brief Constructor zonder argumenten.

@details Ingangsbereik en uitgangsbereik kunnen met de functie setRanges()
worden ingevoerd.
*/
DataScale :: DataScale() {
inputRangeBegin = 0;
inputRangeEnd = 0;
outputRangeBegin = 0;
outputRangeEnd = 0;

outputLimitLow = 0;
outputLimitHigh = 0;
outputHysteresisLow = 0;
outputHysteresisHigh = 0;
errorCode = 0;
}


/**
@brief Constructor met argumenten.

@details Voor zowel de ingang als de uitgang wordt een minimale en
een maximale waarde opgeven.
De ondergrensschakelaar wordt standaard op de minimale uitgangswaarde gezet
en de bovengrensschakelaar op de maximale uitgangswaarde.
De hysteresis (dode zone) van beide grensschakelaars standaard op 0.

@param[in] irb Minimale waarde ingangsbereik
@param[in] ire Maximale waarde ingangsbereik
@param[in] orb Minimale waarde uitgangsbereik
@param[in] ore Maximale waarde ingangsbereik
*/
DataScale :: DataScale(int32_t irb, int32_t ire, int32_t orb, int32_t ore) {
inputRangeBegin = irb;
inputRangeEnd = ire;
outputRangeBegin = orb;
outputRangeEnd = ore;

outputLimitLow = outputRangeBegin;
outputLimitHigh = outputRangeEnd;
outputHysteresisLow = 0;
outputHysteresisHigh = 0;
outputAlarmLow = outputRangeBegin;
outputAlarmHigh = outputRangeEnd;
errorCode = 0;
flagLimitLow = false;
flagLimitHigh = false;
}


/**
@brief Destuctor.

@details Geheugen weer vrijgeven dat gebruikt werd door een DataScale object.
*/
DataScale :: ~DataScale() {}


/**
@brief In- en uitgangsbereik.

@details Voor zowel de ingang als de uitgang wordt een minimale en
een maximale waarde opgeven.

@param[in] irb Minimale waarde ingangsbereik
@param[in] ire Maximale waarde ingangsbereik
@param[in] orb Minimale waarde uitgangsbereik
@param[in] ore Maximale waarde ingangsbereik
*/
void DataScale :: setRanges(int32_t irb, int32_t ire, int32_t orb, int32_t ore) {
if (inputRangeBegin != irb) {
inputRangeBegin = irb;
}

if (inputRangeEnd != ire) {
inputRangeEnd = ire;
}

if (outputRangeBegin != orb) {
outputRangeBegin = orb;
}

if (outputRangeEnd != ore) {
outputRangeEnd = ore;
}
}


/**
@brief Onder- en boven drempel en hysteresis v/d grensschakelaars.

@details Geef de waarden waarop de onder- en bovengrensschakelaars schakelen en
de hysteresis van beide schakelpunten.

@param[in] oll Waarde waarop benedengrensschakelaar schakelt
@param[in] ohl Hysteresis van de benedengrensschakelaar
@param[in] olh Waarde waarop bovengrensschakelaar schakelt
@param[in] ohh Hysteresis van de bovengrensschakelaar
*/
void DataScale :: setLimits(int32_t oll, int32_t ohl, int32_t olh, int32_t ohh) {
if (outputLimitLow != oll) {
outputLimitLow = oll;
}

if (outputHysteresisLow != ohl) {
outputHysteresisLow = ohl;
}

if (outputLimitHigh != olh) {
outputLimitHigh = olh;
}

if (outputHysteresisHigh != ohh) {
outputHysteresisHigh = ohh;
}
}


/**
@brief Grenswaarde en hysteresis v/d onderdrempel.

@details Geef de waarde waarop de ondergrensschakelaars schakelt en
de hysteresis.

@param[in] oll Waarde waarop ondergrensschakelaar schakelt
@param[in] ohl Hysteresis van de ondergrensschakelaar
*/
void DataScale :: setLowerLimit(int32_t oll, int32_t ohl) {
if (outputLimitLow != oll) {
outputLimitLow = oll;
}

if (outputHysteresisLow != ohl) {
outputHysteresisLow = ohl;
}
}


/**
@brief Grenswaarde en hysteresis v/d bovemdrempel.

@details Geef de waarde waarop de bovengrensschakelaars schakelt en
de hysteresis.

@param[in] oll Waarde waarop bovengrensschakelaar schakelt
@param[in] ohl Hysteresis van de bovengrensschakelaar
*/
void DataScale :: setUpperLimit(int32_t olh, int32_t ohh) {
if (outputLimitHigh != olh) {
outputLimitHigh = olh;
}

if (outputHysteresisHigh != ohh) {
outputHysteresisHigh = ohh;
}
}


/**
@brief Grenswaarde onderste- en bovenste alarmpunt.

@details Geef de waarden waarop de onderste en bovenste alarmschakelaar schakelen.

@param[in] oal Setpoint onderste alarmschakelaar
@param[in] oah Setpoint bovenste alarmschakelaar
*/
void DataScale :: setAlarms(int32_t oal, int32_t oah) {
if (outputAlarmLow != oal) {
outputAlarmLow = oal;
}

if (outputAlarmHigh != oah) {
outputAlarmHigh = oah;
}
}


/**
@brief Grenswaarde onderste alarmpunt.

@details Geef de waarde waarop de onderste alarmschakelaar schakelt.

@param[in] oal Setpoint onderste alarmschakelaar
*/
void DataScale :: setLowerAlarm(int32_t oal) {
if (outputAlarmLow != oal) {
outputAlarmLow = oal;
}
}


/**
@brief Grenswaarde bovenste alarmpunt.

@details Geef de waarde waarop de bovenste alarmschakelaar schakelt.

@param[in] oah Setpoint bovenste alarmschakelaar
*/
void DataScale :: setUpperAlarm(int32_t oah) {
if (outputAlarmHigh != oah) {
outputAlarmHigh = oah;
}
}


/**
@brief Ingangswaarde.

@details Na invoer van de ingangswaarde wordt de uitgangswaarde, na
controle op de ingegeven waarden, berekend.

@param[in] iv Ingangswaarde
*/
void DataScale :: setInputValue(int32_t iv) {
if (inputValue != iv) {
inputValue = iv;
if (getErrorCode() == 0) {
calcOutputValue();
}
}
}


/**
@brief Foutcode.

@details Controle op het opgegeven uitgangsbereik en drempels.

@return errorCode (uint8_teger):

0 = Geen fout.
1 = Uitgangswaarden niet juist opgegeven.
2 = Onder- en/of bovendremel niet juist opgegeven.
3 = Combinatie van bovenstaande fouten.
*/
uint8_t DataScale :: getErrorCode() {
errorCode = 0;

/* fout in uitgang: begin uitgangsbereik is groter dan einde uitgangsbereik */
if (outputRangeBegin >= outputRangeEnd) {
errorCode += 1;
}

/* fout bij drempels: onderdrempel is groter dan bovendrempel of
onderdrempel is kleiner dan begin uigangsbereik of
onderdrempel is groter dan einde uitgangsbereik of
bovendrempel is kleiner dan begin uitgangsbereik of
bovendrempel is groter dan einde uitgangsbereik
*/
if ((outputLimitLow > outputLimitHigh) ||
(outputLimitLow < outputRangeBegin) ||
(outputLimitLow > outputRangeEnd) ||
(outputLimitHigh < outputRangeBegin) ||
(outputLimitHigh > outputRangeEnd)) {
errorCode += 2;
}

return errorCode;
}


/*
Een waarde tussen de minimale en maximale ingangswaarde wordt geschaald naar
een waarde tussen de minimale en maximale uitgangswaarde volgens lineare interpolatie.
*/
void DataScale :: calcOutputValue() {

/* met Arduino's map() functie */
// outputValue = map(inputValue, inputRangeBegin, inputRangeEnd, outputRangeBegin, outputRangeEnd);

/* met achterliggende functie */
outputValue = (inputValue - inputRangeBegin) * (outputRangeEnd - outputRangeBegin) /
(inputRangeEnd - inputRangeBegin) + outputRangeBegin;
}


/**
@brief Uitgangswaarde.

@delails De berekende uitgangswaarde wordt gegeven.

@return Uitgangswaarde (uint8_teger)
*/
int32_t DataScale :: getOutputValue() {
return outputValue;
}


/**
@brief Ondergrensschakelaar.

@details Geeft true of false afhankelijk van de volgend 4 situaties:

1. huidige waarde zit boven de grenswaarde plus hysteresus(dode gebied):
resultaat is false.
2. huidige waarde zit in dode gebied en heeft ondergrens nog niet gepasseerd:
resultaat blijft false.
3. huidige waarde is kleiner dan grenswaarde:
resultaat is true.
4. huidige waarde zit in dode gebied en heeft eerst onderngrens al bereikt:
resultaat blijft true.

@return True of false
*/
bool DataScale :: isLimitLow() {
/* uitgangswaarde kleiner dan onderdrempel */
if (outputValue <= outputLimitLow) {
flagLimitLow = true;
return true;
}

/* uitgangswaarde groter dan onderdrempel plus hysteresis */
if (outputValue > (outputLimitLow + outputHysteresisLow)) {
flagLimitLow = false;
return false;
}

/* anders: uitgangswaarde in dode gebied */
if (flagLimitLow) {
return true;
} else {
return false;
}
}


/**
@brief Bovengrensschakelaar.

@details Geeft true of false afhankelijk van de volgend 4 situaties:

1. huidige waarde zit onder de grenswaarde minus hysteresus(dode gebied):
resultaat is false.
2. huidige waarde zit in dode gebied en heeft bovengrens nog niet gepasseerd:
resultaat blijft false.
3. huidige waarde is groter dan grenswaarde:
resultaat is true.
4. huidige waarde zit in dode gebied en heeft eerst bovengrens al bereikt:
resultaat blijft true.

@return True of false
*/
bool DataScale :: isLimitHigh() {
/* uitgangswaarde groter dan bovendrempel */
if (outputValue >= outputLimitHigh) {
flagLimitHigh = true;
return true;
}

/* uitgangswaarde kleiner dan bovendrempel minus hysteresis */
if (outputValue < (outputLimitHigh - outputHysteresisHigh)) {
flagLimitHigh = false;
return false;
}

/* anders: uitgangswaarde in dode gebied */
if (flagLimitHigh) {
return true;
} else {
return false;
}
}


/**
@brief Tussengrens schakelaar.

@details Geeft true als de uitgangswaarde tussen boven- en onderdrempel zit, anders false.
Hysteresis van de onder- en bovendrempel hebben geen invloed.

@return True of false
*/
bool DataScale :: isBetweenLimits() {
if ((outputValue <= outputLimitLow) || (outputValue >= outputLimitHigh)) {
return false;
} else {
return true;
}
}


/**
@brief On low, off high.

@details Geeft true als de uitgangswaarde op of beneden de onderdrempel komt en
false als de uitgangswaarde op of boven de bovendrempel komt.

@return True of false
*/
bool DataScale :: isOnLowOffHigh() {
bool S; // setingang RS-flipflop
bool R1; // resetingang RS-flipflop
static bool Q1; // uitgang RS-flipflop

if (outputValue <= outputLimitLow) {
S = true;
} else {
S = false;
}

if (outputValue >= outputLimitHigh) {
R1 = true;
} else {
R1 = false;
}

Q1 = (S || Q1) && !R1;
return Q1;
}


/**
@breef Onderalarmschakelaar.

@details Geeft true als de uitgangswaarde op of beneden het onderste alarmpunt zit,
anders false.

@return True of false
*/
bool DataScale :: isAlarmLow() {
if (outputValue <= outputAlarmLow) {
return true;
} else {
return false;
}
}


/**
@brief Bovenalarmschakelaar.

@details Geeft true als de uitgangswaarde op of boven het bovenste alarmpunt zit,
anders false.
@return True of false
*/
bool DataScale :: isAlarmHigh() {
if (outputValue >= outputAlarmHigh) {
return true;
} else {
return false;
}
}

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

Re: DataScale()

Berichtdoor Koepel » 27 Jun 2018, 19:34

Er zijn twee dingen die er tamelijk stekelig uit springen: een 'static' variabele in een Class en Serial.end().

Het mooie van een Class is, dat die meerdere keren te gebruiken is. Maar een static variabele gooit alles overhoop. Wanneer er tien objecten van die Class zijn, dan gebruiken ze allemaal diezelfde ene static variabele :!:
Kun je Q1 van de flip-flop ook in de Class als private variabele zetten ?

Om de tekst naar de seriële monitor te stoppen gebruik je Serial.end(). Dat stopt de seriële poort, maar je blijft Serial.print() gebruiken. Wanneer je eenmalig tekst wil laten zien, dan kun je die beter in de setup() plaatsen, en de loop() leeg laten.

Het gebruik van arrays van twee byte ('ol', 'hl', 'tn', enzovoorts) is een beetje omslachtig. Dat kan anders. Bijvoorbeeld één functie die 'T' of 'F' afdrukt en die alleen een 'bool' variabele als parameter heeft.

In de sketch (het *.ino bestand in de sketch-map) wordt meestal dit gedaan:
Code: Alles selecteren
#include <DataScale.h>
Met '<' en '>'. Dat maakt bij Arduino niet veel uit, maar het geeft aan dat het een library is, en geen *.h bestand uit de sketch-map (van het map van het project zelf).

Het ziet er verder heel strak uit en is duidelijk.

Berichten: 163
Geregistreerd: 15 Dec 2012, 21:02
Woonplaats: Delfzijl

Re: DataScale()

Berichtdoor FPCUser » 28 Jun 2018, 22:37

@Koepel
Bedankt voor je opbouwende kritiek. Hier kan ik wat mee.
Ik kom t.z.t. hierop terug.
Kan alleen wel een tijdje duren...

Terug naar C code

Wie is er online?

Gebruikers in dit forum: Geen geregistreerde gebruikers en 7 gasten