这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » [Let'sDo第3期]DIY一个电子测光表总结贴

共1条 1/1 1 跳转至

[Let'sDo第3期]DIY一个电子测光表总结贴

菜鸟
2024-11-15 23:46:58     打赏
本次实现功能是eps32 的i2c接口获取BH1750FVI 光强的lux值,分别实现摄影中的3种曝光模式,并可以通过微信小程序蓝牙控制对应的光圈,快门,拍照.曝光模式自动曝光:AE(自动曝光):自动曝光模式下,相机会根据测光结果自动决定光圈和快门的组合,以获得合适的曝光。用户可以设定曝光补偿来微调相机自动计算的曝光值。光圈优先:  AV(光圈优先自动曝光):在AV模式下,用户可以自行选择光圈大小,相机会自动运算出合适的快门速度以获得正确的曝光。这种模式适合控制景深,例如在拍摄人像时,通过选择大光圈来实现背景虚化的效果。快门优先:TV(快门优先自动曝光):与AV模式相对应,TV模式允许用户自行选择快门速度,相机会自动调整光圈大小以获得正确的曝光。这种模式适合捕捉动态场景,比如运动摄影,用户可以通过设置快门速度来冻结动作或创造运动模糊的效果。使用说明:屏幕显示光圈,快门,BH1750FVI获取的lux值,iso曝光信息.右上角会显示曝光模式分别AE,TV,AV,可以使用D0按键进行模式切换.同时可以通过遮挡或者光源照射方式来观察屏幕曝光参数变化.可以按D2按钮进行控制舵机模拟拍摄.同时屏幕会闪屏一下.蓝牙控制通过手机小程序代码链接DigiKey&EEPW 的设备.连接成功以后屏幕背景会变成蓝色.大概这样同时可以使用手机根据开发板设置的曝光模式不同进行快门,光圈,拍照控制.如下项目代码:本项目使用arduino作为开发ide,一共涉及到几个模块.按钮 tft屏幕 BH1750FVI 舵机 蓝牙ble ws2812都分别对功能在项目上做了拆分都比较简单.有一些注释.不得不说arduino对于多文件开发是真的一点都不友好...使用到的库
NimBLEDevice
M5_UNIT_8SERVO
M5_DLight
Adafruit-ST7735-Library
Adafruit_NeoPixel
Adafruit_TestBed
目录结构
9959 ◯  tree -L 3                                                                                                                                                                                              ⏎
.
├── BleCamera
│   ├── BLEManager.cpp
│   ├── BLEManager.h
│   ├── BleCamera.ino
│   ├── ButtonController.cpp
│   ├── ButtonController.h
│   ├── CalculateParams.cpp
│   ├── CalculateParams.h
│   ├── DisplayManager.cpp
│   ├── DisplayManager.h
│   ├── LedController.cpp
│   ├── LedController.h
│   ├── ServoController.cpp
│   └── ServoController.h
└── Toolbox
    ├── app.js
    ├── app.json
    ├── app.wxss
    ├── components
    │   └── navigation-bar
    ├── images
    │   └── bluetooth.png
    ├── package-lock.json
    ├── package.json
    ├── pages
    │   ├── ble
    │   ├── bt
    │   ├── index
    │   └── mqtt
    ├── project.config.json
    ├── project.private.config.json
    ├── sitemap.json
    └── utils
        ├── mqtt.min.js
        └── util.js

12 directories, 24 files
BleCamera本项目的固件项目Toolbox小程序蓝牙控制项目BleCamera(固件代码)BleCamera.ino 项目主文件
#include <Arduino.h>
#include "CalculateParams.h"
#include "BLEManager.h"
#include "ServoController.h"
#include "DisplayManager.h"
#include "ButtonController.h"
#include "ledController.h"

extern bool deviceConnected;
extern int currentAperture;       // 光圈值,如 f/1.8
extern int currentShutterSpeed ; // 快门速度,如 1/1000 秒
extern int currentISO;           // ISO 值
extern ExposureMode exposureMode; // 曝光模式

void setup() {
    Serial.begin(115200);
    buttonInit();
    calculateInit();
    bleInit();
    tftInit();
    servoInit();
    ledInit();
    Serial.println("Start");
}

void loop() {
    buttonTick();
    calculate();
    displayParameters(currentAperture, currentShutterSpeed, readLUX(), currentISO, exposureMode);
    delay(10);
}
BLEManager 蓝牙外设
#include <NimBLEDevice.h>
#include <M5_UNIT_8SERVO.h>
#include "DisplayManager.h"
#include "ServoController.h"

NimBLEServer *pServer;
NimBLEService *pService;
NimBLECharacteristic *pPhotoCharacteristic;
NimBLECharacteristic *pApertureCharacteristic;
NimBLECharacteristic *pShutterCharacteristic;
NimBLECharacteristic *pIsoCharacteristic;

// BLE 客户端连接状态
bool deviceConnected = false;
int currentAperture = 18;       // 模拟光圈值,如 f/1.8
int currentShutterSpeed = 1000; // 模拟快门速度,如 1/1000 秒
int currentISO = 100;           // 模拟 ISO 值

// 服务和特性的 UUID
const char *SERVICE_UUID = "0000fea6-0000-1000-8000-00805f9b34fb";
const char *PHOTO_CHAR_UUID = "b5f90072-aa8d-11e3-9046-0002a5d5c51b";
const char *APERTURE_CHAR_UUID = "0000fea7-0000-1000-8000-00805f9b34fb";
const char *SHUTTER_CHAR_UUID = "0000fea8-0000-1000-8000-00805f9b34fb";
const char *ISO_CHAR_UUID = "0000fea9-0000-1000-8000-00805f9b34fb";

// 通知当前设置
void notifyCurrentSettings() {
  if (deviceConnected) {
    // 光圈通知
    char apertureValue[10];
    snprintf(apertureValue, sizeof(apertureValue), "f/%.1f", currentAperture / 10.0);
    pApertureCharacteristic->setValue(apertureValue);
    pApertureCharacteristic->notify();

    // 快门速度通知
    char shutterValue[10];
    snprintf(shutterValue, sizeof(shutterValue), "1/%d", currentShutterSpeed);
    pShutterCharacteristic->setValue(shutterValue);
    pShutterCharacteristic->notify();

    // ISO 通知
    char isoValue[10];
    snprintf(isoValue, sizeof(isoValue), "ISO %d", currentISO);
    pIsoCharacteristic->setValue(isoValue);
    pIsoCharacteristic->notify();

    Serial.printf("通知更新: 光圈: %s, 快门速度: %s, ISO: %s\n", apertureValue, shutterValue, isoValue);
  }
}

// BLE 服务回调
class ServerCallbacks : public NimBLEServerCallbacks {
  void onConnect(NimBLEServer* pServer) {
    Serial.println("BLE 客户端已连接");
    deviceConnected = true;
    notifyCurrentSettings(); // 客户端连接后立即通知当前数据
  }

  void onDisconnect(NimBLEServer* pServer) {
    Serial.println("BLE 客户端已断开连接");
    deviceConnected = false;
    pServer->startAdvertising(); // 重新开始广播
  }
};

// 拍照特性写入回调
class PhotoCharacteristicCallbacks : public NimBLECharacteristicCallbacks {
  void onWrite(NimBLECharacteristic *pCharacteristic) {
    std::string rxValue = pCharacteristic->getValue();
    if (rxValue.length() > 0 && rxValue[0] == 0x03 && rxValue[1] == 0x01) {
      Serial.println("执行拍照指令");
      Serial.printf("当前光圈: f/%.1f, 当前快门速度: 1/%d 秒, ISO: %d\n",
                    currentAperture / 10.0, currentShutterSpeed, currentISO);

      flashScreen();              
      shutter();             
    }
  }
};

// 光圈控制特性写入回调
class ApertureCharacteristicCallbacks : public NimBLECharacteristicCallbacks {
  void onWrite(NimBLECharacteristic *pCharacteristic) {
    std::string rxValue = pCharacteristic->getValue();
    if (rxValue.length() > 0) {
      currentAperture = (int)rxValue[0];
      Serial.printf("设置光圈为: f/%.1f\n", currentAperture / 10.0);
      notifyCurrentSettings(); // 更新通知
    }
  }
};

// 快门控制特性写入回调
class ShutterCharacteristicCallbacks : public NimBLECharacteristicCallbacks {
  void onWrite(NimBLECharacteristic *pCharacteristic) {
    std::string rxValue = pCharacteristic->getValue();
    if (rxValue.length() > 1) {
      currentShutterSpeed = (int)rxValue[0] * 100 + (int)rxValue[1] * 10;
      Serial.printf("设置快门速度为: 1/%d 秒\n", currentShutterSpeed);
      notifyCurrentSettings(); // 更新通知
    }
  }
};

// ISO 控制特性写入回调
class IsoCharacteristicCallbacks : public NimBLECharacteristicCallbacks {
  void onWrite(NimBLECharacteristic *pCharacteristic) {
    std::string rxValue = pCharacteristic->getValue();
    if (rxValue.length() > 0) {
      currentISO = (int)rxValue[0] * 100;
      Serial.printf("设置 ISO 为: %d\n", currentISO);
      notifyCurrentSettings(); // 更新通知
    }
  }
};

// 初始化 BLE
void bleInit() {
  NimBLEDevice::init("DigiKey&EEPW");

  pServer = NimBLEDevice::createServer();
  pServer->setCallbacks(new ServerCallbacks());

  // 创建 GoPro 服务
  pService = pServer->createService(SERVICE_UUID);

  // 创建拍照特性
  pPhotoCharacteristic = pService->createCharacteristic(
    PHOTO_CHAR_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE
  );
  pPhotoCharacteristic->setCallbacks(new PhotoCharacteristicCallbacks());

  // 创建光圈特性
  pApertureCharacteristic = pService->createCharacteristic(
    APERTURE_CHAR_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY
  );
  pApertureCharacteristic->setCallbacks(new ApertureCharacteristicCallbacks());

  // 创建快门速度特性
  pShutterCharacteristic = pService->createCharacteristic(
    SHUTTER_CHAR_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY
  );
  pShutterCharacteristic->setCallbacks(new ShutterCharacteristicCallbacks());

  // 创建 ISO 特性
  pIsoCharacteristic = pService->createCharacteristic(
    ISO_CHAR_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY
  );
  pIsoCharacteristic->setCallbacks(new IsoCharacteristicCallbacks());

  // 启动服务并开始广播
  pService->start();
  pServer->getAdvertising()->start();
  Serial.println("BLE 广播开始,等待客户端连接...");
}
ButtonController 按钮逻辑
#ifndef DISPLAY_HANDLER_H
#define DISPLAY_HANDLER_H
#include "CalculateParams.h"

// ISO 范围限制
#define ISO_MIN 100
#define ISO_MAX 102400

// 快门速度范围
#define SHUTTER_MIN 20    // 最慢 1/20 秒
#define SHUTTER_MAX 6000  // 最快 1/6000 秒

#include <M5_DLight.h>

extern int currentAperture;      // 模拟光圈值,如 f/1.8
extern int currentShutterSpeed;  // 模拟快门速度,如 1/1000 秒
extern int currentISO;           // 模拟 ISO 值

M5_DLight sensor;
uint16_t lux = 0;
ExposureMode exposureMode = AUTO;

// 基础曝光参数结构体
struct ExposureParams {
  float aperture;  // 光圈值 F值
  int shutter;   // 快门速度(秒)
  int iso;         // ISO感光度
  float lux;       // 环境光照度(lux)
};

void calculateInit() {
  sensor.begin();

  // if (!sensor.begin()) {
  //   Serial.println("光照传感器初始化失败");
  //   while (1);
  // }
  // Serial.println("光照传感器初始化成功");
  sensor.setMode(CONTINUOUSLY_H_RESOLUTION_MODE);
}

uint16_t readLUX() {
  lux = sensor.getLUX();
  return lux;
}

// 自动曝光计算
ExposureParams calculateAutoExposure(float lux) {
  ExposureParams params;

  // 基于光照度计算合适的曝光参数
  if (lux > 100000) {
    params.aperture = 16.0;
    params.shutter = 1000;
    params.iso = 100;
  } else if (lux > 10000) {
    params.aperture = 8.0;
    params.shutter =  500;
    params.iso = 200;
  } else if (lux > 1000) {
    params.aperture = 5.6;
    params.shutter = 250;
    params.iso = 400;
  } else if (lux > 100) {
    params.aperture = 4.0;
    params.shutter =  125;
    params.iso = 600;
  } else if (lux > 10) {
    params.aperture = 2.8;
    params.shutter = 60;
    params.iso = 800;
  } else {
    params.aperture = 2.0;
    params.shutter = 30;
    params.iso = 1600;
  }
  params.lux = lux;
  return params;
}

// 光圈优先模式计算
ExposureParams calculateAperturePriority(float lux, float targetAperture) {
  ExposureParams params;
  params.aperture = targetAperture;

  // 基于目标光圈和光照度计算快门速度
  float ev = log2(lux / 2.5);
  params.shutter =  (pow(2, ev) / params.aperture * params.aperture);

  // 根据快门速度调整ISO
  if (params.shutter < 2000) {
    params.iso = 100;
  } else if (params.shutter < 500) {
    params.iso = 200;
  } else if (params.shutter < 125) {
    params.iso = 400;
  } else {
    params.iso = 800;
  }

  params.lux = lux;
  return params;
}

// 快门优先模式计算
ExposureParams calculateShutterPriority(float lux, float targetShutter) {
  ExposureParams params;
  params.shutter = targetShutter;

  // 基于目标快门速度和光照度计算光圈值
  float ev = log2(lux / 2.5);
  params.aperture = sqrt(pow(2, ev) * targetShutter);

  // 根据光圈值调整ISO
  if (params.aperture > 11) {
    params.iso = 100;
  } else if (params.aperture > 8) {
    params.iso = 200;
  } else if (params.aperture > 4) {
    params.iso = 400;
  } else {
    params.iso = 800;
  }

  params.lux = lux;
  return params;
}

ExposureParams calculateExposure(ExposureMode mode, float lux) {
  if (mode == AUTO) {
    return calculateAutoExposure(lux);
  } else if (mode == APERTURE) {
    return calculateAperturePriority(lux, currentAperture / 10.0f);
  } else {
    return calculateShutterPriority(lux, currentShutterSpeed);
  }
}

void calculate() {
  ExposureParams params = calculateExposure(exposureMode, lux);
  currentAperture = params.aperture * 10;
  currentShutterSpeed =  params.shutter;
  currentISO = params.iso;
  Serial.print("当前拍摄模式: ");
  Serial.println(exposureMode);

  Serial.print("ISO 调整后的 EV 值: ");
  Serial.println(params.iso);

  Serial.print("快门速度 (T): 1/ ");
  Serial.print(params.shutter);
  Serial.println(" 秒");

  Serial.print("光圈值 (F): ");
  Serial.println(params.aperture);

  Serial.println("------------------------");
}

#endif
CalculateParams.cpp  lux计算逻辑
#ifndef DISPLAY_HANDLER_H
#define DISPLAY_HANDLER_H
#include "CalculateParams.h"

// ISO 范围限制
#define ISO_MIN 100
#define ISO_MAX 102400

// 快门速度范围
#define SHUTTER_MIN 20    // 最慢 1/20 秒
#define SHUTTER_MAX 6000  // 最快 1/6000 秒

#include <M5_DLight.h>

extern int currentAperture;      // 模拟光圈值,如 f/1.8
extern int currentShutterSpeed;  // 模拟快门速度,如 1/1000 秒
extern int currentISO;           // 模拟 ISO 值

M5_DLight sensor;
uint16_t lux = 0;
ExposureMode exposureMode = AUTO;

// 基础曝光参数结构体
struct ExposureParams {
  float aperture;  // 光圈值 F值
  int shutter;   // 快门速度(秒)
  int iso;         // ISO感光度
  float lux;       // 环境光照度(lux)
};

void calculateInit() {
  sensor.begin();

  // if (!sensor.begin()) {
  //   Serial.println("光照传感器初始化失败");
  //   while (1);
  // }
  // Serial.println("光照传感器初始化成功");
  sensor.setMode(CONTINUOUSLY_H_RESOLUTION_MODE);
}

uint16_t readLUX() {
  lux = sensor.getLUX();
  return lux;
}

// 自动曝光计算
ExposureParams calculateAutoExposure(float lux) {
  ExposureParams params;

  // 基于光照度计算合适的曝光参数
  if (lux > 100000) {
    params.aperture = 16.0;
    params.shutter = 1000;
    params.iso = 100;
  } else if (lux > 10000) {
    params.aperture = 8.0;
    params.shutter =  500;
    params.iso = 200;
  } else if (lux > 1000) {
    params.aperture = 5.6;
    params.shutter = 250;
    params.iso = 400;
  } else if (lux > 100) {
    params.aperture = 4.0;
    params.shutter =  125;
    params.iso = 600;
  } else if (lux > 10) {
    params.aperture = 2.8;
    params.shutter = 60;
    params.iso = 800;
  } else {
    params.aperture = 2.0;
    params.shutter = 30;
    params.iso = 1600;
  }
  params.lux = lux;
  return params;
}

// 光圈优先模式计算
ExposureParams calculateAperturePriority(float lux, float targetAperture) {
  ExposureParams params;
  params.aperture = targetAperture;

  // 基于目标光圈和光照度计算快门速度
  float ev = log2(lux / 2.5);
  params.shutter =  (pow(2, ev) / (params.aperture * params.aperture));

  // 根据快门速度调整ISO
  if (params.shutter < 2000) {
    params.iso = 100;
  } else if (params.shutter < 500) {
    params.iso = 200;
  } else if (params.shutter < 125) {
    params.iso = 400;
  } else {
    params.iso = 800;
  }

  params.lux = lux;
  return params;
}

// 快门优先模式计算
ExposureParams calculateShutterPriority(float lux, float targetShutter) {
  ExposureParams params;
  params.shutter = targetShutter;

  // 基于目标快门速度和光照度计算光圈值
  float ev = log2(lux / 2.5);
  params.aperture = sqrt(pow(2, ev) * targetShutter);

  // 根据光圈值调整ISO
  if (params.aperture > 11) {
    params.iso = 100;
  } else if (params.aperture > 8) {
    params.iso = 200;
  } else if (params.aperture > 4) {
    params.iso = 400;
  } else {
    params.iso = 800;
  }

  params.lux = lux;
  return params;
}

ExposureParams calculateExposure(ExposureMode mode, float lux) {
  if (mode == AUTO) {
    return calculateAutoExposure(lux);
  } else if (mode == APERTURE) {
    return calculateAperturePriority(lux, currentAperture);
  } else {
    return calculateShutterPriority(lux, currentShutterSpeed);
  }
}

void calculate() {
  ExposureParams params = calculateExposure(exposureMode, lux);
  currentAperture = params.aperture * 10;
  currentShutterSpeed =  params.shutter;
  currentISO = params.iso;
  Serial.print("当前拍摄模式: ");
  Serial.println(exposureMode);

  Serial.print("ISO 调整后的 EV 值: ");
  Serial.println(params.iso);

  Serial.print("快门速度 (T): 1/ ");
  Serial.print(params.shutter);
  Serial.println(" 秒");

  Serial.print("光圈值 (F): ");
  Serial.println(params.aperture);

  Serial.println("------------------------");
}

#endif
DisplayManager.cpp 显示处理逻辑
#include <Adafruit_GFX.h>     // Core graphics library
#include <Adafruit_ST7789.h>  // Hardware-specific library for ST7789
#include <SPI.h>
#include "CalculateParams.h"

extern bool deviceConnected;

// Use dedicated hardware SPI pins
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

GFXcanvas16 canvas(240, 135);

void tftInit() {
  Serial.print(F("Hello! Feather TFT Test"));

  // turn on backlite
  pinMode(TFT_BACKLITE, OUTPUT);
  digitalWrite(TFT_BACKLITE, HIGH);

  // turn on the TFT / I2C power supply
  pinMode(TFT_I2C_POWER, OUTPUT);
  digitalWrite(TFT_I2C_POWER, HIGH);
  delay(10);

  // initialize TFT
  tft.init(135, 240);  // Init ST7789 240x135
  tft.setRotation(3);
  tft.fillScreen(ST77XX_GREEN);

  Serial.println(F("Initialized"));
}

void flashScreen() {
  tft.fillScreen(ST77XX_WHITE);
  delay(500);
}

void displayParameters(int aperture, int shutter_speed, int lux, int iso, ExposureMode exposureMode) {

  static int lastAperture = -1;
  static int lastShutterSpeed = -1;
  static unsigned int lastLux = 0;
  static float lastIso = -1;
  static bool lastDeviceConnected = false;
  static ExposureMode lastExposureMode = AUTO;

  if (lastAperture != aperture || lastShutterSpeed != shutter_speed || lastLux != lux || lastIso != iso || lastDeviceConnected != deviceConnected || lastExposureMode != exposureMode) {

    lastAperture = aperture;
    lastShutterSpeed = shutter_speed;
    lastLux = lux;
    lastIso = iso;
    lastDeviceConnected = deviceConnected;
    lastExposureMode = exposureMode;

    // 使用 canvas 清除背景
    canvas.fillScreen(deviceConnected ? ST77XX_BLUE : ST77XX_BLACK);

    // 设置字体颜色和大小
    canvas.setTextColor(ST77XX_WHITE);
    canvas.setTextSize(2);

    // 显示光圈值
    canvas.setCursor(10, 5);
    canvas.printf("Aperture: f/%.1f", aperture / 10.0f);

    // 显示快门速度
    canvas.setCursor(10, 35);
    canvas.printf("Shutter: 1/%d", shutter_speed);

    // 显示 Lux 值
    canvas.setCursor(10, 65);
    canvas.printf("Lux: %u", lux);

    // 显示 ISO 值
    canvas.setCursor(10, 95);
    canvas.printf("ISO: %d", iso);

    // 在右上角显示曝光模式
    const char* modeStr = "";
    uint16_t bgColor = ST77XX_BLACK;  // 默认背景色

    switch (exposureMode) {
      case AUTO:
        modeStr = "AE";        // 自动曝光
        bgColor = ST77XX_RED;  // 红色背景
        break;
      case APERTURE:
        modeStr = "AV";          // 光圈优先
        bgColor = ST77XX_GREEN;  // 绿色背景
        break;
      case SHUTTER:
        modeStr = "TV";            // 快门优先
        bgColor = ST77XX_MAGENTA;  // 紫色背景
        break;
    }

    // 设置右上角曝光模式背景色
    canvas.fillRect(240 - 40, 0, 40, 20, bgColor);
    canvas.setCursor(240 - 35, 5);      // 设置文本位置
    canvas.setTextColor(ST77XX_WHITE);  // 文字为白色
    canvas.setTextSize(2);
    canvas.print(modeStr);

    // 将 canvas 的内容绘制到屏幕
    tft.drawRGBBitmap(0, 0, canvas.getBuffer(), 240, 135);
  }
}
LedController.cpp
#include <Adafruit_NeoPixel.h>
#include "Adafruit_TestBed.h"

extern Adafruit_TestBed TB;

void ledInit() {
  TB.neopixelPin = PIN_NEOPIXEL;
  TB.neopixelNum = 1; 
  TB.begin();
}

void ledOn() {
  TB.setColor(WHITE);
}
ServoController.cpp
#include "ServoController.h"
#include <M5_UNIT_8SERVO.h>

M5_UNIT_8SERVO unit_8servo;

void servoInit() {
  unit_8servo.begin();
  unit_8servo.setAllPinMode(SERVO_CTL_MODE);
}

void rotateServos(int angle) {
  for (uint8_t i = 0; i < 8; i++) {
    unit_8servo.setServoAngle(i, angle);
  }
}

void shutter() {
  unit_8servo.setServoAngle(1, 180);
  unit_8servo.setServoAngle(2, 180);
  vTaskDelay(500);                  // 等待 500ms 防止重复触发
  unit_8servo.setServoAngle(1, 0);  // 设定所有舵机回正到 0 度
  unit_8servo.setServoAngle(2, 0);  // 设定所有舵机回正到 0 度
}
Toolbox(小程序代码)bt/device/ device.js
const app = getApp()
const apertureList = [18, 20, 22, 28, 32, 40, 56, 80, 110, 160]; // 光圈列表 除以10
const shutterSpeedList = [1, 2, 4, 8, 15, 30, 60, 125, 250, 500, 1000, 2000, 4000]; // 快门速度列表 (单位: n分之1/秒)

// 当前光圈和快门速度的索引
let currentApertureIndex = 0;
let currentShutterSpeedIndex = 0;

// 获取下一个光圈值
function nextAperture() {
  currentApertureIndex = (currentApertureIndex + 1) % apertureList.length; // 循环列表
  const apertureValue = apertureList[currentApertureIndex];
  console.log(`当前光圈值: f/${apertureValue}`);
  return apertureValue;
}

// 获取下一个快门速度值
function nextShutterSpeed() {
  currentShutterSpeedIndex = (currentShutterSpeedIndex + 1) % shutterSpeedList.length; // 循环列表
  const shutterSpeedValue = shutterSpeedList[currentShutterSpeedIndex];
  console.log(`当前快门速度: 1/${shutterSpeedValue} 秒`);
  return shutterSpeedValue;
}

Page({
  data: {
    title: "测光笔",
    inputText: 'mootek',
    receiveText: '',
    name: '',
    connectedDeviceId: '',
    services: [],
    characteristics: [],
    connected: true,
    serviceId: "0000FEA6-0000-1000-8000-00805F9B34FB",
    apertureCharId: '0000FEA7-0000-1000-8000-00805F9B34FB', // 光圈控制特性 UUID
    shutterCharId: '0000FEA8-0000-1000-8000-00805F9B34FB', // 快门控制特性 UUID
    photoCharId: 'B5F90072-AA8D-11E3-9046-0002A5D5C51B', // 拍照特性 UUID
    isoCharId: "0000FEA9-0000-1000-8000-00805F9B34FB",
    aperture: "f/1.2",
    shutter: "1/4000",
    iso: "ISO 100",
  },
  onLoad(options) {
    const { connectedDeviceId, name } = options;
    this.setData({
      name: decodeURIComponent(name),
      connectedDeviceId,
    });
    // 获取蓝牙服务
    this.getBLEDeviceServices();
    wx.onBLEConnectionStateChange((res) => {
      console.log("蓝牙连接状态:", res.connected);
      this.setData({ connected: res.connected });
    });
  },
  // 获取蓝牙服务
  getBLEDeviceServices() {
    const { connectedDeviceId } = this.data;
    wx.getBLEDeviceServices({ deviceId: connectedDeviceId })
    .then( (res) => {
      console.log("服务列表:", res.services);
      this.setData({ services: res.services });
      this.getCharacteristics(res.services[0].uuid);
    })
    .catch((err) => {
      console.error("获取服务失败:", err);
    })
  },
  // 获取特征值
  getCharacteristics(serviceId) {
    const { connectedDeviceId } = this.data;
    wx.getBLEDeviceCharacteristics({ deviceId: connectedDeviceId, serviceId })
    .then((res) => {
      console.log("特征值列表:", res.characteristics);
      this.setData({ characteristics: res.characteristics });
      // 启用需要监听的特征值
      this.enableNotify(this.data.isoCharId);
      this.enableNotify(this.data.apertureCharId);
      this.enableNotify(this.data.shutterCharId);
    })
    .catch((err) => {
      console.error("获取特征值失败:", err);
    });
  },
  // 启用 Notify
  enableNotify(characteristicId) {
    const { connectedDeviceId, services } = this.data;
    wx.notifyBLECharacteristicValueChange({
      state: true,
      deviceId: connectedDeviceId,
      serviceId: services[0].uuid,
      characteristicId
    })
    .then(() => {
      console.log(`启用通知成功:${characteristicId}`);
    })
    .catch((err) => {
      console.error(`启用通知失败:${characteristicId}`, err);
    });
  },

  // 写入特征值
  writeCharacteristic(characteristicId, buffer) {
    const { connectedDeviceId, serviceId } = this.data;
    wx.writeBLECharacteristicValue({ deviceId: connectedDeviceId, serviceId, characteristicId, value: buffer })
    .then(() => {
      console.log(`写入特征值成功:${characteristicId}`);
    })
    .catch((err) => {
      console.error(`写入特征值失败:${characteristicId}`, err);
    });
  },

  // 设置光圈
  setAperture(apertureValue) {
    const buffer = new ArrayBuffer(1);
    new DataView(buffer).setUint8(0, apertureValue);
    this.writeCharacteristic(this.data.apertureCharId, buffer);
    console.log(`光圈设置为 f/${apertureValue / 10}`);
  },
  // 设置快门速度
  setShutterSpeed(shutterSpeed) {
    const buffer = new ArrayBuffer(2);
    const dataView = new DataView(buffer);
    dataView.setUint8(0, Math.floor(shutterSpeed / 100));
    dataView.setUint8(1, shutterSpeed % 100);
    this.writeCharacteristic(this.data.shutterCharId, buffer);
    console.log(`快门速度设置为 1/${shutterSpeed} 秒`);
  },
  // 拍照
  takePhoto() {
    const buffer = new ArrayBuffer(2);
    const dataView = new DataView(buffer);
    dataView.setUint8(0, 0x03);
    dataView.setUint8(1, 0x01);
    this.writeCharacteristic(this.data.photoCharId, buffer);
    console.log("拍照指令已发送");
  },
  handleNextAperture(){
    this.setAperture(nextAperture());
  },
  handleNextShutterSpeed(){
    this.setShutterSpeed(nextShutterSpeed());
  },
  handleTakePhoto(){
    this.takePhoto();
  },
  onReady: function () {
    wx.onBLECharacteristicValueChange((res) => {
      const receiveText = app.buf2string(res.value);
      console.log("特征值变化:", receiveText);
      if(res.characteristicId === this.data.apertureCharId){
        this.setData({ aperture: receiveText });
      } else if (res.characteristicId === this.data.isoCharId){
        this.setData({ iso: receiveText });
      } else if (res.characteristicId === this.data.shutterCharId){
        this.setData({ shutter: receiveText });
      }
    });
  }
});
bt/search/ search.js
const app = getApp()
Page({
  data: {
    title: "测光笔",
    list_height: wx.getWindowInfo().safeArea.height - 88,
    searching: false,
    devicesList: []
  },
  handleSearch() {
    if (!this.data.searching) {
      wx.closeBluetoothAdapter()
        .then(console.log)
        .catch(console.log)
      wx.openBluetoothAdapter()
        .then(() => {
          return wx.getBluetoothAdapterState().then(console.log)
        })
        .then(() => wx.startBluetoothDevicesDiscovery())
        .then((res) => {
          console.log(res)
          this.setData({
            searching: true,
            devicesList: []
          })
        }).catch((err) => {
          console.log(err)
          wx.showModal({
            title: '提示',
            content: '请检查手机蓝牙是否打开',
            showCancel: false,
            success(res) {
              wx.openAppAuthorizeSetting().then(console.log).catch(console.log)
              // wx.openSystemBluetoothSetting().then(console.log).catch(console.log)
              this.setData({
                searching: false
              })
            }
          })
        })
    } else {
      wx.stopBluetoothDevicesDiscovery().then(res => {
        console.log(res)
        this.setData({
          searching: false
        })
      })
    }
  },
  handleConnect(e) {
    wx.stopBluetoothDevicesDiscovery({
      success: (res) => {
        console.log(res)
        this.setData({
          searching: false
        })
      }
    })
    wx.showLoading({
      title: '连接蓝牙设备中...',
    })
    const { id, dataset } = e.currentTarget;
    wx.createBLEConnection({
      deviceId: id,
      success(res) {
        console.log("连接蓝牙设备成功" + res)
        wx.hideLoading()
        wx.showToast({
          title: '连接成功',
          icon: 'success',
          duration: 1000
        })
        wx.navigateTo({
          url: '../device/device?connectedDeviceId=' + e.currentTarget.id + '&name=' + encodeURIComponent(dataset.name)
        })
      },
      fail(res) {
        console.log(res)
        wx.hideLoading()
        wx.showModal({
          title: '提示',
          content: '连接失败',
          showCancel: false
        })
      }
    })
  },
  onLoad() {
    wx.authorize({ scope: 'scope.bluetooth' })
      .then(({ errMsg }) => {
        if (errMsg !== 'authorize:ok') {
          return wx.openSetting()
        }
      }).catch(console.log)

    wx.onBluetoothAdapterStateChange((res) => {
      console.log(res)
      this.setData({
        searching: res.discovering
      })
      if (!res.available) {
        this.setData({
          searching: false
        })
      }
    })

    wx.onBluetoothDeviceFound((devices) => {
      devices.devices
        .filter(v => v.name.trim())
        .forEach(newDevice => {
          // 转换 `advertisData` 为 Hex 字符串
          newDevice.advertisData = newDevice.advertisData
            ? app.buf2hex(newDevice.advertisData)
            : '';
          console.log("发现新设备:", newDevice);
          // 检查设备是否已存在于列表中
          const isExist = this.data.devicesList.some(
            (item) => item.deviceId === newDevice.deviceId
          );
          // 不存在则添加到设备列表
          if (!isExist) {
            this.data.devicesList.push(newDevice);
            this.setData({
              devicesList: this.data.devicesList,
            });
          }
        })
    });
  },
  onHide() {
    this.setData({
      devicesList: []
    })
    if (this.data.searching) {
      wx.stopBluetoothDevicesDiscovery().then((res) => {
        console.log(res)
        this.setData({
          searching: false
        })
      })
    }
  }
})
项目源码:https://github.com/netseye/Let-sDo3演示视频:(等转接板到了在进行完善)



共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]