这次来分享一次突发奇想的经历。文章主要是要实现将ADC模块获取的光敏电阻数值转换成标准单位勒克斯的光照强度,虽然说由于实验方法和实验环境,最终结果并不是很准确,但也算是一次创意小实验。
最终的代码可以来这里下载:
Android端
MCU端

本文目录

  • 一、前言(无关技术的废话,可以跳过)
  • 二、具体实现思路
  • 三、实验设备和环境
    • 1.软件
    • 2.硬件
    • 3.其它
  • 四、Android端开发
    • 1.搭建界面
    • 2.搭建SQLite数据库
      • 2.1创建类
      • 2.2创建数据库Helper
      • 2.3创建Adapter
    • 3.在MainAcivity使用传感器服务获取数据
  • 五、MCU端开发
    • 1.使用STM32CubeMX配置引脚
    • 2.在Keil中设置串口发送和ADC电压数值的读取
      • 2.1设置串口发送的函数
      • 2.2设置ADC的读取函数
    • 3.在主循环中读取电压和发送数据
  • 六、实际测试
    • 1.测试前准备
    • 2.正式测试
  • 七、数据拟合处理
    • 1.数据汇总
    • 2.拟合处理

一、前言(无关技术的废话,可以跳过)

最近在项目搞STM32和光敏电阻传感器,辛辛苦苦地找了厂家客服和很多资料,发现都没有光敏电阻阻值或者电压转换成光照强度的公式。
百度了一下,发现我还是太高估了光敏电阻的精确度了,大多数光敏电阻传感器只是提供一个大概的明暗程度的判断,有一些三线的光敏电阻传感器只是提供一个DO口,输出就是1位二进制表示的明和暗。四线的光敏电阻传感器就有提供一个AO口,输出的是12位二进制表示的电压,相比来说就准确了许多,但是不同厂家甚至是同一个厂家的不同光敏电阻,对于光照强度的转换相去甚远,所以基本上找不到一条符合所有光敏电阻的转换公式。如果要更加精准的光照强度,更多的是使用光敏二极管或者是数字照度仪等。
但是,我不甘心呀,项目需要的是光照强度,而到手的光敏电阻传感器我也不想就这么地废了。所以我就想:能不能自己给光敏电阻传感器测试一下,计算出属于它的公式。实际上,我只要有一个能够测光照强度的东西就可以实现。突然想到,手机不就有这个玩意吗?虽然手机的也不够精准,但还是值得一试的,于是,我就有一个大胆的思路:

  1. 开发一个可以获取手机光线传感器数据并且存入数据库的APP;
  2. 将手机和我的光敏电阻传感器同步同向测试,获取两者的数据;
  3. 将手机获取的以勒克斯(lx)作为单位的数据,和光敏电阻的数据进行拟合,获得两者之间的转换公式。

二、具体实现思路

三、实验设备和环境

1.软件

  • Keil uVision5
  • Android Studio 3.6.3
  • 串口调试助手
  • SQLite Expert Personal
  • Excel 2016

2.硬件

  • 光敏电阻传感器(4线)
  • STM32F103C8T6
  • ST-Link
  • CH340(USB转TTL)
  • 手机(使用Android系统的)

3.其它

  • 绝缘胶带
  • 手机支架(方便测试而已,没有的话可以不用)
  • 光源(比如:手电筒、台灯之类,本文用的是另一个手机的手电筒)

四、Android端开发

1.搭建界面

首先在Android Studio中创建一个空白工程,因为本实验比较简单,所以只需要一个MainActivity就可以了。在activity_main.xml中添加简单的文字说明、两个按钮(开始和删除数据)以及ListView(用于展示数据)。

除此之外还要完成一个listview_item.xml的界面,这个就是用在ListView的每个item中的界面,在Adapter中适配。

2.搭建SQLite数据库

2.1创建类

这里虽然我们要存储的数据很简单,只有光照强度这一个浮点数值,但是为了方便操作数据库,还是要创建一个类,同时为了和MCU端收集的数据进行匹配,也添加了一个自增的变量id。

package com.peanuo.lighttest;public class Light {private int id;private double lux;public double getLux() {return lux;}public int getId() {return id;}public void setLux(double lux) {this.lux = lux;}public void setId(int id) {this.id = id;}
}

2.2创建数据库Helper

这里继承了SQLiteOpenHelper类来完成我们自定义的数据库的创建。在构造方法中创建数据库,在重写的onCreate方法中使用SQL语句创建数据表。然后是增加两个方法insertData和deleteData来方便操作数据库。最后的query方法是查询数据表的中全部,也就是用来给ListView展示数据的。

