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

这次来分享一次突发奇想的经历。文章主要是要实现将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张图片

三、实验设备和环境

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(用于展示数据)。
使用拟合方法实现光敏电阻传感器数值与光照强度的近似转换_第2张图片
除此之外还要完成一个listview_item.xml的界面,这个就是用在ListView的每个item中的界面,在Adapter中适配。
使用拟合方法实现光敏电阻传感器数值与光照强度的近似转换_第3张图片

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();
    }


    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE light(id INTEGER PRIMARY KEY AUTOINCREMENT, light REAL)");
    }

    @Override
    public 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;
    }

    @Override
    public int getCount() {
        return list==null ? 0 :list.size();
    }

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public 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);
        
    @Override
    protected void onResume() {
        super.onResume();
        sensorManager.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override
    protected void onPause() {
        super.onPause();
        sensorManager.unregisterListener(sensorEventListener);
    }

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

//开始测试的线程
    private class TestThread extends Thread{
        @Override
        public 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() {
                    @Override
                    public void run() {
                        showData();
                    }
                });
            }
        }
    }

五、MCU端开发

1.使用STM32CubeMX配置引脚

首先根据自己的MCU型号创建工程,然后在SYS中根据自己使用的调试器选择Debug。我这里是使用ST-Link,所以选择Serial Wire。
使用拟合方法实现光敏电阻传感器数值与光照强度的近似转换_第4张图片
接着,就是配置ADC和USAR了。根据自己的板子随便选,我这里选择的是ADC1 IN8和USART1。这两个外设的配置都保持默认的参数就行了。
最后在生成代码之前,要去Project设置好名称、文件位置和对应的IDE。然后点击靠近右上角的GENERATE CODE就行了。
使用拟合方法实现光敏电阻传感器数值与光照强度的近似转换_第5张图片
注意一下,可以点击Code Generator里面在下面图示的位置打一个钩,这样生成的代码文件就会分类放好,如果没有打勾的话,就会全部放在main文件中。
使用拟合方法实现光敏电阻传感器数值与光照强度的近似转换_第6张图片

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的开始按钮。这样才能保证两端是同时开始的。
  • 然后就使用外部的光源逐渐地改变亮度。尽量让亮度的范围广一点,才能让拟合计算出来的数据更加准确。

使用拟合方法实现光敏电阻传感器数值与光照强度的近似转换_第7张图片

七、数据拟合处理

1.数据汇总

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

使用拟合方法实现光敏电阻传感器数值与光照强度的近似转换_第8张图片

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

使用拟合方法实现光敏电阻传感器数值与光照强度的近似转换_第9张图片

2.拟合处理

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

使用拟合方法实现光敏电阻传感器数值与光照强度的近似转换_第10张图片

你可能感兴趣的:(使用拟合方法实现光敏电阻传感器数值与光照强度的近似转换)