added settings

master
Yorick GEOFFRE 2 years ago
parent e6ae8a3487
commit b75ef82d2e

@ -0,0 +1,34 @@
#include "imagemaster.h"
#include <QImage>
#include <QColor>
#include <QVector>
#include <QBuffer>
#include <QFile>
#include <QDir>
#include <QStandardPaths>
#include <QDebug>
#include <QDateTime>
void saveImageFromColorArray(const QVector<QColor> &colorArray, int width, int height, int scale) {
QImage image(width, height, QImage::Format_ARGB32);
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
int index = y * width + x;
image.setPixelColor(x, y, colorArray.at(index));
}
}
QImage scaledImage = image.scaled(width * scale, height * scale, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
QString path = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
QString currentDateTime = QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss");
QString fileName = QDir(path).filePath(currentDateTime + ".png");
if (scaledImage.save(fileName)) {
qDebug() << "Image saved successfully";
} else {
qDebug() << "Failed to save image";
}
}

@ -0,0 +1,9 @@
#ifndef IMAGEMASTER_H
#define IMAGEMASTER_H
#include <QVector>
#include <QColor>
void saveImageFromColorArray(const QVector<QColor> &colorArray, int width, int height, int scale);
#endif // IMAGEMASTER_H

@ -11,6 +11,7 @@
class PollingTimer : public QObject class PollingTimer : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(unsigned int timeout READ getTimeout WRITE setTimeout NOTIFY timeoutChanged)
protected: protected:
std::atomic<bool> shouldRun; std::atomic<bool> shouldRun;
std::thread* myThread = nullptr; std::thread* myThread = nullptr;
@ -24,7 +25,15 @@ public:
PollingTimer(Command* c) : shouldRun(false), _c(c){} PollingTimer(Command* c) : shouldRun(false), _c(c){}
Q_INVOKABLE void start(); Q_INVOKABLE void start();
Q_INVOKABLE void stop(); Q_INVOKABLE void stop();
Q_INVOKABLE inline void setDelay(const unsigned int& timeout) {this->timeout = timeout;} unsigned int getTimeout() const {return timeout;}
void setTimeout(const unsigned int& t) {
if (t != timeout) {
timeout = t;
emit timeoutChanged();
}
}
signals:
void timeoutChanged();
}; };
#endif // POLLINGTIMER_H #endif // POLLINGTIMER_H

@ -2,10 +2,12 @@ import Sailfish.Silica 1.0
import harbour.i2ctool.I2cif 1.0 import harbour.i2ctool.I2cif 1.0
import melexis.driver 1.0 import melexis.driver 1.0
import QtQuick 2.0 import QtQuick 2.0
import QtQuick.Layouts 1.1
Page { Page {
id: mainPage id: mainPage
property bool isInitialized: false property bool isInitialized: false
property bool isRunning: false
SilicaListView { SilicaListView {
anchors.fill: parent anchors.fill: parent
@ -24,11 +26,11 @@ Page {
} }
} }
} }
Column { ColumnLayout {
id: column id: column
width: parent.width width: parent.width
spacing: Theme.paddingLarge spacing: Theme.paddingLarge
anchors.horizontalCenter: parent.horizontalCenter
PageHeader PageHeader
{ {
title: "ThermI2C" title: "ThermI2C"
@ -49,12 +51,15 @@ Page {
id: startButton id: startButton
text: mainPage.isInitialized ? "Stop" : "Start" text: mainPage.isInitialized ? "Stop" : "Start"
onClicked: { onClicked: {
if(mainPage.isInitialized){ if(mainPage.isRunning){
mainPage.isInitialized = false; mainPage.isRunning = false;
polling_timer.stop(); polling_timer.stop();
}else{ }else{
mainPage.isInitialized = true; mainPage.isRunning = true;
mlx90640.fuzzyInit(); if(!mainPage.isInitialized){
mlx90640.fuzzyInit();
mainPage.isInitialized = true;
}
polling_timer.start(); polling_timer.start();
} }
} }
@ -65,13 +70,9 @@ Page {
Row Row
{ {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: -100
Grid { Grid {
id: grid id: grid
columns: 32 columns: 32
y: parent.height + height
x: (parent.width + width) / 2
Repeater { Repeater {
id: repeater id: repeater
model: 768 model: 768
@ -95,6 +96,31 @@ Page {
} }
} }
} }
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.paddingLarge
Button {
id: photoButton
text: "📷"
width: 120
height: 120
onClicked: {
thermalRenderer.capture()
}
}
Button {
id: settingsButton
text: "⚙️"
width: 120
height: 120
onClicked: {
console.log("Settings button clicked.")
pageStack.push(Qt.resolvedUrl("SecondPage.qml"))
}
}
}
} }
} }
} }

