顯示具有 ArduinoIDE2 標籤的文章。 顯示所有文章
顯示具有 ArduinoIDE2 標籤的文章。 顯示所有文章

2023年12月27日 星期三

ESP32 3-axis controller

Purpose:
我利用手上沒在使用的三個滑軌和三個57步進馬達架構一個XYZ三軸方向的移動平台, 並使用ESP32作為3Axis controller控制移動方向. 有單軸指令, 例如 :X10  :Y10  :Z10跟三軸連動指令:P10,10,10

I used three unused slide rails and three 57 stepper motors to construct a mobile platform in the XYZ three-axis direction, and used ESP32 as the 3Axis controller to control the movement direction. There are single-axis instructions, for example: X10:Y10: Z10 and three-axis linkage command: P10,10,10

Architectures:  




Circuit:
Y axis set and Z axis set are the same method.

#define MACHINE_NAME            "ESP32_V4"

#define X_STEP_PIN              GPIO_NUM_12
#define X_DIRECTION_PIN         GPIO_NUM_14
#define Y_STEP_PIN              GPIO_NUM_26
#define Y_DIRECTION_PIN         GPIO_NUM_15
#define Z_STEP_PIN              GPIO_NUM_27
#define Z_DIRECTION_PIN         GPIO_NUM_33

#define X_LIMIT_PIN             GPIO_NUM_17
#define Y_LIMIT_PIN             GPIO_NUM_4
#define Z_LIMIT_PIN             GPIO_NUM_16


// OK to comment out to use pin for other features
#define STEPPERS_DISABLE_PIN    GPIO_NUM_13

#define SPINDLE_TYPE            SpindleType::PWM
#define SPINDLE_OUTPUT_PIN      GPIO_NUM_2   // labeled SpinPWM
#define SPINDLE_ENABLE_PIN      GPIO_NUM_22  // labeled SpinEnbl

#define COOLANT_MIST_PIN        GPIO_NUM_21  // labeled Mist
#define COOLANT_FLOOD_PIN       GPIO_NUM_25  // labeled Flood
#define PROBE_PIN               GPIO_NUM_32  // labeled Probe

Fundamental:

DM542 microstep driver
The DM542 is a new generation microstep stepper motor driver. Due to the adoption of the advanced bipolar constant-current chopper driver technology, it shows stable operation, provides excellent high torque. Moreover, it significantly reduces the noises and vibration of the operating motor. The DM542 has the feature of low-noise, low-vibration and low-heating. The DM542is DC18-50V power supply. It applies to 2-phase hybrid stepper motor under 4.2Acurrent The DM542 has 15 kinds of microsteps. The maximum step number is 25600 steps/rev (microstep is 1/128). The peak operating current ranges from 1.0A to 4.2A., and the output current has 8 stalls. The DM542 has automatic semi-flow, motor misconnected , over-voltage, under-voltage, andover-current protection functions.

Reference the previous article link on blog:
Arduino 控制三顆馬達

YouTube Demo:



ESP32 Code:
#include <AccelStepper.h>
#include <MultiStepper.h>
//--------- Flag structure --------------------------------------
typedef struct _vFlag
{
  uint8_t BTFlag = 0;
  uint8_t initial_Flag = 0;
} vFlag;
vFlag *flag_Ptr;
vFlag flag;

//------LED------------------
#define LED_BUILTIN 2
char ctemp[20];
//--------- uart structure --------------------------------------
//----------uart--------------
#define LINE_BUFFER_LENGTH 64
typedef struct _vUart
{
  char c;
  int lineIndex = 0;
  int line1Index = 0;
  int BTlineIndex = 0;
  bool lineIsComment;
  bool lineSemiColon;
  char line[128];
  char BTline[20];
  String inputString;
  String BTinputString;
  int V[16];
  char ctemp[30];
  char I2C_Data[80];
  float Voltage[16];
  int Buffer[128];
} vUart;
vUart *Uart_Ptr;
vUart Uart;
//--------- motor structure --------------------------------------
typedef struct _axis_state_t
{
  uint8_t pulPin[6]; //--X,Y,Z,A,B,C
  uint8_t dirPin[6];
  uint8_t enaPin[6];
  uint8_t HomePin[6];
  long RotatorAngle[6];
  boolean AXISFlag[6];
  uint8_t HomeFlag[6];
  uint8_t RotatorFlag[6];
  float FeedRate[6];
  float MaxSpeed[6];
  float Acceleration[6];
  int AXISNumber[2];
  int DC_Speed[6];
} axis_state_t;
axis_state_t *gc_Ptr;
axis_state_t gc;
//----------multi motor ---------------------------------------
MultiStepper steppers;
long positions[3];  
#define Stepper_ENABLE 13  
#define X_LIMIT_PIN    17
#define Y_LIMIT_PIN    4
#define Z_LIMIT_PIN    16
AccelStepper stepperX (1,12,14); // name of stepper motor (1 = driver, pin 2 = pulse, pin 3 = direction)  
AccelStepper stepperY (1,26,15); // name of stepper motor (1 = driver, pin 4 = step, pin 5 = direction)
AccelStepper stepperZ (1,27,33); // name of stepper motor (1 = driver, pin 6 = step, pin 7 = direction)
//------------------------------------------------------------------------------
//-------------------------------------
TaskHandle_t huart;
TaskHandle_t hfunction;
TaskHandle_t hmultimotor;  //TaskHandler

