【ESP32/ESP8266】GPS定位数据解析与超声测距(ATGM336H+HC-SR04)

目录

前言

一、芯片介绍

ESP-12F可用输出引脚列表

禁止使用的输出引脚

 二、材料准备

三、代码编写

1 超声测距部分

2 GPS部分

3 完整代码

​四、实现效果

五、 参考


前言

做拓展项目需要要用GPS定位数据以及超声设计,之前没有用过ESP-12F这个开发板去做过,都是用到STM32板子,今天尝试用Arduino环境进行开发,并实现GPS经纬度数据获取以及超声测距

一、芯片介绍

这款芯片的可用io口还是比较少的,只适合做小型的设备控制,不用看图上有很多io口,其实他很多都不能用,否则就会导致启动错误,我已经踩过坑了

ESP-12F可用输出引脚列表

GPIO编号物理引脚特殊限制最大输出电流PWM支持推荐用途
GPIO4Pin 1012mA✔️LED控制、传感器驱动
GPIO5Pin 11启动时需高电平12mA✔️继电器、数字输出
GPIO12Pin 14启动时需低电平12mA✔️按键输入、低功耗设备
GPIO13Pin 1512mA✔️SPI MOSI、数据总线
GPIO14Pin 16启动时需高电平12mA✔️SPI CLK、高速信号
GPIO15Pin 17必须启动时低电平12mA✔️接地设备控制

禁止使用的输出引脚

GPIO编号限制原因
GPIO0启动模式选择(需外部上拉)
GPIO2内部上拉,启动时需高电平
GPIO16仅支持开漏输出

 二、材料准备

准备这是四个材料就行了

ESP8266开发板ESP8266-Node
显示屏-0.96寸显示屏-0.96寸

GPS模块ATGM336H

 GPS是每一秒自动进行发送数据,当GPS状态的闪烁,就说明获取到了卫星定位了

超声波模块HC-SR04

三、代码编写

 这个的话,我主要调一个重要部分,最后会附上源码,因为我代码里面带了联网部分,我这边就先暂时把联网部分屏蔽掉的

1 超声测距部分

一个io设置为输入,一个设置为输出

// 检测距离
void Distance_text(void) {

  // 发送触发信号
  digitalWrite(trigPin, LOW);   // 确保先拉低Trig引脚
  delayMicroseconds(2);         // 维持2微秒
  digitalWrite(trigPin, HIGH);  // 触发脉冲
  delayMicroseconds(10);        // 维持10微秒
  digitalWrite(trigPin, LOW);   // 结束触发
  // 测量回波脉冲持续时间
  distance = float(pulseIn(echoPin, HIGH) * 17) / 1000;
  if ((int)distance < 50) {

  } else {

  }
  // 显示结果
  // Serial.print("disc:");
  // if (distance > 0) {
  //   Serial.print(distance);
  //   Serial.println(" cm");
  // } else {
  //   Serial.println("out");
  // }
}

2 GPS部分

使用模拟串口,进行数据接收和解析。并且这里利用了显示了时区,我们的时区需要+8H,就是当前时间,程序已经完成这部分

void gpsParser() {
  while (gpsSerial.available()) {
    char Res = gpsSerial.read();

    if (Res == '$') {
      point1 = 0;
      memset(USART_RX2_BUF, 0, sizeof(USART_RX2_BUF));
    }

    USART_RX2_BUF[point1++] = Res;

    // 检测GPRMC/GNRMC语句
    if (point1 >= 6 && USART_RX2_BUF[3] == 'R' && USART_RX2_BUF[4] == 'M' && USART_RX2_BUF[5] == 'C') {

      if (Res == '\n') {
        memset(Save_Data.GPS_Buffer, 0, sizeof(Save_Data.GPS_Buffer));
        memcpy(Save_Data.GPS_Buffer, USART_RX2_BUF, point1);
        Save_Data.isGetData = true;
        point1 = 0;
      }
    }

    if (point1 >= sizeof(USART_RX2_BUF) - 1) {
      point1 = 0;
    }
  }
}