@ -1,30 +1,77 @@
import QtQuick 2.0 import QtQuick 2.0
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import Sailfish.Pickers 1.0
import QtQuick.Layouts 1.1
Page { Page {
id: page id: page
// The effective value will be restricted by ApplicationWindow.allowedOrientations
allowedOrientations: Orientation.All allowedOrientations: Orientation.All
SilicaListView { PageHeader {
id: listView id: header
model: 20 title: "Settings Page"
anchors.fill: parent }
header: PageHeader {
title: qsTr("Nested Page") Column {
width: parent.width
spacing: Theme.paddingLarge
anchors.left: parent.left
anchors.right: parent.right
anchors.top: header.bottom
anchors.topMargin: Theme.paddingLarge
SectionHeader { text: "Performance" }
Label {
text: "FPS Limit"
anchors.left: parent.left
anchors.leftMargin: Theme.horizontalPageMargin
}
TextField {
id: fpsPicker
width: parent.width - Theme.horizontalPageMargin * 2
placeholderText: "Enter FPS limit(1-60)"
validator: IntValidator { bottom: 1; top: 60 }
onTextChanged: {
if (fpsPicker.acceptableInput) {
polling_timer.timeout = (1000/parseInt(text, 10))
}
}
}
SectionHeader { text: "Display" }
Label {
text: "Min-Max Value"
anchors.left: parent.left
anchors.leftMargin: Theme.horizontalPageMargin
}
Slider {
id: rangeSlider
width: parent.width - Theme.horizontalPageMargin * 2
stepSize: 1
value: 50
minimumValue: 0
maximumValue: 100
onValueChanged: {
// handle value change
}
}
SectionHeader { text: "Options" }
Label {
text: "Noise"
anchors.left: parent.left
anchors.leftMargin: Theme.horizontalPageMargin
} }
delegate: BackgroundItem { Switch {
id: delegate id: noiseToggle
anchors.right: parent.right
Label { anchors.rightMargin: Theme.horizontalPageMargin
x: Theme.horizontalPageMargin onCheckedChanged: {
text: qsTr("Item") + " " + index mlx90640.stubMode = checked
anchors.verticalCenter: parent.verticalCenter
color: delegate.highlighted ? Theme.highlightColor : Theme.primaryColor
} }
onClicked: console.log("Clicked " + index)
} }
VerticalScrollDecorator {}
} }
} }