void MultiMotorTask(void *pvParameters);
void vUARTTask(void *pvParameters);
void vFunctionTask(void *pvParameters);
//------------------------------------------------------------------------------
void initial()
{
  Serial.println(F("Create Task"));
  //----------------------------------------------------------------------
  xTaskCreatePinnedToCore(
    vUARTTask, "UARTTask" // A name just for humans
    ,
    1024 // This stack size can be checked & adjusted by reading the Stack Highwater
    ,
    NULL, 3 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,
    &huart //handle
    ,
    0);

  //----------------------------------------------------------------------
  initMultiMotorTask();
  //----------------------------------------------------------------------
}
void initMultiMotorTask(void)
{
  // Create the task
  xTaskCreatePinnedToCore(MultiMotorTask, "MultiMotorTask", 1024, NULL, 3, &hmultimotor, 1);
  // Check the results
  if (hmultimotor == NULL)
  {
    Serial.println("Create MultiMotor task failed");
  }
  else
  {
    Serial.println("MultiMotor task up and running");
    vTaskSuspend(hmultimotor);
  }
}
//------------------------------------------------------------------------------
void MultiMotorTask(void *pvParameters)
{
  (void)pvParameters;

  while(1)
  {
    digitalWrite(Stepper_ENABLE, LOW);
    if (gc.AXISFlag[0] || gc.AXISFlag[1] || gc.AXISFlag[2])
    {
      if (gc.RotatorFlag[0] == 1)
      {
        //Serial.println( "1" );
        gc.RotatorAngle[0] = gc.RotatorAngle[0] * 500;
        //gc.RotatorAngle[0] = gc.RotatorAngle[0] * 100;   //F4設定
        positions[0] = gc.RotatorAngle[0];
      }
      else
      {
        positions[0] = stepperX.currentPosition();
      }

      if (gc.RotatorFlag[1] == 1)
      {
        //Serial.println( "2" );
        gc.RotatorAngle[1]=gc.RotatorAngle[1]*500;//F4設定
        positions[1] = gc.RotatorAngle[1];
      }
      else
      {
        positions[1] = stepperY.currentPosition();
      }

      if (gc.RotatorFlag[2] == 1)
      {
        //Serial.println( "3" );
        gc.RotatorAngle[2] = gc.RotatorAngle[2] * 100;
        //gc.RotatorAngle[2] = gc.RotatorAngle[2] * 250;//F4設定
        positions[2] = gc.RotatorAngle[2];
      }
      else
      {
        positions[2] = stepperZ.currentPosition();
      }
      //----multi motor
      //positions[0]=gc.RotatorAngle[0];
      //positions[1]=gc.RotatorAngle[1];
      //vTaskSuspend(hled);
      steppers.moveTo(positions);
      steppers.runSpeedToPosition();
      //steppers.run();

      if (abs(stepperX.currentPosition()) == abs(gc.RotatorAngle[0]))
      {
        gc.AXISFlag[0] = false;
        if (gc.RotatorFlag[0] == 1)
        {
          gc.AXISNumber[0]--;
        }
        Serial.println( "X_OK" );
        //vTaskSuspend( hmultimotor );
      }
      if (abs(stepperY.currentPosition()) == abs(gc.RotatorAngle[1]))
      {
        gc.AXISFlag[1] = false;
        if (gc.RotatorFlag[1] == 1)
        {
          gc.AXISNumber[0]--;
        }
        Serial.println( "Y_OK" );
        //vTaskSuspend( hmultimotor );
      }
      if (abs(stepperZ.currentPosition()) == abs(gc.RotatorAngle[2]))
      {
        gc.AXISFlag[2] = false;
        if (gc.RotatorFlag[2] == 1)
        {
          gc.AXISNumber[0]--;
        }
        Serial.println( "Z_OK" );
        //vTaskSuspend( hmultimotor );
      }
      if (gc.AXISNumber[0] == 0)
      {
        //vTaskResume(hled);
        for (int i = 0; i < 6; i++)
        {
          gc.RotatorFlag[i] = 0;
        }
        //vTaskSuspend(hmultimotor);
      }
    }
   
    vTaskDelay(1);
    //vTaskDelay(configTICK_RATE_HZ);
  }
}
void initMultiMotorSetup(void)
{
  gc.pulPin[0] = 26;
  gc.dirPin[0] = 25;
  //gc.HomePin[0] = 8;
  //gc.enaPin[0]=8;
  gc.pulPin[1] = 32;
  gc.dirPin[1] = 33;
  //gc.enaPin[1]=8;
  //gc.HomePin[1] = 9;
  gc.pulPin[2] = 34;
  gc.dirPin[2] = 35;
  //gc.enaPin[2]=8;
  //gc.HomePin[2] = 10;

  for (int i = 0; i < 6; i++)
  {
    //pinMode(gc.pulPin[i], OUTPUT);
    //pinMode(gc.dirPin[i], OUTPUT);
    //pinMode(gc.enaPin[i], OUTPUT);
    //digitalWrite(gc.enaPin[i], HIGH);

    gc.RotatorAngle[i] = 0;
    gc.AXISFlag[i] = false;
    gc.HomeFlag[i] = 0;
    gc.RotatorFlag[i] = 0;

    gc.FeedRate[i] = 1000;
    gc.MaxSpeed[i] = 4000;
    gc.Acceleration[i] = 800;
  }

  gc.AXISNumber[0] = 0;
  gc.AXISNumber[1] = 0;


  pinMode(Stepper_ENABLE, OUTPUT);
  digitalWrite(Stepper_ENABLE, HIGH);
  //pinMode(26, OUTPUT);
  stepperX.setMaxSpeed(4000);
  stepperX.setAcceleration(1000);
  stepperX.setSpeed(4000); //:f4000,4000,4000


  stepperY.setMaxSpeed(4000); //:ms8000,8000,8000
  stepperY.setAcceleration(1000);
  stepperY.setSpeed(1000);

  stepperZ.setMaxSpeed(4000);
  stepperZ.setAcceleration(800);
  stepperZ.setSpeed(2000);

  // Then give them to MultiStepper to manage
  steppers.addStepper(stepperX);
  steppers.addStepper(stepperY);
  steppers.addStepper(stepperZ);
}
//-------------------------------------------------
void setup()
{
  Serial.begin(9600);
  Serial.println(F("init"));
  initial();
  initMultiMotorSetup();
}
//-----------------------------------------
void loop()
{
  Serial.print(F("Main at core:"));
  Serial.println(xPortGetCoreID());
  while(1)
  {
    vTaskDelay(1);
  }
}
//----------------------------------------
void processCommand(char *data)
{
  int len, xlen, ylen, zlen, alen;
  int tempDIO;
  String stemp;

  len = Uart.inputString.length();
  //memset(ctemp, 0, sizeof(ctemp));
  //---------------------------------------
  if (strstr(data, "VER") != NULL)
  {
    Serial.println(F("ESP32_20231226"));
  }
  //-----------Motor----------------------
  if (strstr(data, ":P") != NULL)
  {
    //:p200,100,300
    for (int i = 2; i < len; i++)
    {
      ctemp[i - 2] = data[i];
      if (data[i] == ',')
      {
        xlen = i;
        ctemp[xlen] = '\0';
        gc.RotatorAngle[0] = atoi(ctemp);
        //Serial.println(xlen);
        //Serial.println(gc.RotatorAngle[0]);
        break;
      }
    }

    int j = (xlen + 1);
    for (int i = j; i < len; i++)
    {
      ctemp[i - j] = data[i];
      if (data[i] == ',')
      {
        ylen = i;
        ctemp[ylen] = '\0';
        gc.RotatorAngle[1] = atoi(ctemp);
        //Serial.print("debug- ");
        //Serial.println(gc.RotatorAngle[1]);
        break;
      }
    }
    int k = (ylen + 1);
    for (int i = k; i < len; i++)
    {
      zlen = i;
      ctemp[i - k] = data[i];
    }
    ctemp[zlen] = '\0';
    gc.RotatorAngle[2] = atoi(ctemp);
    //Serial.println(zlen);
    //Serial.println(gc.RotatorAngle[2]);

    gc.AXISFlag[0] = true;
    gc.AXISFlag[1] = true;
    gc.AXISFlag[2] = true;
    gc.RotatorFlag[0] = 1;
    gc.RotatorFlag[1] = 1;
    gc.RotatorFlag[2] = 1;
    gc.AXISNumber[0] = 3;
    vTaskResume(hmultimotor);
  }

  if (strstr(data, ":F"))
  {
    //:f4000,4000,2000
    for (int i = 2; i < len; i++)
    {
      Uart.ctemp[i - 2] = data[i];
      if (data[i] == ',')
      {
        xlen = i;
        Uart.ctemp[xlen] = '\0';
        gc.FeedRate[0] = atoi(Uart.ctemp);
        //Serial.println(xlen);
        //Serial.println(gc.RotatorAngle[0]);
        break;
      }
    }
    int j = (xlen + 1);
    for (int i = j; i < len; i++)
    {
      Uart.ctemp[i - j] = data[i];
      if (data[i] == ',')
      {
        ylen = i;
        Uart.ctemp[ylen] = '\0';
        gc.FeedRate[1] = atoi(Uart.ctemp);
        //Serial.println(ylen);
        //Serial.println(gc.RotatorAngle[1]);
        break;
      }
    }
    int k = (ylen + 1);
    for (int i = k; i < len; i++)
    {
      zlen = i;
      Uart.ctemp[i - k] = data[i];
    }
    Uart.ctemp[zlen] = '\0';
    gc.FeedRate[2] = atoi(Uart.ctemp);

    stepperX.setSpeed(gc.FeedRate[0]);
    stepperY.setSpeed(gc.FeedRate[1]);
    stepperZ.setSpeed(gc.FeedRate[2]);
  }

  if (strstr(data, ":MS"))
  {
    //float MaxSpeed[6];
    //:p200,100,300
    for (int i = 2; i < len; i++)
    {
      Uart.ctemp[i - 2] = data[i];
      if (data[i] == ',')
      {
        xlen = i;
        Uart.ctemp[xlen] = '\0';
        gc.MaxSpeed[0] = atoi(Uart.ctemp);
        //Serial.println(xlen);
        //Serial.println(gc.RotatorAngle[0]);
        break;
      }
    }
    int j = (xlen + 1);
    for (int i = j; i < len; i++)
    {
      Uart.ctemp[i - j] = data[i];
      if (data[i] == ',')
      {
        ylen = i;
        Uart.ctemp[ylen] = '\0';
        gc.MaxSpeed[1] = atoi(Uart.ctemp);
        //Serial.println(ylen);
        //Serial.println(gc.RotatorAngle[1]);
        break;
      }
    }
    int k = (ylen + 1);
    for (int i = k; i < len; i++)
    {
      zlen = i;
      Uart.ctemp[i - k] = data[i];
    }
    Uart.ctemp[zlen] = '\0';
    gc.MaxSpeed[2] = atoi(Uart.ctemp);

    stepperX.setMaxSpeed(gc.MaxSpeed[0]);
    stepperY.setMaxSpeed(gc.MaxSpeed[1]);
    stepperZ.setMaxSpeed(gc.MaxSpeed[2]);
  }
  if (strstr(data, ":ACC"))
  {
    //float Acceleration[6];
    //:ACC1000,1000,1000
    for (int i = 2; i < len; i++)
    {
      Uart.ctemp[i - 2] = data[i];
      if (data[i] == ',')
      {
        xlen = i;
        Uart.ctemp[xlen] = '\0';
        gc.Acceleration[0] = atoi(Uart.ctemp);
        //Serial.println(xlen);
        //Serial.println(gc.RotatorAngle[0]);
        break;
      }
    }
    int j = (xlen + 1);
    for (int i = j; i < len; i++)
    {
      Uart.ctemp[i - j] = data[i];
      if (data[i] == ',')
      {
        ylen = i;
        Uart.ctemp[ylen] = '\0';
        gc.Acceleration[1] = atoi(Uart.ctemp);
        //Serial.println(ylen);
        //Serial.println(gc.RotatorAngle[1]);
        break;
      }
    }
    int k = (ylen + 1);
    for (int i = k; i < len; i++)
    {
      zlen = i;
      Uart.ctemp[i - k] = data[i];
    }
    Uart.ctemp[zlen] = '\0';
    gc.Acceleration[2] = atoi(Uart.ctemp);

    stepperX.setAcceleration(gc.Acceleration[0]);
    stepperY.setAcceleration(gc.Acceleration[1]);
    stepperZ.setAcceleration(gc.Acceleration[2]);
  }

  if (strstr(data, ":X") != NULL)
  {
    gc.HomeFlag[0] = 3;
    gc.AXISFlag[0] = true;
    for (int i = 2; i < len; i++)
    {
      ctemp[i - 2] = data[i];
    }
    ctemp[len - 2] = '\0';
    gc.RotatorAngle[0] = atoi(ctemp);
    gc.RotatorFlag[0] = 1;
    //Serial.println(gc.RotatorAngle[0]);
    gc.AXISNumber[0] = 1;
    vTaskResume(hmultimotor);
  }
  if (strstr(data, ":Y") != NULL)
  {
    gc.AXISFlag[1] = true;
    for (int i = 2; i < len; i++)
    {
      ctemp[i - 2] = data[i];
    }
    ctemp[len - 2] = '\0';
    gc.RotatorAngle[1] = atoi(ctemp);
    //Serial.println(gc.RotatorAngle[1]);
    gc.RotatorFlag[1] = 1;

    gc.AXISNumber[0] = 1;
    vTaskResume(hmultimotor);
  }

  if (strstr(data, ":Z") != NULL)
  {
    gc.AXISFlag[2] = true;
    for (int i = 2; i < len; i++)
    {
      ctemp[i - 2] = data[i];
    }
    ctemp[len - 2] = '\0';
    gc.RotatorAngle[2] = atoi(ctemp);
    //Serial.println(gc.RotatorAngle[2]);
    gc.RotatorFlag[2] = 1;

    gc.AXISNumber[0] = 1;
    vTaskResume(hmultimotor);
  }
 
}
//-----------------------------------------

