sensors-software icon indicating copy to clipboard operation
sensors-software copied to clipboard

Frage: SPS30 Lüfter läuft ständig ?

Open tom-r opened this issue 5 years ago • 6 comments

Frage zum SPS30 : Am Anfang wird einmalig mit sps30_start_measurement() der Sensor gestartet (Lüfter an, und die Messungen laufen im Hintergrund mit 1/s). Dann wird alle 11 Sekunden (SPS30_WAITING_AFTER_LAST_READ) ein Messwert ausgelesen, "gemerkt" und beim normalen Messintervall (den "145s") der Mittelwert aus den 11s-Messungen an die APIs üergeben. Die Frage geht jetzt nicht um die Mittelwertbildung, sondern warum der Lüfter dauernd "volle Pulle" läuft und Strom zieht, wenn nur alle 11s ein Wert genommen wird ? Hat das eine tieferen Grund ? Oder ist das nur, weil sich bisher niemand im Detail mit dem SPS30 befasst hat? Man kann die Messungen auch problemlos stoppen mit sps30_stop_measurement(), nur muß man halt beim erneuten Anfahren mittels sps30_start_measurement() solange mit sps30_read_data_ready(&data_ready) pollen, bis ein gültiger Messwert vorliegt. Das dauert ca 1 s .... Hier ist das entsprechend angepasste Code Snippet aus der loop() Prozedur :