void parseGpsBuffer() {
  char* subString;
  char* subStringNext;
  char usefullBuffer[2] = { 0 };
  int commaCount = 0;

  subString = strtok(Save_Data.GPS_Buffer, ",");
  while (subString != NULL) {
    switch (commaCount) {
      case 1:  // UTC时间
        strncpy(Save_Data.UTCTime, subString, sizeof(Save_Data.UTCTime));
        break;
      case 2:  // 数据有效性
        strncpy(usefullBuffer, subString, sizeof(usefullBuffer));
        break;
      case 3:  // 纬度
        strncpy(Save_Data.latitude, subString, sizeof(Save_Data.latitude));
        break;
      case 4:  // N/S
        strncpy(Save_Data.N_S, subString, sizeof(Save_Data.N_S));
        break;
      case 5:  // 经度
        strncpy(Save_Data.longitude, subString, sizeof(Save_Data.longitude));
        break;
      case 6:  // E/W
        strncpy(Save_Data.E_W, subString, sizeof(Save_Data.E_W));
        break;
    }
    subString = strtok(NULL, ",");
    commaCount++;
  }

  Save_Data.isUsefull = (usefullBuffer[0] == 'A');
  Save_Data.isParseData = true;
}

void printGpsBuffer() {
  if (Save_Data.isUsefull) {
    // 转换经度
    longitude_sum = atof(Save_Data.longitude);
    int longitude_int = (int)(longitude_sum / 100);
    float longitude = longitude_int + (longitude_sum - longitude_int * 100) / 60;

    // 转换纬度
    latitude_sum = atof(Save_Data.latitude);
    int latitude_int = (int)(latitude_sum / 100);
    float latitude = latitude_int + (latitude_sum - latitude_int * 100) / 60;
    // 转化为经纬度
    longitude_sum = longitude_int + ((longitude_sum / 100 - longitude_int) * 100) / 60;
    latitude_sum = latitude_int + ((latitude_sum / 100 - latitude_int) * 100) / 60;


    // 时间解析修正
    String utcTime = Save_Data.UTCTime;
    if (utcTime.length() >= 6) {
      // 安全解析
      hour = utcTime.substring(0, 2).toInt();    // 小时
      minute = utcTime.substring(2, 4).toInt();  // 分钟
      second = utcTime.substring(4, 6).toInt();  // 秒
      // 时区调整(UTC+8)
      hour += 8;
      // 处理溢出
      if (hour >= 24) {
        hour -= 24;
        // 日期+1(需补充日期处理逻辑)
      } else if (hour < 0) {
        hour += 24;
        // 日期-1
      }

      // 时间有效性验证
      if (hour < 0 || hour >= 24 || minute < 0 || minute >= 60 || second < 0 || second >= 60) {
        Serial.println("Invalid Time Data");
        return;
      }

      格式化输出
      Serial.printf("Local Time: %02d:%02d:%02d\n", hour, minute, second);
    } else {
      Serial.println("UTC Time Format Error");
    }

    Serial.print("Latitude: ");
    Serial.print(latitude, 6);
    Serial.print(" ");
    Serial.println(Save_Data.N_S);

    Serial.print("Longitude: ");
    Serial.print(longitude, 6);
    Serial.print(" ");
    Serial.println(Save_Data.E_W);
    Serial.println("---------------------");
  } else {
    Serial.println("GPS Data Invalid");
  }
}

3 完整代码

编译芯片的选择:ESPino(ESP-12)Module

#include <SoftwareSerial.h>
#include <PubSubClient.h>
#include <Ticker.h>
#include <ESP8266WiFi.h>
//OLED
#include <U8g2lib.h>
#include <Wire.h>
//JSON
#include <ArduinoJson.h>

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "NET";
const char* password = "12345678";
const char* mqttServer = "iot-06z00axdhgfk24n.mqtt.iothub.aliyuncs.com";
// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/