//-------------------------------------------
void vUARTTask(void *pvParameters)
{
  (void)pvParameters;

  Serial.print(F("UARTTask at core:"));
  Serial.println(xPortGetCoreID());
  for (;;)
  {
    while (Serial.available() > 0)
    {
      Uart.c = Serial.read();
 
      if ((Uart.c == '\n') || (Uart.c == '\r'))
      { // End of line reached
        if (Uart.lineIndex > 0)
        { // Line is complete. Then execute!
          Uart.line[Uart.lineIndex] = '\0'; // Terminate string
          //Serial.println( F("Debug") );
          //Serial.println( Uart.inputString );
          processCommand(Uart.line);
          Uart.lineIndex = 0;
          Uart.inputString = "";
        }
        else
        {
          // Empty or comment line. Skip block.
        }
        Uart.lineIsComment = false;
        Uart.lineSemiColon = false;
        Serial.println(F("ok>"));
      }
      else
      {
        //Serial.println( c );
        if ((Uart.lineIsComment) || (Uart.lineSemiColon))
        {
          if (Uart.c == ')')
            Uart.lineIsComment = false; // End of comment. Resume line.
        }
        else
        {
          if (Uart.c == '/')
          { // Block delete not supported. Ignore character.
          }
          else if (Uart.c == '~')
          { // Enable comments flag and ignore all characters until ')' or EOL.
            Uart.lineIsComment = true;
          }
          else if (Uart.c == ';')
          {
            Uart.lineSemiColon = true;
          }
          else if (Uart.lineIndex >= LINE_BUFFER_LENGTH - 1)
          {
            Serial.println("ERROR - lineBuffer overflow");
            Uart.lineIsComment = false;
            Uart.lineSemiColon = false;
          }
          else if (Uart.c >= 'a' && Uart.c <= 'z')
          { // Upcase lowercase
            Uart.line[Uart.lineIndex] = Uart.c - 'a' + 'A';
            Uart.lineIndex = Uart.lineIndex + 1;
            Uart.inputString += (char)(Uart.c - 'a' + 'A');
          }
          else
          {
            Uart.line[Uart.lineIndex] = Uart.c;
            Uart.lineIndex = Uart.lineIndex + 1;
            Uart.inputString += Uart.c;
          }
        }
      }
    } //while (Serial.available() > 0)
   
    vTaskDelay(1);
  }
}



