ESP32/ESP8266系列之AMG8833热成像

用ESP32与Android 实现热成像

说明:通过ESP32获取AMG8833的温度数据(Arduino IDE),然后通过UDP通信与Android 通信,在Android 上采用双三次插值算法进行插值显示热成像。

先上效果:

  • 左图未插值,右图为插值64*64的结果

在这里插入图片描述

点击观看效果视频https://www.bilibili.com/video/BV1Cv411576m

AMG8833热成像模块简单介绍:

AMG8833模块是8*8的红外热传感器阵列,通过IIC通信返回64个独立的温度数据。它的测量温度范围为0~80度,分辨率为2.5度,刷新率为10HZ。供电3-3.6V(模块会带5V转3.3V),SCL、SDA是IIC接口,INT是中断阈值引脚。

在这里插入图片描述

ESP32获取AMG8833的数据并通过UDP进行发送:

ESP32获取AMG8833数据主要采用Adafruit AMG88xx Library库,可以直接在Arduino上搜索AMG8833即可找到。

/*
 * ESP32端发送设置的温度Smin、Smax、实际的温度Tmin、Tmax、以及对应64个0-255颜色下标
*/
#include <Wire.h>
#include <Adafruit_AMG88xx.h>
#include <WiFi.h>
#include <AsyncUDP.h> //引用以使用异步UDP
#include <Ticker.h>//时间回调函数
#include <ArduinoJson.h>
//时间回调定义
Ticker ticker1; //声明Ticker对象

Adafruit_AMG88xx amg;

//WIFI常量定义
const char *ssid="xiaoluo";
const char *password="12345678";

char UDPSend_data[100];

float pixels[64];//传感器读取的64个数据

int Smin=20;//设置的温度
int Smax=30;
int Tmin , Tmax;//实际的温度


//UDP定义
AsyncUDP udp;                     //创建UDP对象
unsigned int localUdpPort = 8080; //本地端口号

//接收到UDP客户端的信息处理,json格式处理
void onPacketCallBack(AsyncUDPPacket packet)
{
  StaticJsonBuffer<2048> jsonBuffer;
  JsonObject& root = jsonBuffer.parseObject(packet.data());
  int a= root["DICar"]["data"];
  if(a==1)
  {
    Smin++;
  }
  else if(a==2)
  {
    Smin--;
  }
  else if(a==3)
  {
    Smax--;
  }
  else if(a==4)
  {
    Smax++;
  }

  Serial.write(packet.data(), packet.length());
  Serial.println();
  //localUdpPort = packet.remotePort();//返回目标端口号
  //packet.print("reply data\r\n");
}

//时间回调函数,2s发送一次数据
void callback1() 
{
  //装载设置温度值
  UDPSend_data[0]=(char)Smin;
  UDPSend_data[1]=(char)Smax;
  //获取实际最大温度、最小温度
  Temp_get_max_min(pixels);
  //装载实际最大最小温度值
  UDPSend_data[2]=(char)Tmin;
  UDPSend_data[3]=(char)Tmax;
  Smax = Tmax+1;
  Smin = Tmin-4;
  //装载0-255的颜色对应
  for(int i=0;i<64;i++)
  {
    int colorIndex = map(pixels[i], Smin, Smax, 0, 255);//映射函数
    colorIndex = constrain(colorIndex, 0, 255);//限制函数
    UDPSend_data[i+4]=(char)colorIndex;//装载对应颜色
  }
  //打印看效果
  Serial.println((int)UDPSend_data[0]);
  Serial.println((int)UDPSend_data[11]);
  Serial.println((int)UDPSend_data[2]);
  Serial.println((int)UDPSend_data[3]);
  for(int j=1;j<65;j++)
  {
    Serial.print((int)UDPSend_data[j+3]);
    Serial.print(", ");
    if( j%8 == 0) Serial.println();
  }
  Serial.println("&&&&&");
  udp.broadcastTo(UDPSend_data, localUdpPort);
}
//获取最大最小值
void Temp_get_max_min(float temp[])
{
  Tmin = 100;
  Tmax = 0;
  for(int i=0;i<64;i++)
  {
    if(Tmax<temp[i])Tmax=(int)temp[i];
    if(Tmin>temp[i])Tmin=(int)temp[i];
  }
}


