K_b0KBsM · 8月2日

创客项目秀 | 基于 XIAO 开发板的PerceptionClock项目 (三)

编程:

首先,我需要确定在编写这段代码时需要实现的目标:
1、它需要在 ESP32 上运行;
2、使用Wi-Fi同步时间;
3、通过超声波传感器控制步进电机;
4、控制LED灯条的亮度。

转换为代码结构后,它包括以下结构
A.设置网络连接;
B.LED配置;
C.定时器配置;
D.实现多任务处理。

设置网络连接:

查找所有必需的库文件:

#include<Arduino.h>
#include <WiFi.h>
#include "Ultrasonic.h"
#include <String.h>
#include "FastLED.h"

首先介绍Arduino核心库,WiFi库用于网络功能,超声波库用于读取超声波测距传感器,String用于字符串处理,FastLED用于控制LED灯带。

Wi-Fi 设置

const char *ssid = "x.factory";             // Wi-Fi SSID
const char *password = "make0314";          // Wi-Fi 密码

此处设置了用于连接到 Wi-Fi 网络的 SSID 和密码。请注意,这里需要 2.4G 网络。如果没有,则需要提前在网络设置中设置。

此处设置了用于连接到 Wi-Fi 网络的 SSID 和密码。请注意,这里需要 2.4G 网络。如果没有,则需要提前在网络设置中设置。

LED配置

LED灯条配置

#define NUM_LEDS 100            
#define LED_DT 9                
#define LED_TYPE WS2812         
#define COLOR_ORDER GRB 

此代码定义了使用的 LED 数量,我在这里设置了 100 盏灯 ,连接到 ESP32 的数据引脚(我定义 GPIO9)、LED 类型和颜色序列。

LED亮度和颜色配置

uint8_t max_bright = 128;       
CRGB leds[NUM_LEDS];            
CRGB myRGBcolor(50,50,50);

设置 LED 的最大亮度(调试后,这里我设置亮度 128)并定义一个 CRGB 阵列来控制 LED 灯条的每个 LED。还定义了颜色变量 myRGBcolor。

其他硬件配置

Ultrasonic ultrasonic(4);
const int dirPin = 1;
const int stepPin = 2;
...

初始化步进电机的超声波传感器、方向和步进引脚。

定时器配置:

与时间相关的定义和变量

#define NTP1 "ntp1.aliyun.com"
...
String minute;
String second;
String hour;

定义 NTP 服务器地址和用于存储时间的字符串变量。
实现多任务处理:
多任务处理功能

xTaskOne 
xTaskTwo

这两个函数是 FreeRTOS 任务,分别用于循环测量距离和控制 LED 灯带的亮度。
Setup 函数:

void setup() {
  ...
  WiFi.begin(ssid, password);
  ...
  configTime(8 * 3600, 0, NTP1, NTP2, NTP3);
  ...
  xTaskCreate(...);
}

在设置功能中,初始化串口,设置步进电机控制引脚,连接Wi-Fi网络,获取网络时间,创建前面描述的任务。

其他功能:

loop 函数:
此功能包括时间同步和根据超声波传感器读数控制步进电机的旋转,以模拟时钟时针、分针和秒针的运动。
onestep用于步进电机的一步操作。 fast用于将步进电机快速旋转一整步。
getCurrentAngle_F和getCurrentAngle_S用于计算步进电机旋转的角度。
setClock 用于读取网络时间和更新时间变量。
经过多次测试和集成,最终的代码如下所示

#include<Arduino.h>
#include <WiFi.h>
#include "Ultrasonic.h"
#include <String.h>
#include "FastLED.h"           

//wifi config
const char *ssid = "x.factory";             // WIFI account
const char *password = "make0314";         // WIFI password

#define USE_MULTOCRE 0
#define NUM_LEDS 100            // set the num of led
#define LED_DT 9                // LED pin
#define LED_TYPE WS2812         // LED Light Strip Model
#define COLOR_ORDER GRB         

uint8_t max_bright = 128;       // LED max bright

CRGB leds[NUM_LEDS];            

//CRGB ColorName方法定义颜色                        
CRGB myRGBcolor(50,50,50);   // myRGBcolor(rValue,gValue,bValue)
                            // rValue: 0 - 255
                            // gValue: 0 - 255
                            // bValue: 0 - 255

Ultrasonic ultrasonic(4);
const int dirPin = 1;  // dir pin
const int stepPin = 2; // step pin
int num = 0;
const int STEPS_PER_REV = 1;
const float stepAngle = 0.9; // Step angle of stepper motor
const int microstepping = 1; // 1 = full step
long stepCount_S = 0;  // num of step fow slow