if (cfg::sps30_read && ( !sps30_init_failed)) {
		if ((msSince(starttime) - SPS30_read_timer) > SPS30_WAITING_AFTER_LAST_READ) {
			struct sps30_measurement sps30_values;
			int16_t ret_SPS30;
			uint16_t data_ready = 0;
			SPS30_read_timer = msSince(starttime);
			ret_SPS30 = sps30_start_measurement();//Messung starten, Lüfter an ...
			delay(500);
    		       do {
        		    ret_SPS30 = sps30_read_data_ready(&data_ready); //warten bis Messwerte da sind ...
        		    if (ret_SPS30 < 0)
					debug_outln_info(F("SDS30: Error reading data-ready flag"));
        		    else if (!data_ready)
					debug_outln_info(F("SDS30: Data not yet ready, no new measurement available"));
        		    else{
					debug_outln_info(F("SPS30: Data is ready"));
            		    break;
				}
        		    delay(200); /* retry in 200ms */
    		       } while (1);

			ret_SPS30 = sps30_read_measurement(&sps30_values);//Messwerte holen
			++SPS30_read_counter;
			if (ret_SPS30 < 0) {
				debug_outln_info(F("SPS30 error reading measurement"));
				SPS30_read_error_counter++;
			} else {
				ret_SPS30 = sps30_stop_measurement();//Messungen stoppen, Lüfter aus ...
 				if (ret_SPS30 < 0) {
					debug_outln_info(F("SPS30 Error stopping measurement"));
			    }

				if (SPS_IS_ERR_STATE(ret_SPS30)) {
					debug_outln_info(F("SPS30 measurements may not be accurate"));
...

Gruß,

Thomas

tom-r avatar Feb 09 '20 09:02 tom-r

Frage 2: Lt. Handbuch wird für das Autocleaning 1/Tag vorgeschlagen (bzw. alle 4 Tage). OK, das sind Beispielswerte ... Wir cleanen alle 7200 Sekunden, also alle 2h ... Warum so oft ? Gibt's negative Erfahrungen mit zu langen Intervallen ? Danke und Gruß, Thomas

tom-r avatar Feb 09 '20 10:02 tom-r

ZUr ersten Frage: Wenn der SPS30 (wie alle anderen optischen PM-Sensoren auch) aus war, muss eine gewisse Zeit Luft angesaugt werden, bevor die ersten richtigen Werte gelesen werden können. Da dies meist zwischen 10 und 20 Sekunden liegt (einfach mal in den Datenblättern suchen nach warm up time), wird beim SPS30 der Lüfter nicht abgeschaltet.

ricki-z avatar Feb 09 '20 16:02 ricki-z

OK, verstanden. Im Handbuch steht auf S.2 "Start up time < 8s" Aber wenn wir sowieso mit 1/s messen und dann einen Mittelwert bilden, dann hab ich da was (vermutlich) besseres aus meinem OpenCPN Projekt ... Nennt sich "exponential smoothing". Neuer Wert rein, Mittelwert raus, ohne die Anzahl mitzuzählen ... Kannst Dir gar nicht vorstellen aus was für verrauschten Werten da noch gute Kurven rauskommen ... Mal probieren ...

tom-r avatar Feb 10 '20 08:02 tom-r

Schau Dir mal das "exponential Smoothing" an (wiki). Gerade wenn es um kontinuierlich anfallende 'time series' Daten handelt, vereinfacht das alles deutlich ... Das glättet besser als mit 'arithmetischem Mittel' mit all seinen Nachteilen (gewichtete Ecken, counter mitlaufen lassen, etc.)... Ich hab mal auf die Schnelle eine Minifunktion geschrieben:

float alpha=0.5;
static float ExpSmoothVal(float newval,float SmoothedValue,float oldSmoothedValue)
{
    if (SmoothedValue==-1.0){ //Startwert setzen
      SmoothedValue = newval;
      oldSmoothedValue = SmoothedValue;
    }
    SmoothedValue = alpha*newval + (1.0f - alpha)*oldSmoothedValue;
    return SmoothedValue;
}

newval ist der aktuelle Sensorwert alpha ist die Gewichtung, je kleiner der Wert desto größer die Glättung ! Werte zwischen 0 und 1; alpha=1 = gar keine Glättung alpha muß man halt einmal testen und einstellen; In der u.a. Logausgabe ist alpha=0.5 gesetzt (ziemlich schwach geglättet), aber bei 11Sek/Messwert ist das mal ein Start ...

Die Funktion rufe die im loop() auf, innerhalb von

if (cfg::sps30_read && ( !sps30_init_failed)) {
		if ((msSince(starttime) - SPS30_read_timer) > SPS30_WAITING_AFTER_LAST_READ) {
            ...
				if (SPS_IS_ERR_STATE(ret_SPS30)) {
					debug_outln_info(F("SPS30 measurements may not be accurate"));
					SPS30_read_error_counter++;
				}
				value_SPS30_P0=last_value_SPS30_P0;
				last_value_SPS30_P0 = ExpSmoothVal(sps30_values.mc_1p0,last_value_SPS30_P0,value_SPS30_P0);
				value_SPS30_P1=last_value_SPS30_P1;
				last_value_SPS30_P1 = ExpSmoothVal(sps30_values.mc_10p0,last_value_SPS30_P1,value_SPS30_P1);
				value_SPS30_P2=last_value_SPS30_P2;
				last_value_SPS30_P2 = ExpSmoothVal(sps30_values.mc_2p5,last_value_SPS30_P2,value_SPS30_P2);
				value_SPS30_P4=last_value_SPS30_P4;
				last_value_SPS30_P4 = ExpSmoothVal(sps30_values.mc_4p0,last_value_SPS30_P4,value_SPS30_P4);
				value_SPS30_N05=last_value_SPS30_N05;
				last_value_SPS30_N05 = ExpSmoothVal(sps30_values.nc_0p5,last_value_SPS30_N05,value_SPS30_N05);
				value_SPS30_N1=last_value_SPS30_N1;
				last_value_SPS30_N1 = ExpSmoothVal(sps30_values.nc_1p0,last_value_SPS30_N1,value_SPS30_N1);
				value_SPS30_N25=last_value_SPS30_N25;
				last_value_SPS30_N25 = ExpSmoothVal(sps30_values.nc_2p5,last_value_SPS30_N25,value_SPS30_N25);
				value_SPS30_N4=last_value_SPS30_N4;
				last_value_SPS30_N4 = ExpSmoothVal(sps30_values.nc_4p0,last_value_SPS30_N4,value_SPS30_N4);
				value_SPS30_N10=last_value_SPS30_N10;
				last_value_SPS30_N10 = ExpSmoothVal(sps30_values.nc_10p0,last_value_SPS30_N10,value_SPS30_N10);
				value_SPS30_TS=last_value_SPS30_TS;
				last_value_SPS30_TS = ExpSmoothVal(sps30_values.tps,last_value_SPS30_TS,value_SPS30_TS);
				RESERVE_STRING(SMdata, LARGE_STR);  //temp. Logausgabe
				SMdata=String(sps30_values.mc_1p0); //temp. Logausgabe
				SMdata+= ";";                       //temp. Logausgabe
				SMdata+=String(last_value_SPS30_P0);//temp. Logausgabe
                ...

In fetchSensorSPS fällt dann die Mittelwertbildung und das Rücksetzen der Counter weg und man weist nur noch dem JSon String den last_value_xxx zu ...

static void fetchSensorSPS30(String& s) {
	debug_outln_verbose(FPSTR(DBG_TXT_START_READING), FPSTR(SENSORS_SPS30));

	add_Value2Json(s, F("SPS30_P0"), F("PM1.0: "), last_value_SPS30_P0);
...

Hier sind mal 2 Logausgaben der SPS30 Werte (alpha=0.5), da sieht man schön wie der gelättete (,jew. der 2.) Wert mitzieht ...

mc_1p0;P0;mc_10p0;P1;mc_2p5;P2;mc_4p0;P4;nc_0p5;NC05;nc_1p0;NC1;nc_2p5;NC2.5;nc_4p0;NC4;nc_10p0;NC10;tps;TPS 3.78;3.78;4.00;4.00;4.00;4.00;25.41;25.41;30.00;30.00;30.18;30.18;30.19;30.19;30.20;30.20;0.35;0.35 4.21;3.99;4.45;4.22;4.45;4.22;28.26;26.84;33.38;31.69;33.57;31.88;33.59;31.89;33.60;31.90;0.38;0.37 4.43;4.21;4.68;4.45;4.68;4.45;29.75;28.29;35.14;33.41;35.34;33.61;35.36;33.63;35.37;33.63;0.42;0.39 4.67;4.44;4.94;4.70;4.94;4.70;31.38;29.84;37.06;35.24;37.27;35.44;37.29;35.46;37.30;35.47;0.41;0.40 4.52;4.48;4.78;4.74;4.78;4.74;30.39;30.11;35.89;35.56;36.10;35.77;36.11;35.79;36.12;35.79;0.41;0.41 PM1.0: 4.48 PM2.5: 4.74 PM4.0: 4.74 PM 10: 4.74 NC0.5: 30.11 NC1.0: 35.56 NC2.5: 35.77 NC4.0: 35.79 NC10: 35.79 TPS: 0.41 5.64;5.06;5.96;5.35;5.96;5.35;37.89;34.00;44.74;40.15;45.00;40.39;45.02;40.41;45.03;40.41;0.43;0.42 5.42;5.24;5.74;5.54;5.74;5.54;36.44;35.22;43.03;41.59;43.29;41.84;43.31;41.86;43.32;41.87;0.45;0.43 5.54;5.39;5.85;5.70;5.85;5.70;37.19;36.21;43.92;42.76;44.18;43.01;44.20;43.03;44.21;43.04;0.46;0.45 5.32;5.35;5.63;5.66;5.63;5.66;35.75;35.98;42.21;42.49;42.46;42.73;42.48;42.75;42.49;42.76;0.46;0.45 4.97;5.16;5.26;5.46;5.26;5.46;33.39;34.68;39.43;40.96;39.67;41.20;39.68;41.22;39.69;41.23;0.46;0.46 5.07;5.12;5.36;5.41;5.36;5.41;34.05;34.37;40.21;40.58;40.44;40.82;40.46;40.84;40.47;40.85;0.45;0.45 PM1.0: 5.12 PM2.5: 5.41 PM4.0: 5.41 PM 10: 5.41 NC0.5: 34.37 NC1.0: 40.58 NC2.5: 40.82 NC4.0: 40.84 NC10: 40.85 TPS: 0.45

Und viel größer wird's Programm dadurch auch nicht. Würde sich auch für die Luftdruck &Temperaturdaten anbieten, die kann man auch kontinuerlich abfragen. Beim SDS 11 mit seinen 5 Messungen alle zweieinhalb Minuten wird das allerdings nicht funktionieren, aber wenn der SPS30 sowieso dauernd läuft bietet sich das an ...

Gruß,

Thomas

tom-r avatar Feb 10 '20 15:02 tom-r

Hallo, ich bekomm leider derzeit nicht mehr hin, als eine CO2 Ampel zu basteln. Allerdings betreibe ich ein SPS30 Airrohr an einer Insel-Solaranlage und bin dringend darauf angewiesen, den Stromverbrauch zu reduzieren.

Wie muss ich den Code (oben) anpassen, damit die Messungen "nur" alle fünf Minuten stattfinden? Oder reicht es (so verstehe ich den Code oben), wenn ich, nach der Anpassung, im Webfrontend das Messintervall "hoch" setze?

Eine Energiesparfirmware-Version wäre übrigens toll.

Sihamann avatar Jan 11 '21 11:01 Sihamann

Ja. Sie müssen nur das Messintervall anpassen.

pjgueno avatar Jan 12 '21 09:01 pjgueno