diff --git a/src/binarystreamreader.cpp b/src/binarystreamreader.cpp
new file mode 100644
--- /dev/null
+++ b/src/binarystreamreader.cpp
@@ -0,0 +1,190 @@
+/*
+  Copyright © 2016 Hasan Yavuz Özderya
+
+  This file is part of serialplot.
+
+  serialplot is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  serialplot is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with serialplot.  If not, see .
+*/
+
+#include 
+#include 
+
+#include "binarystreamreader.h"
+#include "floatswap.h"
+
+BinaryStreamReader::BinaryStreamReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) :
+    AbstractReader(device, channelMan, parent)
+{
+    paused = false;
+    skipByteRequested = false;
+    sampleCount = 0;
+
+    _numOfChannels = _settingsWidget.numOfChannels();
+    connect(&_settingsWidget, &BinaryStreamReaderSettings::numOfChannelsChanged,
+            this, &BinaryStreamReader::numOfChannelsChanged);
+    connect(&_settingsWidget, &BinaryStreamReaderSettings::numOfChannelsChanged,
+            this, &BinaryStreamReader::onNumOfChannelsChanged);
+
+    // initial number format selection
+    onNumberFormatChanged(_settingsWidget.numberFormat());
+    connect(&_settingsWidget, &BinaryStreamReaderSettings::numberFormatChanged,
+            this, &BinaryStreamReader::onNumberFormatChanged);
+
+    // enable skip byte button
+    connect(&_settingsWidget, &BinaryStreamReaderSettings::skipByteRequested,
+            [this]()
+            {
+                skipByteRequested = true;
+            });
+}
+
+QWidget* BinaryStreamReader::settingsWidget()
+{
+    return &_settingsWidget;
+}
+
+unsigned BinaryStreamReader::numOfChannels()
+{
+    return _numOfChannels;
+}
+
+void BinaryStreamReader::enable(bool enabled)
+{
+    if (enabled)
+    {
+        QObject::connect(_device, &QIODevice::readyRead,
+                         this, &BinaryStreamReader::onDataReady);
+    }
+    else
+    {
+        QObject::disconnect(_device, 0, this, 0);
+    }
+}
+
+void BinaryStreamReader::pause(bool enabled)
+{
+    paused = enabled;
+}
+
+void BinaryStreamReader::onNumberFormatChanged(NumberFormat numberFormat)
+{
+    switch(numberFormat)
+    {
+        case NumberFormat_uint8:
+            sampleSize = 1;
+            readSample = &BinaryStreamReader::readSampleAs;
+            break;
+        case NumberFormat_int8:
+            sampleSize = 1;
+            readSample = &BinaryStreamReader::readSampleAs;
+            break;
+        case NumberFormat_uint16:
+            sampleSize = 2;
+            readSample = &BinaryStreamReader::readSampleAs;
+            break;
+        case NumberFormat_int16:
+            sampleSize = 2;
+            readSample = &BinaryStreamReader::readSampleAs;
+            break;
+        case NumberFormat_uint32:
+            sampleSize = 4;
+            readSample = &BinaryStreamReader::readSampleAs;
+            break;
+        case NumberFormat_int32:
+            sampleSize = 4;
+            readSample = &BinaryStreamReader::readSampleAs;
+            break;
+        case NumberFormat_float:
+            sampleSize = 4;
+            readSample = &BinaryStreamReader::readSampleAs;
+            break;
+    }
+}
+
+void BinaryStreamReader::onNumOfChannelsChanged(unsigned value)
+{
+    _numOfChannels = value;
+}
+
+void BinaryStreamReader::onDataReady()
+{
+    // a package is a set of channel data like {CHAN0_SAMPLE, CHAN1_SAMPLE...}
+    int packageSize = sampleSize * _numOfChannels;
+    int bytesAvailable = _device->bytesAvailable();
+    int numOfPackagesToRead =
+        (bytesAvailable - (bytesAvailable % packageSize)) / packageSize;
+
+    if (paused)
+    {
+        // read and discard data
+        _device->read(numOfPackagesToRead*packageSize);
+        return;
+    }
+
+    if (bytesAvailable > 0 && skipByteRequested)
+    {
+        _device->read(1);
+        skipByteRequested = false;
+        bytesAvailable--;
+    }
+
+    if (bytesAvailable < packageSize) return;
+
+    double* channelSamples = new double[numOfPackagesToRead*_numOfChannels];
+
+    for (int i = 0; i < numOfPackagesToRead; i++)
+    {
+        for (unsigned int ci = 0; ci < _numOfChannels; ci++)
+        {
+            // channelSamples[ci].replace(i, (this->*readSample)());
+            channelSamples[ci*numOfPackagesToRead+i] = (this->*readSample)();
+        }
+    }
+
+    for (unsigned int ci = 0; ci < _numOfChannels; ci++)
+    {
+        addChannelData(ci,
+                       channelSamples + ci*numOfPackagesToRead,
+                       numOfPackagesToRead);
+    }
+    emit dataAdded();
+
+    delete channelSamples;
+}
+
+
+template double BinaryStreamReader::readSampleAs()
+{
+    T data;
+
+    _device->read((char*) &data, sizeof(data));
+
+    if (_settingsWidget.endianness() == LittleEndian)
+    {
+        data = qFromLittleEndian(data);
+    }
+    else
+    {
+        data = qFromBigEndian(data);
+    }
+
+    return double(data);
+}
+
+void BinaryStreamReader::addChannelData(unsigned int channel,
+                                        double* data, unsigned size)
+{
+    _channelMan->addChannelData(channel, data, size);
+    sampleCount += size;
+}