long stepCount_F = 0;  // num of step fow fast
long RangeInCentimeters = 1000;

int remember_min;  //rec min
int remember_sec;  // rec sec
int remember_hour; // rec hour

// Time info
#define NTP1 "ntp1.aliyun.com"
#define NTP2 "ntp2.aliyun.com"
#define NTP3 "ntp3.aliyun.com"


String minute;
String second;
String hour;

/*
Task one
  This function continuously measures the distance using an ultrasonic sensor,
  calculates the average of three measurements, and then prints the average distance.
*/
void xTaskOne(void *xTask1){

  int rangeMeasurements[3]; // Storage of three measurements
  int sum = 0; // For totalizing measured values

  while (1) {
    for (int i = 0; i < 3; i++) {  // Perform three measurements
      rangeMeasurements[i] = ultrasonic.MeasureInCentimeters(); // Get the distance in centimeters from the ultrasonic sensor
      sum += rangeMeasurements[i]; // Add the current measurement to the sum
      delay(250); 
    }

    // Calculation of the average value
    int averageRange = sum / 3;
    RangeInCentimeters = averageRange;

    sum = 0;
    delay(500); 
  }
  vTaskDelete(NULL);
}

/*
Task two
  This function is responsible for controlling the brightness of an LED strip.
*/
void xTaskTwo(void *xTask2){

  while (1) {
    // increase brightness
    for (double i = 20;i<150;i+=0.5){
      FastLED.setBrightness(i);     
      myRGBcolor.r = 50; 
      myRGBcolor.b = 50; 
      myRGBcolor.g = 50;
      fill_solid(leds, NUM_LEDS, myRGBcolor);   
      FastLED.show();      
      delay(10);  
    }
    // Reduced brightness
    for (double i = 150;i>20;i-=0.5){
      FastLED.setBrightness(i);     
      myRGBcolor.r = 50; 
      myRGBcolor.b = 50;
      myRGBcolor.g = 50;
      fill_solid(leds, NUM_LEDS, myRGBcolor);   
      FastLED.show();      
      delay(10);  
    }
  }
  vTaskDelete(NULL);
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  delay(500); 

  // Set the pin modes for the stepper motor control pins
  pinMode(stepPin,OUTPUT); 
  pinMode(dirPin,OUTPUT);

  // Set WiFi to station mode and attempt to connect to the specified network
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  // Set WiFi to station mode and attempt to connect to the specified network
  LEDS.addLeds<LED_TYPE, LED_DT, COLOR_ORDER>(leds, NUM_LEDS);  

  // Set the brightness of the LED strip to the maximum value
  FastLED.setBrightness(max_bright);                           

  // Wait for the WiFi connection to be established
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected!");
  configTime(8 * 3600, 0, NTP1, NTP2, NTP3);


#if !USE_MULTCORE
  // Create task one with a specified name, stack size, priority, and no parameters
  xTaskCreate(
    xTaskOne,/* Task function. */
    "TaskOne",/* String with name of task. */
    4096,/* Stack size in bytes.*/
    NULL,/* parameter passed as input of the task */
    1,/* priority of the task.(configMAx PRIORITIES - 1 being the highest, and @ being the lowest.) */
    NULL);/* Task handle.*/


  // Create task two with a specified name, stack size, priority, and no parameters
  xTaskCreate(
    xTaskTwo,/* Task function.*/
    "TaskTwo",/* String with name of task. */
    4096,/* Stack size in bytes.*/
    NULL,/* parameter passed as input of the task */
    2,/* priority of the task.(configMax PRIORITIES - 1 being the highest, and  being the lowest.) */
    NULL);  /* Task handle.*/

#else
  xTaskCreatepinnedToCore(xTaskOne,"TaskOne",4096,NULL,1,NULL,0);
  xTaskCreatepinnedToCore(xTaskTwo,"TaskTwo",4896,NULL,2,NULL,1);

#endif


}