@ -23,6 +23,15 @@ int MLX90640::mlx90640_DumpEE(uint8_t slaveAddr, uint16_t *eeData)
return mlx90640_I2CRead(slaveAddr, mlx90640_EEPROM_START_ADDRESS, mlx90640_EEPROM_DUMP_NUM, eeData); return mlx90640_I2CRead(slaveAddr, mlx90640_EEPROM_START_ADDRESS, mlx90640_EEPROM_DUMP_NUM, eeData);
} }
void MLX90640::setStubMode(const bool& val)
{
if (m_stubMode != val) {
m_stubMode = val;
mlx90640_I2CSetStub(val);
emit stubModeChanged();
}
}
int MLX90640::mlx90640_SynchFrame(uint8_t slaveAddr) int MLX90640::mlx90640_SynchFrame(uint8_t slaveAddr)
{ {
uint16_t dataReady = 0; uint16_t dataReady = 0;

@ -89,16 +89,26 @@
#include <stdint.h> #include <stdint.h>
#define IGNORE_I2C_DISCONNECTS //comment out this line to enable noise generation / demo mode //#define IGNORE_I2C_DISCONNECTS //comment out this line to enable noise generation / demo mode
class MLX90640 : public QObject class MLX90640 : public QObject
{ {
Q_OBJECT Q_OBJECT
protected:
bool m_stubMode = true;
public: public:
Q_PROPERTY(bool initialized READ getInitialized WRITE setInitialized NOTIFY initializedChanged) Q_PROPERTY(bool initialized READ getInitialized WRITE setInitialized NOTIFY initializedChanged)
Q_PROPERTY(QVector<float> imageVect READ getImageVect NOTIFY dataReady) Q_PROPERTY(QVector<float> imageVect READ getImageVect NOTIFY dataReady)
Q_PROPERTY(bool stubMode READ getStubMode WRITE setStubMode NOTIFY stubModeChanged)
bool getStubMode() {
return m_stubMode;
}
void setStubMode(const bool& val);
QVector<float> getImageVect() const { QVector<float> getImageVect() const {
return this->imageVect; return this->imageVect;
} }
@ -176,6 +186,12 @@ public:
uint16_t eeMLX90640[832]; uint16_t eeMLX90640[832];
try{ try{
status = mlx90640_SetRefreshRate (0x33,0x06); //32hz status = mlx90640_SetRefreshRate (0x33,0x06); //32hz
if(status != 0 && m_stubMode){
perlin.fillWithPerlinNoise(imageVect, 32, 24, 1234567);
emit initializedChanged();
emit dataReady(imageVect);
return;
}
int curRR = mlx90640_GetRefreshRate (0x33); int curRR = mlx90640_GetRefreshRate (0x33);
fprintf(stderr, "refresh rate: %d\n", curRR); fprintf(stderr, "refresh rate: %d\n", curRR);
status = mlx90640_SetResolution(0x33,0x00); //16 bit res status = mlx90640_SetResolution(0x33,0x00); //16 bit res
@ -188,31 +204,19 @@ public:
fprintf(stderr, "dump EEprom...\n"); fprintf(stderr, "dump EEprom...\n");
status = mlx90640_DumpEE (slaveAddress, eeMLX90640); status = mlx90640_DumpEE (slaveAddress, eeMLX90640);
usleep(1000); usleep(1000);
#ifndef IGNORE_I2C_DISCONNECTS
if(status != 0){
perlin.fillWithPerlinNoise(imageVect, 32, 24, 1234567);
emit initializedChanged();
emit dataReady(imageVect);
return;
}
#endif
fprintf(stderr, "extract parameters...\n"); fprintf(stderr, "extract parameters...\n");
status = mlx90640_ExtractParameters(eeMLX90640, &mlx90640); status = mlx90640_ExtractParameters(eeMLX90640, &mlx90640);
usleep(1000); usleep(1000);
#ifndef IGNORE_I2C_DISCONNECTS
if(status == 0) if(status != 0 && m_stubMode){
getData();
else{
perlin.fillWithPerlinNoise(imageVect, 32, 24, 1234567); perlin.fillWithPerlinNoise(imageVect, 32, 24, 1234567);
emit dataReady(imageVect); emit dataReady(imageVect);
}else{
getData();
} }
#else
getData();
#endif
}catch(...){ }catch(...){
#ifndef IGNORE_I2C_DISCONNECTS if(m_stubMode)
perlin.fillWithPerlinNoise(imageVect, 32, 24, 1234567); perlin.fillWithPerlinNoise(imageVect, 32, 24, 1234567);
#endif
} }
emit initializedChanged(); emit initializedChanged();
@ -236,9 +240,8 @@ public:
imageVect[i] = mlx90640Image[i]; imageVect[i] = mlx90640Image[i];
} }
} else { } else {
#ifndef IGNORE_I2C_DISCONNECTS if(m_stubMode)
perlin.fillWithPerlinNoise(imageVect, 32, 24, 1234567); perlin.fillWithPerlinNoise(imageVect, 32, 24, 1234567);
#endif
} }
emit dataReady(imageVect); emit dataReady(imageVect);
@ -253,7 +256,6 @@ public:
} }
MLX90640() : imageVect(768){ MLX90640() : imageVect(768){
fuzzyInit();
} }
QVector<float> imageVect; QVector<float> imageVect;
@ -280,5 +282,6 @@ protected:
signals: signals:
void dataReady(QVector<float> data); void dataReady(QVector<float> data);
void initializedChanged(); void initializedChanged();
void stubModeChanged();
}; };
#endif #endif