void setup() {
    Serial.begin(115200);
    Serial.println(F("AMG88xx pixels"));
    WiFi.softAP(ssid, password);//设置热点
    IPAddress myIP = WiFi.softAPIP();//获取本地IP
    Serial.println(myIP);
    udp.listen(localUdpPort);//监听udp端口
    udp.onPacket(onPacketCallBack); //注册收到数据包事件
    bool status;
    status = amg.begin();//sda,scl默认SDA 21,SCL22
    if (!status) {
        Serial.println("Could not find a valid AMG88xx sensor, check wiring!");
        while (1);
    }
    delay(100); // let sensor boot up
    ticker1.detach(); //停止ticker1
    ticker1.attach_ms(200, callback1);//时间回调启动200ms
}

void loop() { 
  //读取温度
  amg.readPixels(pixels);
  delay(150);
}

在Android 上显示图像:

/*
* 对应ESP32_AMG8833_1
* */
package com.example.amg8833_01;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

public class MainActivity extends AppCompatActivity {
    private ImageView imageView;
    private TextView tv_show;
    private Button bt_inter_up;
    private Button bt_inter_down;
    private Button bt_max_up;
    private Button bt_max_down;
    private Button bt_min_up;
    private Button bt_min_down;

    private int data;
    private int interpolation =64;//插值大小63好
    private int T_max;//实际最大温度
    private int T_min;
    private int S_max;//设置的最大温度
    private int S_min;
    private int temp_64[]=new int[64];
    private float BiBubic_a = -1.85f;//BiCubic基函数-0.72
    private int data_pre[]=new int[interpolation*interpolation];//上一个数据
    private int data_now[]=new int[interpolation*interpolation];//现在的数据
    private int data_pre_2[]=new int[interpolation*interpolation];
    private Bitmap alter = null;
    private Paint paint = null;
    private Canvas canvas = null;

    /*
     * UDP通信定义
     * */
    private String   SeverIP="192.168.4.1";//目标IP
    private   int    UDPSeverPort= 8080;//服务器端口
    private   int    UDPClientPort= 9000;//客户端端口
    private InetAddress UDPServerAddress = null;
    DatagramSocket UDPSendSocket =null;//定义UDP 发送socket
    DatagramSocket UDPReceiveSocket  =null;//定义UDP 接收socket,必须分开,不然公用会堵塞