2023年12月5日 星期二

DIY Voice controlled ESP32+WS2812 Strip

Purpose:
利用天貓精靈的語音指令去控制ESP32上的WS2812的燈條, 實現改變燈條顏色和燈條的亮度.
Use Tmall Genie's voice commands to control the WS2812 light strip on the ESP32 to change the color and brightness of the light strip
.
Fundamental:
WS2812 RGB LED, it is an LED with a built-in driver chip function, and uses a 5050 LED package to add a driver chip, and the driving method adopts serial input and output.
We will learn how to install WS2812 on an ESP32 board and use it to control a string of addressable LEDs.

Adafruit_NeoPixel.h Library

天貓精靈APP:


Bemfa topic define:
Reference https://cloud.bemfa.com/docs/#/
topic字段值说明:
根据topic字段后三位判断主题类型
当主题名字后三位是001时为插座设备。
When the last three digits of the topic name are 001, it is a socket device.
当主题名字后三位是002时为灯泡设备。
When the last three digits of the topic name are 002, it is the light bulb device.
当主题名字后三位是003时为风扇设备。
当主题名字后三位是004时为传感器设备。
当主题名字后三位是005时为空调设备。
当主题名字后三位是006时为开关设备。
当主题名字后三位是009时为窗帘设备。

Reference the previous article link on blog:
ESP32與WS2812的繽紛世界