Ticker ticker;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
int count;  // Ticker计数用变量
int count_2;
// ****************************************************
// 注意!以下需要用户根据然也物联平台信息进行修改!否则无法工作!
// ****************************************************
const char* mqttUserName = "smartdevice&h9sjy2rtcTI";                                                          // 服务端连接用户名(需要修改)
const char* mqttPassword = "5ba9463de09043190a8a743647518c46b1db6e1bfc2e0021198efe3384580772";                 // 服务端连接密码(需要修改)
const char* clientId = "h9sjy2rtcTI.smartdevice|securemode=2,signmethod=hmacsha256,timestamp=1739158837609|";  // 客户端id (需要修改)
const char* subTopic = "/broadcast/h9sjy2rtcTI/test1";                                                         // 订阅主题(需要修改)
const char* pubTopic = "/broadcast/h9sjy2rtcTI/test2";                                                         // 订阅主题(需要修改)
const char* willTopic = "/broadcast/h9sjy2rtcTI/test2";                                                        // 遗嘱主题名称(需要修改)
// ****************************************************

//遗嘱相关信息
const char* willMsg = "esp8266 offline";  // 遗嘱主题信息
const int willQos = 0;                    // 遗嘱QoS
const int willRetain = false;             // 遗嘱保留

const int subQoS = 1;            // 客户端订阅主题时使用的QoS级别(截止2020-10-07,仅支持QoS = 1,不支持QoS = 2)
const bool cleanSession = true;  // 清除会话(如QoS>0必须要设为false)

// 模式选择
int Flage = 0;
                         
// LED 配置
#define LED 2
bool ledStatus = HIGH;
int Waning = 0;
int Ledstate = 0;
// OLED
#define SCL 14
#define SDA 12
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, SCL, SDA, /*reset=*/U8X8_PIN_NONE);
char str[50];  //拼接字符使用
// 定义超声波传感器引脚
#define trigPin 5
#define echoPin 4
float distance;

// 配置软串口(根据实际接线修改引脚)
SoftwareSerial gpsSerial(13, 15);  // RX: D8, TX: D7
// GPS数据结构
struct {
  char GPS_Buffer[128];
  char UTCTime[12];
  char latitude[16];
  char N_S[2];
  char longitude[16];
  char E_W[2];
  bool isGetData = false;
  bool isParseData = false;
  bool isUsefull = false;
} Save_Data;
// 全局变量
char USART_RX2_BUF[256];
unsigned char point1 = 0;
float longitude_sum, latitude_sum;
int hour, minute, second;

