Arduino Smooting adc0 en adc1 ADS1115

blackdog

Golden Member

Hi,

Ik ben voor een bepaald project met wat code voor de Arduino aan het stoeien, en het gaat om een ADC printje met een ADS1115 er op.
De Adafruit library voor de ADS1115 heeft niet de mogelijkheid een lagere sample waarde in te stellen.
Wat op zich geen groot probleem is, omdat ik eerder met succes de "AnalogSmooth.h" library gebruikt hebt om netjes te middelen.

Link naar de library
https://github.com/MichaelThessel/arduino-analog-smooth

Deze code is een stukje van wat uiteindelijk nodig is, er zijn twee sensoren een voor het controleren van de Oven temperatuur en de tweede sensor
wil ik van het zelfde type hebben (TMP37 van Analog Devices) en die meet de temperatuur van de spanningsreferentie print die ook in de oven zit.
Na calibratie en goed opgewarmt te zijn, moeten deze twee sensoren de zelfde temperatuur gaan aangeven.
Dit wik ik zo doen om ondermeer de drift enigsins te kunnen meten.
Er komt ook nog een derde sensor in de oven welke de BME280 is, zodat ik drie sensoren met elkaar kan vergelijken.

De code die nu uit de werkende sensor ingang komt "adc0" heeft met een dopje over de TMP37 sensor tegen de tocht en 50x middeling een vrij stabiele uitlezing als ik niet te veel beweeg :+
De uitlezing heeft een resolutie van 0,0001C.

De ADC is zo ingesteld dat de gain 4x is, dat houd in dat het spanningsbereik +-1,024V is.
De TMP37 Sensor geeft bij 50C een spanning af van rond de +1V.
Bij een oventemperatuur van 45C gebruik ik dus vooi de volle resolutie van de ADC.
Er komt nog een extra sensing bij voor foutsituaties, zodat ik dan de electronica in de oven niet ga koken...

Maar nu lukt het mij niet om de AnalogSmooth library voor twee ingangen van de ADS1115 te gebruiken, dan krijg ik gekke fouten.
Gaarne speek ik jullie deskundigheid aan, om mij te onderwijzen hoe ik deze code werkbaar kan maken. :+

c code:


#include <Arduino.h>
#include <U8x8lib.h>
#include <Adafruit_ADS1015.h>
#include <AnalogSmooth.h>



// Defaults smooting window size = 10, 100max.
AnalogSmooth as = AnalogSmooth(50);

// moet hier een variatie van bovenstaande regel staan om de smooth functie voor adc1 te kunnen gebruiken?

U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);

Adafruit_ADS1115 ads;                         /* Use this for the 16-bit version */

float smooth_pid;
float smooth_ref;
float pid_float;
float ref_float;

const float VPS = 1.024 / 32768.0;           // volts per step geld voor alle ADC ingangen
unsigned int val = 0;

//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------

void setup(void)
{
  Serial.begin(115200);

  ads.setGain(GAIN_FOUR);                    // 4x gain   +/- 1.024V  1 bit = 0.5mV    0.03125mV
  ads.begin();

  u8x8.begin();
  u8x8.setPowerSave(0);
}

//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------

void loop(void)
{

 int16_t adc0, adc1;

adc0 = ads.readADC_SingleEnded(0);         // Get value PID sensor input
adc1 = ads.readADC_SingleEnded(1);         // Get value Reference sensor input 

// measure code runtime
unsigned long start = millis();


  float smooth_pid = as.smooth(adc0);      // Smoothing with window size xx PID Sensor
  float smooth_ref = as.smooth(adc1);      // Smoothing with window size xx ReferencePrint

  
  pid_float = smooth_pid * VPS;            // convert to voltage for pid sensor
  ref_float = smooth_ref * VPS;            // convert to voltage for reference sensor  
 
  
  Serial.print("Temp PID C = ");
  Serial.println(pid_float / 0.02, 4);     // Convert to temperature voor de PID controler
  
  Serial.print("Temp Ref C = ");
  Serial.println(ref_float/ 0.02, 4);      // Convert to temperature voor de referentie print
  
  u8x8.setFont(u8x8_font_victoriabold8_r);
  u8x8.setCursor(0, 0);
  u8x8.print(pid_float / 0.02, 4);        // Plaatst TMP37 sensor waarde van de PID controler in Celsius op het scherm

  u8x8.setCursor(0, 4);
  u8x8.print(ref_float / 0.02, 4);        // Plaatst TMP37 sensor waarde van de Referentie print in Celsius op het scherm

unsigned long end = millis();             // Berekend deel van de code looptijd
unsigned long delta = end - start;

  u8x8.setCursor(0, 6);
  u8x8.print(delta);                      // Plaatst code looptijd in millis op het scherm

  delay(2);
}

Ik heb dus geen idee of ik de smooth functie meerdere keren kan aanroepen, dus ook voor mijn adc1 ingang.
Kan dit niet, of maak ik fouten in de manier waarop.
Er waren nog wat meer code regels, maar een aantal ben ik kwijt geraakt die niet werkte in het bovenstaande stukje.

Dank alvast,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"
It's the rule that you live by and die for It's the one thing you can't deny Even though you don't know what the price is. It is justified.

Ik ken die libraries niet, maar zo op het eerste gezicht zou ik zeggen dat je 2 aparte smooth variabelen moet declareren:

code:


// Defaults smooting window size = 10, 100max.
AnalogSmooth as_pid = AnalogSmooth(50);
AnalogSmooth as_ref = AnalogSmooth(50);

Die kun je dan later gebruiken om te filteren (denk ik)

code:


  float smooth_pid = as_pid.smooth(adc0);      // Smoothing with window size xx PID Sensor
  float smooth_ref = as_ref.smooth(adc1);      // Smoothing with window size xx ReferencePrint

blackdog

Golden Member

Hi,

Als eerste dank voor de tips!

Roland, leuke website! ik zag al wat projectjes via die site die nabouw waardig zijn.
Die middeling ga ik verder niet gebruiken, dit omdat wat ik nu heb, goed genoeg werkt en ik zuinig moet zijn wat betreft werk en opslag geheugen van de Pro mini op 8MHZ en 3.3V.

deKees
Dat werkt dus niet wat je liet zien! :+
Maar je hebt me de goede kant op geduuwd, want de code werkt nu, ik kan alleen niet goed be-redeneren waarom het zo werkt zoals ik het heb toegepast.
Dit is via trial and error uitgevonden, je moet toch wat. ;)

Eerst even de code die nu werkt met oom de BME280 sensor er in opgenomen.
De waardes ophalen uit de sensor worden door een hele kleine library gedaan, dit omdat het allemaal wat veel ruimte kost zoals ik "programmmer"
Verder wat variabelen er uitgekegeld, en heb die vaste waarde gewoon in de code regel geplaast.

Ik had een variabel voor de waardes tussen de haakjes en achter de slash in deze twee regels, maar dit werkt net zo goed.

c code:


 pid_float = smooth_pid *(1.024 / 32768.0);
 u8x8.print(pid_float / 0.02, 4); 

De voorlopige code, die nu goed loopt.

c code:


#include <Wire.h>
#include <U8x8lib.h>
#include <Adafruit_ADS1015.h>
#include <AnalogSmooth.h>
#include <TinyBME280.h>

AnalogSmooth as_pid = AnalogSmooth(50);
AnalogSmooth as_ref = AnalogSmooth(50);

U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);

Adafruit_ADS1115 ads;                         /* Use this for the 16-bit version */

float smooth_pid;
float smooth_ref;
float pid_float;
float ref_float;

//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------

void setup(void)
{
//  Serial.begin(115200);

  Wire.begin();
  Wire.setClock(1000000);

  ads.setGain(GAIN_FOUR);                    // 4x gain   +/- 1.024V  1 bit = 0.5mV    0.03125mV
  ads.begin();

  u8x8.begin();
  u8x8.setPowerSave(0);

  BME280setup();
  
}

//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------

void loop(void)
{

 int16_t adc0, adc1;

  adc0 = ads.readADC_SingleEnded(0);                      // Get value PID sensor input
  adc1 = ads.readADC_SingleEnded(1);                      // Get value reference sensor input 


  float smooth_pid = as_pid.smooth(adc0);               // Smoothing with window size xx PID Sensor
  float smooth_ref = as_ref.smooth(adc1);               // Smoothing with window size xx ReferencePrint

  
  pid_float = smooth_pid *(1.024 / 32768.0);            // convert to voltage for pid sensor
  ref_float = smooth_ref *(1.024 / 32768.0);            // convert to voltage for reference sensor  
 


  float t_bme280 = BME280temperature();
  
  u8x8.setFont(u8x8_font_victoriabold8_r);
  u8x8.setCursor(0, 0);
  u8x8.print(pid_float / 0.02, 4);                      // Plaatst TMP37 sensor waarde van de PID controler in Celsius op het scherm

  u8x8.setCursor(0, 3);
  u8x8.print(ref_float / 0.02, 4);                      // Plaatst TMP37 sensor waarde van de Referentie print in Celsius op het scherm

  u8x8.setCursor(0, 6);
  u8x8.print(t_bme280 /100, 2);                      // Plaatst code looptijd in millis op het scherm

  delay(2);
}


Doel
Dit sleept zich al een langere tijd voort in mijn LAB, een Oventje voor een spanningreferentie of voor een andere toepassing met ene temperatuur tussen de 40 a 50 Graden °C
Dan niet van die mooie dikke stukken Alu buis die ik normaal gebruik, maar gemaakt van de onderkant van een Aluminium spuitbus.

Hierop is weerstandsdraad gewikkeld en dit wordt dan aangestuurd door een Arduino met PID software.
Die PID software is dus gewoon een hele langzame PWM, standaard staat de "Window" waarde op 1Hz, waarschijnlijk komt deze waarde op een paar Hz te staan,
dit afhankelijk van wat ik meet met mijn sensoren en hoe goed de controler regeld.
Dit is dus een meet-oven, normaal heb je geen drie temperatuur sensoren in een oven zitten.

Allemaal mooi en aardig, maar met zo'n dunne wand oven, is er niet zo veel thermische massa daardoor zal de "window" waarde wel een aantal Hz gaan bedragen.
Een tweede punt is dat de bedrading dan een wat groter probleem gaat worden, daar lekt allemaal energie door weg.