Architectures:


YouTube Demo:

ESP32 Code:
#include <WiFi.h>
#include <WiFiClient.h>
#include <Adafruit_NeoPixel.h>

//巴法云服务器地址默认即可
#define TCP_SERVER_ADDR "bemfa.com"
//服务器端口,tcp创客云端口8344
#define TCP_SERVER_PORT "8344"

//********************需要修改的部分*******************//
#define DEFAULT_STASSID  "ras_2.4"     //WIFI名称,区分大小写,不要写错
#define DEFAULT_STAPSW   "181415181415"  //WIFI密码

String UID = "your UID of bemfa";  //用户私钥,可在控制台获取,修改为自己的UID
String TOPIC =   "fan003";         //主题名字,可在控制台新建
String TOPIC1 =   "adapter001";         //主题名字,可在控制台新建
String TOPIC2 =   "led002";         //主题名字,可在控制台新建
//const int LED_Pin = D2;              //单片机引脚
//------------------------------------------------
// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#define LED_PIN    27
// How many NeoPixels are attached to the Arduino?
#define LED_COUNT  180
// NeoPixel brightness, 0 (min) to 255 (max)
#define BRIGHTNESS 200 // Set BRIGHTNESS to about 1/5 (max = 255)
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
int colorIndex = 0;
int endIndex = 0;
String getcolor = "";
int colorvalue = 65280;
int beforecolorBrightness = 20;
int aftercolorBrightness = 20;
int colorflag = 0;
//-----Relay------------------
#define RELAY1       32
#define RELAY2       33
#define RELAY3       25
#define RELAY4       26
#define RELAY5       5
#define RELAY6       17
#define RELAY7       16
#define RELAY8       4
//**************************************************//
//最大字节数
#define MAX_PACKETSIZE 512
//设置心跳值30s
#define KEEPALIVEATIME 30*1000