@ -36,3 +36,7 @@ int mlx90640_I2CWrite(uint8_t slaveAddr,uint16_t writeAddress, uint16_t data)
{ {
return i2cDriverSingleton::getinstance()->i2cWrite(slaveAddr, writeAddress, data); return i2cDriverSingleton::getinstance()->i2cWrite(slaveAddr, writeAddress, data);
} }
void mlx90640_I2CSetStub(const bool& stubmode){
i2cDriverSingleton::getinstance()->stubMode = stubmode;
}

@ -25,4 +25,5 @@
extern int mlx90640_I2CWrite(uint8_t slaveAddr,uint16_t writeAddress, uint16_t data); extern int mlx90640_I2CWrite(uint8_t slaveAddr,uint16_t writeAddress, uint16_t data);
extern int mlx90640_I2CRead(uint8_t slaveAddr,uint16_t startAddress, uint16_t nMemAddressRead, uint16_t *data); extern int mlx90640_I2CRead(uint8_t slaveAddr,uint16_t startAddress, uint16_t nMemAddressRead, uint16_t *data);
extern void mlx90640_I2CFreqSet(int freq); extern void mlx90640_I2CFreqSet(int freq);
extern void mlx90640_I2CSetStub(const bool& stubmode);
#endif #endif

@ -38,6 +38,7 @@ public:
QString firstTimeDefault(QString index); QString firstTimeDefault(QString index);
bool stubMode = false;
signals: signals:
void i2cProbingChanged(); void i2cProbingChanged();
void i2cError(); void i2cError();