Een snel te maken fout is om b.v. de massa voor de referentie en de ovencontroler te gaan combineren, dit om een draad uit te sparen.
Met andere PWM ovens heb ik daar voldoende van geleert, gewoon niet doen.
De ovencontroler rust ik uit met een voeding die geschakeld kan worden dus bij het opstarten zeg +15V en als de oven bij 44C is gaat de 15V voeding omlaag naar zeg 7 V.
Dan krijg je dus een snelle opwarming en daarna als de temperatur bijna bereikt is een mooie soepele regeling.
Ook kan ik de "window" waarde van de PID software dynamisch gaan regelen, in ieder geval wil ik daar mee gaan spelen om te kijken of dit zinnig is.

Door de dunne buis is de thermische massa vrij laag t.o.v. de printjes die in de oven worden gemonteerd, dus dat is allemaal maar afwachten hoe dit uitpakt wat de PID regeling betreft.
Bij het bouwen hou ik dus rekening met de opstelling en de thermische massa,
grote kans dat ik delen van de print verwijder die niet nodig zijn om de thermische massa van de printen kleiner te maken.

Ook ben ik bezig geweest het verbuik van de Pro Mini controler zo laag mogelijk te maken.
De LEDS zijn verwijder en ook de spannings regelaar.
Verder schakel ik dan ook nog de ADC in de controler uit en eventueel nog andere onderdelen als deze zinning zijn.
In slaap brengen is natuurlijk niet zinning, daar de oven continu ge-regeld wordt.

De controler gaat wel met i2c naar buiten voor het OLED display en een MCP23017 i2c IO extender zodat ik ook nog wat data live uit de oven kan halen en b.v. met encoders of drukknopjes wat waarden kan aanpassen.

Twee plaatjes om het topic wat op te leuken.
Dit is de test setup, de ovenbuis is niet aangesloten maar er bui gezet voor de goede indruk.
Links de programmeer unit die vast zit aan het kleine zwarte breadboard.
Rehts van de Pro Mini is net onder de draden van de i2c bus (Geel + Groen) een TO220 3.3V regelaar te zien, dit omdat ik de regelaar van de print verwijderd had.

Het horizontale printje is de ADS1115 ADC en het kleine printje er onder is de BME280 sensor.
Deze BME280 sensor mag je eigenlijk niet continu uitlezen, dan wordt hij intern warm van en dat geeft dus fouten, ik denk nog na over een oplossing.
Zoiets als 1x per 10 seconde meten, is denk ik wel voldoende als hij in een oventje zit.
Onder het transparante dopje zitten twee TMP37 sensoren op het breadboard geprikt, dat dopje is nodig, anders weet je niet wat ruis op het sensor signaal, of gewoon een temperatuur verandering.

https://www.bramcam.nl/NA/SpuitbusOven/SpuitbusOven-01.png

En dit is de ovenbuis, hier een "Rituals" buisje, maar meerdere merken gebuiken de zelfde buis.
De blauwe blob aan de binnenzijde bovenaan de buis, is de TMP37 sensor, rood, zwart en groen zijn de +, - en de uitgang.
De twee wat dikkere blauwe draden zijn de oven wikkeling, deze is dus met weerstandsdraad bifilair gewikkeld om zo min mogelijk veld in de buis op te wekken.
De MOSFet die de wikkeling aanstuurd wordt ook voorzien van wat RC combinaties om de steile flanken uit de processor wat trager te maken.
We zijn hier niet met een hoge efficientie regeling bezig voor b.v. voedingen, dat kleine deel extra warmte in de transistor is onbelangrijk, een lager stoornivea is wel belangrijk.
https://www.bramcam.nl/NA/SpuitbusOven/SpuitbusOven-02.png

Groet,
Bram

[Bericht gewijzigd door blackdog op 8 juni 2020 13:48:26 (18%)]

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Die AnalogSmooth library is tamelijk duur.
Elke AnalogSmooth variabele heeft intern een array van 100 floats, dus dat kost je 400 bytes Ram. Je hebt er 2, dus samen 800 bytes.

Terwijl je met een window van 50 de helft niet gebruikt.

Zie AnalogSmooth.h :

code:


class AnalogSmooth
{
	public:
		AnalogSmooth(unsigned int windowSize);
		AnalogSmooth();
		float analogReadSmooth(uint8_t pin);
		float smooth(float value);
	private:
		unsigned int _windowSize;
		uint8_t _pin;
		float _analog[100];
		unsigned int _analogPointer;
		unsigned int _maxPointer;
		void _init(unsigned int windowSize);
};

In de code voor AnalogSmooth.h kun je dat aanpassen door de array size terug te brengen naar 50.

code:


		float _analog[50];  // Moet evengroot zijn als WindowSize
blackdog

Golden Member

Hi deKees, ;)

Dat werkt! de variabelen grote is een stuk naar beneden gegaan, dank!
Nu moet ik blijven onthouden wat ik heb gedaan, toch maar een LAB boekje hierover gaan bijhouden.
Ik heb in de library folder van AnalogSmooth een copy van de .h file gezet met de orginele inhoud, dat helpt ook.
Net als het typen van deze paar regels hierover.

Net de MCP23017 i2c expander aan de controler gehangen en er knippert nu een LED op een van de poorten via de Adafruit Library die ik hiervoor gebruik.
Dus de pin configuratie van dit IC is nu goed en er is communicatie.

Volgende stap!

Hartelijk dank!

Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Eentje die zeer efficient is qua ram en processorkracht:

c code:


class Avg
{
    int N;
    float avg = 0;
    
public:
    Avg(int weight)
    {
        N = weight;
    }
    
    float Do(float input)
    {
        return (avg = avg + (input - avg) / N);
    }
    
};