//tcp客户端相关初始化,默认即可
WiFiClient TCPclient;
String TcpClient_Buff = "";
unsigned int TcpClient_BuffIndex = 0;
unsigned long TcpClient_preTick = 0;
unsigned long preHeartTick = 0;//心跳
unsigned long preTCPStartTick = 0;//连接
bool preTCPConnected = false;

//相关函数初始化
//连接WIFI
void doWiFiTick();
void startSTA();

//TCP初始化连接
void doTCPClientTick();
void startTCPClient();
void sendtoTCPServer(String p);

//风扇控制函数
void turnOn();
void turnOff();
int LEDFlag = 2;
#ifndef LED_BUILTIN
#define LED_BUILTIN 2
#endif
TaskHandle_t hled;
void initial()
{
  Serial.println(F("Create Task"));
  //--------------- create task----------------------------------
  xTaskCreatePinnedToCore(
    vLEDTask, "LEDTask" // A name just for humans
    ,
    1024 // This stack size can be checked & adjusted by reading the Stack Highwater
    ,
    NULL, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,
    &hled //handle
    ,
    0);
  //----------------------------------------
  //----------------------------------------------------------------------
  //vTaskSuspend(hfunction); //暫停TASK運行
  //----------------------------------------------------------------------
}
/*
  *发送数据到TCP服务器
 */
void sendtoTCPServer(String p){
 
  if (!TCPclient.connected())
  {
    Serial.println("Client is not readly");
    return;
  }
  TCPclient.print(p);
  Serial.println("[Send to TCPServer]:String");
  Serial.println(p);
}
/*
  *初始化和服务器建立连接
*/
void startTCPClient(){
  if(TCPclient.connect(TCP_SERVER_ADDR, atoi(TCP_SERVER_PORT))){
    Serial.print("\nConnected to server:");
    Serial.printf("%s:%d\r\n",TCP_SERVER_ADDR,atoi(TCP_SERVER_PORT));
   
    String tcpTemp="";  //初始化字符串
    String tcpTemp1="";  //初始化字符串
    String tcpTemp2="";  //初始化字符串
    tcpTemp = "cmd=1&uid="+UID+"&topic="+TOPIC+"\r\n"; //构建订阅指令
    sendtoTCPServer(tcpTemp); //发送订阅指令
    tcpTemp="";//清空
    tcpTemp1 = "cmd=1&uid="+UID+"&topic="+TOPIC1+"\r\n"; //构建订阅指令
    sendtoTCPServer(tcpTemp1); //发送订阅指令
    tcpTemp1="";//清空
    tcpTemp2 = "cmd=1&uid="+UID+"&topic="+TOPIC2+"\r\n"; //构建订阅指令
    sendtoTCPServer(tcpTemp2); //发送订阅指令
    tcpTemp2="";//清空

    preTCPConnected = true;
    preHeartTick = millis();
    TCPclient.setNoDelay(true);
  }
  else{
    Serial.print("Failed connected to server:");
    Serial.println(TCP_SERVER_ADDR);
    TCPclient.stop();
    preTCPConnected = false;
  }
  preTCPStartTick = millis();
}


