Creating a waveform plot from temperature sensor data in C#
Tutorial
Create a waveform plot from temperature sensor data in C# using a collection of 5 different sensors and LightningChart .NET
Introduction
Today we will talk about waveforms and their usefulness. As always, we will accompany this article with a programming exercise using C#, and the Lightning Chart .NET library. If this is your first time with LC .NET, I recommend that you download the interactive examples application, with which you can generate this and other charts in WPF and other projects.
The application has a free trial; however, you can download the project attached to this article. For this project, we will use the information collected by 5 sensors. Each sensor corresponds to a degree of temperature, and in turn, each sensor has a slight variation in the precision of its data.
We will use that variation along with the corresponding temperature, to generate a waveform that shows these differences between sensors. That said, let’s begin!
Waveform Plot
A waveform chart is a type of graph that visually represents an audio signal or wave. It is used to show the variation over time of a continuous signal, usually an audio waveform (such as a sound recording).
This type of graph shows how the amplitude of the signal changes over time, allowing you to visually see fluctuations in the intensity of the wave. Some key features of a waveform chart:
- Horizontal axis (X): Represents time, usually in seconds or milliseconds.
- Vertical axis (Y): Represents the amplitude of the signal, which indicates the intensity or volume of the wave at any given moment.
- Waveform: Shows how the audio signal moves from one side to the other, with peaks (when the amplitude is high) and valleys (when the amplitude is low or zero).
Waveform charts are widely used in audio editing, music production, and signal analysis software, as they provide a clear way to visualize the structure and changes in audio signals. They are also used in other contexts, such as visualizing data from other types of continuous signals.
A waveform chart is not limited to audio alone, it can also be applied to show variations of other continuous signals over time, such as temperature.
When you use a waveform chart to show temperatures, what you would be doing is depicting how the temperature changes over a specific period. In this case, the vertical (Y) axis would show temperatures on a scale, such as degrees Celsius (°C), Fahrenheit (°F), or Kelvin (K), depending on the unit you are using.
The waveform chart would show how the temperature rises in the morning, peaks at midday, and then falls in the afternoon and evening. This graph could help you analyze weather patterns, adjust heating or cooling systems, or predict future temperature behavior.
How do I interpret the data using waveform charts?
There are several properties that we must know to interpret these charts:
- Amplitude: Shows the value of the variable at an instant.
- Frequency and Period: Frequency is how many cycles occur each time, and the period is the time it takes to complete a cycle.
- Peaks and Valleys: Peaks are the maximum value, and valleys are the minimum value.
In this type of chart, we will find several types of waves:
- Sine waves: For periodic signals.
- Square waves: For digital signals.
- Irregular signals: For non-periodic or fluctuating data.
To analyze trends, variations in the peaks must be located.
How does temperature monitoring work with sensors?
Temperature monitoring with sensors involves measuring and tracking the temperature of an environment or object using devices called temperature sensors. These sensors detect changes in temperature and convert them into an electrical signal, which can then be read and analyzed by a system or device.
Types of Temperature Sensors and Data Collection:
- Thermistors: These are temperature-sensitive resistors. Their resistance changes with temperature, and this variation can be measured to determine the temperature.
- Thermocouples: These are composed of the union of two different metals. The temperature difference between the unions generates a voltage, which is proportional to the temperature difference.
- RTDs (Resistance Temperature Detectors): These use a metal (usually platinum) whose resistance increases with temperature. The change in resistance is measured to determine the temperature.
- Infrared Sensors: These sensors detect infrared radiation emitted by an object. The amount of radiation is correlated with the temperature of the object, allowing the temperature to be measured without direct contact.
- Semiconductor Sensors: These are used in microelectronics and provide temperature data by measuring a small change in voltage across a semiconductor material.
Project Overview
Feel free to download the ZIP file to follow this waveform plot project. The synthetic sensor dataset contains over 3000 samples, each representing a set of sensor readings. It consists of six columns: Temperature, Sensor1, Sensor2, Sensor3, Sensor4, and Sensor5.
The dataset is designed to mimic a scenario where temperature readings are influenced by multiple independent sensor measurements. The values of the independent variables and the added noise introduce variability into the temperature readings.
Access the dataset from Kaggle.
Download the project to follow the tutorial
Local Setup
- OS: 32-bit or 64-bit Windows Vista or later, Windows Server 2008 R2 or later.
- DirectX: 9.0c (Shader model 3 and higher) or 11.0 compatible graphics adapter.
- Visual Studio: 2022 for development, not required for deployment.
- Platform .NET Framework: installed version 8.0 or newer.
Now go to the next URL and download LightningChart .NET. You’ll then be redirected to a sign-in form where you’ll have to complete a simple sign-up process to get access to your LightningChart account.
After signing in to your account, you can download the SDK “free trial” version that allows you to use important features for this tutorial. When you download the SDK, you’ll have a .exe file like this:
The installation will be a typical Windows process, so please continue with it until it is finished. After the installation, you will see the following programs:
License Manager
In this application, you will see the purchase options. All the projects that you will create with this trial SDK will be available for future developments with all features enabled.
Code Behind
We will start by initializing our objects that will contain our charts:
public void Dispose()
{
gridChartWaveform1.Children.Clear();
gridChartWaveform2.Children.Clear();
gridChartWaveform3.Children.Clear();
gridChartWaveform4.Children.Clear();
gridChartWaveform5.Children.Clear();
}
We will first clean up the XAML components that will contain the charts:
<Grid Grid.Row="0" Name="gridChartWaveform1"/>
<GridSplitter Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Stretch" ShowsPreview="True" Height="5"/>
<Grid Grid.Row="1" Name="gridChartWaveform2"/>
<GridSplitter Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Stretch" ShowsPreview="True" Height="5"/>
<Grid Grid.Row="2" Name="gridChartWaveform3"/>
<GridSplitter Grid.Row="3" VerticalAlignment="Center" HorizontalAlignment="Stretch" ShowsPreview="True" Height="5"/>
<Grid Grid.Row="3" Name="gridChartWaveform4"/>
<GridSplitter Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Stretch" ShowsPreview="True" Height="5"/>
<Grid Grid.Row="4" Name="gridChartWaveform5"/>
Now we will create our Lightning Chart objects:
private void InitializeCharts()
{
// Initialize all the chartWaveformSensor objects if not already initialized
if (_chartWaveformSensor1 == null) _chartWaveformSensor1 = new LightningChart();
if (_chartWaveformSensor2 == null) _chartWaveformSensor2 = new LightningChart();
if (_chartWaveformSensor3 == null) _chartWaveformSensor3 = new LightningChart();
if (_chartWaveformSensor4 == null) _chartWaveformSensor4 = new LightningChart();
if (_chartWaveformSensor5 == null) _chartWaveformSensor5 = new LightningChart();
}
To create multiple charts at once, we will add each of the LightningChart objects to the “chartWaveformSensors” list, this way, we can use a loop to generate a chart for each object:
private List<LightningChart> CreateWaveformCharts()
{
chartWaveformSensors.Add(_chartWaveformSensor1);
chartWaveformSensors.Add(_chartWaveformSensor2);
chartWaveformSensors.Add(_chartWaveformSensor3);
chartWaveformSensors.Add(_chartWaveformSensor4);
chartWaveformSensors.Add(_chartWaveformSensor5);
int chartIndex = 1;
foreach (var chartWaveformSensor in chartWaveformSensors)
{
CreateWaveformChart(chartWaveformSensor, chartIndex);
chartIndex++;
}
return chartWaveformSensors;
}
Code Behind – CreateWaveformChart
This method allows you to create and display graphs to represent the temperature signal from a sensor, configuring both the appearance and axes of the graph and ensuring that it is displayed in the correct location in the user interface.
- Disable rendering temporarily:
BeginUpdate()is called to disable rendering of the chart while its properties are updated, which improves performance.
- Chart Settings: Set the chart name to “Temperature Waveform chart”. Remove the chart title text (
chartWaveformSensor.Title.Text = ""), thus hiding any default text.
chartWaveformSensor.BeginUpdate();
chartWaveformSensor.ChartName = "Temperature Waveform chart";
chartWaveformSensor.Title.Text = "";
- Chart placement: Depending on the value of chartIndex (a number between 1 and 5), the chart is added to one of several grids (gridChartWaveform1, gridChartWaveform2, etc.) in the user interface. This arranges the chart in different areas of the window.
switch (chartIndex)
{
case 1:
gridChartWaveform1.Children.Add(chartWaveformSensor);
break;
case 2:
gridChartWaveform2.Children.Add(chartWaveformSensor);
break;
case 3:
gridChartWaveform3.Children.Add(chartWaveformSensor);
break;
case 4:
gridChartWaveform4.Children.Add(chartWaveformSensor);
break;
case 5:
gridChartWaveform5.Children.Add(chartWaveformSensor);
break;
}
- Creating a data series: A data series called
SampleDataSeriesis created that will represent the discrete sensor signal, setting the line color to a random value and disabling user interaction with the series. The series is added to the graph.
SampleDataSeries sds = new SampleDataSeries(chartWaveformSensor.ViewXY, chartWaveformSensor.ViewXY.XAxes[0], chartWaveformSensor.ViewXY.YAxes[0])
{
AllowUserInteraction = false
};
sds.Title.Text = $"Sensor {chartIndex}";
sds.LineStyle.Color = GetRandomColor();
chartWaveformSensor.ViewXY.SampleDataSeries.Add(sds);
- Axis Configuration: The X axis is titled “Time”, representing time. The Y axis is titled “Temperature”, representing temperature.
- Enable rendering: Finally,
EndUpdate()is called to allow the chart to be rendered with the new settings.
// Configure x-axis.
chartWaveformSensor.ViewXY.XAxes[0].Title.Text = "Time";
// Configure y-axis.
chartWaveformSensor.ViewXY.YAxes[0].Title.Text = "Temperature";
// Allow chart rendering.
chartWaveformSensor.EndUpdate();
GetSignal
This code generates a combined signal of two sine waves, scales them based on sensor data, and then combines them with temperature data before returning the result.
Input: The GetSignal method receives three parameters:
samplingFrequency: the sampling frequency of the signal,temperatureData: an array with the temperature data,sensorData: an array with sensor data.
GetSignal(double samplingFrequency, double[] temperatureData, double[] sensorData)
Signal Generation
A SignalGenerator object is created with the sampling frequency.
SignalGenerator gen = new SignalGenerator
{
SamplingFrequency = (int)samplingFrequency
};
Two sinusoidal components are added to the generator. The first wave has an amplitude of 10 and a frequency of 10 Hz.
SineComponent sine1 = new SineComponent
{
Amplitude = 10,
Frequency = 10
};
gen.WaveformSines.Add(sine1);
The second wave has an amplitude of 2 and a frequency of 400 Hz.
SineComponent sine2 = new SineComponent
{
Amplitude = 2,
Frequency = 400
};
gen.WaveformSines.Add(sine2);
Sample Generation
The number of samples (samplesCount) is calculated based on time (timeLenSec = 1 second) and sampling rate. Samples of the sine waves are generated and stored in samples.
int samplesCount = (int)(timeLenSec * samplingFrequency);
double[][] samples = gen.GenerateBlock(samplesCount);
int sampleCount = 0;
Adjustment with Sensor Data
Each sample of the generated waveform is multiplied by the corresponding sensorData values, modifying the original samples.
foreach (double item in samples[0])
{
var sample = samples[0][sampleCount];
samples[0][sampleCount] = sample * sensorData[sampleCount];
sampleCount++;
}
Combine Temperature and Signal Data
A result array of the same length as temperatureData is created. Each temperatureData value is then summed with the corresponding adjusted sample of the generated signal, storing the result in the result.
double[] result = new double[temperatureData.Length];
int count = 0;
foreach(double item in temperatureData)
{
var res = item + samples[0][count];
result[count] = res;
count++;
}
Output
The method returns the result array, which is the sum of the temperature data with the fitted signals.
CalculateSignalGenerator
This method gets the data (temperature and sensor) from a data set, generates a modified signal with that data, and then updates a chart to display that signal. During this process, it temporarily disables the rendering of the chart, configures the data series with the new signal, adjusts the chart view, and finally enables rendering again.
The method is aimed at:
- Get input data.
- Generate a modified signal.
- Update a chart with the newly generated signal.
Obtaining the graph and data
- A chart is selected from the charts list using the
chartIndexindex. - dataSet: A data set is obtained using the
getData()function, which returns a list of data. - samplingFrequency: Obtains the number of elements in the second data set (
dataSet[0] - Temperature), which defines the sampling frequency. - temperatureData: The first data set (
dataSet[0]), representing the temperatures, is extracted and converted to an array (ToArray()). - sensorData: The dataset corresponding to the selected chart is extracted, i.e.
dataSet[chartIndex+1], and is also converted to an array. A 1 is added, because the first index corresponds to the array of temperature values. - Signal generation: The GetSignal function is called to obtain the generated signal data, using
samplingFrequency,temperatureData, andsensorDataparameters.
var chart = charts[chartIndex];
var dataSet = getData();
var samplingFrequency = dataSet[0].Count;
var temperatureData = dataSet[0].ToArray();
var sensorData = dataSet[chartIndex+1].ToArray();
double[] signalData = GetSignal(samplingFrequency, temperatureData, sensorData);
Setting up the data series
- The first data series (
SampleDataSeries[0]) is selected in the chart. - The
SamplingFrequencyproperty is updated with the previously calculated sampling frequency. FirstSampleTimeStampis set to the first sampling time (which is the inverse of the sampling frequency since the frequency is assumed to be 1/s).SamplesDoubleis assigned to thesignalDataarray, which contains the values of the generated signal.
SampleDataSeries sds = chart.ViewXY.SampleDataSeries[0];
sds.SamplingFrequency = samplingFrequency;
sds.FirstSampleTimeStamp = 1.0 / samplingFrequency;
sds.SamplesDouble = signalData;
Adjusting the graph view
ZoomToFit automatically adjusts the graph view so that the newly added data (in this case, the generated signal) is displayed correctly in the window.
chart.ViewXY.ZoomToFit();
Conclusion
The waveform chart is quite useful, since thanks to the shape it represents, it allows us to immediately identify the variations in the behavior of a set of data over time. It is a two-dimensional chart on a Cartesian plane.
In this case, we use a series of dotted lines and generate a third value. The third value increases or decreases with the aim of forming a wave. In this case, we create these values randomly with the help of the Sine Waveform tool, which will help us shape our line.
To these values, we multiply the value of each sensor, with the aim of generating variations in our waves:
Continue learning with LightningChart
Best OxyPlot Alternative in 2026: GPU Rendering, 3D Charts, Commercial Support
OxyPlot has been a reliable reference point in the .NET scientific and engineering charting space for over a decade. MIT-licensed, platform-neutral in its rendering model (which is how it achieves coverage across WPF, WinForms, Xamarin, Avalonia, and MAUI from a...
7 Best Plotly.js Alternatives in 2026: Faster, Lighter, No Context Limits
Plotly.js holds a unique position in the JavaScript charting ecosystem. Data scientists already know it from Python, when Plotly.py generates a chart in a Jupyter notebook, it's Plotly.js rendering it in the browser. That continuity between languages is genuinely...
7 Best Highcharts Alternatives in 2026: Faster, Cheaper, and More Capable
Highcharts has been a reliable workhorse for enterprise JavaScript charts since 2009. Solid documentation, broad chart type coverage, WCAG accessibility that's genuinely best-in-class. A lot of teams have built a lot of dashboards on it over the years. But teams also...