int main()
{
    Avg avg = Avg(3);
    
    
    for(int i = 0; i<20; i++)
    {
        printf("%f\n", avg.Do(1));
        if(i == 10)
            printf("%f\n", avg.Do(2));
    }
}

Let wel op, dit is niet hetzelfde als een moving averaging filter, maar het doet denk ik wel wat jij wilt. Je kunt de invloed van een nieuw sample aanpassen door de weight te veranderen. Een kleiner getal is een grotere invloed van een nieuwe sample.

Klik voor een demo:
http://cpp.sh/6uchsh

Ja dat kan ook inderdaad. Nadeel is wel dat je dan langzaam naar de eindwaarde kruipt na een stap verandering. Om dat op te lossen moet je nog wel wat code toevoegen.

De AnalogSmooth neemt altijd het gemiddelde van de laatste x (hier 50) samples. Oudere samples spelen niet meer mee. Dus die is na 50 samples weer helemaal bij.

Klopt, in theorie komt hij er nooit. In de praktijk daarentegen wel aangezien de float beperkt is in zijn decimalen. Met een N van 4 ben je er in 51 stappen ook. Anders zou je bijvoorbeeld bij een grote afwijking tussen input en average kunnen zeggen dat je de input als nieuwe average neemt. Hoe groot deze stap is hangt af van je applicatie.

@Blackdog

Het werkt als volgt:

as_pid is een variable van het type AnalogSmooth. Die heeft meerdere componenten intern, o.a. een array met samples en nog een paar variabelen voor administratie. Samen is dat een 'object'.

Telkens als je smooth(x) aanroept wordt de nieuwe x bijgeschreven in de array, en vervolgens krijg je het gemiddelde van alle samples in de array. Zo krijg je dus het gemiddelde van de laatste 50 samples.

Dat werkt ook voor as_ref, maar natuurlijk moet je die dan een eigen variabele geven zodat die een eigen array met ref-samples kan gebruiken.

De smooth(x) funktie is een member funktie van AnalogSmooth, en gebruikt dus de array in de variabele waarop die wordt aangeroepen.
as_pid.smooth(x) en as_ref.smooth(x) gebruiken dus elk een andere array met samples.

blackdog

Golden Member

Hi Hardbass,

Dank voor je code, al begrijp ik er niet veel van.
Ik weet natuurlijk wel dat er verschillende manieren om te middelen zijn.

Een voorbeeldje, de gene waarvan in mijn HP3458A heb gekocht die deed heel veel onderzoek aan deze DMM's en maakte ook spannings referenties.
Hij had me wat scripts gegenven en uitgelegt dat b.v. als hij 10V referenties meet hij altijd de twee hoogstwe en de twee laagste waarden weg gooit.
Dit omdat deze weinig met de gemiddelde waarde te maken heeft en vaak stoorsignalen zijn.
Vooral de pieken kunnen ook met een bepaald effect te maken hebben welke popcornnoise is, dus dat altijd negeren is ook weer niet goed.
Oja , ik heb ook de website even bekeken, leuk dat je dit ook "online" kan testen.

Terug naar mijn AnalogSmoot library...
Ik heb nu bijna alles in de code staan en er is nog ruimte over, dus ik hoef niet over te stappen naar andere code omdat deze kleiner is.
Misschien is die code die hardbass laat zien misschien wel beter, maar voorlopig gebruik ik wat ik nu heb om tijd te besparen omdat ik dit begrijp en het al werkt.
Maar belangrijker, ik heb nog geen idee hoe goed de PID gaat werken met de lage thermische massa van de oven behuizing.

Gevoelsmatig denk ik dat de filtering ook weer niet te strak moet worden, anders loopt de sensor waarde te veel achter op de feiten, en dat geeft dus phase lag in de loop.
Met de gekozne waarde van "50" voor een open systeem hier op tafel is het als de luchtreiniger uit staat (heb last van hooikoort) en het raam dicht,
dan veranderd het laatste digit(0.0001°C maar langzaam.
Dan moet ik wel stil zitten, kleine bewegingen van mijn licham zorgen dan al voor voldoende thermiek om omlaag of omhoog te schieten. :+

Gote kans dat ik naar een waarde van 10 toe kan, het mooei is ook dat ik via de software heel mooi een sprongopdracht kan doen om te zien hoe stabiel de loop is.
In de setup bepaald de volgende regel de waarde waar de PID software naartoe regeld.

c code:


  Setpoint = 34200;

Zeg nu dat ik door een drukknop de 34200 waarde 35000 maak, dan kan ik aan het temperatuurverloop zien of het een goed genoeg gedempt regelgedrag is.

Wel heel makkelijk om het op deze manier te testen!
Mijn eerste PID oventje werkte trouwens al goed met een 18B20 sensor, en ik dacht laat ik twee dingen aanpassen en dat is een lineaire sensor met een hoge resolutie ADC dit samen met een spuitbus als oven.

Nog iets wat ik vanmiddag heb getest
Dat is of ik een MOSFet op voorraad had die bij 3V voldoende geleid bij 1,1-Ampere en dat is een FDP8880.
Deze heeft een voldoende lage Ri bij 3V zodat ik wat marge heb bij 3.3V voeding.
De volgende stap is om deze MOSFet de flanken dus wat minder scherp te maken, de waarden zijn natuurlijk afhankelijk van de stijlheid van de MOSFet.
Verder ook nog van de capaciteit van de Drain naar de Gate, ik laat wel zien wat het geworden is.

Genoeg, tijd voor een stevige wandeling!

Groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"
blackdog

Golden Member

Hi,

Even iets heel anders...
Vanavond stopte de Arduino IDE er mee, ik sloot wat versies af met code die ik nog open had staan, en daarna starte hij niet meer op.
Herinstallatie gedaan, werkt niet.

Dus iemand van jullie die dit probleem kent?

Dank en groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Gisteren precies hetzelfde voorgehad bij IDE 1.8.12 en W10.

Volgens internet is een file corrupt geworden.

In de map C:\Users\%gebruikersnaam\AppData\Local\Arduino15

Verwijder (of hernoem)
library_index.json en/of
package_index.json.

In mijn geval moest ik package_index.json verwijderen.

Mijn thuis is waar mijn Weller staat
blackdog

Golden Member

Hi Pertinax, :)