void loop() {

  int current_angle;    // Variable to store the current angle of the actuator in 360 degree
  int getCurrentAngle; // Variable to store the total angle of the actuator

  if(RangeInCentimeters  > 300){
    setClock(); // Function to read the current time
    int num_hour = hour.toInt(); // Convert the current hour to an integer

    if(num_hour>12){
      num_hour = num_hour - 12; // Convert to 12-hour format if greater than 12
    }

    // Check if the current hour is different from the remembered hour
    if(num_hour != remember_hour){
      remember_hour = num_hour; // Update the remembered hour
      int target_angle = num_hour * 30; // Calculate the target angle based on the hour
      Serial.print("hour is, ");  
      Serial.println(num_hour);
      getCurrentAngle = getCurrentAngle_S(); // Get the current angle
      current_angle = getCurrentAngle % 360; // Update the current angle, wrapping around at 360 degrees

      // Determine the direction to rotate and perform the rotation
      if(target_angle > current_angle){
        for(int i = 0; i < (target_angle - current_angle)/0.9; i++){
          onestep();
          delay(10);
        }
      }else{
        for(int i = 0; i < (target_angle + 360 - current_angle)/0.9; i++){
          onestep();
        }
      }
      // getCurrentAngle = getCurrentAngle_S();
      // current_angle = getCurrentAngle % 360;
      // Serial.print("current angle is ");
      // Serial.println(current_angle);
    }

  }
  else if(RangeInCentimeters  < 300 && RangeInCentimeters  > 50){ // Minutes logic
    remember_hour = 60;
    setClock();
    int num_min = minute.toInt();
    if(num_min != remember_min){
      remember_min = num_min;
      int target_angle = num_min * 6;
      Serial.print("min is, ");
      Serial.println(num_min);
      getCurrentAngle = getCurrentAngle_S();
      current_angle = getCurrentAngle % 360;

      if(target_angle > current_angle){
        for(int i = 0; i < (target_angle - current_angle)/0.9; i++){
          onestep();
          delay(10);
        }
      }else{
        for(int i = 0; i < (target_angle + 360 - current_angle)/0.9; i++){
          onestep();
        }
      }
    }
 } else{ // Seconds logic
  remember_min = 70;
  remember_hour = 60;
  setClock();
  int num_sec = second.toInt();

  if(num_sec != remember_sec){
    remember_sec = num_sec;
    int target_angle = num_sec * 6;
    Serial.print("sec is, ");
    Serial.println(num_sec);
    getCurrentAngle = getCurrentAngle_S();
    current_angle = getCurrentAngle % 360;

    if(target_angle > current_angle){
      for(int i = 0; i < (target_angle - current_angle)/0.9; i++){
        onestep();
        // Serial.print(i);
        delay(10);
      }
    }else{
      for(int i = 0; i < (target_angle + 360 - current_angle)/0.9; i++){
        onestep();
      }
    }
    getCurrentAngle = getCurrentAngle_S();
    current_angle = getCurrentAngle % 360;
    Serial.print("current angle is ");
    Serial.println(current_angle);
  }

 }


}

void onestep(){

  digitalWrite(dirPin,HIGH); 
  digitalWrite(stepPin,HIGH); 
  delayMicroseconds(1000); 
  digitalWrite(stepPin,LOW); 
  delayMicroseconds(1000); 
  stepCount_S++; // update the num of step
  // delay(1000); 
}
void fast(){
digitalWrite(dirPin,HIGH);

  for(int x = 0; x < STEPS_PER_REV ; x++) {
    digitalWrite(stepPin,HIGH);
    delayMicroseconds(1000);
    digitalWrite(stepPin,LOW);
    delayMicroseconds(1000);
    stepCount_F++; 
  }

}


float getCurrentAngle_F() {
  return stepCount_F * (stepAngle / microstepping);
}
float getCurrentAngle_S() {
  return stepCount_S * (stepAngle / microstepping); 
}


//time_t now;
void setClock()
{
  struct tm timeInfo; 
  if (!getLocalTime(&timeInfo))
  { 
    Serial.println("Failed to obtain time");
    return;
  }
  //Serial.print(asctime(&timeInfo)); 
  // String date = WDAY_NAMES[timeInfo.tm_wday];
  // Serial.println(date.c_str());
  // sprintf_P(buff1, PSTR("%04d-%02d-%02d %s"), timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday, WDAY_NAMES[timeInfo.tm_wday].c_str());
  // String shuju = String(timeInfo.tm_year + 1900); 
  // shuju += "-";
  // // shuju += timeInfo.tm_mon + 1; 
  // // shuju += "-";
  // shuju += timeInfo.tm_mday; 
  // shuju += " ";
  // shuju += timeInfo.tm_hour; 
  // shuju += ":";
  // shuju += timeInfo.tm_min;
  // shuju += ":";
  // shuju += timeInfo.tm_sec;
  // shuju += " ";
  // // shuju += WDAY_NAMES[timeInfo.tm_wday].c_str(); 
  // Serial.println(shuju.c_str());
  minute = timeInfo.tm_min;
  second = timeInfo.tm_sec;
  hour = timeInfo.tm_hour;
}

外观设计:

image.jpg
image.jpg
image.jpg
image.jpg
image.jpg

集成:

以下为组装过程:
https://www.youku.com/video/X...

image.jpg
https://www.youku.com/video/X...

https://www.youku.com/video/X...

推荐阅读
关注数
6654
内容数
38
深度服务产业的国际化双创平台
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息