Luftqualitäts-Monitor + Windows App


Allgemeine Informationen

Der Luftqualitäts-Monitor besteht aus einer Platine mit den entsprechenden Bauteilen und kann über eine Windowsapp konfiguriert und visualisiert werden. Sämtliche Quellcodes wurde von uns selbst entworfen. Das ganze Projekt ist schon etwas älter und wurde zu Corona-Anfangszeiten entwickelt. Hintergrund bzw. die angedachte Funktionsweise war die Ermittlung der Anzahl an organischen Teilchen in der Atemluft. Mit dieser Information und der u. a. grafischen Darstellung der Werte, konnte der richtige Zeitpunkt für das Lüften des Raumes ermittelt werden. Dadurch sollte eine größere Belastung durch die Atemluft mit Corona-Viren gemindert werden. Die App sowie das Arduino Projekt könnt Ihr direkt von unserer Seite downloaden. Euch steht natürlich frei, die ganzen Elemente nach euren Interessen anzupassen.

Viel Spaß beim Nachbauen oder Umbauen.


Inhaltsverzeichnis

Hardware

Auflistung aller Hardware-Komponenten und dem zugehörigen Schaltplan.
Im Download-Bereich könnt Ihr den Schaltplan als KiCad-Projekt herunterladen.
Die Bilder können durch einen Mausklick vergrößert werden.

Bauteile

  1. Kondensatoren.: C1 = Elektrolytkondensator 10 µF/16V | C2 = Keramik-Kondensator 100 nf/16V
  2. 1 x LP2950 3V3-Spannungswandler
  3. Widerstand R1-R3 220 Ohm

Platine

Mit der Maus über das Bild fahren, um es zu animieren.

Schaltplan

Gezeichnet mit KiCad 6.0

Software

Über die Software können die Hardwareeinstellungen vorgenommen werden. Zusätzlich kann der Verlauf aller Werte in einem Diagramm dargestellt werden. Hierzu muss das Modul mit einem PC über USB verbunden werden. Im Anschluss kann die Software gestartet werden. Die Software ermittelt eigenständig die Com-Schnittstelle und baut eine Verbindung auf.

Mit dem Dropdown Menü am linken unteren Rand, kann zwischen den verschiedenen Funktionen der Software umgeschaltet werden.

  • Konfiguration anpassen
  • CO₂ (parts per million)
  • TVOC (Gesamtkonzentration flüchtigen organischen Verbindungen)
  • Temperatur in °C
  • Relative Feuchte rF%

Konfiguration anpassen