Dank voor de tip, deze werkt alleen niet bij mij.

Ik heb al vele opschoon acties gedaan om alle residu van Arduino te verwijderen en schoon te beginnen, geen resultaat.

Op het forum van Arduino.cc loopt een topic hierover maar er is nog geen zinnige reactie van de heren van Arduino.

Even afwachten maar...

Groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

@blackdog:

Volgens mij meet je temperaturen, en die veranderen op een tijdschaal van seconden. Je sampled 860 keer per seconde op de ADS1115.

Dus als het goed is heb je ruim 1000 samples die (ongeveer) hetzelfde zijn. Met AnalogSmooth middel je er hooguit 100.

Ik heb zelf goede ervaring met een exponentieel gemiddelde. deKees poste al code die met een /N werkt. Ik gebruik meestal een N die een macht van 2 is, zodat je met een bitshift kunt werken.

c code:


inline uint16_t exp_average(uint16_t avg, uint16_t val) {
	avg -= ((avg >> 6) & 0x3FF);
	avg += val;
	return avg;
}

Dit is met een deeltal van 64 (die (avg >> 6) deelt door 26), en specifiek voor de arduino/AVR ADC (die 10 bits is (vandaar de 0x3FF).

Dit geeft een trage meting (je hebt zo'n 750 samples nodig om 16-bits nauwkeurigheid te krijgen, i.e. hetzelfde effect als 64 gewoon gemiddelde samples).
Maar het geheugen en CPU gebruik is vele malen lager.

Dat voorbeeld was van Hardbass, niet van mij :)

Maar dat werkt wel een stuk beter dan dit nieuwe voorbeeld.
Hier krijg je alleen maar een fout resultaat doordat een deel van het vorige resultaat bij de nieuwe waarde wordt opgeteld.

blackdog

Golden Member

Hi,

De Arduino IDE werkt weer met de oplossing gevonden op het forum van arduino.cc

Veel tijd verprutst door de domme actie van Arduino Coders...
Typisch gevalletje dat je software afhankelijk is van je Internet verbinding, lijkt wel op een "kill switch" van de Arduino IDE fabrikant. :+
Wat deze fout betreft mogen ze van mij diep door het zand getrokken worden....
(Ze geven zelf al aan, dat ze veel beter moeten gaan testen, sic...)

Temperatuur Sensoren
Verder nog wat uitgezocht over temperatuur sensoren en wat testten gedaan met mijn sensor collectie.
Er is eigenlijk weinig te vinden die hoge resolutie kan leveren en die ook redelijk stabiel zijn of zelfs maar verkrijgbaar voor en redelijke prijs.

Ten opzichte van mijn eerdere zoekopdracht jaren geleden naar sensoren, zijn er maar een paar nieuwe sensoren die gebruikt kunnen worden in mijn toepassing, met de eisen die ik hiervoor stel.

Het hele probleem is natuurlijk dat in de sensoren computers zitten, lage ruis moeten hebben en ook een heel laag stroomverbruik, dit i.v.m. eigen opwarming.
Begrijp mij niet verkeert, ik vind het heel knap wat ik nu kan kopen t.o.v. 10 jaar geleden, er is zoveel vooruitgang op sensoren gebied!
Alleen al deze sensoren van TI: LMT70, TMP61, TMP117 en de HDC2021, en er zijn nog een tiental fabrikanten met moderne sensoren.

Een mooi voorbeeld is de BME280, maar die een flinke opwarming heeft als je hem continu zou uitlezen.(wordt vermeld in da datasheet)
Dit continu uitlezen is in mijn toepassing natuurlijk niet nodig, 1x per 30 seconde zou voldoende zijn, de temperatuur uitlezing van deze sensor is alleen ter controle
om te zien of de andere twee sensoren en de BME280 verlopen t.o.v elkaar, dit is een meet/doe ervaring op oventje. ;)

Maar dit opwarmen van je sensor, welke het ook is , is wel iets waar je rekening mee moet houden.

Middeling code
Leuk dat jullie meerdere vormen van middeling hier laten zien, maar ik gaf al eerder aan, dat mijn code voorlopig klein genoeg is om in een Pro Mini te passen.
Loop ik alsnog vast, dan kom ik terug op jullie alternatieve filtering voorstellen.