/*
  *检查数据,发送心跳
*/
void doTCPClientTick(){
 //检查是否断开,断开后重连
   if(WiFi.status() != WL_CONNECTED) return;

  if (!TCPclient.connected()) {//断开重连

  if(preTCPConnected == true){

    preTCPConnected = false;
    preTCPStartTick = millis();
    Serial.println();
    Serial.println("TCP Client disconnected.");
    TCPclient.stop();
  }
  else if(millis() - preTCPStartTick > 1*1000)//重新连接
    startTCPClient();
  }
  else
  {
    if (TCPclient.available()) {//收数据
      char c =TCPclient.read();
      TcpClient_Buff +=c;
      TcpClient_BuffIndex++;
      TcpClient_preTick = millis();
     
      if(TcpClient_BuffIndex>=MAX_PACKETSIZE - 1){
        TcpClient_BuffIndex = MAX_PACKETSIZE-2;
        TcpClient_preTick = TcpClient_preTick - 200;
      }
      preHeartTick = millis();
    }
    if(millis() - preHeartTick >= KEEPALIVEATIME){//保持心跳
      preHeartTick = millis();
      Serial.println("--Keep alive:");
      sendtoTCPServer("cmd=0&msg=keep\r\n");
    }
  }
  if((TcpClient_Buff.length() >= 1) && (millis() - TcpClient_preTick>=200))
  {//data ready
    TCPclient.flush();
    Serial.println("Buff");
    Serial.println(TcpClient_Buff);
   
    if((TcpClient_Buff.indexOf("fan003") > 0))
    {
      if((TcpClient_Buff.indexOf("&msg=on") > 0))
      {
        turnOn();
      }
      else if((TcpClient_Buff.indexOf("&msg=off") > 0))
      {
        turnOff();
      }
    }
    //-------------------------------------------------------------
    if((TcpClient_Buff.indexOf("adapter001") > 0))
    {
      if((TcpClient_Buff.indexOf("&msg=on") > 0))
      {
        turnAdapterOn();
      }
      else if((TcpClient_Buff.indexOf("&msg=off") > 0))
      {
        turnAdapterOff();
      }
    }
    //-------------------------------------------------------------
    if((TcpClient_Buff.indexOf("led002") > 0))
    {
      //--------------------------------
      if((TcpClient_Buff.indexOf("&msg=on") > 0))
      {
        turnLedOn();
        if(colorflag == 0)
        {
          for(int i=0;i<LED_COUNT;i++)
          {
            strip.setPixelColor(i, strip.Color(255, 255, 255, beforecolorBrightness)); // Set white
          }
          strip.show();
          colorflag=1;
        }
        //----------------------------------
        //cmd=2&uid=1de98fdcc4554cfa8525aa2e08b74736&topic=led002&msg=on#80
        if(TcpClient_Buff.length() > 65 )
        {
          colorIndex = TcpClient_Buff.indexOf('#');
          endIndex = TcpClient_Buff.lastIndexOf('/r');
          getcolor = "";
          //c语言字符串查找,查找&msg=位置
          //int msgIndex = TcpClient_Buff.indexOf("&msg=");
          getcolor= TcpClient_Buff.substring(colorIndex+1,endIndex);
          beforecolorBrightness= getcolor.toInt();
          Serial.print( " voice colorBrightness--");
          Serial.println(beforecolorBrightness);
          aftercolorBrightness=map(beforecolorBrightness, 0, 100, 0, 255);
          Serial.print( " after colorBrightness--");
          Serial.println(aftercolorBrightness);
          strip.setBrightness(aftercolorBrightness);
          strip.show();
        }
//-- cmd=2&uid=1de98fdcc4554cfa8525aa2e08b74736&topic=led002&msg=on#100#16711680
        if(TcpClient_Buff.length() > 69)
        {
          colorIndex = TcpClient_Buff.lastIndexOf('#');
          endIndex = TcpClient_Buff.lastIndexOf('/r');
          getcolor = "";
          //c语言字符串查找,查找&msg=位置
          //int msgIndex = TcpClient_Buff.indexOf("&msg=");
          getcolor= TcpClient_Buff.substring(colorIndex+1,endIndex);
          colorvalue=getcolor.toInt();
          Serial.print( " colorvalue--");
          Serial.println(colorvalue);
          for(int i=0;i<LED_COUNT;i++)
          {
            strip.setPixelColor(i, colorvalue);
          }
          strip.setBrightness(aftercolorBrightness);
          strip.show();
        }       
      }
      else if((TcpClient_Buff.indexOf("&msg=off") > 0))
      {
        turnLedOff();
      }
    }
    TcpClient_Buff="";
    TcpClient_BuffIndex = 0;
  }
}