package com.peanuo.lighttest.database;import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;import com.peanuo.lighttest.Light;import java.util.ArrayList;
import java.util.List;public class LightHelper extends SQLiteOpenHelper {private SQLiteDatabase sqLiteDatabase;public LightHelper(Context context) {super(context,"lightDB",null, 1);sqLiteDatabase = this.getWritableDatabase();}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("CREATE TABLE light(id INTEGER PRIMARY KEY AUTOINCREMENT, light REAL)");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }public boolean insertData(double light) {ContentValues values  = new ContentValues();values.put("light",light);return sqLiteDatabase.insert("light", null, values)>0;}public boolean deleteData(Context context){return context.deleteDatabase("lightDB");}public List<Light> query(){List<Light> list = new ArrayList<Light>();Cursor cursor = sqLiteDatabase.query("light", null, null, null, null, null, "id desc");if (cursor != null){while (cursor.moveToNext()){Light info = new Light();int id = cursor.getInt(cursor.getColumnIndex("id"));double lux = cursor.getDouble(cursor.getColumnIndex("light"));info.setId(id);info.setLux(lux);list.add(info);}cursor.close();}return list;}
}

2.3创建Adapter

创建这个适配器就是用来给契合ListView和数据库的。这里要继承BaseAdapter类,然后实现getCount、getItem、getItemId、getView这些方法,最后的ViewHoled类是使用了优化加载list的方法。

package com.peanuo.lighttest.database;import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;import com.peanuo.lighttest.Light;
import com.peanuo.lighttest.R;import java.util.List;public class LightAdapter extends BaseAdapter {private LayoutInflater layoutInflater;private List<Light> list;public LightAdapter(Context context, List<Light> list){this.layoutInflater = LayoutInflater.from(context);this.list = list;}@Overridepublic int getCount() {return list==null ? 0 :list.size();}@Overridepublic Object getItem(int position) {return list.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;if(convertView == null){convertView = layoutInflater.inflate(R.layout.listview_item, null);holder = new ViewHolder();holder.id = convertView.findViewById(R.id.item_id);holder.light = convertView.findViewById(R.id.item_light);convertView.setTag(holder);}else  {holder = (ViewHolder) convertView.getTag();}Light info = (Light) getItem(position);holder.id.setText(String.valueOf(info.getId()));holder.light.setText(String.valueOf(info.getLux()));return convertView;}class ViewHolder{TextView id,light;}
}

3.在MainAcivity使用传感器服务获取数据

MainAcivity中要先创建SensorManager和Sensor,分别是所有传感器服务的变量以及具体传感器的变量。TYPE_LIGHT就是指向光线传感器的。在activity处于onResume时,要注册传感器服务,在处于onPause时要注销。

        sensorManager = (SensorManager)this.getSystemService(SENSOR_SERVICE);assert sensorManager != null;sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);@Overrideprotected void onResume() {super.onResume();sensorManager.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);}@Overrideprotected void onPause() {super.onPause();sensorManager.unregisterListener(sensorEventListener);}

创建自定义的线程来实现测试,同时在其中使用runOnUiThread来更新界面数据。(由于我对Android的线程还不太了解,所以此处这样处理并不是最佳的)

//开始测试的线程private class TestThread extends Thread{@Overridepublic void run() {super.run();while (isTesting){try {Thread.sleep(4000);if (lightHelper.insertData(light)) {Log.i("Light","光照强度:"+light);}} catch (InterruptedException e) {e.printStackTrace();Log.i("Thread","测试线程已停止");//break;}MainActivity.this.runOnUiThread(new Runnable() {@Overridepublic void run() {showData();}});}}}

五、MCU端开发

1.使用STM32CubeMX配置引脚

首先根据自己的MCU型号创建工程,然后在SYS中根据自己使用的调试器选择Debug。我这里是使用ST-Link,所以选择Serial Wire。

接着,就是配置ADC和USAR了。根据自己的板子随便选,我这里选择的是ADC1 IN8和USART1。这两个外设的配置都保持默认的参数就行了。
最后在生成代码之前,要去Project设置好名称、文件位置和对应的IDE。然后点击靠近右上角的GENERATE CODE就行了。

注意一下,可以点击Code Generator里面在下面图示的位置打一个钩,这样生成的代码文件就会分类放好,如果没有打勾的话,就会全部放在main文件中。

2.在Keil中设置串口发送和ADC电压数值的读取

找到STM32CubeMX生成的代码文件,打开Keil工程文件。
首先,我们要将芯片的启动文件添加进去工程。(不知道此处是我的Keil配置问题还是STM32Cube本身的问题,生成的代码总是得自己亲手添加启动文件进去工程)
注意,在使用STM32CubeMX生成的代码,在Keil中编辑的时候自己添加的代码要在BEGIN注释和END注释之间,否则用STM32CubeMX重新生成代码之后,不在两个注释之间的代码会被清除。

2.1设置串口发送的函数