I2C rotary encoders
Voor de makkelijkheid heb ik wat rotary encoders via i2c aangeschaft van Sparkfun.
Deze encoders kan je met een vier aderig kabeltje doorlussen en ook softwarematig van een ander adres voorzien.
Alle debounce is al ingebouw en ze hebben een knopje dat aaangestuurd/verlicht wordt door ene drie kleuren LED, daar heb ik verder geen behoefte aan, maar het kan.
Dit alles wordt afgehandeld door een AT-Tiny84 onder op het printje.

Ook de counter limieten kan je instellen in de library voor deze modules, laat het nu zo zijn, dat in twee van deze modules nog de eerste versie firmware zit en de limiten dan nog niet werken...
Deze documentatie is nogal halfbakken wat het updaten van de firmware betreft en via Sparkfun krijg ik geen reactie.
Dus hier bij een link naar de firmware die ik gevonden heb voor dit product en hoor graag jullie bevindingen hoe ik die firmware in de AT-Tiny84 krijg die onder op het printje zit.
Dat zal via de i2c bus moeten, daar er geen andere bus op het printje van de QWIIC TWIST zit.

Link firmware pagina Sparkfun QWIIC TWIST zit.
https://github.com/sparkfun/Qwiic_Twist/tree/master/Firmware/Qwiic_Twi…

Dan vast en gegroet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Programmeren via I2C gaat niet zonder speciale bootloader.

Maar op het schema staat wel een ISP connector (J7).
Die zit ook op de print volgens mij, al is hij wel tamelijk onzichtbaar gemaakt.

blackdog

Golden Member

Hi deKees,

Heel opmerkzaam van je. :-)
Ik heb net bericht gekregen dan ik de twee printjes met de oude firmware mag terug sturen om dan weer twee printjes met de goede firmware te krijgen.

Misschien hebben de printjes die ik hier heb al die speciale bootloader...
Daar heb ik totaal geen kaas van gegeten.

Ik denk nog even na over het terug sturen, kost ook weer geld.
Verder doet de gene met wel de 1.2 versie van de firmware, niet wat wat ik graag wil met de max/min setting, dus tja, met niet echt happy met de aanschaf.

Ik heb net nog een keer de omschrijving in de voorbeeld code van "Example12_SetLimit.ino" gelezen, lezen jullie het ook eens...


This example shows how to set the max encoder value.
For example, setting the limit to 359 will not allow the encoder read values to go above 359, looping back to zero instead.
Useful when the encoder value is mapped directly onto a volume setting, FM freq, etc.

This feature is available on Qwiic Twist firmware v1.2 and above.

Het klopt dat de code niet verder telt dan de hier als voorbeeld opgegeven 359 en daarna doodleuk naar "0" en daarna weer verder omhoog gaat.
Als voorbeeld wordt een volume! setting gegeven of b.v. frequentie instellening aangegeven waarbij dat makkelijk zou zijn.

Ware het niet dat als je naar "0" draaid en er overheen gaat je in één keer naar 359 spring, MAXIMAAL VOLUME!
Geen idee wat die coders roken, maar sporen doen ze niet in mijn ogen.

Er zitten allerlij functies in de library/firmware zoals hoelang geleden je de knop hebt aangeraakt, drukken of draaien enz.
Mooi, prachtig, maar wat betreft basis functies ontbreekt het gewoon.

Ik zou in de code de minimale en de maximale waarde willen aangeven zoals dit: twist.setMinLimit(18); en twist.setMaxLimit(275);
Dit samen met de twist.setCount(33); heb je dan de minimale functies die de moduul in mijn ogen zou moeten hebben.

Mooi zou zijn dat je het dynamisch gedrag ook zou kunnen aanpassen, dus sneller draaien maakt grotere stappen van de counter.
Dit alles wou ik ook aan Sparkfun laten weten of op hun forum te vragen, ware het niet dat beide niet reageren op mijn verzoeken via e-mail of account aanmaak.

Nu zit ik met modules die niet doen wat ik graag wil.
Misschien wil ik wel te veel...

Het doel is dus met drie encoders via i2c de P, de I en de D in te kunnen stellen als de oven is ingebouwd, net als de mogelijkheid een kleine temperatuursprong te doen.
Dit om de PID waarde te testen/instellen en de waarden dan uiteindelijk op te slaan in de processor als ik de goede waarden hiervoor heb gevonden.
De temperatuur sprong kan ik met een drukknoppje doen via de i2c extender daar hoeft in principe geen encoder aan.

Waarom i2c gebuik, dat is om een minimale hoeveelheid bedrading van/naar de oven te laten lopen om het zo stabiel mogelijk te houden.
De controller en de ADC komen in de oven net als de MOSFet die de bifilaire gewikkelde verwarming aanstuurd.

Zie ieder draadje naar/van de oven maar als een gat met een bepaalde diameter in je emmer met water, hoe meer draadjes en ook hoe dikker het draadje,
des te meer moet je de emmer steeds bijvullen om het waterniveau op het streepje in de emmmer te houden.
Met de daarbij behorende "onrust" (thermiek van de lucht in de oven) in het water.

Nu moet ik wat code gaan fabrieken die als je voorbij de limit gaat stel 62 dat ik dan de counter weer terug set met de "setCount" functie
Zoiets van: als count = gelijk of hoger is dan 62 dan setCount(62) en het zelfde voor de laagste waarde die ik wil hebben, kijken of ik daar vanavond aan kan werken.

Dat waren weer even de perikelen van gisteren en vandaag betrteffende de oven sturing.

Groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Ik weet niet of het helpt, maar ik heb juist vorige week een paar printjes binnengekregen met 3 encoders en een I2C interface. Ik heb nog wel wat printjes beschikbaar.

Basis software is beschikbaar, inclusief versnelling, maar zonder limieten. Die kun je volgens mij net zo goed aan de host kant inbouwen. Ik ga nog wel een .ino driver maken die dat ondersteunt.

Er zitten wel erg veel lichtjes op. Dat is vooral voor het edukatieve effect. In de praktijk kun je die wel weglaten.

Ware het niet dat als je naar "0" draaid en er overheen gaat je in één keer naar 359 spring, MAXIMAAL VOLUME!

Het kan bij sommige toepassingen handig zijn het is natuurlijk niet alleen voor volume alhoewel ik nu even niets kan bedenken. Ik zou toch liever hebben dat hij gewoon op 0 blijft hangen hoelang je ook draait. Ook voor de max waarde die dan ineens naar 0 springen is ook niet altijd wenselijk. En ik zou ook liever hebben dat het op die waarde blijft hangen hoe ver je ook draait.

Ik ben wel blij me je nieuwe vondst het maakt het gebruik van encoders een stuk eenvoudiger

Ik snap heel goed dat je de smooth lib blijft gebruiken. Het werkt en je snapt hoe het werkt. Het kan beter maar is absoluut niet noodzakelijk. Daarbij komt dat ik mijn voorbeeldje een C++ class is, dat is die library ook maar dat zit grotendeels onderwater verstopt.

Wat betreft het regelen van de PID, als ik het goed begrijp doe je dit eenmalig en programmeer je het vervolgens hard coded in je controller? Ik zou zelf niet de moeite nemen om dit met encoders te doen. Ik zou zelf via de seriële interface een paar commando's beschikbaar maken om de PID waardes aan te passen. En meteen een commando om de samples naar buiten te gooien zodat je ze op de PC in een grafiek kan plotten. Dan kan het feest pas echt beginnen, je kunt de PID namelijk berekenen. Een oud docent van mij heeft een mooi stukje hierover op zijn website staan: http://www.vandelogt.nl/uk_regelen_pid.php

Eigenlijk wel leuk om zoiets te bouwen, ik zoek al een tijdje naar een 'oventje' wat rond de 300°C kan blijven hangen. Nu is voor mij de nauwkeurigheid van een gasbrander al voldoende maar des al niet te min leuk om te bouwen. Als ik nog eens wat tijd over heb zal ik eens kijken of ik dat in een Arduino kan proppen.

blackdog

Golden Member

Hi deKees,

Altijd leuk om met je print te stoeien, stuur me een e-mail dan kunnen we de verkoop regelen.
Waarvoor heb je de processor bedoeld op het printje, algemeen?

hardbass
Dat ik niet direct met je code aan het werk ga is gewoon tijd die ik nodig heb om code te doorgronden...
Net voor ik dit stukje type, heb ik weer je stukje code bestudeerd, ik raak door mijn taalprobleem in de war door b.v. "weight" op twee plekken en de de vele versies van "avg"
Als je dan met Avg en avg aankomt moet ik tien keer kijken wat er nu bedoeld wordt.

Je begrijpt denk ik wel dat ik dan liever de tijd gebruik aan de code die nu al goed genoeg werkt en aan de rest van het project.

Ik heb zoiets waar ik nu mee bezig in dit topic al eens meer gemaakt, andere sensoren, ander oven materiaal en veel aanpassingen aan de PID waarden gedaan om te testen wat de effecten waren, empirisch leren.
Je wordt gek van het uploaden, dan draai ik liever aan een paar encoders voor de PID waarden en dan samen met een schakelaar waarmee ik de temperatuur een klein sprongetje kan laten maken.
Dit om de effecten van de verandering vvan de PID waarden te testen.

Met de andere PID oven hoefte ik door de extreem goede koppeling tussen de sensor en de ovenbuis (18B20 in een dikke Alu buis wand geboord en gelijmt) maar weinig in te stellen,
maar dat wist ik niet toen ik begon. ;)

De vorige ovenhad weinig PID nodig, dit waren de settings voor die oven, die trouwens binnen een paar honderste °C bleef.

c code:


const byte p = 40;
const byte i = 1;
const byte d = 1;
int WindowSize = 1000;
double Setpoint, Input, Output;

De website die je aangaf had ik al eens gezien, maar wat voor jouw stukje code geld betreffende mijn brein, geld ook voor die website,
kost zeer veel moeite voor mij om er een nagel achter te krijgen.

Zie de i2c manier die ik nu kies ook maar voor de toekomst zodat ik een soort testkastje kan maken met een aantal i2c devices zodat ik snel zaken kan testen/ontwikkelen.
Daarom lijkt mij het printje van deKees ook handig.

Oja, ik heb er ook nog aan gedacht om de tweede TMP37 sensor die op de Referentie print zit, te gebruiken als meetsensor voor de PID.
Je zou dat een beetje kunnen vergelijken met het brouwen van bier door de langere tijdconstante die hierbij optreed.
Veder heb je dan ondermeer als nadeel dat de ADC op de controlerprint minder stabiel gehouden wordt.
Maar ik blijf voorlopig bij mijn eerste opset met de TMP37 Sensor die aan de ovenbuis vast zit.
Zoveel afwegingen...

Groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"