void setup() {

  Serial.begin(9600);     // 启动串口通讯
  gpsSerial.begin(9600);  // GPS模块常用波特率
  gpsSerial.println("GPS Parser Started");
  Serial.print("欢迎使用");  
  //OELD
  u8g2.begin();
  u8g2.setFont(u8g2_font_t0_16_tf);  //设定字体  u8g2_font_ncenB08_tr

  // //设置ESP8266工作模式为无线终端模式
  // WiFi.mode(WIFI_STA);
  // // 连接WiFi
  // connectWifi();
  // // 设置MQTT服务器和端口号
  // mqttClient.setServer(mqttServer, 1883);
  // mqttClient.setCallback(receiveCallback);
  // // 连接MQTT服务器
  // connectMQTTserver();

  // Ticker定时对象
  ticker.attach(1, tickerCount);
  // LED和蜂鸣器

  pinMode(LED, OUTPUT);                 // 设置板上LED引脚为输出模式
  digitalWrite(LED, ledStatus);  // 启动后关闭板上LED

  pinMode(trigPin, OUTPUT);  // 设置Trig引脚为输出模式
  pinMode(echoPin, INPUT);   // 设置Echo引脚为输入模式

}
void loop() {
  // 如果开发板未能成功连接服务器,则尝试连接服务器
  // if (!mqttClient.connected()) {
  //   connectMQTTserver();
  // } else {
    //1秒更新一次
    if (count_2 >= 1) {

      Distance_text();  // 测量距离
      u8g2.clearBuffer();
      // 解析 GPS
      if (Save_Data.isGetData) {
        parseGpsBuffer();
        Save_Data.isGetData = false;
      }
      if (Save_Data.isParseData) {
        printGpsBuffer();
        Save_Data.isParseData = false;
      }
      if (ledStatus == LOW) {
        ledStatus = HIGH;
        digitalWrite(LED, ledStatus);

      } else {
        ledStatus = LOW;
        digitalWrite(LED, ledStatus);
      }
      if ((int)longitude_sum > 1) {
        sprintf(str, "Log: %.6f", longitude_sum);
        u8g2.drawStr(0, 16, str);
        sprintf(str, "Lat: %.6f", latitude_sum);
        u8g2.drawStr(0, 32, str);
        sprintf(str, "Time: %02d:%02d:%02d", hour, minute, second);
        u8g2.drawStr(0, 48, str);
      } else {
        sprintf(str, "Log: error", longitude_sum);
        u8g2.drawStr(0, 16, str);
        sprintf(str, "Lat: error", latitude_sum);
        u8g2.drawStr(0, 32, str);
        sprintf(str, "Time:error", hour, minute, second);
        u8g2.drawStr(0, 48, str);
      }

      sprintf(str, "Disc: %.2f CM", distance);
      u8g2.drawStr(0, 64, str);
      u8g2.sendBuffer();  //显示

      count_2 = 0;
    }
    //3秒发送一次
    if (count >= 4) {

      // 每隔3秒钟发布一次信息
      // pubMQTTmsg();
      count = 0;
    }
  // }

  gpsParser();
  // 处理信息以及心跳
  // mqttClient.loop();
}
// 连接WiFi
void connectWiFi() {
  WiFi.begin(ssid, password);
  //Serial.print("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    
  }
 // Serial.println("\nConnected! IP: " + WiFi.localIP().toString());
}
//计时器
void tickerCount() {
  count++;
  count_2++;
}
// 连接MQTT服务器并订阅信息
void connectMQTTserver() {
  // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
  /* 连接MQTT服务器
  boolean connect(const char* id, const char* user, 
                  const char* pass, const char* willTopic, 
                  uint8_t willQos, boolean willRetain, 
                  const char* willMessage, boolean cleanSession); 
  若让设备在离线时仍然能够让qos1工作,则connect时的cleanSession需要设置为false                
                  */
  if (mqttClient.connect(clientId, mqttUserName,
                         mqttPassword, willTopic,
                         willQos, willRetain, willMsg, cleanSession)) {
    // Serial.print("MQTT Server Connected. ClientId: ");
    // Serial.println(clientId);
    // Serial.print("MQTT Server: ");
    // Serial.println(mqttServer);

    subscribeTopic();  // 订阅指定主题

  } else {
    // Serial.print("MQTT Server Connect Failed. Client State:");
    // Serial.println(mqttClient.state());
    delay(5000);
  }
}

// 收到信息后的回调函数
void receiveCallback(char* topic, byte* payload, unsigned int length) {
  // Serial.print("Message Received [");
  // Serial.print(topic);
  // Serial.print("] ");
  // for (int i = 0; i < length; i++) {
  //   Serial.print((char)payload[i]);
  // }
  //解析数据
  massage_parse_json((char*)payload);
  //回传数据
  // pubMQTTmsg();
}

// 订阅指定主题
void subscribeTopic() {
  // 通过串口监视器输出是否成功订阅主题以及订阅的主题名称
  // 请注意subscribe函数第二个参数数字为QoS级别。这里为QoS = 1
  if (mqttClient.subscribe(subTopic, subQoS)) {
    //Serial.println(subTopic);
  } else {
    //Serial.print("Subscribe Fail...");
    connectMQTTserver();  // 则尝试连接服务器
  }
}

// 发布信息
void pubMQTTmsg() {
  char pubMessage[254];
  // 数据流
  MQTT_FillBuf(pubMessage);

  // 实现ESP8266向主题发布信息
  if (mqttClient.publish(pubTopic, pubMessage)) {
    //Serial.println("Publish Topic:");Serial.println(pubTopic);
    //Serial.println("Publish message:");
    //Serial.println(pubMessage);
  } else {
    subscribeTopic();  // 则尝试连接服务器
    //Serial.println("Message Publish Failed.");
  }
}

// ESP8266连接wifi
void connectWifi() {

  u8g2.clearDisplay();  // 清屏
  sprintf(str, "Wait ...  ");
  u8g2.drawStr(0, 32, str);
  u8g2.sendBuffer();  //显示
  WiFi.begin(ssid, password);

  //等待WiFi连接,成功连接后输出成功信息
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }

  Serial.println("WiFi Connected!");
}