void startSTA(){
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
  WiFi.begin(DEFAULT_STASSID, DEFAULT_STAPSW);
}

/**************************************************************************
                       WIFI
***************************************************************************/
/*
  WiFiTick
  检查是否需要初始化WiFi
  检查WiFi是否连接上,若连接成功启动TCP Client
  控制指示灯
*/
void doWiFiTick()
{
  static bool startSTAFlag = false;
  static bool taskStarted = false;
  static uint32_t lastWiFiCheckTick = 0;

  if (!startSTAFlag) {
    startSTAFlag = true;
    startSTA();
    Serial.printf("Heap size:%d\r\n", ESP.getFreeHeap());
  }

  //未连接1s重连
  if ( WiFi.status() != WL_CONNECTED )
  {
    if (millis() - lastWiFiCheckTick > 1000)
    {
      lastWiFiCheckTick = millis();
    }
  }
  //连接成功建立
  else {
    if (taskStarted == false) {
      taskStarted = true;
      Serial.print("\r\nGet IP Address: ");
      Serial.println(WiFi.localIP());
      startTCPClient();
    }
  }
}
//打開fan
void turnOn(){
  Serial.println("Turn ON");
  //digitalWrite(LED_Pin,LOW);
  digitalWrite(RELAY1,LOW);
}
//关闭灯泡
void turnOff(){
  Serial.println("Turn OFF");
  digitalWrite(RELAY1,HIGH);
}
//打开灯泡
void turnAdapterOn(){
  Serial.println("Turn1 ON");
  //digitalWrite(LED_Pin,LOW);
  digitalWrite(RELAY2,LOW);
}
//关闭灯泡
void turnAdapterOff(){
  Serial.println("Turn1 OFF");
  digitalWrite(RELAY2,HIGH);
}
//打开灯泡
void turnLedOn()
{
  Serial.println("Turn2 ON");
  //digitalWrite(LED_Pin,LOW);
  digitalWrite(RELAY3,LOW);
}
//关闭灯泡
void turnLedOff(){
  Serial.println("Turn2 OFF");
  digitalWrite(RELAY3,HIGH);
 
  strip.clear();
  strip.show();
}
// 初始化,相当于main 函数
void setup() {
  Serial.begin(9600);
  Serial.println(F("init"));
  initial();

  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.setBrightness(beforecolorBrightness);
  strip.clear();          
  strip.show();
  delay(1000);

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(RELAY1,OUTPUT);
  digitalWrite(RELAY1,HIGH);
  pinMode(RELAY2,OUTPUT);
  digitalWrite(RELAY2,HIGH);
  pinMode(RELAY3,OUTPUT);
  digitalWrite(RELAY3,HIGH);
  pinMode(RELAY4,OUTPUT);
  digitalWrite(RELAY4,HIGH);
  pinMode(RELAY5,OUTPUT);
  digitalWrite(RELAY5,HIGH);
  pinMode(RELAY6,OUTPUT);
  digitalWrite(RELAY6,HIGH);
  pinMode(RELAY7,OUTPUT);
  digitalWrite(RELAY7,HIGH);
  pinMode(RELAY8,OUTPUT);
  digitalWrite(RELAY8,HIGH);

}

//循环
void loop() {
  if (WiFi.status() != WL_CONNECTED)
  {
    doWiFiTick();
  }
  doTCPClientTick();  
}
//-------------------------------------------------------------------------
static void vLEDTask(void *pvParameters)
{
  (void)pvParameters;
  int      head          = LED_COUNT - 1;
  int      tail          = 0;

  Serial.println(F("LEDTask at core:"));
  Serial.println(xPortGetCoreID());
  pinMode(LED_BUILTIN, OUTPUT);
  for (;;) // A Task shall never return or exit.
  {
    if(LEDFlag == 0)
    {
      digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
      vTaskDelay(800);
    }
    else if(LEDFlag == 1)
    {
      digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
      vTaskDelay(800);
      digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
      vTaskDelay(800);
    }
    else if(LEDFlag == 2)
    {
      digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
      vTaskDelay(300);
      digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
      vTaskDelay(300);
    }
    else
    {
      vTaskDelay(10);
    }
  }
}