要现在usart.h头文件中声明函数void u1_printf(char* fmt, …);
然后在usart.c文件中添加串口发送的函数。这里使用的是类似于printf的方法。

void u1_printf(char* fmt,...)
{  uint8_t i,j; va_list ap; va_start(ap,fmt);vsprintf((char*)USART1_TX_BUF,fmt,ap);va_end(ap);i=strlen((const char*)USART1_TX_BUF);        //此次发送数据的长度for(j=0;j<i;j++)                           //循环发送数据{while((USART1->SR&0X40)==0);          //循环发送,直到发送完毕   USART1->DR=USART1_TX_BUF[j];  }
}

2.2设置ADC的读取函数

同样,首先要在头文件中声明函数uint16_t read_adc(void);
然后到c文件中添加函数。
注意此函数读取的数字不是标准单位的电压伏特值,而是ADC读取得使用12位二进制表示的电压,还未按照比例换算成电压伏特值。由于我的目标是转换成光照强度,所以先按比例转换成电压没有必要,所以就直接使用ADC读取的数值了。

uint16_t read_adc(void)
{uint16_t temp;HAL_ADC_Start(&hadc1);HAL_ADC_PollForConversion(&hadc1, 50);if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC)){temp = HAL_ADC_GetValue(&hadc1);}return temp;
}

3.在主循环中读取电压和发送数据

  /* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */HAL_Delay(3000);id++;light = read_adc();u1_printf("id: %d  light: %d\r\n", id, light);}/* USER CODE END 3 */

六、实际测试

1.测试前准备

  • 找一个比较暗的地方做实验。
  • 将实验手机开启开发者模式,然后允许USB安装,使用USB连接到电脑,通过Android Studio调试的方法将app安装到手机上。
  • 在Keil将开发好的工程编译下载到开发板上
  • 把光敏电阻传感器的引脚接到开发板对应的GPIO口引脚。
  • 将CH340连接到电脑以及串口对应的开发板的引脚。
  • 用绝缘胶带将光敏电阻传感器贴在手机靠近光线传感器的部分,并且朝向要和手机正面一样,每个手机的位置都不太一样,不过基本都是在屏幕上部听筒的附近。
  • 打开串口助手和端口,准备接收串口数据。
  • 打开手机APP,这里最好的话手机可以一直开着开发者模式,保持充电,而且开启“充电时保持不休眠”的功能。

2.正式测试

  • 做好上述的准备工作之后,就可以着手开始了。
  • 要先启动单片机,之后MCU端会有3秒的等待时间(LED灯会闪烁),时间到了就点击手机APP的开始按钮。这样才能保证两端是同时开始的。
  • 然后就使用外部的光源逐渐地改变亮度。尽量让亮度的范围广一点,才能让拟合计算出来的数据更加准确。

七、数据拟合处理

1.数据汇总

  • 将串口助手收集到的数据复制到excel中,在excel中使用分列的方式将id和light数据提取出来放在工作表Sheet1中
  • Android端的数据库可以通过Android Studio提取出来,将手机连接到电脑,打开Android Studio的Device File Explorer,查看连接至电脑的设备,可以直接获取我们的数据库文件,路径是:data/data/自己应用的包名/databases,然后就可以看到我们的数据库文件了,点击save as保存,此处要自己添加一个后缀名.db。

  • 在SQLite Expert Personal打开我们保存的数据库,然后复制数据到excel的Sheet2。
  • 在excel的Sheet3中将两个数据的id进行匹配,最后整理成两列数据,如下图所示。

2.拟合处理

  • 选中两列数据,然后插入散点图。
  • 选择散点图,添加趋势线,然后在趋势线选项中点击显示R平方值。
  • 选择“指数”、“多项式”、“乘幂”等不同的趋势线,对比R平方值,选择最接近1的一个,就大功告成了,可以看到实验拟合的结果了。