Als je er baat bij hebt kan ik de code wel wat lees vriendelijker neerzetten. Ik kan me voorstellen dat het verwarrend is om te lezen met de benamingen die ik gekozen heb en geen commentaar bij de code. Je zou mijn antwoord kunnen vergelijken met een stukje schema zonder uitleg. :)

Het blijft lastig te snappen als je het concept 'object oriented programming (OOP)' niet kent. Hier is wel weer veel over te vinden mocht je dat interessant vinden. Wat trouwens heel goed werkt om te leren begrijpen hoe software werkt is debugging. Stap voor stap door de code gaan heeft mij erg veel geleerd!

c code:




//Definieer het object Average
class Average
{
	//Om het gemiddelde te kunnen uitrekenen moeten een aantal waardes worden onthouden.
	//Deze staan binnen de scope van dit object. (aangegeven met brackets '{}') 
	//Deze variabelen zijn dus enkel te benaderen binnen dit object.
	
	//Hoe zwaar nieuwe samples meetellen in de berekening.
    int internal_weight = 0;
	
	//Een variabele om het resultaat in op te slaan, deze hebben we bij iedere sample weer nodig.
    float internal_result = 0;
    
public:
	
	//De constructor van het object Average. Deze wordt 1x aangeroepen bij het creeeren van dit object. (Dit gebeurt in de main())
    Average(int weight)
    {
        internal_weight = weight;
    }
    
	//De functie die voor iedere nieuwe sample opnieuw wordt aangeroepen om het nieuwe gemiddelde te berekenen.
    float Do(float input)
    {
		//Het rekensommetje om het nieuwe gemiddelde te berekenen.
		internal_result = internal_result + (input - internal_result) / internal_weight;

		//Geef het resultaat terug.
        return internal_result;
    }
    
};




int main()
{
	//Ik maak hier 3 instances van het object 'Average'. Zo kunnen er 3 gemiddeldes worden berekend.
	//Je ziet dat hier de constructor van het object 'Average' wordt aangeroepen met een argument van 3.
    Average avgCalculator1 = Average(3);
	Average avgCalculator2 = Average(3);
	Average avgCalculator3 = Average(3);
    
    //Een klein testje, bereken het gemiddelde 20x
    for(int i = 0; i<20; i++)
    {
		//Dit zouden de meetwaardes moeten worden, nu is hier statisch een waarde ingevuld voor het testen.
		float measuredValue1 = 1;
		float measuredValue2 = 8;
		float measuredValue3 = 4;
		
		//Bereken een nieuw gemiddelde.
		float averageValue1 = avgCalculator1.Do(measuredValue1);
		float averageValue2 = avgCalculator2.Do(measuredValue2);
		float averageValue3 = avgCalculator3.Do(measuredValue3);
		
		//Print de nieuwe waardes.
        printf("%f \t", averageValue1);
		printf("%f \t", averageValue2);
		printf("%f \n", averageValue3);
    }
}

blackdog

Golden Member

Hi hardbass, :-)

Dank je voor je inzet, ik lees je code morgen als ik wat frisser ben.
Dan beloof ik dat ik echt mijn best doe met wat opmerkingen over vragen die bij me opkomen.

Ik zij in een vorige post dat ik de limiten zou testen vanavond en dat heb ik gedaan, zal jullie ook vertellen waar de meeste tijd in zat.
Ik worstel echt met wanner nu welke haakjes komen en hoeveel van welke
Waarom moet twist.getCount() tussen extra haakjes () staan in onderstaande vergelijking?

c code:


  if ((twist.getCount()) >= 15) {

Nog een puntje waarom het in eerste instantie niet werkte, als je deze functie gebruikt om de Maximale waarde van de counter op te geven: twist.setLimit(15);
Dan werken de ingestelde limiten niet goed, als je de twist.setLimit echter op "0" set werke het zonder storingen,zo dus twist.setLimit(0);
Dan zet de library deze functie uit.

De code die nu netjes met limiten werkt staat hieronder en deze geeft een melding als de max of min waarde bereik is, kan handig zijn.
Bij mij gebruikt om de code te testen in de serieel monitor.

c code:


#include "SparkFun_Qwiic_Twist_Arduino_Library.h"                       //Click here to get the library: http://librarymanager/All#SparkFun_Twist
TWIST twist;                                                            //Create instance of this object

void setup() {
  Wire.setClock(400000);                                                //Als het goed is werkt de i2c bus onderdelen die ik gebruik allen op deze snelheid 

  Serial.begin(115200);                                                 //Set de serieele monitor op een hoge snelheid om beter foutjes te kunnen dedecteren
 
  twist.begin();

  twist.setLimit(0);                                                   // setLimit moet op "0" staan voor goede werking van de limit waarden in de loop code

  twist.setCount(8);                                                   // plaatst een voorkeurwaarde in de counter na het resetten van de controler, hier is dat de waarde 8
  
}

void loop() {
  
  if ((twist.getCount()) >= 15) {
  twist.setCount(15);
  Serial.println();
  Serial.print("Counter Max. is bereikt!");
  }
  
  else if ((twist.getCount()) < 0) {
  twist.setCount(1);
  Serial.print("Counter Min. is bereikt!");
  }
  
  Serial.print("Count: ");
  Serial.print(twist.getCount());
  Serial.println();


Blij dat dit werkt, dan hoef ik de twee printjes die oude firmware hebben niet terug te sturen.

Groet,
Bram

Waarheden zijn "Illusies waarvan men vergeten is dat het illusies zijn"