• 使用 Qt for Android 获取并利用手机传感器数据(下篇)使用C++实现功能


    在上一篇,我们搭建了开发环境。本篇,使用C++代码真正实现功能。我们使用UDP协议从手机上指定发送的目的地、端口。效果如下图,完整工程参考https://gitcode.net/coloreaglestdio/qtcpp_demo/-/tree/master/android/sensors2pc

    移动端1移动端2桌面
    APP1App在这里插入图片描述

    1. 在PC端实现程序并调试

    我们建立一个Qt的Widgets程序,添加 position, sensors模块。
    GUI

    • 界面里支持设置目的地址、端口。
    • 界面里可以设置GPS与各个传感器的刷新速度。
    • 传感器使用Qt枚举,并创建刷新函数。

    传感器的主对话框类如下:

    #ifndef DLGSTP_H
    #define DLGSTP_H
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    QT_BEGIN_NAMESPACE
    namespace Ui { class DlgSTP; }
    QT_END_NAMESPACE
    
    class DlgSTP : public QDialog
    {
    	Q_OBJECT
    public:
    	DlgSTP(QWidget *parent = nullptr);
    	~DlgSTP();
    	void EnumSensors();
    	void openGPS();
    protected:
    	void timerEvent(QTimerEvent * evt) override;
    private:
    	Ui::DlgSTP *ui;
    	QStandardItemModel * m_pMsgMod = 0;
    	int m_nTimer = -1;
    protected:
    	//Sensors Update Lambdas
    	QList<std::function<void (void)> > m_sensorUpdaters;
    protected:
    	//GPS
    	QGeoPositionInfoSource *m_pos_source = 0;
    protected:
    	//Net Send
    	QUdpSocket * m_psock = 0;
    	QStringList m_listInfo;
    };
    #endif // DLGSTP_H
    
    //cpp
    
    DlgSTP::DlgSTP(QWidget *parent)
    	: QDialog(parent)
    	, ui(new Ui::DlgSTP)
    	, m_pMsgMod(new QStandardItemModel(this))
    	, m_psock(new QUdpSocket(this))
    {
    	ui->setupUi(this);
    	ui->listView_msg->setModel(m_pMsgMod);
    	showMaximized();
    	//Open GPS Device
    	openGPS();
    	//Enum all sensors
    	EnumSensors();
    	loadSettings();
    	m_nTimer = startTimer(20);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    1.1 枚举传感器并建立界面

    通过Qt的Sensors可以枚举到传感器的取值。

    void DlgSTP::EnumSensors()
    {
    	QList<QByteArray> sensors = QSensor::sensorTypes();
    	QString strSensors = "Sensors:\n";
    	for (QByteArray stp : sensors)
    	{
    		QSensor * sensor = new QSensor(stp);
    		sensor->start();
    		//添加界面刷新
    		QListView * lstView = new QListView(this);
    		QStandardItemModel * m_pMod = new QStandardItemModel(this);
    		lstView->setModel(m_pMod);
    		QString name = stp;
    		ui->tabWidget->addTab(lstView,name);
    		//设置刷新函数(供定时器调用),使用Lamdba后期调用,省的建立函数了。
    		m_sensorUpdaters<<[sensor,m_pMod,this,lstView,name](void)->void
    		{
    			QSensorReading *reading = sensor->reading();
    			QString str = "SENSOR="+name + ";\n";
    			//获取当前传感器有多少数值
    			int n = reading->valueCount();
    			for (int i=0;i<n;++i)
    			{
    				QVariant vt = reading->value(i);
    				str += QString("%1").arg(vt.toString());
    			}
    			//消息写入m_listInfo,后续发送
    			m_listInfo << str;
    			m_pMod->appendRow(new QStandardItem(str));
    			lstView->scrollToBottom();
    		};
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    值得注意的是,上面的代码是在实际代码中进行了简化。实际代码里为了避免频繁刷新界面的同时,迅速吞吐传感器数据,进行了一些处理。可参考实际源码。

    1.2 初始化GPS

    GPS是手机的一个重要功能。通过初始化GPS,可以实时获取位置、时刻。

    void DlgSTP::openGPS()
    {
    	m_pos_source = QGeoPositionInfoSource::createDefaultSource(0);
    	if (m_pos_source)
    	{
    		//Add Tab
    		QListView * lstView = new QListView(this);
    		QStandardItemModel * m_pMod = new QStandardItemModel(this);
    		lstView->setModel(m_pMod);
    		ui->tabWidget->addTab(lstView,"GPS");
    		//直接把GPS刷新信号绑定到Lambda
    		connect (m_pos_source,
    				 &QGeoPositionInfoSource::positionUpdated,
    				 [lstView,m_pMod,this](const QGeoPositionInfo &update)->void
    		{
    			double lat = update.coordinate().latitude();
    			double lon = update.coordinate().longitude();
    			QDateTime dtm = update.timestamp();
    			QString str = QString("SENSOR=GPS;\nGMT_TIME=%1;\nLAT=%2;\nLON=%3;\n")
    					.arg(dtm.toString("yyyy-MM-dd HH:mm:ss"))
    					.arg(lat,0,'f',7)
    					.arg(lon,0,'f',7);
    				//消息写入m_listInfo,后续发送
    				m_listInfo << str;
    			m_pMod->appendRow(new QStandardItem(str));
    			lstView->scrollToBottom();
    		});
    		//即使出错了,也继续开始,把出错的信息绑定到Lambda
    		connect (m_pos_source,
    				 &QGeoPositionInfoSource::errorOccurred,
    				 [this](QGeoPositionInfoSource::Error pe)->void{
    			m_pos_source->startUpdates();
    			m_pMsgMod->appendRow(new QStandardItem(QString("GPS Err Code %1.").arg(int(pe))));
    		});
    		//开启GPS
    		m_pos_source->setUpdateInterval(ui->horizontalSlider_gps->value());
    		m_pos_source->startUpdates();
    	}
    	else
    		m_pMsgMod->appendRow(new QStandardItem("No GPS Found!"));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    1.3 发送UDP

    设置一个定时器,进行UDP发射。定时器的尺度是20ms的整数倍,可以调整。

    void  DlgSTP::timerEvent(QTimerEvent * evt)
    {
    	if (evt->timerId()==m_nTimer)
    	{
    		++m_clk;
    		//UDP Send
    		const int updateITV = ui->horizontalSlider_freq->value();
    		const int updateGUI = (50 / (updateITV>50?50:updateITV) )* updateITV;
    		if (m_clk % updateITV ==0 )
    		{
    			//调用各个Lambda刷新传感器,消息写入m_listInfo
    			foreach(auto fn, m_sensorUpdaters)
    				fn();
    			//发送
    			QHostAddress addr (ui->lineEdit_ip->text());
    			int port = ui->spinBox_port->value();
    			foreach(QString i, m_listInfo)
    				m_psock->writeDatagram(i.toLocal8Bit(),addr,port);
    			m_nTotalSent += m_listInfo.size();
    			m_listInfo.clear();
    			//适时更新界面
    			if (m_clk % updateGUI==0)
    			{
    				m_pMsgMod->appendRow(new QStandardItem(QString("%1>%2 items sent.")
    													   .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"))
    													   .arg(m_nTotalSent)));
    				if (m_pMsgMod->rowCount()>MAX_ROWS_LSTV)
    					m_pMsgMod->removeRows(0,m_pMsgMod->rowCount()-MAX_ROWS_LSTV);
    				ui->listView_msg->scrollToBottom();
    			}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    我们在PC上可以先调试,发现PC上竟然也有很多传感器。
    在这里插入图片描述

    2 部署到Android 手机

    我们在手机上打开调试模式,一般是连续击打系统版本号,即可打开。打开后,手机会提示允不允许USB调试,点击允许即可。

    如果编译Debug版本,是不需要证书签名的。如果是Release,需要签名。

    2.1 设置应用程序的名字和图标

    在项目设置里,直接创建app的manifest

    Add创建后,即可编辑名字、图标:
    在这里插入图片描述

    2.2 创建证书

    在构建选项里,选择创建证书,输入必要信息后完成创建。注意,Release版本如果构建不成功,就要重新开启一下签名。为了安全,QtCreator会确保是你本人在烧写程序。

    在这里插入图片描述创建后,选择“”包签名“”即可开启签名。注意,Release版本如果构建不成功,就要重新开启一下签名。为了安全,QtCreator会确保是你本人在烧写程序,会经常清除这个选项,以便您再次输入密码。

    2.3 部署并运行程序

    确保在Qt的编译栏选取了适当的设备(API版本一致),且绿色按钮出现(而非红色)

    在这里插入图片描述

    3 在PC上接收数据

    当计算机、手机处于一个局域网,或者是处于IP可达的网络时,在PC端就可以接收到消息了。

    在这里插入图片描述

  • 相关阅读:
    postgresql源码学习(35)—— 检查点⑤-检查点中的XLog清理机制
    【LeetCode热题100】--739.每日温度
    11. Flash助手推荐的弹窗广告怎么删除
    【计组】计算机系统体系结构
    聊聊动态线程池的9个场景
    KNN算法 c++实现
    Springboot信息泄露以及heapdump的利用
    VMWare配置桥接
    公众号数据分析总结怎么做?教你玩转公众号后台数据
    Json“牵手”亚马逊商品详情数据方法,亚马逊商品详情API接口,亚马逊API申请指南
  • 原文地址:https://blog.csdn.net/goldenhawking/article/details/128161336