// 数据封装
unsigned char MQTT_FillBuf(char* buf) {

  char text[256];
  memset(text, 0, sizeof(text));

  strcpy(buf, "{");
  if ((int)latitude_sum > 1) {
    memset(text, 0, sizeof(text));
    sprintf(text, "\"lat\":\"%.6f\",", latitude_sum);  // Temp是数据流的一个名称,temper是温度值
    strcat(buf, text);
    memset(text, 0, sizeof(text));
    sprintf(text, "\"log\":\"%.6f\",", longitude_sum);  // Temp是数据流的一个名称,temper是温度值
    strcat(buf, text);
  }


  memset(text, 0, sizeof(text));
  sprintf(text, "\"disc\":\"%d\",", (int)distance);  // Temp是数据流的一个名称,temper是温度值
  strcat(buf, text);

  memset(text, 0, sizeof(text));
  sprintf(text, "\"waring\":\"%d\",", Waning);  // Temp是数据流的一个名称,temper是温度值
  strcat(buf, text);

  memset(text, 0, sizeof(text));
  sprintf(text, "\"led\":\"%d\"", digitalRead(LED_BUILTIN));  // Temp是数据流的一个名称,temper是温度值
  strcat(buf, text);

  memset(text, 0, sizeof(text));
  sprintf(text, "}");
  strcat(buf, text);

  return strlen(buf);
}

// 解析json数据
void massage_parse_json(char* message) {
  // 声明一个 JSON 文档对象
  StaticJsonDocument<200> doc;
  // 解析 JSON 数据
  DeserializationError error = deserializeJson(doc, (const char*)message);
  // 检查解析是否成功
  if (error) {
    // Serial.print("Failed to parse JSON: ");
    // Serial.println(error.c_str());
    return;
  }
  // 从解析后的 JSON 文档中获取键值对
  int cmd = doc["cmd"];
  JsonObject data = doc["data"];
  switch (cmd) {
    case 1:
      Ledstate = data["led"];
      count = 1;
      break;
    case 2:
      if((int)latitude_sum > 1){
        sprintf(str, "当前时间:%02d:%02d:%02d", hour, minute, second);
        gpsSerial.println(str);
      }else{
        sprintf(str, "请先到开阔地带进行定位定位", hour, minute, second);
        gpsSerial.println(str);
      }
      count = 1;
      break;
    case 3:

      count = 1;
      break;
  }
}
// 检测距离
void Distance_text(void) {

  // 发送触发信号
  digitalWrite(trigPin, LOW);   // 确保先拉低Trig引脚
  delayMicroseconds(2);         // 维持2微秒
  digitalWrite(trigPin, HIGH);  // 触发脉冲
  delayMicroseconds(10);        // 维持10微秒
  digitalWrite(trigPin, LOW);   // 结束触发
  // 测量回波脉冲持续时间
  distance = float(pulseIn(echoPin, HIGH) * 17) / 1000;
  if ((int)distance < 50 ) {

    Waning = 1;
  } else {

    Waning = 0;
  }

  // 显示结果
  // Serial.print("disc:");
  // if (distance > 0) {
  //   Serial.print(distance);
  //   Serial.println(" cm");
  // } else {
  //   Serial.println("out");
  // }
}

void gpsParser() {
  while (gpsSerial.available()) {
    char Res = gpsSerial.read();

    if (Res == '$') {
      point1 = 0;
      memset(USART_RX2_BUF, 0, sizeof(USART_RX2_BUF));
    }

    USART_RX2_BUF[point1++] = Res;

    // 检测GPRMC/GNRMC语句
    if (point1 >= 6 && USART_RX2_BUF[3] == 'R' && USART_RX2_BUF[4] == 'M' && USART_RX2_BUF[5] == 'C') {

      if (Res == '\n') {
        memset(Save_Data.GPS_Buffer, 0, sizeof(Save_Data.GPS_Buffer));
        memcpy(Save_Data.GPS_Buffer, USART_RX2_BUF, point1);
        Save_Data.isGetData = true;
        point1 = 0;
      }
    }

    if (point1 >= sizeof(USART_RX2_BUF) - 1) {
      point1 = 0;
    }
  }
}