    //the colors we will be use
    private int camColors[] = {0x480F,
            0x400F,0x400F,0x400F,0x4010,0x3810,0x3810,0x3810,0x3810,0x3010,0x3010,
            0x3010,0x2810,0x2810,0x2810,0x2810,0x2010,0x2010,0x2010,0x1810,0x1810,
            0x1811,0x1811,0x1011,0x1011,0x1011,0x0811,0x0811,0x0811,0x0011,0x0011,
            0x0011,0x0011,0x0011,0x0031,0x0031,0x0051,0x0072,0x0072,0x0092,0x00B2,
            0x00B2,0x00D2,0x00F2,0x00F2,0x0112,0x0132,0x0152,0x0152,0x0172,0x0192,
            0x0192,0x01B2,0x01D2,0x01F3,0x01F3,0x0213,0x0233,0x0253,0x0253,0x0273,
            0x0293,0x02B3,0x02D3,0x02D3,0x02F3,0x0313,0x0333,0x0333,0x0353,0x0373,
            0x0394,0x03B4,0x03D4,0x03D4,0x03F4,0x0414,0x0434,0x0454,0x0474,0x0474,
            0x0494,0x04B4,0x04D4,0x04F4,0x0514,0x0534,0x0534,0x0554,0x0554,0x0574,
            0x0574,0x0573,0x0573,0x0573,0x0572,0x0572,0x0572,0x0571,0x0591,0x0591,
            0x0590,0x0590,0x058F,0x058F,0x058F,0x058E,0x05AE,0x05AE,0x05AD,0x05AD,
            0x05AD,0x05AC,0x05AC,0x05AB,0x05CB,0x05CB,0x05CA,0x05CA,0x05CA,0x05C9,
            0x05C9,0x05C8,0x05E8,0x05E8,0x05E7,0x05E7,0x05E6,0x05E6,0x05E6,0x05E5,
            0x05E5,0x0604,0x0604,0x0604,0x0603,0x0603,0x0602,0x0602,0x0601,0x0621,
            0x0621,0x0620,0x0620,0x0620,0x0620,0x0E20,0x0E20,0x0E40,0x1640,0x1640,
            0x1E40,0x1E40,0x2640,0x2640,0x2E40,0x2E60,0x3660,0x3660,0x3E60,0x3E60,
            0x3E60,0x4660,0x4660,0x4E60,0x4E80,0x5680,0x5680,0x5E80,0x5E80,0x6680,
            0x6680,0x6E80,0x6EA0,0x76A0,0x76A0,0x7EA0,0x7EA0,0x86A0,0x86A0,0x8EA0,
            0x8EC0,0x96C0,0x96C0,0x9EC0,0x9EC0,0xA6C0,0xAEC0,0xAEC0,0xB6E0,0xB6E0,
            0xBEE0,0xBEE0,0xC6E0,0xC6E0,0xCEE0,0xCEE0,0xD6E0,0xD700,0xDF00,0xDEE0,
            0xDEC0,0xDEA0,0xDE80,0xDE80,0xE660,0xE640,0xE620,0xE600,0xE5E0,0xE5C0,
            0xE5A0,0xE580,0xE560,0xE540,0xE520,0xE500,0xE4E0,0xE4C0,0xE4A0,0xE480,
            0xE460,0xEC40,0xEC20,0xEC00,0xEBE0,0xEBC0,0xEBA0,0xEB80,0xEB60,0xEB40,
            0xEB20,0xEB00,0xEAE0,0xEAC0,0xEAA0,0xEA80,0xEA60,0xEA40,0xF220,0xF200,
            0xF1E0,0xF1C0,0xF1A0,0xF180,0xF160,0xF140,0xF100,0xF0E0,0xF0C0,0xF0A0,
            0xF080,0xF060,0xF040,0xF020,0xF800};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findID();
        Draw_init(320,240);初始化画笔等参数
        UDPReceive();
        /*
         * 监听事件
         * */
        bt_inter_up.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                interpolation ++;
            }
        });
        bt_inter_down.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                interpolation --;
            }
        });
        bt_max_up.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                data = 4;sendData();
            }
        });
        bt_max_down.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                data = 3;sendData();
            }
        });
        bt_min_up.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                data = 1;sendData();
            }
        });
        bt_min_down.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                data = 2;
                sendData();
            }
        });
    }
    /*
     * json数据打包发送方法
     * */
    private void sendData(){
        String sendData ;//创建接收缓存区;
        sendData = "{\"DICar\":{\"data\":"+data+"}}";
        UDPSend(sendData);
    }

    /*
     * UDP发送数据
     * */
    private void UDPSend(final String send_data){
        //开启线程发送数据
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    UDPSendSocket = new DatagramSocket(UDPClientPort);
                    UDPServerAddress = InetAddress.getByName(SeverIP);
                    int length_data = send_data.length();
                    DatagramPacket sendPacket = new DatagramPacket(send_data.getBytes(),length_data,UDPServerAddress,UDPSeverPort);
                    UDPSendSocket.send(sendPacket);
                    UDPSendSocket.close();
                } catch (SocketException | UnknownHostException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    //初始化画图工具
    private void Draw_init(int width,int height){
        // 创建一个画布
        alter = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);// ARGB_8888就是由4(ARGB)个8位组成即32位
        canvas = new Canvas(alter);//创建Canvas对象
        paint = new Paint();//创建画笔
    }
    //画一个像素 pixel
    private void Draw_pixel(float left , float top , float right , float bottom,int color){
        //画一个矩形像素,left top right bottom  确保bottom>top left<right
        RectF rf = new RectF(left, top, right, bottom);
        paint.setColor(color);//   (低温)-1667185-- -65536(高温)
        //paint.setAlpha(1);//设置透明度
        canvas.drawRect(rf, paint);
        imageView.setImageBitmap(alter);//显示
    }
    //画一幅图,传入坐标数据
    private void Draw_picture(int[] image_data,int rate){
        int i ,j;
        float width = 320/rate,height =240/rate;
        int color ;//获取颜色对应值
        for(i = 0;i<rate;i++){
            for(j=0;j<rate;j++){
                color = camColors[image_data[i*rate+j]];
                color=Color_16to32(color);
                Draw_pixel(j*width,i*height,j*width+width,i*height+height,color);
            }
        }
    }


    /*
     * 16位颜色转32位颜色
     * */
    private int Color_16to32(int Color_16)
    {
        int Color_32 = 0xAA000000;
        int R,G,B;
        R = (Color_16>>8) & 0xF8;
        G = (Color_16>>4) & 0x7e;
        B = Color_16 & 0x1f;
        Color_32 = Color_32|(R<<16);
        Color_32 = Color_32|(G<<8);
        Color_32 = Color_32|B;
        return Color_32;
    }
    /*
     * 限制函数
     * */
    private int Limit(int data , int max , int min){
        int return_data;
        if(data > max)return_data = max;
        else if(data < min)return_data = min;
        else return_data = data;
        return return_data;
    }

    /*
     * 双三次插值
     * */
    private int[] Bicubic_interpolation(int []image_source ,int rate){
        int[] image_result = new int[rate * rate];//定义新的长度
        int Source_rate = (int) Math.sqrt(image_source.length);//获取来源图片长、宽,必须是n*n形
        float interpolation_x, interpolation_y;//插值的位置
        int Int_x ;//插值的位置int型
        int Int_y ;//插值的位置int型
        float delta_x ;//插值的位置小数部分
        float delta_y ;//插值的位置小数部分
        float Sum;//求和
        for (int row = 0; row < rate; row++) {  //行
            for (int column = 0; column < rate; column++) {  //列
                //获取插值位置
//                interpolation_x = (float) Source_rate/rate *row;
//                interpolation_y = (float) Source_rate/rate * column ;
                interpolation_x = (float) (Source_rate)/(rate) * (row + (float) 0.5) - (float) 0.5;//i*a/m
                interpolation_y = (float) (Source_rate)/(rate) * (column + (float) 0.5) - (float) 0.5;
                //得到位置整型
                Int_x = (int)Math.floor(interpolation_x);
                Int_y = (int)Math.floor(interpolation_y);
                delta_x = interpolation_x-Int_x;
                delta_y = interpolation_y-Int_y;
                Sum =0;
                //image_result[row * rate + column] = Int_x;
                //得到相邻的周围16个值
                for(int n=-1;n<3;n++){//-1,0,1,2
                    for(int m=-1;m<3;m++){
                        if((Int_x+n)<0|(Int_y+m)<0|(Int_x+n)>Source_rate-1|(Int_y+m)>Source_rate-1)continue;
                        Sum = Sum+image_source[(Int_x+n)*Source_rate+(Int_y+m)]*BiBubic(n-delta_x)*BiBubic(m-delta_y);
                    }
                }
                Sum = Limit((int)Sum,255,0);
                image_result[row * rate + column] = (int)Sum;
            }
        }
        return image_result;
    }
    //    //计算系数
    private float BiBubic(float x){
        float result ;
        x = Math.abs(x);//取绝对值
//        if(x>0&&x<1)
//        {
//            result = (BiBubic_a + 2)*x * x* x - (BiBubic_a + 3)*x * x + 1;
//        }
//        else if(x>1&&x<2)
//        {
//            result =  BiBubic_a*x  * x  * x  - 5 * BiBubic_a*x  * x + 8 * BiBubic_a*x  - 4 * BiBubic_a;
//        }
//下面是节约算力
        if(x>0&&x<1)
        {
            result = (float) (1.4*x * x* x - 2.4*x * x + 1);
        }
        else if(x>1&&x<2)
        {
            result = (float) (-0.6*x  * x  * x  +3*x  * x -4.8*x  +2.4);
        }
        else result =0;
        return result;
    }