@ -89,7 +89,7 @@ int main(int argc, char *argv[]) {
//when a frame is ready, pass it to the renderer //when a frame is ready, pass it to the renderer
QObject::connect(thermal, SIGNAL(dataReady(QVector<float>)), thermalRenderer, SLOT(receiveNewData(QVector<float>)), Qt::ConnectionType::DirectConnection); QObject::connect(thermal, SIGNAL(dataReady(QVector<float>)), thermalRenderer, SLOT(receiveNewData(QVector<float>)), Qt::ConnectionType::DirectConnection);
pt->setDelay(100); pt->setTimeout(100);
view.rootContext()->setContextProperty("polling_timer",pt); view.rootContext()->setContextProperty("polling_timer",pt);
view.rootContext()->setContextProperty("mlx90640", thermal); view.rootContext()->setContextProperty("mlx90640", thermal);

@ -1,4 +1,5 @@
#include "thermaldatarenderer.h" #include "thermaldatarenderer.h"
#include "imagemaster.h"
#include <chrono> #include <chrono>
void ThermalDataRenderer::receiveNewData(QVector<float> data) { void ThermalDataRenderer::receiveNewData(QVector<float> data) {
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
@ -13,9 +14,34 @@ void ThermalDataRenderer::receiveNewData(QVector<float> data) {
if(renderBuffer.size() < data.size()) if(renderBuffer.size() < data.size())
renderBuffer = QVector<QColor>(data.size()+1); renderBuffer = QVector<QColor>(data.size()+1);
QVector<float> sortedData = data;
std::sort(sortedData.begin(), sortedData.end());
const float lowerPercentile = 0.2f;
const float upperPercentile = 0.98f;
int lowerIndex = static_cast<int>(lowerPercentile * sortedData.size());
int upperIndex = static_cast<int>(upperPercentile * sortedData.size());
minValue = sortedData[lowerIndex];
maxValue = sortedData[upperIndex];
//fprintf(stderr, "minValue: %f\t", minValue);
//fprintf(stderr, "maxValue: %f\n", maxValue);
attributers[activeAttributer]->maxValue = maxValue;
attributers[activeAttributer]->minValue= minValue;
for (int i = 0; i < data.size() && i < renderBuffer.size(); ++i) { for (int i = 0; i < data.size() && i < renderBuffer.size(); ++i) {
renderBuffer[i] = attributers[activeAttributer]->encode(data[i]); renderBuffer[i] = attributers[activeAttributer]->encode(data[i]);
} }
if(takeCapture){
saveImageFromColorArray(renderBuffer, 32, 24, 5); //5 times upscale from 32x24 to 160x120
takeCapture = false;
}
emit dataChanged(); emit dataChanged();
auto end = std::chrono::high_resolution_clock::now(); auto end = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(); auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();

@ -22,6 +22,7 @@ class ThermalDataRenderer : public QObject
unsigned int activeAttributer; unsigned int activeAttributer;
float minValue = 0.0f; float minValue = 0.0f;
float maxValue = 1.0f; float maxValue = 1.0f;
bool takeCapture = false;
public: public:
Q_PROPERTY(QVector<std::shared_ptr<ColorAttributer>> attributers READ getAttributers NOTIFY attributersChanged) Q_PROPERTY(QVector<std::shared_ptr<ColorAttributer>> attributers READ getAttributers NOTIFY attributersChanged)
Q_PROPERTY(QStringList attributerNames READ getAttributerNames NOTIFY attributerNamesChanged) Q_PROPERTY(QStringList attributerNames READ getAttributerNames NOTIFY attributerNamesChanged)
@ -44,6 +45,10 @@ public:
return QColor(0,0,0); return QColor(0,0,0);
} }
Q_INVOKABLE void capture(){
takeCapture = true;
}
inline void addAttributer(ColorAttributerPtr attributer) { inline void addAttributer(ColorAttributerPtr attributer) {
attributers.push_back(attributer); attributers.push_back(attributer);
emit attributerNamesChanged(); emit attributerNamesChanged();

@ -17,6 +17,7 @@ CONFIG += sailfishapp
SOURCES += src/thermi2c.cpp \ SOURCES += src/thermi2c.cpp \
colorattributer.cpp \ colorattributer.cpp \
command.cpp \ command.cpp \
imagemaster.cpp \
perlin.cpp \ perlin.cpp \
pollingtimer.cpp \ pollingtimer.cpp \
src/mlx90640_API.cpp \ src/mlx90640_API.cpp \
@ -52,6 +53,7 @@ HEADERS += \
colorattributer.h \ colorattributer.h \
command.h \ command.h \
datapollcommand.h \ datapollcommand.h \
imagemaster.h \
perlin.h \ perlin.h \
pollingtimer.h \ pollingtimer.h \
src/mlx90640_API.h \ src/mlx90640_API.h \

File diff suppressed because it is too large Load Diff

@ -8,15 +8,4 @@
<translation>Mein Cover</translation> <translation>Mein Cover</translation>
</message> </message>
</context> </context>
<context>
<name>SecondPage</name>
<message>
<source>Nested Page</source>
<translation>Unterseite</translation>
</message>
<message>
<source>Item</source>
<translation>Element</translation>
</message>
</context>
</TS> </TS>

@ -8,15 +8,4 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>SecondPage</name>
<message>
<source>Nested Page</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Item</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS> </TS>

Loading…
Cancel
Save