使用拟合方法实现光敏电阻传感器数值与光照强度的近似转换相关推荐

  1. ESP32 直接使用WIFI进行UDP通信, 将光敏电阻传感器数值, 显示在OLED上 - 米思齐/Arduino

    ESP32 使用WIFI UDP协议通信 ESP32主: 读取光敏传感器数值, 并通过UDP发送 ESP32从: 读取主机发送的数值, 并显示在OLED显示屏上 实际效果 左边为ESP32, 连接OL ...

  2. 基于最小二乘法的磁力计椭球拟合方法

    基于最小二乘法的磁力计椭球拟合方法 在写飞控代码时,必然要对磁力计的测量数据进行校正,本文将介绍一种简单实用的校正方法–基于最小二乘法的椭球拟合方法. 本文椭球拟合部分来自博文IMU加速度.磁力计校正 ...

  3. matlab中离开网格的流量,数学建模【数据处理方法(一维、二维插值方法;数据拟合方法;插值and拟合的MATLAB实现)】...

    [学习网址:MOOC---郑州轻工业大学---数学建模与实验]数学建模专栏 笔记01[第1.2章][概述.软件介绍] 笔记02[第3章][数据处理方法] 笔记03[第4章][规划模型] 笔记04[第5 ...

  4. 基于MATLAB的多项式数据拟合方法研究-毕业论文

    摘要:本论文先介绍了多项式数据拟合的相关背景,以及对整个课题做了一个完整的认识.接下来对拟合模型,多项式数学原理进行了详细的讲解,通过对文献的阅读以及自己的知识积累对原理有了一个系统的认识.介绍多项式 ...

  5. 第三十二篇、基于Arduino uno,获取光敏电阻传感器的原始值和光照强度——结果导向

    0.结果 说明:先来看看串口调试助手显示的结果,第一个值是原始的模拟电压值,第二个值是含光量,也就是光照强度,如果是你想要的,可以接着往下看. 1.外观 说明:虽然光敏电阻传感器形态各异,但是原理和代 ...

  6. matlab三角多项式拟合,三角多项式拟合方法及地震数据处理

    撰写目的和基本思路 撰写目的:基于地震数据光滑性较差的特征,以往拘泥于寻找地震数据数字特征等常规的统计分析方法局限性太大.为了反映地震数据统计规律的本质特征,提出三角多项式拟合方法. 基本思路:将所得 ...

  7. Gps高程拟合matlab代码,几种GPS高程拟合方法分析与比较.doc

    几种GPS高程拟合方法分析与比较 几种GPS高程拟合方法分析与比较 摘 要:文章论述了几种常用的GPS高程拟合的方法,并在MATLAB中编制了相应的程序,建立了相应的GPS高程拟合模型,并通过实例数据 ...

  8. 本地方法(JNI)——数值参数与返回值

    [0]README 1) 本文部分文字描述 转自 core java volume 2 , 旨在理解 本地方法(JNI)--数值参数与返回值 的基础知识 : 2) for source code, p ...

  9. 数模【数据的拟合方法-总结】【温度与电阻的关系模型、农业生产实验模型、血药浓度模型、化工氯气生产等级模型、人口预测模型】

    前提文章:数学建模[数据处理方法(一维.二维插值方法:数据拟合方法:插值and拟合的MATLAB实现)]

  10. MATLAB在数学建模中的应用 随手笔记(二)----- 数据拟合方法

    声明:本篇文章只是个人知识点归纳总结,不代表全书内容,望各位大佬不喜勿喷.梳理顺序是按照书籍的实际顺序梳理. 作者:sumjess 目前内容:第一章 数学建模常规方法及其MATLAB实现---数据拟合 ...

最新文章

  1. python+requests+unittest+excel_接口自动化测试 unittest+request+excel(踩‘坑’)
  2. 查看删除对象(活动目录快照配置管理系列五)
  3. 自学python后能干什么-学习python后能做哪方面的工作
  4. PagerHelper-分页类
  5. boost::endian模块实现算术缓冲区的测试程序
  6. SVM实战:如何进行乳腺癌检测
  7. 信息学奥赛一本通(2056:【例3.7】最大的数)
  8. Python_多进程编程
  9. 孩子春节猛吃零食怎么办?专家支招:先用蔬菜填饱肚子
  10. oracle chinese_china.al32utf8,Oracle11g字符集更改为AL32UTF8
  11. 声道测试音频_功率放大器测试方法
  12. Swift - 43 - 继承, 多态, 析构函数
  13. ICML2021会议论文整理
  14. 【linux内核分析与应用-陈莉君】内核同步概述
  15. 达梦V8归档备份参数NOT BACKED UP的使用
  16. 关于JavaScript面向对象
  17. sql compact 转mysql_如何将数据导入到 SQL Server Compact Edition 数据库中(五)
  18. python打开文件写入内容_python-11 文件的打开和写入
  19. ncnn环境搭建二 - windows下Vulkan和ncnn编译安装
  20. 手机APP(功能)测试重点

热门文章

  1. linux系统中的i386/i686和x86_64有什么区别
  2. 内网服务器做了映射还是无法用公网访问,内网PC无法使用服务器NAT映射后的公网地址访问服务器的故障分析...
  3. BGP 路径属性 (Path attribute)
  4. Python 实现 Discuz论坛附件下载权限绕过漏洞
  5. 人工智能之语音识别概述(一)
  6. 中国省市区乡县名称代码对照表
  7. Springboot 基础6 - Redis实战
  8. 基于STM32移植UCGUI图形界面框架(3.9.0源码版本)
  9. 新型智慧城市城市大脑大数据平台顶层设计规划建设方案
  10. CMMI有几个过程域?