//图片相加取平均,可以有效滤除随机噪声
    private void Picture_filter(){
        for(int i=0;i<interpolation;i++){
            for(int j=0;j<interpolation;j++){
                data_now[i*interpolation+j]= (data_pre[i*interpolation+j]+data_now[i*interpolation+j]+data_pre_2[i*interpolation+j])/3;
                //data_now[i*interpolation+j]= (data_pre[i*interpolation+j]+data_now[i*interpolation+j])/2;
            }
        }
        data_pre_2 = data_pre;
        data_pre = data_now;//把当前值给上一个
    }

    /*
     * 数据提取
     * */
    private void Data_Get(byte[] data){
        //先提取设置温度值最小
        S_min = data[0]& 0xff;
        //先提取设置温度值最大
        S_max = data[1]& 0xff;
        //提取实际温度数据
        T_min = data[2]& 0xff;
        T_max = data[3]& 0xff;
        //提取温度对应颜色
        for(int i=0;i<64;i++)
        {
            temp_64[i]=data[i+4]&0xff;;
        }
    }

    /*
     * UDP接收数据
     * */
    private void UDPReceive(){
        //开启线程接收数据
        new Thread(new Runnable() {
            @Override
            public void run() {
                byte[] Receive_buffer = new byte[1024];//创建接收缓存区
                try {
                    UDPReceiveSocket = new DatagramSocket(UDPSeverPort);
                    while(true){
                        DatagramPacket receivePacket = new DatagramPacket(Receive_buffer,Receive_buffer.length);
                        UDPReceiveSocket.receive(receivePacket);
                        byte[] ReceiveBuffers= receivePacket.getData();//存储缓存
                        if(ReceiveBuffers.length!=0)
                        {
                            //数据提取
                            Data_Get(ReceiveBuffers);
                            //插值结果
                            data_now=Bicubic_interpolation(temp_64,interpolation);
                            //data_now=DL_interpolation(temp_64,interpolation);
                            //图片均值滤波
                            Picture_filter();
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    //显示数据
                                    tv_show.setText(null);
                                    tv_show.append("当前最高温度:"+T_max+" 度\n当前最低温度:"+T_min+" 度\n 插值大小:"+interpolation+"\n设置的温度:\nMAX = "+S_max+" 度\nMIN= "+S_min+" 度");
                                    Draw_picture(data_now,interpolation);
                                }
                            });
                        }
                    }
                } catch (SocketException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    //查找ID
    private void findID(){
        tv_show=(TextView)findViewById(R.id.tv_show);
        bt_inter_down = (Button) findViewById(R.id.BT_INTER_DOWN);
        bt_inter_up = (Button) findViewById(R.id.BT_INTER_UP);
        bt_max_down = (Button) findViewById(R.id.BT_MAX_DOWN);
        bt_max_up = (Button) findViewById(R.id.BT_MAX_UP);
        bt_min_down = (Button) findViewById(R.id.BT_MIN_DOWN);
        bt_min_up = (Button) findViewById(R.id.BT_MIN_UP);
        imageView = (ImageView) findViewById(R.id.imageView);
    }
}

XML:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="56dp">

<TextView
    android:id="@+id/tv_show"
    android:layout_width="327dp"
    android:layout_height="151dp"
    android:layout_marginBottom="16dp"
    android:shadowColor="#FF0000"
    android:text="当前最高温度:0 度 \n当前最低温度:0 度 \n 插值大小:16 \n设置的温度:\nMAX = 0 度\nMIN= 0 度"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent" />

<Button
    android:id="@+id/BT_MIN_DOWN"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginEnd="24dp"
    android:layout_marginRight="24dp"
    android:layout_marginBottom="26dp"
    android:text="MIN_DOWN"
    app:layout_constraintBottom_toTopOf="@+id/tv_show"
    app:layout_constraintEnd_toEndOf="parent" />

<Button
    android:id="@+id/BT_MAX_UP"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginLeft="16dp"
    android:layout_marginTop="19dp"
    android:text="MAX_UP"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/BT_INTER_UP" />

<Button
    android:id="@+id/BT_MIN_UP"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginBottom="13dp"
    android:text="MIN_UP"
    app:layout_constraintBottom_toTopOf="@+id/BT_MIN_DOWN"
    app:layout_constraintStart_toStartOf="@+id/BT_MIN_DOWN" />

<Button
    android:id="@+id/BT_INTER_UP"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginLeft="16dp"
    android:text="INTER_UP"
    app:layout_constraintBottom_toBottomOf="@+id/BT_INTER_DOWN"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@+id/BT_INTER_DOWN" />

<Button
    android:id="@+id/BT_MAX_DOWN"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginLeft="16dp"
    android:layout_marginTop="12dp"
    android:text="MAX_DOWN"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/BT_MAX_UP" />

<Button
    android:id="@+id/BT_INTER_DOWN"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginBottom="21dp"
    android:text="INTER_DOWN"
    app:layout_constraintBottom_toTopOf="@+id/BT_MIN_UP"
    app:layout_constraintEnd_toEndOf="@+id/BT_MIN_UP" />

<ImageView
    android:id="@+id/imageView"
    android:layout_width="320dp"
    android:layout_height="240dp"
    android:layout_marginStart="45dp"
    android:layout_marginLeft="45dp"
    android:layout_marginTop="16dp"
    android:layout_marginEnd="45dp"
    android:layout_marginRight="45dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:srcCompat="@color/colorAccent" />

</androidx.constraintlayout.widget.ConstraintLayout>
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小罗-LWX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值