void parseGpsBuffer() {
  char* subString;
  char* subStringNext;
  char usefullBuffer[2] = { 0 };
  int commaCount = 0;

  subString = strtok(Save_Data.GPS_Buffer, ",");
  while (subString != NULL) {
    switch (commaCount) {
      case 1:  // UTC时间
        strncpy(Save_Data.UTCTime, subString, sizeof(Save_Data.UTCTime));
        break;
      case 2:  // 数据有效性
        strncpy(usefullBuffer, subString, sizeof(usefullBuffer));
        break;
      case 3:  // 纬度
        strncpy(Save_Data.latitude, subString, sizeof(Save_Data.latitude));
        break;
      case 4:  // N/S
        strncpy(Save_Data.N_S, subString, sizeof(Save_Data.N_S));
        break;
      case 5:  // 经度
        strncpy(Save_Data.longitude, subString, sizeof(Save_Data.longitude));
        break;
      case 6:  // E/W
        strncpy(Save_Data.E_W, subString, sizeof(Save_Data.E_W));
        break;
    }
    subString = strtok(NULL, ",");
    commaCount++;
  }

  Save_Data.isUsefull = (usefullBuffer[0] == 'A');
  Save_Data.isParseData = true;
}

void printGpsBuffer() {
  if (Save_Data.isUsefull) {
    // 转换经度
    longitude_sum = atof(Save_Data.longitude);
    int longitude_int = (int)(longitude_sum / 100);
    float longitude = longitude_int + (longitude_sum - longitude_int * 100) / 60;

    // 转换纬度
    latitude_sum = atof(Save_Data.latitude);
    int latitude_int = (int)(latitude_sum / 100);
    float latitude = latitude_int + (latitude_sum - latitude_int * 100) / 60;
    // 转化为经纬度
    longitude_sum = longitude_int + ((longitude_sum / 100 - longitude_int) * 100) / 60;
    latitude_sum = latitude_int + ((latitude_sum / 100 - latitude_int) * 100) / 60;


    // 时间解析修正
    String utcTime = Save_Data.UTCTime;
    if (utcTime.length() >= 6) {
      // 安全解析
      hour = utcTime.substring(0, 2).toInt();    // 小时
      minute = utcTime.substring(2, 4).toInt();  // 分钟
      second = utcTime.substring(4, 6).toInt();  // 秒
      // 时区调整(UTC+8)
      hour += 8;
      // 处理溢出
      if (hour >= 24) {
        hour -= 24;
        // 日期+1(需补充日期处理逻辑)
      } else if (hour < 0) {
        hour += 24;
        // 日期-1
      }

      // 时间有效性验证
      if (hour < 0 || hour >= 24 || minute < 0 || minute >= 60 || second < 0 || second >= 60) {
        Serial.println("Invalid Time Data");
        return;
      }

      // 格式化输出
      Serial.printf("Local Time: %02d:%02d:%02d\n", hour, minute, second);
    } else {
      Serial.println("UTC Time Format Error");
    }

    Serial.print("Latitude: ");
    Serial.print(latitude, 6);
    Serial.print(" ");
    Serial.println(Save_Data.N_S);

    Serial.print("Longitude: ");
    Serial.print(longitude, 6);
    Serial.print(" ");
    Serial.println(Save_Data.E_W);
    Serial.println("---------------------");
  } else {
    Serial.println("GPS Data Invalid");
  }
}

编译完成后图片

 四、实现效果

开启串口会输出

五、 参考

Arduino IDE 使用安装以及ESP32库的导入(离线)https://blog.csdn.net/herui_2/article/details/135296814?spm=1001.2014.3001.5501

ESP32/ESP8622 -- 使用MQTT协议连接云平台(带图文说明)https://herui.blog.csdn.net/article/details/135317019?spm=1001.2014.3001.5502


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿柒学起来

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

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

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

打赏作者

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

抵扣说明:

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

余额充值