Mit einem Klick auf die Grüne- oder Rot-Fläche, werden diese markiert. Hierbei nimmt auch der Zahlenwert in der Mitte die jeweilige Farbe an. Ein Klick auf die Zahl zwischen + und -, ändert die Schrittgröße. Über Plus und Minus lassen sich nun die Schaltschwellen der LED`s einstellen. Maßgebend ist hierbei der CO₂-Wert.

Auf die gleiche Weise lassen sich die Min- und Max-Werte (im Bild 800 und 1700) verändert. Hierzu muss aber auf das jeweilige Textfeld geklickt werden.

Alle Einstellungen werden, mit dem Button ‚Speichern‘, auf dem EEPROM gespeichert. Über Abbruch, werden die Daten aus dem Speicher wieder in das Programm geladen.

*Zum vergrößern klicken.

CO2 (parts per million)

Der CO2-Gehalt in der Luft wird in parts per million (Anteile pro Million), kurz ppm, oder in Prozent (%), beziehungsweise Volumenprozent (Vol. -%) angegeben.

Welcher CO2 Wert ist normal?
Danach gelten Konzentrationen unter 1000 ppm Kohlendioxid in der Raumluft als unbedenklich, Konzentrationen zwi- schen 1000 und 2000 ppm als auffällig und Konzentrationen über 2000 ppm als in- akzeptabel.

Je nach eingestellten Grenzwerten (siehe Konfiguration anpassen), werden in diesem Diagramm die Schaltschwellen der LEDs eingeblendet. Hierbei gilt: Unter Grün, leuchtet die Grüne LED. Zwischen Grün und der roten Linie die gelbe LED. Über der roten Linie, die rote LED.

*Zum vergrößern klicken.

TVOC (Gesamtkonzentration flüchtigen organischen Verbindungen)

Die Gesamtkonzentration flüchtigen organischen Verbindungen (TVOC) in ppb (Teile pro Milliarde) ist die Summe aus Hunderten verschiedenen flüchtigen organischen Verbindungen in Innenräumen, deren Zusammensetzung sich ständig verändert.

Die TVOC-Konzentration kann nur durch die Analyse aller einzelnen Verbindungen gemessen werden, beispielsweise durch eine Gaschromatographie und Massenspektrometrie (GC-MS).

*Zum vergrößern klicken.

Temperatur °C

Zur objektiven Bestimmung der Temperatur wird häufig eine Skala mit der Einteilung Grad Celsius (°C) genutzt.

*Zum vergrößern klicken.

Relative Feuchte rF%

Was ist relative Luftfeuchtigkeit? Die relative Luftfeuchtigkeit gibt Aufschluss darüber, wie viel Prozent die Luft mit Wasser(dampf) gesättigt ist. Grundsätzlich gilt: je wärmer die Lufttemperatur, desto mehr Wasser kann diese aufnehmen. Bei 100% relativer Feuchte ist die Luft vollständig mit Wasser(dampf) gesättigt.

*Zum vergrößern klicken.

Quellcode Luftqualitäts-Monitor Windows APP

Das Programm wurde in der Programmiersprache VB.net geschrieben. Der Quellcode wird durch das Einbinden in die Homepage automatisch angepasst. Es werden z. B. Zeilenumbrüche eingefügt. Er kann somit nicht 1 zu 1 in dein Visual Studio Projekt übernommen werden. Visual Studio ist eine von dem Unternehmen Microsoft angebotene integrierte Entwicklungsumgebung für verschiedene Hochsprachen. Gerne könnt das Programm über den Download-Bereich herunterladen. Wollt Ihr jedoch das Visual Studio Projekt, würde ich euch bitten, mir eine Nachricht über das Kontaktformular zukommen zu lassen. Ich melde ich dann per E-Mail bei euch.

Imports System.IO.Ports 'Serielle Schnittstellen import
Imports System.Windows.Forms.DataVisualization.Charting

Public Class C02WarnForm

#Region "Globale Variablen"
    Dim arrComlist As New ArrayList
    Dim intBootCount As Integer = 0 'Definiert den Bootschritt
    Dim intDeviceFound As Integer = 0 'Gerät gefunden = 1
    Dim intActDevice As Integer = 0 'Aktuelles Gerät das verbunden werden soll
    Dim intGrenzWert1 As Integer 'Grenzwert default Werte
    Dim intGrenzWert2 As Integer 'Grenzwert default Werte
#End Region

#Region "Form laden"
    Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
        UpdateTxtStatus("Ermittle alle verfügbaren Com-Schnittstellen...", False)
        cboChartTyp.SelectedIndex = 0
        BootTimer.Enabled = True
    End Sub
#End Region

#Region "Bootvorgang"
    Private Sub BootTimer_Tick(sender As Object, e As EventArgs) Handles BootTimer.Tick
        If intBootCount = 0 Then
            BootTimer.Enabled = False
            GetAllCom()
            BootTimer.Interval = 4000
            BootTimer.Enabled = True
            intBootCount = 1
        ElseIf intBootCount = 1 Then
            CheckComForDevice()
        ElseIf intBootCount = 2 Then
            Application.Exit()
        End If
    End Sub
    Private Sub GetAllCom()
        For Each strPortDevice In My.Computer.Ports.SerialPortNames
            On Error Resume Next
            SerialPort.PortName = strPortDevice
            SerialPort.Open()
            If SerialPort.IsOpen Then
                UpdateTxtStatus(strPortDevice & " als möglich Schnittstelle gefunden.", False)
                SerialPort.Close()
                arrComlist.Add(strPortDevice)
            End If
        Next
    End Sub
    Private Sub CheckComForDevice()
        If ((intDeviceFound = 0) And (intActDevice < arrComlist.Count.ToString)) Then
            If SerialPort.IsOpen = True Then
                SerialPort = New SerialPort
                SerialPort.BaudRate = 115200
                SerialPort.Close()
            End If
            SerialPort.PortName = arrComlist(intActDevice)
            SerialPort.Open()
            UpdateTxtStatus("Verbindung Aufgebaut zu " & arrComlist(intActDevice) & ". Warte auf Verifizierung.", False)
            intActDevice += 1
        Else
            intBootCount = 2
            BootTimer.Interval = 4000
            UpdateTxtStatus("Gerät nicht gefunden! Anwendung wird in 4 Sekunden beendet!", True)
        End If
    End Sub
#End Region

#Region "Empfange serielle Daten"
    Private Sub SerialPort_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles SerialPort.DataReceived 'Bei Datenempfang über serielle Verbindung Kamera
        Dim strDataRec As String = ""
        Try
            Do Until strDataRec.Contains(vbCrLf) Or strDataRec.Contains(vbCr)
                strDataRec &= SerialPort.ReadExisting
            Loop
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
        strDataRec = strDataRec.Replace(vbCrLf, "").Replace(Chr(29), "").Replace(vbCr, "")
        Me.Invoke(Sub() Call DataRecive(strDataRec))
        SerialPort.DiscardInBuffer()
    End Sub
    Private Sub DataRecive(strReciveValue As String)
        If ((strReciveValue.Contains("#CO2-OK;")) And (intDeviceFound = 0)) Then
            BootTimer.Enabled = False
            UpdateTxtStatus("Verbindung zum Gerät hergestellt...", False)
            SerialPort.Write("#CO2-D;")
            UpdateTxtStatus("Anfrage Grenzwerte...", False)
            intDeviceFound = 1
            ButtonControl(0, Color.White)
        ElseIf ((strReciveValue.Contains("#CO2-D,")) And (intDeviceFound = 1)) Then
            strReciveValue = strReciveValue.Replace("#CO2-D,", "").Replace(";", "")
            UpdateTxtStatus("Grenzwerte Empfangen: " & strReciveValue, False)
            Dim aryTextFile() As String = strReciveValue.Split(",")
            lblMinVal.Text = aryTextFile(0)
            intGrenzWert1 = CInt(aryTextFile(1)) - CInt(lblMinVal.Text)
            intGrenzWert2 = CInt(aryTextFile(2)) - CInt(lblMinVal.Text)
            GrenzwertShow(intGrenzWert1, intGrenzWert2)
        ElseIf ((strReciveValue.Contains("#CO2-W,")) And (intDeviceFound = 1)) Then
            strReciveValue = strReciveValue.Replace("#CO2-W,", "").Replace(";", "")
            Dim arrRecValue() As String = strReciveValue.Split(",")
            UpdateTxtStatus("CO2 Warngerät V1.1", False)
            UpdateTxtStatus("CO2: " & arrRecValue(0) & " ppm   /   TVOC: " & arrRecValue(1) & " Volatile organic compounds", False)
            UpdateTxtStatus("Temp: " & arrRecValue(2) & " °C   /   Feuchte: " & arrRecValue(3) & " %", False)
            If cboChartTyp.SelectedIndex = 0 Then
                UpdateAktValue(arrRecValue(0))
                ChartValue.Visible = False
                lbl10sek.Visible = False
            Else
                ChartValue.Visible = True
                lbl10sek.Visible = True
            End If
            DrawChart(arrRecValue(0), arrRecValue(1), arrRecValue(2).Replace(".", ","), arrRecValue(3).Replace(".", ","), False, CInt(lblAktMin.Text), CInt(lblAktMax.Text))
        End If
    End Sub
#End Region

#Region "Status anpassen"
    Private Sub UpdateTxtStatus(strText As String, bolClear As Boolean)
        Static bolLocked As Boolean = False
        If bolClear = False And bolLocked = False Then
            lblStatus3.Text = lblStatus2.Text
            lblStatus2.Text = lblStatus1.Text
            lblStatus1.Text = strText
            lblTimestamp3.Text = lblTimeStamp2.Text
            lblTimeStamp2.Text = lblTimeStamp1.Text
            lblTimeStamp1.Text = System.DateTime.Now.ToString()
        ElseIf bolClear = True And bolLocked = False Then
            lblStatus3.Text = ""
            lblStatus2.ForeColor = Color.Red
            lblStatus2.Text = strText
            lblStatus1.Text = ""
            bolLocked = True
            lblTimestamp3.Text = ""
            lblTimeStamp2.Text = System.DateTime.Now.ToString()
            lblTimeStamp1.Text = ""
        End If
    End Sub
#End Region

#Region "Button Control"
    Private Sub ButtonControl(intFunc As Integer, datacolor As Color)
        If intFunc = 0 Then 'Nach Booten
            cmdClose.Enabled = True
            pnlGruen.Enabled = True
            pnlRot.Enabled = True
            lblMinVal.Enabled = True
            lblMaxVal.Enabled = True
        ElseIf intFunc = 1 Or intFunc = 2 Then
            cmdplus.Enabled = True
            cmdminus.Enabled = True
            cmdFaktor.BackColor = datacolor
        End If
        If intFunc = 2 Then
            lblMinVal.BackColor = datacolor
            lblMaxVal.BackColor = datacolor
        ElseIf intFunc = 3 Then
            cmdplus.Enabled = False
            cmdminus.Enabled = False
            cmdSave.Enabled = False
            cmdSaveCancel.Enabled = False
        End If
    End Sub
    Private Sub ColorPanel_Click(sender As Object, e As EventArgs) Handles pnlRot.Click, pnlGruen.Click, pnlGelb.Click, lblMinVal.Click, lblMaxVal.Click
        Select Case True
            Case sender Is pnlRot Or sender Is pnlGruen
                ButtonControl(2, Color.Gainsboro)
                ButtonControl(1, (sender.backcolor))
            Case sender Is lblMinVal Or sender Is lblMaxVal
                ButtonControl(2, Color.Chocolate)
            Case sender Is pnlGelb
                ButtonControl(2, Color.Gainsboro)
                ButtonControl(3, Color.Gainsboro)
        End Select
    End Sub
    Private Sub PlusMinus_Click(sender As Object, e As EventArgs) Handles cmdplus.Click, cmdminus.Click
        If cmdFaktor.BackColor = Color.Red Then
            Select Case True
                Case sender Is cmdplus
                    GrenzwertShow(intGrenzWert1, intGrenzWert2 + cmdFaktor.Text)
                Case sender Is cmdminus
                    GrenzwertShow(intGrenzWert1, intGrenzWert2 - cmdFaktor.Text)
            End Select
        ElseIf cmdFaktor.backcolor = Color.Lime Then
            Select Case True
                Case sender Is cmdplus
                    GrenzwertShow(intGrenzWert1 + cmdFaktor.Text, intGrenzWert2)
                Case sender Is cmdminus
                    GrenzwertShow(intGrenzWert1 - cmdFaktor.Text, intGrenzWert2)
            End Select
        ElseIf cmdFaktor.backcolor = Color.Chocolate Then
            Select Case True
                Case sender Is cmdplus
                    If lblMinVal.BackColor = Color.Chocolate Then
                        lblMinVal.Text = CInt(lblMinVal.Text) + CInt(cmdFaktor.Text)
                    End If
                Case sender Is cmdminus
                    If ((lblMinVal.BackColor = Color.Chocolate) And (CInt(lblMinVal.Text) - CInt(cmdFaktor.Text) >= 300)) Then
                        lblMinVal.Text = CInt(lblMinVal.Text) - CInt(cmdFaktor.Text)
                    Else
                        UpdateTxtStatus("Grenzwert liegt ausserhalb des gültigen minimalen Bereiches!", False)
                    End If
            End Select
        End If
    End Sub
    Private Sub cmdFaktor_Click(sender As Object, e As EventArgs) Handles cmdFaktor.Click
        If cmdFaktor.Text = 100 Then
            cmdFaktor.Text = 1
        Else
            cmdFaktor.Text = cmdFaktor.Text * 10
        End If
    End Sub
    Private Sub cmdSave_Change(sender As Object, e As EventArgs) Handles lblAktMax.TextChanged, lblAktMin.TextChanged, lblMaxVal.TextChanged
        If cmdFaktor.BackColor <> Color.Gainsboro Then
            cmdSave.Enabled = True
            cmdSaveCancel.Enabled = True
        End If
    End Sub
    Private Sub cmdSave_Click(sender As Object, e As EventArgs) Handles cmdSave.Click, cmdSaveCancel.Click
        ButtonControl(2, Color.Gainsboro)
        ButtonControl(3, Color.Gainsboro)
        Select Case True
            Case sender Is cmdSaveCancel
                SerialPort.Write("#CO2-D;")
                UpdateTxtStatus("Anfrage Grenzwerte...", False)
            Case sender Is cmdSave
                UpdateTxtStatus("Neue Grenzwerte werden übertragen...", False)
                SerialPort.Write("#CO2-S," & lblMinVal.Text & "," & lblAktMin.Text & "," & lblAktMax.Text & ";")
                UpdateTxtStatus("Empfange aktuelle Grenzwerte nach dem Speichern...", False)
                SerialPort.Write("#CO2-D;")
        End Select
    End Sub
#End Region

#Region "Grenzwerte anpassen"
    Private Sub GrenzwertShow(intGrenzeVal1 As Integer, intGrenzeVal2 As Integer)
        If intGrenzeVal1 > 46 And intGrenzeVal1 < intGrenzeVal2 And intGrenzeVal2 < 854 Then
            'Farben anpassen
            pnlGruen.BackColor = Color.Lime
            pnlGelb.BackColor = Color.Yellow
            pnlRot.BackColor = Color.Red
            'Anpassung Balken
            pnlGruen.Location = New Point(0, pnlGruen.Location.Y)
            pnlGruen.Width = intGrenzeVal1
            pnlGelb.Location = New Point(intGrenzeVal1, pnlGelb.Location.Y)
            pnlGelb.Width = 900 - intGrenzeVal1 - (900 - intGrenzeVal2)
            pnlRot.Location = New Point(intGrenzeVal2, pnlRot.Location.Y)
            pnlRot.Width = 900 - intGrenzeVal2
            'Anpassung Variablen
            intGrenzWert1 = intGrenzeVal1
            intGrenzWert2 = intGrenzeVal2
            'Anpassung Textfeld und Balken
            pnlAktMin.Location = New Point(intGrenzeVal1 - 2, pnlAktMin.Location.Y)
            pnlAktMax.Location = New Point(intGrenzeVal2 - 2, pnlAktMax.Location.Y)
            lblAktMin.Location = New Point(intGrenzeVal1 - 42, lblAktMin.Location.Y)
            lblAktMax.Location = New Point(intGrenzeVal2 + 2, lblAktMax.Location.Y)
            'Werte anzeigen
            ShowThreshold()
        Else
            UpdateTxtStatus("Grenzwert liegt ausserhalb des gültigen Bereich!", False)
        End If
    End Sub
    Private Sub lblMinVal_TextChanged(sender As Object, e As EventArgs) Handles lblMinVal.TextChanged
        If lblMinVal.Text <> "" Then
            lblMaxVal.Text = CInt(lblMinVal.Text) + 900
            ShowThreshold()
        End If
    End Sub
    Private Sub ShowThreshold()
        Dim intMaxSpace As Integer = CInt(pnlRot.Width) + CInt(pnlGelb.Width) + CInt(pnlGruen.Width)
        Dim intAktSpace = CInt(lblMaxVal.Text) - CInt(lblMinVal.Text)
        lblAktMin.Text = (intAktSpace * intGrenzWert1 / intMaxSpace) + CInt(lblMinVal.Text)
        lblAktMax.Text = (intAktSpace * intGrenzWert2 / intMaxSpace) + CInt(lblMinVal.Text)
    End Sub
    Private Sub UpdateAktValue(intValue As Integer)
        lblAktVal.Text = intValue
        If ((intValue - CInt(lblMinVal.Text) - 26) <= 26) Then
            lblAktVal.Location = New Point(1, lblAktVal.Location.Y)
            panAktVal.Location = New Point(26, panAktVal.Location.Y)
            lblAktVal.BackColor = Color.Lime
        ElseIf ((intValue - CInt(lblMinVal.Text) - 26) >= 874) Then
            lblAktVal.Location = New Point(847, lblAktVal.Location.Y)
            panAktVal.Location = New Point(872, panAktVal.Location.Y)
            lblAktVal.BackColor = Color.Red
        Else
            lblAktVal.Location = New Point(intValue - CInt(lblMinVal.Text) - 26, lblAktVal.Location.Y)
            panAktVal.Location = New Point(intValue - CInt(lblMinVal.Text) - 2, panAktVal.Location.Y)
            lblAktVal.BackColor = Color.White
        End If
    End Sub
#End Region

#Region "Chart anzeigen"
    Public Sub DrawChart(sinCO2 As Single, sinTVOC As Single, sinTemp As Single, sinRF As Single, bolReset As Boolean, intMinGrenz As Integer, intMaxGrenz As Integer)
        'Array Listen definieren
        Static arrC02_10 As New List(Of Single)
        Static arrC02_1000 As New List(Of Single)
        Static arrTVOC_10 As New List(Of Single)
        Static arrTVOC_1000 As New List(Of Single)
        Static arrTemp_10 As New List(Of Single)
        Static arrTemp_1000 As New List(Of Single)
        Static arrRF_10 As New List(Of Single)
        Static arrRF_1000 As New List(Of Single)
        Static arrXVals As New List(Of Single)
        Dim arrYVals As New List(Of Single)
        'Weitere Variablen für Graphen
        Dim arrGreen As New List(Of Integer) From {intMinGrenz, intMinGrenz}
        Dim arrRed As New List(Of Integer) From {intMaxGrenz, intMaxGrenz}
        Dim arr0to1000 As New List(Of Integer) From {0, 1000}
        'Reset
        If bolReset = True Then
            arrC02_10.Clear()
            arrC02_1000.Clear()
            arrTVOC_10.Clear()
            arrTVOC_1000.Clear()
            arrTemp_10.Clear()
            arrTemp_1000.Clear()
            arrRF_10.Clear()
            arrRF_1000.Clear()
            arrXVals.Clear()
            Exit Sub
        End If
        'Werte zwischenspeichern
        arrC02_10.Add(sinCO2)
        arrTVOC_10.Add(sinTVOC)
        arrTemp_10.Add(sinTemp)
        arrRF_10.Add(sinRF)
        'Schreibe Werte für Graph
        If arrC02_10.Count = 10 Or arrTVOC_10.Count = 10 Or arrTemp_10.Count = 10 Or arrRF_10.Count = 10 Then
            arrC02_1000.Add(Math.Round(arrC02_10.Sum / 10, 0))
            arrTVOC_1000.Add(Math.Round(arrTVOC_10.Sum / 10, 0))
            arrTemp_1000.Add(Math.Round(arrTemp_10.Sum / 10, 2))
            arrRF_1000.Add(Math.Round(arrRF_10.Sum / 10, 2))
            arrC02_10.Clear()
            arrTVOC_10.Clear()
            arrTemp_10.Clear()
            arrRF_10.Clear()
            If arrXVals.Count < 1000 Then
                arrXVals.Add(arrXVals.Count)
            End If
            If arrC02_1000.Count >= 2 Then
                cboChartTyp.Enabled = True
                cmdResetChart.Enabled = True
            End If
        End If
        If arrC02_1000.Count > 1000 Or arrTVOC_1000.Count > 1000 Or arrTemp_1000.Count > 1000 Or arrRF_1000.Count > 1000 Then
            arrC02_1000.RemoveAt(0)
            arrTVOC_1000.RemoveAt(0)
            arrTemp_1000.RemoveAt(0)
            arrRF_1000.RemoveAt(0)
        End If
        'setup the chart
        If cboChartTyp.SelectedIndex > 0 Then
            With ChartValue.ChartAreas(0)
                .AxisX.MajorGrid.LineColor = Color.LightBlue
                .AxisY.MajorGrid.LineColor = Color.LightGray
            End With
            'X-Achse
            Dim intXMax As Integer = RoundUpDown(arrXVals.Count - 1, 1)
            Dim intXInterval As Integer = intXMax / 10
            With ChartValue.ChartAreas(0)
                .AxisX.Minimum = 0
                .AxisX.Maximum = intXMax
                .AxisX.Interval = intXInterval
            End With
            'y-Achse
            Dim intYMin As Integer = 0
            Dim intYmax As Integer = 0
            Dim intYInterval As Integer = 0
            If cboChartTyp.SelectedIndex = 1 Then 'CO2
                intYMin = RoundUpDown(Math.Truncate(arrC02_1000.Min), -1)
                intYmax = RoundUpDown(Math.Truncate(arrC02_1000.Max), 1)
                intYInterval = (intYmax - intYMin) / 10
                arrYVals = arrC02_1000
            ElseIf cboChartTyp.SelectedIndex = 2 Then 'TVOC
                intYMin = RoundUpDown(Math.Truncate(arrTVOC_1000.Min), -1)
                intYmax = RoundUpDown(Math.Truncate(arrTVOC_1000.Max), 1)
                intYInterval = (intYmax - intYMin) / 10
                arrYVals = arrTVOC_1000
            ElseIf cboChartTyp.SelectedIndex = 3 Then 'Temp
                intYMin = RoundUpDown(Math.Truncate(arrTemp_1000.Min), -1)
                intYmax = RoundUpDown(Math.Truncate(arrTemp_1000.Max), 1)
                intYInterval = (intYmax - intYMin) / 10
                arrYVals = arrTemp_1000
            ElseIf cboChartTyp.SelectedIndex = 4 Then 'Feuchte
                intYMin = RoundUpDown(Math.Truncate(arrRF_1000.Min), -1)
                intYmax = RoundUpDown(Math.Truncate(arrRF_1000.Max), 1)
                intYInterval = (intYmax - intYMin) / 10
                arrYVals = arrRF_1000
            End If
            With ChartValue.ChartAreas(0)
                .AxisY.Minimum = intYMin
                .AxisY.Maximum = intYmax
                .AxisY.Interval = intYInterval
            End With
            'Neues Diagramm
            ChartValue.Series.Clear()
            ChartValue.Series.Add("Value")
            'Werte zeichen Blue
            With ChartValue.Series(0)
                .IsVisibleInLegend = False
                .ChartType = DataVisualization.Charting.SeriesChartType.Line
                .Color = Color.Blue
                .BorderDashStyle = ChartDashStyle.Dash
                .Points.DataBindXY(arrXVals, arrYVals)
            End With
            If cboChartTyp.SelectedIndex = 1 Then 'Nur bei C02 Wert
                ChartValue.Series.Add("Min")
                ChartValue.Series.Add("Max")
                'Werte zeichen Green
                With ChartValue.Series(1)
                    .IsVisibleInLegend = False
                    .ChartType = DataVisualization.Charting.SeriesChartType.Line
                    .Color = Color.Green
                    .BorderDashStyle = ChartDashStyle.Dash
                    .Points.DataBindXY(arr0to1000, arrGreen)
                End With
                'Werte zeichen Red
                With ChartValue.Series(2)
                    .IsVisibleInLegend = False
                    .ChartType = DataVisualization.Charting.SeriesChartType.Line
                    .Color = Color.Red
                    .BorderDashStyle = ChartDashStyle.Dash
                    .Points.DataBindXY(arr0to1000, arrRed)
                End With
            End If
        End If
    End Sub
    Private Sub cmdResetChart_Click(sender As Object, e As EventArgs) Handles cmdResetChart.Click
        cboChartTyp.SelectedIndex = 0
        DrawChart(0, 0, 0, 0, True, 0, 0)
        cboChartTyp.Enabled = False
        cmdResetChart.Enabled = False
    End Sub
    Function RoundUpDown(sinZahl As Single, intOffset As Single)
        If ((Math.Round(sinZahl / 10, 0) <> sinZahl / 10) And (sinZahl > 0) And (intOffset < 0)) Then
            intOffset = 0
        ElseIf ((Math.Round(sinZahl / 10, 0) <> sinZahl / 10) And (sinZahl < 0) And (intOffset > 0)) Then
            intOffset = 0
        End If
        sinZahl = Math.Truncate(sinZahl / 10)
        sinZahl = sinZahl + intOffset
        sinZahl = sinZahl * 10
        Return sinZahl
    End Function
#End Region

#Region "Form schließen"
    Private Sub cmdClose_Click(sender As Object, e As EventArgs) Handles cmdClose.Click
        If SerialPort.IsOpen Then
            SerialPort.Write("#CO2-Exit;")
        End If
        UpdateTxtStatus("Verbindung wird getrennt und Anwendung geschlossen...", True)
        intBootCount = 2
        BootTimer.Enabled = True
    End Sub
    Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        If intBootCount < 2 Then
            e.Cancel = True
        End If
    End Sub
#End Region

End Class

Quellcode Arduino

Hier der Arduino Quellcode.
Der Quellcode wird durch das Einbinden in die Homepage automatisch angepasst. Es werden z. B. Zeilenumbrüche eingefügt. Er kann somit nicht 1 zu 1 in die Arduino IDE übernommen werden. Die Arduino IDE (IDE = Integrated Development Environment) ist eine Software zum Programmieren von Arduino-Mikrocontrollern. Sie ermöglicht es, Programme mit der Programmiersprache C oder C++ zu schreiben und auf die Arduino-Boards zu übertragen. Der Arduino-Mikrocontroller führt diese dann aus.
Im Download-Bereich könnt Ihr das Arduino-Projekt herunterladen und ggf. nach euren Wünschen anpassen und erweitern.

#include <EEPROM.h>
#include "Adafruit_CCS811.h"
Adafruit_CCS811 ccs;
#include <SHT21.h>  // include SHT21 library
SHT21 sht; 

int ledRot = 9;//6
int ledGelb = 10;//5
int ledGruen = 11;//4
long myTimer = 0;
long myTimeout = 1000;
int intConnectionOk = 0;
int arrGrenzValue[3] = { 0 };
int intCO2 = 0;
int intTVOC = 0;
float temp;   // variable to store temperature
float humidity; // variable to store hemidity

void setup()
{
  Serial.begin(115200); // opens serial port, sets data rate to 115200 bps  
  pinMode(ledRot, OUTPUT); 
  pinMode(ledGelb, OUTPUT);
  pinMode(ledGruen, OUTPUT);
  readEEprom(0);
  /*C02 Sensor starten*/
  if(!ccs.begin())
  {
    while(1){ShowLed (4);}//Failed to start sensor! Please check your wiring.
  }
  while(!ccs.available())// Wait for the sensor to be ready
  {
    ShowLed (1);
    ShowLed (2);
    ShowLed (3);
    ShowLed (2);
  }
  /*Begin Wire(I2C) SHT21*/
  Wire.begin();
}

void loop()
{
  if (Serial.available()) serialEvent();
  if (millis() > myTimeout + myTimer )
  {
    myTimer = millis();
    /*Von Sensor lesen*/ 
    if(ccs.available())
    {
      if(!ccs.readData())
      {
        intCO2 = ccs.geteCO2();
        intTVOC = ccs.getTVOC();
      }else
      {
        for (int i=0;i<2;i++)
        {
          ShowLed (4);
        }
      }
    }
    /*Von Sensor lesen*/ 
    if (intConnectionOk == 0)
    {
      Serial.println ("#CO2-OK;");
    }
    else if (intConnectionOk == 1)
    {
      readEEprom(0);
      Serial.println("#CO2-D," + String(arrGrenzValue[0],DEC) + "," + String(arrGrenzValue[1],DEC) + "," + String(arrGrenzValue[2],DEC) + ";");
      intConnectionOk = 2;
    }
    else if (intConnectionOk == 2)
    {
      temp = sht.getTemperature();  // get temp from SHT 
      humidity = sht.getHumidity(); // get temp from SHT
      Serial.println("#CO2-W," + String(intCO2,DEC) + "," + String(intTVOC,DEC) + "," + temp + "," + humidity + ";");
    }

    /*LEDS ansteuern*/
    if (intCO2 >= arrGrenzValue[2])
    {
      ShowLed (3);//ROT
    }else if  ((intCO2 < arrGrenzValue[2])&&(intCO2 >= arrGrenzValue[1]))
    {
      ShowLed (2);//GELB
    }else if (intCO2 < arrGrenzValue[1])
    {
      ShowLed (1);//Grün
    }
    /*LEDS ansteuern*/
  }
}

/*//////String Abfrage\\\\\\*/
void serialEvent()
{
  String strSerialReceive;
  if (Serial.findUntil("#", ";"))
  {
    strSerialReceive = Serial.readStringUntil(';');//Grenzwerte auslesen Auslesen und senden.
    if (strSerialReceive == "CO2-D")
    {
      myTimer = millis();
      intConnectionOk = 1;
    }
    else if (strSerialReceive == "CO2-Exit")//Programm beenden und für neue Verbindung vorbereiten
    {
      myTimer = millis();
      intConnectionOk = 0;
    }
    else if (strstr(strSerialReceive.c_str(), "CO2-S,") != NULL)//Neue Grenzwerte empfangen und Speichern im EE
    {
      myTimer = millis();
      strSerialReceive.replace("CO2-S,","");
      StrToInt (strSerialReceive);
      ArrayToEEprom();
    }
  }
}

/*Led ansteuern*/
 void ShowLed (int intLedSwitch)// 1 = Grün, 2 = Gelb, 3 = Rot, 4 = Rot 1mal blinken
 {
  static int intOldLed = 0;
  int arrLedFromToOff[] = {0,0,0};
  if (intLedSwitch < 4)
  {  
    if (intOldLed == 0)
    {
      switch (intLedSwitch)
      {
        case 1:
          analogWrite(ledGruen, 255);
        break;
        case 2:
          analogWrite(ledGelb, 255);
        break;
        case 3:
          analogWrite(ledRot, 255);
        break;
      }
      intOldLed = intLedSwitch;
    }
    else
    {
      if (intOldLed != intLedSwitch)
      {
        if (intOldLed < intLedSwitch)
        {
          arrLedFromToOff[1] = intOldLed + 1;
        }
        if (intOldLed > intLedSwitch)
        {
          arrLedFromToOff[1] = intOldLed - 1;
        }
        arrLedFromToOff[0] = intOldLed;
        intOldLed = arrLedFromToOff[1];
        for (int intReadArr = 0 ; intReadArr <3; intReadArr++)
        {
          if (arrLedFromToOff[intReadArr]==1)
          {
            arrLedFromToOff[2]= 3;
            break;
          }else if (arrLedFromToOff[intReadArr]==3)
          {
            arrLedFromToOff[2]= 1;
            break;
          }
        }
        for (int intLedToArr = 0; intLedToArr < 3; intLedToArr++)
        {
          switch (arrLedFromToOff[intLedToArr])
          {
            case 1:
              arrLedFromToOff[intLedToArr] = ledGruen;
            break;
            case 2:
              arrLedFromToOff[intLedToArr] = ledGelb;
            break;
            case 3:
              arrLedFromToOff[intLedToArr] = ledRot;
            break;
          }
        }
        int test;
        for (int helligkeit =0; helligkeit <= 255; helligkeit = helligkeit +1)
        {
          analogWrite(arrLedFromToOff[2], 0);
          analogWrite(arrLedFromToOff[0],255- helligkeit);
          analogWrite(arrLedFromToOff[1], helligkeit);
          delay (1);
          test = helligkeit;
        } 
        Serial.println(test);    
      }
    }
  }
  else
  {
    analogWrite(ledGruen, 0);
    analogWrite(ledGelb, 0);
    analogWrite(ledRot, 255);
    delay(100);
    analogWrite(ledRot, 0);
    delay(100);
  } 
}

/*Von EEProm lesen mit Startadresse*/
int readEEprom (int startadr)
{
  int intEEAddOffset = 0;
  for (int intReadLay = 0; intReadLay < 3; intReadLay++)
  {
    arrGrenzValue[intReadLay]=(EEPROM.read(intEEAddOffset)*1000)+(EEPROM.read(intEEAddOffset+1)*100)+(EEPROM.read(intEEAddOffset+2)*10)+(EEPROM.read(intEEAddOffset+3));
    intEEAddOffset = intEEAddOffset + 4;
  }
}

/*String in IntergerArray umwandeln*/
void StrToInt(String strValue)
{
  char delimiter[] = ",";
  char *ptr;
  int i = 0;
  char cstrValue[strValue.length() + 1];
  strValue.toCharArray(cstrValue, strValue.length() + 1);
  ptr = strtok(cstrValue, delimiter);
  while (ptr != NULL)
  {
    arrGrenzValue[i] = atoi(ptr);
    i++;
    ptr = strtok(NULL, delimiter);
  }
}

/*Array in EEprom Speichern wenn Werte unterschiedlich*/
void ArrayToEEprom()
{
  int intEEAddOffset = 0;
  byte arrSingelVal[4] = { 0 };
  for (int intArrLay = 0; intArrLay < 3; intArrLay++)
  {
    arrSingelVal[0] = (arrGrenzValue[intArrLay] / 1000) % 10;
    arrSingelVal[1] = (arrGrenzValue[intArrLay] / 100) % 10;
    arrSingelVal[2] = (arrGrenzValue[intArrLay] / 10) % 10;
    arrSingelVal[3] = arrGrenzValue[intArrLay] % 10;

    for (int intSingelVal = 0; intSingelVal < 4; intSingelVal++)
    {
      if (EEPROM.read(intEEAddOffset) != arrSingelVal[intSingelVal])
      {
        EEPROM.write(intEEAddOffset, arrSingelVal[intSingelVal]);
      }
      intEEAddOffset ++;
    }
  }
}

Download

Die App sowie das Arduino Projekt könnt Ihr direkt von unserer Seite downloaden. Euch steht natürlich frei, die ganzen Elemente nach euren Interessen anzupassen.

Bitte Datenschutzerklärung beachten!

Vor, während und nach dem Spielen, werden Daten mit unserem Server ausgetauscht. In unserer Datenschutzerklärung erhaltet Ihr hierzu weitere Informationen.