OpenPNP – 使用主控板STM32_NUCLEO-144实现74路西门子飞达控制板

文章目录

  • openpnp – 74路西门子飞达控制板(主控板STM32_NUCLEO-144)实现
  • 概述
  • 飞达控制底板硬件电路
  • 程序的修改
  • 解决问题 – 同一个命令可以控制2个数字IO
  • 修改过的工程
  • SchultzController_NUCLEO_H723ZG.ino
  • config.h
  • gcode.ino
  • Feeder.cpp
  • Feeder.h
  • 备注
  • 备注
  • END
  • openpnp – 74路西门子飞达控制板(主控板STM32_NUCLEO-144)实现

    概述

    现在调试自己的openpnp设备, 在收尾, 将飞达控制板弄好, 能正常控制设备飞达安装平台上装满西门子二手飞达(52路飞达, 按照2x8mm飞达来算, 就是104个料位)的场景, 设备调试就基本完事了.

    设备刚到手时, 没有飞达控制板, 贴旧飞达控制板时, 用的散料飞达. 散料飞达能用, 不好用.
    贴新版飞达控制板时, 用的旧飞达控制板. 旧飞达控制板在我改了openpnp程序后, 好用, 只是飞达控制路数不够.
    新飞达控制板和旧飞达控制板子电路都相同, 写个程序就好了. 区别是换了STM32_NUCLEO-144的官方板子接口作为主控板接口, 底板可以支持74路, 满足我设备的需求了.

    主控板接口为STM32_NUCLEO-144的arduino接口, 可以选的STM32_NUCLEO-144官方板子很多. 我就直接用手头剩下的STM32_NUCLEO-144_723ZG, 如果选别的型号官方板子, 编程方面修改都是差不多的(STM32 Arduino库), 差别不大.

    新版飞达控制板成品效果如下:

    最后一个XHD2.54-2x10P插座上, 只有4路飞达通讯控制信号.

    对于我自己的设备, 有2个飞达挂壁面(设备前部, 设备后部).

    一个挂壁面有2块转接板(左右各一个), 一块转接板控制13个飞达.

    一个转接板有2个XHD2.54-2x10P的插座. 一个插座控制10路, 一个插座控制3路.
    为了方便, 直接1对一的做XHD2.54-2x10P的插座转接线, 依次连接到飞达控制板.
    里面有飞达挂壁上每个飞达的通讯控制线, 用程序挑出这些通讯线, 进行控制即可.

    上图是旧飞达控制板(主控板为mega2560r3官方板子)的截图, 上面有做好的通讯线, 一个插座上10根绿线(MCU_TX), 10根黄线(MCU_RX)
    等新飞达控制板程序编译过, 就可以接上所有的8个插座, 找出要通讯的飞达对应的通讯管脚号就行.

    飞达控制底板硬件电路

    每一路的子电路, 和官方开源的工程相同.

    STM32-NUCLEO-144的板子, 可以控制大概70~80个飞达. 我这里的电路连接, 控制74个飞达.
    MCU的管脚除了一些不能用的(e.g. VCC, GND, NC, IOREF…), 都用来控制飞达.





    引出的插座, 拿其中一个看看就行, 其他7个插座同理.

    程序的修改

    程序中要解决以下事情:

  • 将官方程序从mage2560r3的工程改为STM32 Arduino H7官方板子的工程, 不改编译不过. 初步已经做了实验(openpnp – use STM32 arduino on SchultzController), 解决了编译问题.
  • 将程序中对应的飞达通讯的信号捡出来, 给出对应飞达编号后, 可以控制.
  • 开始改程序了, 估计得用上几天.

    解决问题 – 同一个命令可以控制2个数字IO

        // --------------------------------------------------------------------------------
        // 插座 - CN7 - 右边第2个 - 后挂壁从左往右数第4把线
        // --------------------------------------------------------------------------------
            // FD_TX70/D11_CN7_14/数字IO号码 = 11
        FEEDER_UART_INFO(11, &softUartToFd_11), // 正对设备背面, 从右往左第10个飞达 M615 N84\r\n M615 N85\r\n
    
                // FD_TX69/D22_CN7_13/数字IO号码 = 22
        FEEDER_UART_INFO(22, &softUartToFd_22), // 正对设备背面, 从右往左第9个飞达 M615 N86\r\n M615 N87\r\n
    
        // 以上2把飞达, 发同一个命令(e.g. M615N84 都可以控制, 查一下是否这2个飞达的RX是否短路了)
        // 查到了, 是开发板的D11, D22短路了
        // 查一下电路板的原理图, 看看是否有跳线将这2个管脚短路了
        // 查了电路图和板子实物, 是这2个管脚之间有跳线连接...
    

    东西都连好, 开始逐一测试飞达通讯(只接一把飞达).
    到了D22, D11时, 发现这2个数字IO控制的都是同一把飞达.
    开始怀疑板子短路? 自己做的线有问题? 都不是.
    我一共焊接了2块飞达控制底板, 换了新的飞达控制底板, 还是如此.
    开始怀疑官方开发板上有跳线, 连接了这2个数字IO.
    先看版图, 点亮这2个数字IO所在的网络, 看看是否有间接的连接?

    看到有2个跳线, 间接的影响D22, D11之间的连接和控制.
    看原理图.

    看板子实物

    可以看到SB33本身就没焊接, 不会影响D22, D11.
    SB35焊接了, 导致D22, D11短接. 所以会出现2个数字IO控制的都是一个飞达.
    将SB35挑掉, 再试试飞达控制, 应该就是这个原因.
    试了一下, D22好使了, 但是D11不好使.
    看了原理图, 如果D11不通过SB35或者SB33, 那D11引出端就是悬空的.
    尝试将SB33用焊锡短接, 如下图, 再试试.

    试了, 不好使. 还是D22可以控制飞达, D11无法控制飞达.
    通过实验, 大概能猜出D22, D11都是D22驱动的. D11就没用了.那就只能将SB33, SB35全部断开, 如下图.
    操作D22, D11时, 都是D22那个引脚上的飞达生效, 说明STM32 Arduino库中, 就是这么实现的.
    为了不误操作, 还是断开SB33, 这样的话, 接在D11引出端子上的飞达就不会和D22上接的飞达一起动作.
    那就少控制一路飞达算了, 不是程序写的有问题, 是板子的arduino库不支持.

    逐个飞达航插接口都测试完了(测试时, 只有唯一的一把飞达插入), 除了D22, D11这里只能在D22上接飞达, D11无法接飞达. 没啥其他问题.
    就少了一把飞达而已. 现在可以接入51把飞达(2x8mm), 支持的子飞达数量为102个.
    因为航插板子布线的问题, 在8个插座中, 有 22(7 + 7 + 7 +1)根飞达发送线没用. 如果将可用的飞达通讯线(RX/TX)都接入这板子飞达控制板, 最多可以控制73把飞达, 支持的子飞达数量为146个.


    现在飞达控制板本身的代码移植和测试就完事了.

    修改过的工程

    SchultzController_NUCLEO_H723ZG.ino

    // @file SchultzController_NUCLEO_H723ZG.ino
    
    #include "config.h"
    #include "Feeder.h"
    
    FeederClass feeders[NUMBER_OF_FEEDERS];
    
    void setup() {
      int i = 0;
    
      UartDebug1.begin(SERIAL_BAUD);  // 调试串口1, 用来上报上报信息.
      UartDebugPrintf1(UART_LOG_LEVEL_VER, "UartDebug1.begin(%d);\r\n", SERIAL_BAUD);
      UartDebugPrintf1(UART_LOG_LEVEL_VER, "%s %s starting...\r\n", PRJ_NAME, PRJ_VER);
    
      UartDebug2.begin(SERIAL_BAUD);  // 调试串口2, 用来上报调试信息, 备用.
      UartDebugPrintf1(UART_LOG_LEVEL_VER, "UartDebug2.begin(%d);\r\n", SERIAL_BAUD);
    
      Serial.begin(SERIAL_BAUD);  // 物理串口, 用来连接上位机, 响应openpnp的命令
      UartDebugPrintf1(UART_LOG_LEVEL_VER, "Serial.begin(%d);\r\n", SERIAL_BAUD);
    
      SoftUartRecv.begin(FEEDER_BAUD);
      SoftUartRecv.listen();
      UartDebugPrintf1(UART_LOG_LEVEL_VER, "SoftUartRecv.begin(%d); and listen\r\n", SERIAL_BAUD);
    
      for (i = 0; i < NUMBER_OF_FEEDERS; i++) {
        feeders[i].setup((uint8_t)i, (uint8_t)(i / LANES_PER_PORT), (uint8_t)(i % LANES_PER_PORT));  // initialize with feeder number, port and lane
      }
    
      // setup listener to serial stream
      setupGCodeProc();
    
      UartDebugPrintf1(UART_LOG_LEVEL_VER, "%s %s ready.\r\n", PRJ_NAME, PRJ_VER);
    }
    
    void loop() {
      // Process incoming serial data and perform callbacks
      listenToSerialStream();
    }
    
    

    config.h

    // @file config.h
    
    #ifndef _CONFIG_h
    #define _CONFIG_h
    
    #include <arduino.h>
    
    #define PRJ_NAME "Schultz"
    #define PRJ_VER "v2.0.1.13_2023_1108_1327"
    
    // 宏 - 调试开关
    #define ENABLE_UART_DEBUG_PRINT1  // 调试串口1, 用来上报调试信息
    #define ENABLE_UART_DEBUG_PRINT2  // 调试串口2, 用来上报调试信息
    
    // 串口日志级别
    #define UART_LOG_LEVEL_INFO 0       // 信息
    #define UART_LOG_LEVEL_ERR 1        // 错误
    #define UART_LOG_LEVEL_COST_TIME 2  // 统计函数执行时间
    #define UART_LOG_LEVEL_VER 3        // 版本信息
    #define UART_LOG_CMD_PROC 4         // 命令处理入口, 看清上位机发的啥命令
    
    #define LANES_PER_PORT 2  // only supports 2 for now
    // The serial port = Feeder Number / LANES_PER_PORT
    // The lane within the port = Feeder Number % LANES_PER_PORT
    
    #define BOARD_NUCLEO_H723ZG
    
    #if defined(BOARD_NUCLEO_H723ZG)
    // 正式工程的子飞达数量(一把物理飞达有LANES_PER_PORT个子飞达)
    #define FEEDER_CNT 74
    #endif
    
    #define NUMBER_OF_FEEDERS (LANES_PER_PORT * FEEDER_CNT)  // number of ports(20) * LANES_PER_PORT
    
    /*
     *  Upstream Serial
     */
    // 让上位机的波特率提高, 可以提高通讯效率
    #define SERIAL_BAUD 115200  // 和上位机通讯的串口(开发板主串口, 上报信息用的软串口)波特率
    #define FEEDER_BAUD 9600    // 和飞达通讯的波特率, 西门子飞达的实际波特率是9600N81, 固定的, 无法更改.
    
    //buffer size for serial commands received
    #define MAX_BUFFFER_MCODE_LINE 64  // no line can be longer than this
    
    #define IN_BUFFER_ARY_CNT 4
    
    /* -----------------------------------------------------------------
    *  FEEDER COMMANDS
    *  ----------------------------------------------------------------- */
    #define CMD_INFO 0x01
    #define CMD_PRE_PICK 0x02
    #define CMD_ADVANCE 0x03
    #define CMD_STATUS 0x15
    #define CMD_EEPROM_READ 0x17
    #define CMD_SELF_TEST 0x18
    #define CMD_EEPROM_WRITE 0x19
    #define CMD_SET_PITCH 0x1c
    
    /* -----------------------------------------------------------------
    *  FEEDER STATUS
    *  ----------------------------------------------------------------- */
    #define STATUS_OK 0x40
    #define STATUS_NO_TAPE_TENSION 0x42  // cover tape won't tension (no tape, or broken tape)
    #define STATUS_NO_TAPE_TRIGGER 0x43  // didn't trigger a retension after 2 feeds
    #define STATUS_FEED_ERROR 0x44       // feed motor stuck
    #define STATUS_INVALID 0             // status not checked since last feed
    
    /* -----------------------------------------------------------------
    *  FEEDER COMM CONSTANTS
    *  ----------------------------------------------------------------- */
    #define RESP_TIMEOUT 100
    #define ACK_TIMEOUT 100
    #define RECV_BYTE_TIMEOUT 10
    
    #define MSG_ACK 0xE0
    
    /* -----------------------------------------------------------------
    *  M-CODES
    *  ----------------------------------------------------------------- */
    #define MCODE_DRIVER_INFO 115
    #define MCODE_PRE_PICK 600
    #define MCODE_ADVANCE 601
    #define MCODE_FEEDER_STATUS 602
    #define MCODE_GET_FEED_COUNT 603
    #define MCODE_CLEAR_FEED_COUNT 623
    #define MCODE_GET_ERR42_COUNT 604
    #define MCODE_GET_ERR43_COUNT 605
    #define MCODE_GET_ERR44_COUNT 606
    #define MCODE_GET_RESET_COUNT 607
    #define MCODE_GET_PITCH 608
    #define MCODE_TOGGLE_PITCH 628
    #define MCODE_GET_FEEDER_ID 610
    #define MCODE_SET_FEEDER_ID 640
    #define MCODE_READ_EEPROM 630
    #define MCODE_GET_FIRMWARE_INFO 615
    #define MCODE_START_SELF_TEST 650
    #define MCODE_STOP_SELF_TEST 651
    
    #define MCODE_CLEAR_RX_BUF 998
    #define MCODE_CMD_HELP 999
    
    //DEFINE config_h-ENDIF!!!
    #endif
    
    

    gcode.ino

    // @file gcode.ino
    
    #include "config.h"
    #include "Feeder.h"
    
    String inputBuffer[IN_BUFFER_ARY_CNT] = { "", "", "", "" };  // Buffer for incoming G-Code lines
    int bufPtr = 0;
    
    /**
    * Look for character /code/ in the inputBuffer and read the float that immediately follows it.
    * @return the value found.  If nothing is found, /defaultVal/ is returned.
    * @input code the character to look for.
    * @input defaultVal the return value if /code/ is not found.
    **/
    float parseParameter(String inBuf, char code, float defaultVal) {
      int codePosition = inBuf.indexOf(code);
      if (codePosition != -1) {
        //code found in buffer
    
        //find end of number (separated by " " (space))
        int delimiterPosition = inBuf.indexOf(" ", codePosition + 1);
    
        float parsedNumber = inBuf.substring(codePosition + 1, delimiterPosition).toFloat();
    
        return parsedNumber;
      } else {
        return defaultVal;
      }
    }
    
    void setupGCodeProc() {
      for (int i = 0; i < 4; i++) {
        inputBuffer[i].reserve(MAX_BUFFFER_MCODE_LINE);
      }
    }
    
    void sendAnswer(uint8_t error, String message) {
      switch (error) {
        case 0:
          {
            MainUartPrintf("ok %s\r\n", message.c_str());
            break;
          }
    
        case 1:
          {
            MainUartPrintf("error %s\r\n", message.c_str());
            break;
          }
    
        case 2:
          {
            MainUartPrintf("%s\r\n", message.c_str());
            MainUartPrintf("ok\r\n");
            break;
          }
      }
    }
    
    bool validFeederNo(int8_t signedFeederNo, uint8_t feederNoMandatory = 0) {
      bool b_rc = false;
    
      do {
        if (signedFeederNo == -1 && feederNoMandatory >= 1) {
          //no number given (-1) but it is mandatory.
          break;
        } else {
          //state now: number is given, check for valid range
          if (signedFeederNo < 0 || signedFeederNo > (NUMBER_OF_FEEDERS - 1)) {
            //error, number not in a valid range
            break;
          } else {
            //valid number
            b_rc = true;
            break;
          }
        }
      } while (0);
    
      if (!b_rc) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "signedFeederNo valid range = [0 ~ %d]\r\n", (NUMBER_OF_FEEDERS - 1));
      }
    
      return b_rc;
    }
    
    bool validPitch(int8_t pitch, uint8_t pitchMandatory = 0) {
      if (pitch == -1 && pitchMandatory >= 1) {
        //no number given (-1) but it is mandatory.
        return false;
      } else {
        //state now: number is given, check for valid range
        if (pitch < 0 || pitch > 1) {
          //error, number not in a valid range
          return false;
        } else {
          //valid number
          return true;
        }
      }
    }
    
    bool validFeederID(int32_t signedFeederID, uint8_t feederIDMandatory = 0) {
      if (signedFeederID == -1 && feederIDMandatory >= 1) {
        //no number given (-1) but it is mandatory.
        return false;
      } else {
        //state now: number is given, check for valid range
        if (signedFeederID < 0 || signedFeederID > 65535) {
          //error, number not in a valid range
          return false;
        } else {
          //valid number
          return true;
        }
      }
    }
    
    /**
    * Read the input buffer and find any recognized commands.  One G or M command per line.
    */
    void processCommand(String cmdBuf) {
      UartDebugPrintf1(UART_LOG_CMD_PROC, "processCommand(%s)\r\n", cmdBuf.c_str());
    
      //get the command, default -1 if no command found
      int cmd = parseParameter(cmdBuf, 'M', -1);
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "command found: M%d\r\n", cmd);
    
      switch (cmd) {
    
        // M115
        case MCODE_DRIVER_INFO:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_DRIVER_INFO\r\n");
            sendAnswer(2, "FIRMWARE_NAME: " PRJ_NAME ", FIRMWARE_VERSION: " PRJ_VER);
          }
          break;
    
        // M600
        case MCODE_PRE_PICK:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_PRE_PICK\r\n");
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
    
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", (int)signedFeederNo);
    
            //check for presence of a mandatory FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            //send pre pick command
            if (!feeders[signedFeederNo].sendPrePick()) {
              sendAnswer(1, "No acknowledge from feeder");
            } else {
              sendAnswer(0, "Shutter opened");
            }
          }
          break;
    
        // M601
        case MCODE_ADVANCE:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_ADVANCE\r\n");
    
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            int8_t overrideErrorRaw = (int)parseParameter(cmdBuf, 'X', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "overrideErrorRaw = %d\r\n", overrideErrorRaw);
    
            bool overrideError = false;
            if (overrideErrorRaw >= 1) {
              overrideError = true;
              UartDebugPrintf1(UART_LOG_LEVEL_INFO, "Argument X1 found, error will be ignored\r\n");
            }
    
            //check for presence of a mandatory FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            //send feed command
            if (!feeders[signedFeederNo].sendAdvance(overrideError)) {
              sendAnswer(1, "Unable to advance tape");
            } else {
              sendAnswer(0, "Tape advanced");
            }
          }
          break;
    
        // M602
        case MCODE_FEEDER_STATUS:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_FEEDER_STATUS\r\n");
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            //check for presence of FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            sendAnswer(0, feeders[signedFeederNo].reportStatus());
          }
          break;
    
        // M603
        case MCODE_GET_FEED_COUNT:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_GET_FEED_COUNT\r\n");
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            //check for presence of FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            uint8_t inBuf[24];
            for (int i = 0; i < 24; i++) {
              inBuf[i] = 0;
            }
    
            if (feeders[signedFeederNo].readEEPROM(inBuf)) {
              UartDebugPrintf1(UART_LOG_LEVEL_INFO, "inBuf(HEX) = [");
              for (int i = 0; i < 24; i++) {
                UartDebugPrintf1(UART_LOG_LEVEL_INFO, "%2.2X ", inBuf[i]);
              }
              UartDebugPrintf1(UART_LOG_LEVEL_INFO, "]\r\n");
    
              uint32_t counth = inBuf[5];
              counth <<= 16;
              uint32_t countm = inBuf[3];
              countm <<= 8;
              uint32_t count = counth + countm + inBuf[2];
              char countStr[22];
              sprintf(countStr, "Feed count: %lu", count);
              sendAnswer(0, countStr);
            } else {
              sendAnswer(1, " no response from feeder");
            }
          }
          break;
    
        // M623
        case MCODE_CLEAR_FEED_COUNT:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_CLEAR_FEED_COUNT\r\n");
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            //check for presence of FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            if (!feeders[signedFeederNo].clearFeedCount()) {
              sendAnswer(1, "Unable to clear feed count");
            } else {
              sendAnswer(0, "Feed count cleared");
            }
          }
          break;
    
        // M604
        case MCODE_GET_ERR42_COUNT:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_GET_ERR42_COUNT\r\n");
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            //check for presence of FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            uint8_t inBuf[24];
            for (int i = 0; i < 24; i++) {
              inBuf[i] = 0;
            }
            if (feeders[signedFeederNo].readEEPROM(inBuf)) {
              uint16_t count = inBuf[12] & 0x0f;
              count <<= 8;
              count += inBuf[8];
              char countStr[22];
              sprintf(countStr, "Error 42 count: %u", count);
              sendAnswer(0, countStr);
            } else {
              sendAnswer(1, " no response from feeder");
            }
          }
          break;
    
        // M605
        case MCODE_GET_ERR43_COUNT:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_GET_ERR43_COUNT\r\n");
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            //check for presence of FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            uint8_t inBuf[24];
            for (int i = 0; i < 24; i++) {
              inBuf[i] = 0;
            }
            if (feeders[signedFeederNo].readEEPROM(inBuf)) {
              uint16_t count = inBuf[12] & 0xf0;
              count <<= 4;
              count += inBuf[9];
              char countStr[22];
              sprintf(countStr, "Error 43 count: %u", count);
              sendAnswer(0, countStr);
            } else {
              sendAnswer(1, " no response from feeder");
            }
          }
          break;
    
        // M606
        case MCODE_GET_ERR44_COUNT:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_GET_ERR44_COUNT\r\n");
    
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            //check for presence of FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            uint8_t inBuf[24];
            for (int i = 0; i < 24; i++) {
              inBuf[i] = 0;
            }
            if (feeders[signedFeederNo].readEEPROM(inBuf)) {
              uint16_t count = inBuf[13] & 0x0f;
              count <<= 8;
              count += inBuf[10];
              char countStr[22];
              sprintf(countStr, "Error 44 count: %u", count);
              sendAnswer(0, countStr);
            } else {
              sendAnswer(1, " no response from feeder");
            }
          }
          break;
    
        // M607
        case MCODE_GET_RESET_COUNT:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_GET_RESET_COUNT\r\n");
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            //check for presence of FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            if (signedFeederNo % 2 != 0) {  // Reset count is only in the lane 1 field, so need to adjust if it is lane 2
              signedFeederNo--;
            }
    
            uint8_t inBuf[24];
            for (int i = 0; i < 24; i++) {
              inBuf[i] = 0;
            }
            if (feeders[signedFeederNo].readEEPROM(inBuf)) {
              uint16_t count = inBuf[13] & 0xf0;
              count <<= 4;
              count += inBuf[11];
              char countStr[22];
              sprintf(countStr, "Reset count: %u", count);
              sendAnswer(0, countStr);
            } else {
              sendAnswer(1, " no response from feeder");
            }
          }
          break;
    
        // M630
        case MCODE_READ_EEPROM:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_READ_EEPROM\r\n");
    
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            //check for presence of FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            uint8_t inBuf[24];
            for (int i = 0; i < 24; i++) {
              inBuf[i] = 0;
            }
    
            if (feeders[signedFeederNo].readEEPROM(inBuf)) {
              char hex[50];
              sprintf(hex, "%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X ", inBuf[0], inBuf[1], inBuf[2], inBuf[3], inBuf[4], inBuf[5], inBuf[6], inBuf[7], inBuf[8], inBuf[9], inBuf[10], inBuf[11], inBuf[12], inBuf[13], inBuf[14], inBuf[15]);
              sendAnswer(0, hex);
            } else {
              sendAnswer(1, " no response from feeder");
            }
          }
          break;
    
        // M610
        case MCODE_GET_FEEDER_ID:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_GET_FEEDER_ID\r\n");
    
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            //check for presence of FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            uint8_t inBuf[24];
            for (int i = 0; i < 24; i++) {
              inBuf[i] = 0;
            }
            bool left = true;
            if (signedFeederNo % 2 != 0) {  // ID is only in the lane 1 field, so need to adjust if it is lane 2
              left = false;
              signedFeederNo--;
            }
    
            if (feeders[signedFeederNo].readEEPROM(inBuf)) {
              uint16_t id = (inBuf[1] << 8) + inBuf[0];
              char idStr[22];
              sprintf(idStr, "ID: %u%s", id, (left ? "L" : "R"));
              sendAnswer(0, idStr);
            } else {
              sendAnswer(1, " no response from feeder");
            }
          }
          break;
    
        // M640
        case MCODE_SET_FEEDER_ID:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_SET_FEEDER_ID\r\n");
    
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            int32_t newFeederID = (int)parseParameter(cmdBuf, 'X', -1);
    
            //check for presence of FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            //must be run from lane 1 of feeder, so adjust.
            if ((signedFeederNo % 2) != 0) {
              --signedFeederNo;
            }
    
            //check for presence of FeederID
            if (!validFeederID(newFeederID, 1)) {
              sendAnswer(1, "feederID missing or invalid");
              break;
            }
    
    
            if (feeders[signedFeederNo].setID(newFeederID)) {
              if (feeders[signedFeederNo + 1].setID(0)) {  // need to clear 2nd lane also
                sendAnswer(0, "ID set");
              } else {
                sendAnswer(1, " no response from feeder");
              }
            } else {
              sendAnswer(1, " no response from feeder");
            }
          }
          break;
    
        // M608
        case MCODE_GET_PITCH:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_GET_PITCH\r\n");
    
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            //check for presence of FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            uint8_t inBuf[24];
            for (int i = 0; i < 24; i++) {
              inBuf[i] = 0;
            }
            if (feeders[signedFeederNo].readEEPROM(inBuf)) {
              char pitch[6];
              sprintf(pitch, "%d MM", (inBuf[4] == 0 ? 2 : 4));
              sendAnswer(0, pitch);
            } else {
              sendAnswer(1, " no response from feeder");
            }
          }
          break;
    
        // M628
        case MCODE_TOGGLE_PITCH:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_TOGGLE_PITCH\r\n");
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
            int8_t pitch;
    
            //check for presence of FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            uint8_t inBuf[24];
            for (int i = 0; i < 24; i++) {
              inBuf[i] = 0;
            }
            if (feeders[signedFeederNo].readEEPROM(inBuf)) {
              if (inBuf[4] == 0) {
                pitch = 1;
              } else {
                pitch = 0;
              }
              //send set pitch command
              if (!feeders[signedFeederNo].setPitch(pitch)) {
                sendAnswer(1, "Unable to set pitch");
              } else {
                sendAnswer(0, "Pitch set");
              }
    
            } else {
              sendAnswer(1, " no response from feeder");
            }
          }
          break;
    
        // M615
        case MCODE_GET_FIRMWARE_INFO:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_GET_FIRMWARE_INFO\r\n");
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            //check for presence of FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            uint8_t inBuf[64];
            for (int i = 0; i < 64; i++) {
              inBuf[i] = 0;
            }
            if (feeders[signedFeederNo].readInfo(inBuf)) {
              char version[25];
              sprintf(version, "Firmware version %d.%d", inBuf[2], inBuf[3]);
              sendAnswer(0, version);
            } else {
              sendAnswer(1, " no response from feeder");
            }
          }
          break;
    
        // M650
        case MCODE_START_SELF_TEST:
          {
            bool b_rc = false;
    
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_START_SELF_TEST\r\n");
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            //check for presence of a mandatory FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            //send self test command
            b_rc = feeders[signedFeederNo].startSelfTest();
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "startSelfTest() return b_rc = %s\r\n", b_rc ? "true" : "false");
    
            if (!b_rc) {
              sendAnswer(1, "err: No acknowledge from feeder");
            } else {
              sendAnswer(0, "ok: Self test started");
            }
          }
          break;
    
        // M651
        case MCODE_STOP_SELF_TEST:
          {
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_STOP_SELF_TEST\r\n");
            int8_t signedFeederNo = (int)parseParameter(cmdBuf, 'N', -1);
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "signedFeederNo = %d\r\n", signedFeederNo);
    
            //check for presence of a mandatory FeederNo
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
    
            //send self test command
            if (!feeders[signedFeederNo].stopSelfTest()) {
              sendAnswer(1, "No acknowledge from feeder");
            } else {
              sendAnswer(0, "Self test stopped");
            }
          }
          break;
    
        case MCODE_CLEAR_RX_BUF:
          {
            int8_t signedFeederNo = 0;
            UartDebugPrintf1(UART_LOG_LEVEL_INFO, "cmdproc : MCODE_CLEAR_RX_BUF\r\n");
            if (!validFeederNo(signedFeederNo, 1)) {
              sendAnswer(1, "feederNo missing or invalid");
              break;
            }
            feeders[signedFeederNo].clearRxBuf();
          }
          break;
    
        // M999
        case MCODE_CMD_HELP:
          {
            MainUartPrintf("command list below:\r\n");
            MainUartPrintf("M115 MCODE_DRIVER_INFO\r\n");
            MainUartPrintf("M600 MCODE_PRE_PICK\r\n");
            MainUartPrintf("M601 MCODE_ADVANCE e.g. M601N0X1 is ok, M601N0X0 is err\r\n");
            MainUartPrintf("M602 MCODE_FEEDER_STATUS\r\n");
            MainUartPrintf("M603 MCODE_GET_FEED_COUNT\r\n");
            MainUartPrintf("M623 MCODE_CLEAR_FEED_COUNT\r\n");
            MainUartPrintf("M604 MCODE_GET_ERR42_COUNT\r\n");
            MainUartPrintf("M605 MCODE_GET_ERR43_COUNT\r\n");
            MainUartPrintf("M606 MCODE_GET_ERR44_COUNT\r\n");
            MainUartPrintf("M607 MCODE_GET_RESET_COUNT\r\n");
            MainUartPrintf("M608 MCODE_GET_PITCH\r\n");
            MainUartPrintf("M628 MCODE_TOGGLE_PITCH\r\n");
            MainUartPrintf("M610 MCODE_GET_FEEDER_ID\r\n");
            MainUartPrintf("M640 MCODE_SET_FEEDER_ID\r\n");
            MainUartPrintf("M630 MCODE_READ_EEPROM\r\n");
            MainUartPrintf("M615 MCODE_GET_FIRMWARE_INFO\r\n");
            MainUartPrintf("M650 MCODE_START_SELF_TEST\r\n");
            MainUartPrintf("M651 MCODE_STOP_SELF_TEST\r\n");
            MainUartPrintf("M998 MCODE_CLEAR_RX_BUF\r\n");
            MainUartPrintf("M999 MCODE_CMD_HELP\r\n");
          }
          break;
    
    
        default:
          sendAnswer(0, "unknown or empty command ignored");
    
          break;
      }
    }
    
    void listenToSerialStream() {
    
      while (Serial.available()) {
    
        // get the received byte, convert to char for adding to buffer
        char receivedChar = (char)Serial.read();
    
        // add to buffer
        inputBuffer[bufPtr] += receivedChar;
    
        // if the received character is a newline, processCommand
        if (receivedChar == '\n') {
          UartDebugPrintf1(UART_LOG_LEVEL_INFO, "inputBuffer[%d] = [%s]\r\n", bufPtr, inputBuffer[bufPtr].c_str());
    
          int curBuf = bufPtr;
          // switch to next buffer while processing this one
          if (++bufPtr >= IN_BUFFER_ARY_CNT)
            bufPtr = 0;
    
          //remove comments
          inputBuffer[curBuf].remove(inputBuffer[curBuf].indexOf(";"));
          inputBuffer[curBuf].trim();
    
          processCommand(inputBuffer[curBuf]);
    
          //clear buffer
          inputBuffer[curBuf] = "";
        }
      }
    }
    
    

    Feeder.cpp

    // @file Feeder.cpp
    
    // @bugfix
    // fatal error: avr/interrupt.h: No such file or directory
    // #include <avr/interrupt.h>
    
    #include <avr/pgmspace.h>
    #include <Arduino.h>
    
    // @bugfix
    // fatal error: util/delay_basic.h: No such file or directory
    // #include <util/delay_basic.h>
    
    #include "Feeder.h"
    #include "config.h"
    
    // 用于飞达总接收的软串口
    // RX - D19_CN7_7
    SoftwareSerial SoftUartRecv(19, 19, false);
    
    // 2个辅助调试串口(软串口, 只能用于上报信息)
    
    // UART_DEBUG1_TX D16_CN7_1, UART_DEBUG1_RX D15_CN7_2
    SoftwareSerial UartDebug1(15, 16);
    
    // UART_DEBUG2_TX D17_CN7_3, UART_DEBUG2_RX D14_CN7_4
    SoftwareSerial UartDebug2(14, 17);
    
    // 向飞达发送用的软串口
    
    // FEEDER_UART_INFO(44, &softUartToFd_44), // FD_TX1/D44_CN8_4/数字IO号码 = 44
    SoftwareSerial softUartToFd_44(44, 44, true);
    
    // FEEDER_UART_INFO(43, &softUartToFd_43), // FD_TX2/D43_CN8_2/数字IO号码 = 43
    SoftwareSerial softUartToFd_43(43, 43, true);
    
    // FEEDER_UART_INFO(45, &softUartToFd_45), // FD_TX3/D45_CN8_6/数字IO号码 = 45
    SoftwareSerial softUartToFd_45(45, 45, true);
    
    // FEEDER_UART_INFO(46, &softUartToFd_46), // FD_TX4/D46_CN8_8/数字IO号码 = 46
    SoftwareSerial softUartToFd_46(46, 46, true);
    
    // FEEDER_UART_INFO(47, &softUartToFd_47), // FD_TX5/D47_CN8_10/数字IO号码 = 47
    SoftwareSerial softUartToFd_47(47, 47, true);
    
    // FEEDER_UART_INFO(48, &softUartToFd_48), // FD_TX6/D48_CN8_12/数字IO号码 = 48
    SoftwareSerial softUartToFd_48(48, 48, true);
    
    // FEEDER_UART_INFO(49, &softUartToFd_49), // FD_TX7/D49_CN8_14/数字IO号码 = 49
    SoftwareSerial softUartToFd_49(49, 49, true);
    
    // FEEDER_UART_INFO(50, &softUartToFd_50), // FD_TX8/D50_CN8_16/数字IO号码 = 50
    SoftwareSerial softUartToFd_50(50, 50, true);
    
    // FEEDER_UART_INFO(0xC0, &softUartToFd_0xC0), // FD_TX9/A0_CN9_1/数字IO号码 = 0xC0 + 0 = 0xC0
    SoftwareSerial softUartToFd_0xC0(0xC0, 0xC0, true);
    
    // FEEDER_UART_INFO(51, &softUartToFd_51), // FD_TX10/D51_CN9_2/数字IO号码 = 51
    SoftwareSerial softUartToFd_51(51, 51, true);
    
    // FD_TX11/A1_CN9/数字IO号码 = 0xC0 + (0xA1 - 0xA0) = 0xC1
    SoftwareSerial softUartToFd_0xC1(0xC1, 0xC1, true);
    
    // FD_TX12/D52_CN9_4/数字IO号码 = 52
    SoftwareSerial softUartToFd_52(52, 52, true);
    
    // FD_TX13/A2_CN9_5/数字IO号码 = 0xC0 + (0xA2 - 0xA0) = 0xC2
    SoftwareSerial softUartToFd_0xC2(0xC2, 0xC2, true);
    
    // FD_TX14/D53_CN9_6/数字IO号码 = 53
    SoftwareSerial softUartToFd_53(53, 53, true);
    
    // FD_TX15/A3_CN9_7/数字IO号码 = 0xC0 + (0xA3 - 0xA0) = 0xC3
    SoftwareSerial softUartToFd_0xC3(0xC3, 0xC3, true);
    
    // FD_TX16/D54_CN9_8/数字IO号码 = 54
    SoftwareSerial softUartToFd_54(54, 54, true);
    
    // FD_TX17/A4_CN9_9/数字IO号码 = 0xC0 + (0xA4 - 0xA0) = 0xC4
    SoftwareSerial softUartToFd_0xC4(0xC4, 0xC4, true);
    
    // FD_TX18/D55_CN9_10/数字IO号码 = 55
    SoftwareSerial softUartToFd_55(55, 55, true);
    
    // FD_TX19/A5_CN9_11/数字IO号码 = 0xC0 + (0xA5 - 0xA0) = 0xC5
    SoftwareSerial softUartToFd_0xC5(0xC5, 0xC5, true);
    
    // FD_TX20/D56_CN9_14/数字IO号码 = 56
    SoftwareSerial softUartToFd_56(56, 56, true);
    
    
    // FD_TX21/D57_CN9_16/数字IO号码 = 57
    SoftwareSerial softUartToFd_57(57, 57, true);
    
    // FD_TX22/D70_CN9_17/数字IO号码 = 70
    SoftwareSerial softUartToFd_70(70, 70, true);
    
    // FD_TX23/D58_CN9_18/数字IO号码 = 58
    SoftwareSerial softUartToFd_58(58, 58, true);
    
    // FD_TX24/D69_CN9_19/数字IO号码 = 69
    SoftwareSerial softUartToFd_69(69, 69, true);
    
    // FD_TX25/D59_CN9_20/数字IO号码 = 59
    SoftwareSerial softUartToFd_59(59, 59, true);
    
    // FD_TX26/D68_CN9_21/数字IO号码 = 68
    SoftwareSerial softUartToFd_68(68, 68, true);
    
    // FD_TX27/D60_CN9_22/数字IO号码 = 60
    SoftwareSerial softUartToFd_60(60, 60, true);
    
    // FD_TX28/D61_CN9_24/数字IO号码 = 61
    SoftwareSerial softUartToFd_61(61, 61, true);
    
    // FD_TX29/D67_CN9_25/数字IO号码 = 67
    SoftwareSerial softUartToFd_67(67, 67, true);
    
    // FD_TX30/D62_CN9_26/数字IO号码 = 62
    SoftwareSerial softUartToFd_62(62, 62, true);
    
    // FD_TX31/D66_CN9_27/数字IO号码 = 66
    SoftwareSerial softUartToFd_66(66, 66, true);
    
    // FD_TX32/D63_CN9_28/数字IO号码 = 63
    SoftwareSerial softUartToFd_63(63, 63, true);
    
    // FD_TX33/D65_CN9_29/数字IO号码 = 65
    SoftwareSerial softUartToFd_65(65, 65, true);
    
    // FD_TX34/D64_CN9_30/数字IO号码 = 64
    SoftwareSerial softUartToFd_64(64, 64, true);
    
    // FD_TX35/D32_CN10_29/数字IO号码 = 32
    SoftwareSerial softUartToFd_32(32, 32, true);
    
    // FD_TX36/D37_CN10_30/数字IO号码 = 37
    SoftwareSerial softUartToFd_37(37, 37, true);
    
    // FD_TX37/D33_CN10_31/数字IO号码 = 33
    SoftwareSerial softUartToFd_33(33, 33, true);
    
    // FD_TX38/D36_CN10_32/数字IO号码 = 36
    SoftwareSerial softUartToFd_36(36, 36, true);
    
    // FD_TX39/D34_CN10_33/数字IO号码 = 34
    SoftwareSerial softUartToFd_34(34, 34, true);
    
    // FD_TX40/D35_CN10_34/数字IO号码 = 35
    SoftwareSerial softUartToFd_35(35, 35, true);
    
    // FD_TX41/D38_CN10_28/数字IO号码 = 38
    SoftwareSerial softUartToFd_38(38, 38, true);
    
    // FD_TX42/D31_CN10_25/数字IO号码 = 31
    SoftwareSerial softUartToFd_31(31, 31, true);
    
    // FD_TX43/D39_CN10_26/数字IO号码 = 39
    SoftwareSerial softUartToFd_39(39, 39, true);
    
    // FD_TX44/D30_CN10_23/数字IO号码 = 30
    SoftwareSerial softUartToFd_30(30, 30, true);
    
    // FD_TX45/D40_CN10_24/数字IO号码 = 40
    SoftwareSerial softUartToFd_40(40, 40, true);
    
    // FD_TX46/D29_CN10_21/数字IO号码 = 29
    SoftwareSerial softUartToFd_29(29, 29, true);
    
    // FD_TX47/D28_CN10_19/数字IO号码 = 28
    SoftwareSerial softUartToFd_28(28, 28, true);
    
    // FD_TX48/D41_CN10_20/数字IO号码 = 41
    SoftwareSerial softUartToFd_41(41, 41, true);
    
    // FD_TX49/D42_CN10_18/数字IO号码 = 42
    SoftwareSerial softUartToFd_42(42, 42, true);
    
    // FD_TX50/D27_CN10_15/数字IO号码 = 27
    SoftwareSerial softUartToFd_27(27, 27, true);
    
    // FD_TX51/D0_CN10_16/数字IO号码 = 0
    SoftwareSerial softUartToFd_0(0, 0, true);
    
    // FD_TX52/D26_CN10_13/数字IO号码 = 26
    SoftwareSerial softUartToFd_26(26, 26, true);
    
    // FD_TX53/D1_CN10_14/数字IO号码 = 1
    SoftwareSerial softUartToFd_1(1, 1, true);
    
    // FD_TX54/A8_CN10_11/数字IO号码 = 0xC8
    SoftwareSerial softUartToFd_0xC8(0xC8, 0xC8, true);
    
    // FD_TX55/D2_CN10_12/数字IO号码 = 2
    SoftwareSerial softUartToFd_2(2, 2, true);
    
    // FD_TX56/A7_CN10_9/数字IO号码 = 0xC7
    SoftwareSerial softUartToFd_0xC7(0xC7, 0xC7, true);
    
    // FD_TX57/D3_CN10_10/数字IO号码 = 3
    SoftwareSerial softUartToFd_3(3, 3, true);
    
    // FD_TX58/A6_CN10_7/数字IO号码 = 0xC6
    SoftwareSerial softUartToFd_0xC6(0xC6, 0xC6, true);
    
    // FD_TX59/D4_CN10_8/数字IO号码 = 4
    SoftwareSerial softUartToFd_4(4, 4, true);
    
    // FD_TX60/D5_CN10_6/数字IO号码 = 5
    SoftwareSerial softUartToFd_5(5, 5, true);
    
    // FD_TX61/D6_CN10_4/数字IO号码 = 6
    SoftwareSerial softUartToFd_6(6, 6, true);
    
    // FD_TX62/D7_CN10_2/数字IO号码 = 7
    SoftwareSerial softUartToFd_7(7, 7, true);
    
    // FD_TX63/D25_CN7_19/数字IO号码 = 25
    SoftwareSerial softUartToFd_25(25, 25, true);
    
    // FD_TX64/D8_CN7_20/数字IO号码 = 8
    SoftwareSerial softUartToFd_8(8, 8, true);
    
    // FD_TX65/D24_CN7_17/数字IO号码 = 24
    SoftwareSerial softUartToFd_24(24, 24, true);
    
    // FD_TX66/D9_CN7_18/数字IO号码 = 9
    SoftwareSerial softUartToFd_9(9, 9, true);
    
    // FD_TX67/D23_CN7_15/数字IO号码 = 23
    SoftwareSerial softUartToFd_23(23, 23, true);
    
    // FD_TX68/D10_CN7_16/数字IO号码 = 10
    SoftwareSerial softUartToFd_10(10, 10, true);
    
    // FD_TX69/D22_CN7_13/数字IO号码 = 22
    SoftwareSerial softUartToFd_22(22, 22, true);
    
    // FD_TX70/D11_CN7_14/数字IO号码 = 11
    SoftwareSerial softUartToFd_11(11, 11, true);
    
    // FD_TX71/D21_CN7_11/数字IO号码 = 21
    SoftwareSerial softUartToFd_21(21, 21, true);
    
    // FD_TX72/D12_CN7_12/数字IO号码 = 12
    SoftwareSerial softUartToFd_12(12, 12, true);
    
    // FD_TX73/D20_CN7_9/数字IO号码 = 20
    SoftwareSerial softUartToFd_20(20, 20, true);
    
    // FD_TX74/D13_CN7_10/数字IO号码 = 13
    SoftwareSerial softUartToFd_13(13, 13, true);
    
    int MainUartPrintf(const char *format, ...) {
      int iRc = 0;
      char szBuf[0x100];
      va_list args;
    
      if (NULL == format) {
        return -1;
      }
    
      memset(szBuf, 0, sizeof(szBuf));
      va_start(args, format);
      iRc = vsnprintf(szBuf, sizeof(szBuf) - 1, format, args);
      va_end(args);
    
      if (iRc <= 0) {
        return iRc;
      }
    
      return Serial.printf(szBuf);
    }
    
    int UartDebugPrintf1(uint8_t log_level, const char *format, ...) {
    #ifndef ENABLE_UART_DEBUG_PRINT1
      return -1;
    #else
      int iRc = 0;
      char szBuf[0x100];
      va_list args;
    
      // 编译前, 进行日志级别的判断选择(串口通讯很浪费时间的), 只显示必要的串口日志.
      // 如果打开串口日志, 因为通讯用了时间, 对应的RESP_TIMEOUT那3个超时时间要加大, 否则会分析回包失败
    
      // 现在打开日志(UART_LOG_LEVEL_INFO), 会导致回包分析失败.
      // 估计是影响了软串口的发送或接收, 具体原因未知(现在程序逻辑改完了, 有一种能正常用的场景就行, 懒的查了, 现在都用了好几天了).
      // 想了一下, 应该是在软串口操作中间发串口日志, 会有关中断/开中断这些操作, 影响了软串口计时器的操作, 导致收包采样失误.
      // 所以要发串口日志, 也要在软串口逻辑不工作的时间段才行. e.g. 错误日志/开机的日志/统计时间的日志
      // 现在关了日志, 通讯响应速度嗖嗖的, 先这样
      // 开着日志跑, 估计和飞达也有关系. 我昨天用的不是这把飞达.
      // 现在打开日志(UART_LOG_LEVEL_INFO), 只能辅助理解程序流程, 正式程序时, 要屏蔽掉日志(UART_LOG_LEVEL_INFO)
      switch (log_level) {
        // 屏蔽case到的日志级别
        case UART_LOG_LEVEL_INFO:
          {
            return -1;
          }
          break;
        default:
          break;
      }
    
    
      if (NULL == format) {
        return -1;
      }
    
      memset(szBuf, 0, sizeof(szBuf));
      va_start(args, format);
      iRc = vsnprintf(szBuf, sizeof(szBuf) - 1, format, args);
      va_end(args);
    
      if (iRc <= 0) {
        return iRc;
      }
    
      return UartDebug1.printf(szBuf);
    #endif  // #ifdef FLAG_UART_DEBUG_PRINT1
    }
    
    int UartDebugPrintf2(uint8_t log_level, const char *format, ...) {
    #ifndef ENABLE_UART_DEBUG_PRINT2
      return -1;
    #else
      int iRc = 0;
      char szBuf[0x100];
      va_list args;
    
      if (NULL == format) {
        return -1;
      }
    
      memset(szBuf, 0, sizeof(szBuf));
      va_start(args, format);
      iRc = vsnprintf(szBuf, sizeof(szBuf) - 1, format, args);
      va_end(args);
    
      if (iRc <= 0) {
        return iRc;
      }
    
      return UartDebug2.printf(szBuf);
    #endif  // #ifndef FLAG_UART_DEBUG_PRINT2
    }
    
    SoftwareSerial *FeederClass::m_spSoftUartRecv = &SoftUartRecv;
    FeederClass::FeederClass() {
      this->m_pSoftUartSend = NULL;
    }
    
    // @bugfix H7的板子的硬件串口只有Serial, 没有Serial1
    // 如果想用到Serial之外的串口, 都只能是软串口
    // 将原来程序中的Serial1, 暂时都换成Serial(或者先注释掉好些), 先让程序编译过, 然后在将调试信息定向到底板原理图上对应的软串口上.
    // 这个Serial1是飞达底板的总接收, 先注释掉, 将程序编译过, 再按照原理图, 定义总接收的软串口, 然后再将Serial1的注释放开.
    // C:\Users\chenx\AppData\Local\Arduino15\packages\STMicroelectronics\hardware\stm32\2.6.0\variants\STM32H7xx\H723Z(E-G)I_H730ZBI_H733ZGI
    
    void FeederClass::setup(uint8_t _feederNo, uint8_t port, uint8_t lane) {
      this->feederNo = _feederNo;
      this->port = port;
      this->lane = lane + 1;  // lanes are numbered starting from 1
    
      FeederClass::setTX(port);
      FeederClass::begin(FEEDER_BAUD);  // serial baud rate
    
      UartDebugPrintf1(UART_LOG_LEVEL_VER, "FeederClass::setup(_feederNo = %d, port = %d, lane = %d), SoftUartToFdAry[%d].m_tx_dig_port_sn = %d), this->m_tx_pin = %d\r\n",
                       _feederNo,
                       port,
                       lane,
                       port,
                       SoftUartToFdAry[port].m_tx_dig_port_sn,
                       this->m_tx_pin);
    }
    
    bool FeederClass::receiveACK() {
      bool b_rc = false;
      uint8_t RXbuf[4];
    
      // 等回包/接收超时
      if (!waitForFeederResponseMessageByTimeout(ACK_TIMEOUT)) {
        return false;
      }
    
      RXbuf[0] = readOneByteFromFeederByTimeout(RECV_BYTE_TIMEOUT, b_rc);
      if (!b_rc) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "false = FeederClass::receiveACK() : recv : xx, xx, xx\r\n");
        return false;
      }
    
      RXbuf[1] = readOneByteFromFeederByTimeout(RECV_BYTE_TIMEOUT, b_rc);
      if (!b_rc) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "false = FeederClass::receiveACK() : recv : 0x%2.2X, xx, xx\r\n", RXbuf[0]);
        return false;
      }
    
      RXbuf[2] = readOneByteFromFeederByTimeout(RECV_BYTE_TIMEOUT, b_rc);
      if (!b_rc) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "false = FeederClass::receiveACK() : recv : 0x%2.2X, 0x%2.2X, xx\r\n", RXbuf[0], RXbuf[1]);
        return false;
      }
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "FeederClass::receiveACK() : recv : 0x%2.2X, 0x%2.2X, 0x%2.2X\r\n", RXbuf[0], RXbuf[1], RXbuf[2]);
    
      b_rc = ((RXbuf[0] == 1) && (RXbuf[1] == 0xE0) && (RXbuf[2] == 0xE1));
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "FeederClass::receiveACK() : b_rc = %s\r\n", (b_rc ? "true" : "false"));
    
      return b_rc;
    }
    
    void FeederClass::clearRxBuf(void) {
      uint8_t uc = '\0';
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, ">> FeederClass::clearRxBuf [");
      while (m_spSoftUartRecv->available()) {
        uc = m_spSoftUartRecv->read();
        UartDebugPrintf1(UART_LOG_LEVEL_INFO, "%2.2X ", uc);
      }
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "]\r\n");
    }
    
    bool FeederClass::waitForFeederResponseMessageByTimeout(int iTimeoutCnt) {
      this->lastTimeCommandSent = millis();
      while (!m_spSoftUartRecv->available()) {
        // RESP_TIMEOUT
        if ((millis() - this->lastTimeCommandSent) > iTimeoutCnt) {
          UartDebugPrintf1(UART_LOG_LEVEL_ERR, "false = FeederClass::waitForFeederResponseMessageByTimeout(%d)\r\n", iTimeoutCnt);
          return false;
        }
    
        delay(1);
      }
    
      return true;
    }
    
    uint8_t FeederClass::readOneByteFromFeederByTimeout(int iTimeoutCnt, bool &b_read_ok) {
      b_read_ok = false;
      uint8_t uc = 0x00;
    
      if (iTimeoutCnt <= 0) {
        b_read_ok = true;
      } else {
        this->lastTimeCommandSent = millis();
        do {
          b_read_ok = m_spSoftUartRecv->available();
          if (b_read_ok) {
            break;
          }
    
          // RESP_TIMEOUT
          if ((millis() - this->lastTimeCommandSent) > iTimeoutCnt) {
            UartDebugPrintf1(UART_LOG_LEVEL_ERR, "false = FeederClass::readOneByteFromFeederByTimeout(%d, b_read_ok)\r\n", iTimeoutCnt);
            break;
          }
    
          delay(1);
        } while (true);
      }
    
      if (!b_read_ok) {
        return 0xFF;
      }
    
      uc = (uint8_t)m_spSoftUartRecv->read();
      // uc = ~uc;
      return uc;
    }
    
    bool FeederClass::receiveMessage(uint8_t *dataBuf) {
      uint8_t RXbuf[4];
      uint8_t inChar;
      uint8_t msgLen = 0;
      uint8_t RXckSum = 0;
      bool gotError = false;
      bool b_rc = false;
      uint8_t i = 0;
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "FeederClass::receiveMessage(%p)\r\n", dataBuf);
      if (!waitForFeederResponseMessageByTimeout(RESP_TIMEOUT)) {
        return false;
      }
    
      // expecting ack message
      memset(RXbuf, 0, sizeof(RXbuf));
      RXbuf[0] = readOneByteFromFeederByTimeout(RECV_BYTE_TIMEOUT, b_rc);
      if (!b_rc) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "false = FeederClass::receiveMessage() : recv :xx xx xx\r\n");
        return false;
      }
    
      RXbuf[1] = readOneByteFromFeederByTimeout(RECV_BYTE_TIMEOUT, b_rc);
      if (!b_rc) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "false = FeederClass::receiveMessage() : recv : 0x%2.2X xx xx\r\n", RXbuf[0]);
        return false;
      }
    
      RXbuf[2] = readOneByteFromFeederByTimeout(RECV_BYTE_TIMEOUT, b_rc);
      if (!b_rc) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "false = FeederClass::receiveMessage() : recv : 0x%2.2X 0x%2.2X xx\r\n", RXbuf[0], RXbuf[1]);
        return false;
      }
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "FeederClass::receiveMessage() : recv : 0x%2.2X 0x%2.2X 0x%2.2X\r\n", RXbuf[0], RXbuf[1], RXbuf[2]);
    
      gotError = (RXbuf[0] != 1) || (RXbuf[1] != 0xE0) || (RXbuf[2] != 0xE1);
    
      // followed by response message
      if (!waitForFeederResponseMessageByTimeout(RESP_TIMEOUT)) {
        return false;
      }
    
      // get message length
      inChar = readOneByteFromFeederByTimeout(RECV_BYTE_TIMEOUT, b_rc);
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "inChar = %d\r\n", inChar);
      if (!b_rc) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "error - get message length\r\n");
        return false;
      }
    
      msgLen = inChar + 1;
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "recv msgLen = %d\r\n", msgLen);
    
      if ((msgLen > 1) && (msgLen < 64)) {  // valid message is 1 to 64 bytes, otherwise ignore it
        dataBuf[0] = inChar;                // store length
    
        UartDebugPrintf1(UART_LOG_LEVEL_INFO, "recv(HEX) : ");
        for (i = 1; i <= msgLen; i++) {
          dataBuf[i] = readOneByteFromFeederByTimeout(RECV_BYTE_TIMEOUT, b_rc);
          if (!b_rc) {
            break;
          }
          UartDebugPrintf1(UART_LOG_LEVEL_INFO, "0x%2.2X ", dataBuf[i]);
        }
        UartDebugPrintf1(UART_LOG_LEVEL_INFO, "\r\n");
    
        if (!b_rc) {
          UartDebugPrintf1(UART_LOG_LEVEL_ERR, "can't get msg content\r\n");
          return false;
        }
    
        /*
        inChar = 19
        recv msgLen = 20
        recv(HEX) : 0xF5 0x01 0x00 0x7C 0x0C 0xE0 0x00 0xFF 0x00 0x31 0x01 0xB5 0x43 0x1B 0x89 0xFF 0xA2 0x00 0x00 0xDF 
        0x00 != 0xAB,  Checksum failed!
        7 No ACK from feeder
        */
    
        /*
        // M602 N19\r\n
        FeederClass::receiveMessage() : recv : 0x01 0xE0 0xE1
        inChar = 2
        recv msgLen = 3
        recv(HEX) : 0x40 0x02 0x44 
        RXckSum, 0x86 != dataBuf[msgLen - 1], 0x44,  Checksum failed!
        */
    
        // 1字节(后面的数据字节数, 不包括校验和) + N字节(数据) + 1字节(累加和)
        // what array content to be verify?
        UartDebugPrintf1(UART_LOG_LEVEL_INFO, "dataBuf[%d] : ", msgLen + 1);
        for (i = 0; i <= msgLen; i++) {
          UartDebugPrintf1(UART_LOG_LEVEL_INFO, "0x%2.2X, ", dataBuf[i]);
          RXckSum += dataBuf[i];
        }
        UartDebugPrintf1(UART_LOG_LEVEL_INFO, "\r\n");
    
        // verify checksum
        RXckSum = 0;
        for (i = 0; i < msgLen; i++) {
          RXckSum += dataBuf[i];
        }
    
        if (RXckSum != dataBuf[msgLen]) {  // verify checksum
          UartDebugPrintf1(UART_LOG_LEVEL_ERR, "RXckSum, 0x%2.2X != dataBuf[msgLen], 0x%2.2X,  Checksum failed!\r\n", RXckSum, dataBuf[msgLen]);
          gotError = true;
        }
        return !gotError;
      } else {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "err : msgLen must > 1 && < 64\r\n");
        return false;
      }
    }
    
    bool FeederClass::sendCommand(uint8_t command) {
      uint32_t tick = millis();
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "1 FeederClass::sendCommand(%d)\r\n", command);
      uint8_t cmdLen = 2;
      uint8_t buf[] = { cmdLen, command, this->lane, 0 };
      uint8_t i;
      uint8_t checksum = 0;
      bool b_rc = false;
    
      // calculate checksum
      for (i = 0; i < cmdLen + 1; i++) {
        checksum += buf[i];
      }
    
      buf[i] = checksum;
    
      clearRxBuf();
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "sending to port[%d],  m_tx_pin[%d] =>(HEX) ", this->port, this->m_tx_pin);
      for (i = 0; i < cmdLen + 2; i++) {
        UartDebugPrintf1(UART_LOG_LEVEL_INFO, "%2.2X ", buf[i]);
      }
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "\r\n");
    
      for (i = 0; i < cmdLen + 2; i++) {
        FeederClass::write(buf[i]);
      }
    
      b_rc = FeederClass::receiveACK();
    
      tick = millis() - tick;
      UartDebugPrintf1(UART_LOG_LEVEL_COST_TIME, "1 use %d(ms), FeederClass::sendCommand(%d)\r\n", tick, command);
    
      return b_rc;
    }
    
    bool FeederClass::sendCommand(uint8_t command, uint8_t *dataBuf) {
      uint32_t tick = millis();
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "2 FeederClass::sendCommand(%d, %p)\r\n", command, dataBuf);
      uint8_t cmdLen = 2;
      uint8_t buf[] = { cmdLen, command, this->lane, 0 };
      uint8_t i;
      uint8_t checksum = 0;
      bool b_rc = false;
    
      // calculate checksum
      for (i = 0; i < cmdLen + 1; i++) {
        checksum += buf[i];
      }
    
      buf[i] = checksum;
    
      clearRxBuf();
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "sending to port[%d], m_tx_pin[%d] =>(HEX) ", this->port, this->m_tx_pin);
      for (i = 0; i < cmdLen + 2; i++) {
        UartDebugPrintf1(UART_LOG_LEVEL_INFO, "%2.2X ", buf[i]);
      }
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "\r\n");
    
      for (i = 0; i < cmdLen + 2; i++) {
        FeederClass::write(buf[i]);
      }
    
      b_rc = FeederClass::receiveMessage(dataBuf);
    
      tick = millis() - tick;
      UartDebugPrintf1(UART_LOG_LEVEL_COST_TIME, "2 use %d(ms), FeederClass::sendCommand(%d, 0x%p)\r\n", tick, command, dataBuf);
    
      return b_rc;
    }
    
    bool FeederClass::sendCommand(uint8_t command, uint8_t *dataBuf, uint8_t offset) {
      uint32_t tick = millis();
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "3 FeederClass::sendCommand(%d, %p, %d)\r\n", command, dataBuf, offset);
      uint8_t cmdLen = 3;
      uint8_t buf[] = { cmdLen, command, this->lane, offset, 0 };
      uint8_t i;
      uint8_t checksum = 0;
      bool b_rc = false;
    
      // calculate checksum
      for (i = 0; i < cmdLen + 1; i++) {
        checksum += buf[i];
      }
    
      buf[i] = checksum;
    
      clearRxBuf();
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "sending to port[%d], m_tx_pin[%d] =>(HEX) ", this->port, m_tx_pin);
      for (i = 0; i < cmdLen + 2; i++) {
        UartDebugPrintf1(UART_LOG_LEVEL_INFO, "%2.2X ", buf[i]);
      }
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "\r\n");
    
      for (i = 0; i < cmdLen + 2; i++) {
        FeederClass::write(buf[i]);
      }
    
      b_rc = FeederClass::receiveMessage(dataBuf);
    
      tick = millis() - tick;
      UartDebugPrintf1(UART_LOG_LEVEL_COST_TIME, "3 use %d(ms), FeederClass::sendCommand(%d)\r\n", tick, command);
    
      return b_rc;
    }
    
    bool FeederClass::sendCommand(uint8_t command, uint8_t dataLen, uint8_t *data) {
      uint32_t tick = millis();
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "4 FeederClass::sendCommand(%d, %d, %p)\r\n", command, dataLen, data);
    
      uint8_t msgLen = dataLen + 2;
      uint8_t buf[msgLen + 2];
      uint8_t i;
      uint8_t checksum = 0;
      bool b_rc = false;
    
      buf[0] = msgLen;
      buf[1] = command;
      buf[2] = this->lane;
    
      for (i = 0; i < dataLen; i++) {
        buf[i + 3] = data[i];
      }
    
      // calculate checksum
      for (i = 0; i < msgLen + 1; i++) {
        checksum += buf[i];
      }
    
      buf[i] = checksum;
    
      clearRxBuf();
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "sending to port[%d], m_tx_pin[%d] => (HEX) ", this->port, m_tx_pin);
      for (i = 0; i < msgLen + 2; i++) {
        UartDebugPrintf1(UART_LOG_LEVEL_INFO, "%2.2X ", buf[i]);
      }
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "\r\n");
    
      for (i = 0; i < msgLen + 2; i++) {
        FeederClass::write(buf[i]);
      }
    
      b_rc = FeederClass::receiveACK();
    
      tick = millis() - tick;
      UartDebugPrintf1(UART_LOG_LEVEL_COST_TIME, "4 use %d(ms), FeederClass::sendCommand(%d, %d, 0x%p))\r\n", tick, command, dataLen, data);
    
      return b_rc;
    }
    
    bool FeederClass::sendPrePick() {
      uint8_t dataBuf[6];
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "send Pre-Pick command\r\n");
    
      if (!FeederClass::sendCommand(CMD_PRE_PICK, dataBuf)) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "1 No ACK from feeder\r\n");
        return false;
      }
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "Received ACK, check status\r\n");
    
      this->feederStatus = dataBuf[1];  // should verify that byte 2 matches the lane?
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "feederStatus(dataBuf[1]) = 0x%2.2X, %s\r\n", this->feederStatus, this->showStatus().c_str());
    
      return true;
    }
    
    bool FeederClass::sendAdvance(bool overrideError) {
      if (this->feederStatus == STATUS_INVALID) {  // need to read status from feeder if it is not up to date
        FeederClass::getFeederStatus();
      }
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "advance triggered showStatus : %s\r\n", this->showStatus().c_str());
    
      //check whether feeder is OK before every advance command
      if (this->feederStatus != STATUS_OK) {
        //feeder is in error state, usually this would lead to exit advance with false and no advancing command sent
    
        if (!overrideError) {
          //error, and error was not overridden -> return false, advance not successful
          UartDebugPrintf1(UART_LOG_LEVEL_ERR, "error, and error was not overridden -> return false, advance not successful\r\n");
          return false;
        } else {
          UartDebugPrintf1(UART_LOG_LEVEL_INFO, "overridden error temporarily\r\n");
        }
      }
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "send advance command\r\n");
    
      if (!FeederClass::sendCommand(CMD_ADVANCE)) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "2 No ACK from feeder\r\n");
        return false;
      }
      return true;
    }
    
    bool FeederClass::setPitch(uint8_t pitch) {
      uint8_t dataBuf[22];
    
      for (uint8_t i = 0; i < 22; i++) {
        dataBuf[i] = 0;
      }
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "Set pitch to %dmm\r\n", pitch);
    
      dataBuf[0] = pitch;
      dataBuf[1] = 0;
    
      if (!FeederClass::sendCommand(CMD_SET_PITCH, 1, dataBuf)) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "3 No ACK from feeder\r\n");
        return false;
      }
    
      // update pitch field in eeprom
      //   Read current EEPROM data
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "1. send read EEPROM command\r\n");
    
      if (!FeederClass::sendCommand(CMD_EEPROM_READ, dataBuf, 0)) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "4 No ACK from feeder\r\n");
        return false;
      }
    
      for (uint8_t i = 1; i < 17; i++) {
        dataBuf[i] = dataBuf[i + 3];
      }
    
      dataBuf[0] = 0;
      dataBuf[5] = pitch;  // pitch byte
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "send write EEPROM command\r\n");
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "EEPROM: ");
      for (uint8_t i = 0; i < 17; i++) {
        UartDebugPrintf1(UART_LOG_LEVEL_INFO, "%2.2X ", dataBuf[i]);
      }
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "\r\n");
    
      if (!FeederClass::sendCommand(CMD_EEPROM_WRITE, 17, dataBuf)) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "5 No ACK from feeder\r\n");
        return false;
      }
    
      return true;
    }
    
    bool FeederClass::feederIsOk() {
      return ((STATUS_OK == this->feederStatus) ? true : false);
    }
    
    bool FeederClass::getFeederStatus() {
      int i = 0;
      uint8_t dataBuf[6];
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "send status command(getFeederStatus)\r\n");
    
      for (i = 0; i < 6; i++) {
        dataBuf[i] = 0;
      }
    
      // UartDebugPrintf1(UART_LOG_LEVEL_INFO, "param 2 dataBuf is %p\r\n", dataBuf);
      // 给了函数参数一个指针, 只要不为空, 在函数里面可以直接对这个指针赋值, 指针地址在函数内外都是一样的
      if (!FeederClass::sendCommand(CMD_STATUS, dataBuf)) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "6 No ACK from feeder\r\n");
        return false;
      }
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "getFeederStatus(), ACK from feeder(HEX) : ");
      for (i = 0; i < 6; i++) {
        UartDebugPrintf1(UART_LOG_LEVEL_INFO, "%2.2X ", dataBuf[i]);
      }
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "\r\n");
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "feederStatus is dataBuf[1] = 0x%2.2X\r\n", dataBuf[1]);
    
      this->feederStatus = dataBuf[1];  // should verify that byte 2 matches the lane?
    
      return true;
    }
    
    bool FeederClass::readEEPROM(uint8_t *dataBuf) {
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "2. send read EEPROM command\r\n");
    
      if (!FeederClass::sendCommand(CMD_EEPROM_READ, dataBuf, 0)) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "7 No ACK from feeder\r\n");
        return false;
      }
    
      uint8_t len = dataBuf[0];
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "len = %d\r\n", len);
      uint8_t i = 0;
    
      // len = 2时, 没进下面这个循环
      for (; i < len - 4; i++) {
        dataBuf[i] = dataBuf[i + 4];
      }
      dataBuf[i] = 0;
    
      return true;
    }
    
    bool FeederClass::readInfo(uint8_t *dataBuf) {
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "send read info command\r\n");
    
      if (!FeederClass::sendCommand(CMD_INFO, dataBuf)) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "8 No ACK from feeder\r\n");
        return false;
      }
    
      return true;
    }
    
    bool FeederClass::clearFeedCount() {
      uint8_t dataBuf[22];
    
      // Read current EEPROM data
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "3. send read EEPROM command\r\n");
    
      if (!FeederClass::sendCommand(CMD_EEPROM_READ, dataBuf, 0)) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "9 No ACK from feeder\r\n");
        return false;
      }
    
      for (uint8_t i = 1; i < 17; i++) {
        dataBuf[i] = dataBuf[i + 3];
      }
    
      dataBuf[0] = 0;
      dataBuf[3] = 0;  // count low byte
      dataBuf[4] = 0;  // count mid byte
      dataBuf[6] = 0;  // count high byte
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "send write EEPROM command\r\n");
    
      if (!FeederClass::sendCommand(CMD_EEPROM_WRITE, 17, dataBuf)) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "10 No ACK from feeder\r\n");
        return false;
      }
    
      return true;
    }
    
    bool FeederClass::setID(int32_t feederID) {
      uint8_t dataBuf[22];
    
      for (uint8_t i = 0; i < 22; i++) {
        dataBuf[i] = 0;
      }
    
      if (this->lane == 1) {
        dataBuf[1] = feederID & 0xFF;         // low byte of ID
        dataBuf[2] = (feederID >> 8) & 0xFF;  // high byte of ID
        dataBuf[7] = 0x31;
        dataBuf[8] = 1;
      } else {
        dataBuf[8] = 0x3c;
      }
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "send write EEPROM command\r\n");
    
      if (!FeederClass::sendCommand(CMD_EEPROM_WRITE, 17, dataBuf)) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "11 No ACK from feeder\r\n");
        return false;
      }
    
      return true;
    }
    
    String FeederClass::reportStatus() {
      FeederClass::getFeederStatus();
      return FeederClass::showStatus();
    }
    
    String FeederClass::showStatus() {
      switch (this->feederStatus) {
        case STATUS_OK:
          return "getFeederStatus: feeder OK";
          break;
        case STATUS_INVALID:
          return "getFeederStatus: invalid, status not updated";
          break;
        case STATUS_NO_TAPE_TENSION:
          return "getFeederStatus: No tape tension.  Tape may be broken";
          break;
        case STATUS_NO_TAPE_TRIGGER:
          return "getFeederStatus: Tape take-up not triggered after multiple feeds";
          break;
        case STATUS_FEED_ERROR:
          return "getFeederStatus: Feed motor did not advance";
          break;
        default:
          char statusCode[34];
          sprintf(statusCode, "Unrecognized status code %02X", this->feederStatus);
          return statusCode;
      }
    }
    
    bool FeederClass::startSelfTest() {
      uint8_t dataBuf[2];
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "send self test command\r\n");
    
      dataBuf[0] = 5;
      dataBuf[1] = 0;
    
      if (!FeederClass::sendCommand(CMD_SELF_TEST, 1, dataBuf)) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "12 No ACK from feeder\r\n");
        return false;
      } else {
        return true;
      }
    }
    
    bool FeederClass::stopSelfTest() {
      uint8_t dataBuf[2];
    
      UartDebugPrintf1(UART_LOG_LEVEL_INFO, "send stop self test command\r\n");
    
      dataBuf[0] = 7;
      dataBuf[1] = 0;
    
      if (!FeederClass::sendCommand(CMD_SELF_TEST, 1, dataBuf)) {
        UartDebugPrintf1(UART_LOG_LEVEL_ERR, "13 No ACK from feeder\r\n");
        return false;
      } else {
        return true;
      }
    }
    
    // @bugfix 参数类型为 uint32_t
    void FeederClass::setTX(uint8_t port) {
      // 这里是动态内存, 在编译时看不出还剩多少内存空间.
      // 如果为了实验, 可以在程序中临时定义对应数量的SoftwareSerial实例, 来估算大概用掉的内存
      // 如果非要洁癖, 那写的就比较丑陋, 需要定义所有发送用的软串口实例, 然后和TXportPin[FEEDER_CNT]关联到一起, 感觉没必要
      // 不过对于下位机程序来说, 让内存用量在编译期间就能确定, 靠谱一些
      // 还是全局定义发送用的软串口实例, 然后放到SoftUartToFdAry中来用
      this->m_pSoftUartSend = SoftUartToFdAry[port].m_pSoftUart;
      this->m_tx_pin = SoftUartToFdAry[port].m_tx_dig_port_sn;
    }
    
    void FeederClass::begin(long speed) {
      if (NULL != this->m_pSoftUartSend) {
        this->m_pSoftUartSend->begin(speed);
      }
    }
    
    size_t FeederClass::write(uint8_t b) {
      if (NULL != this->m_pSoftUartSend) {
        this->m_pSoftUartSend->write(b);
      }
    
      return 1;
    }
    
    

    Feeder.h

    // @file Feeder.h
    
    #ifndef _FEEDER_h
    #define _FEEDER_h
    
    #include "arduino.h"
    #include "config.h"
    #include <SoftwareSerial.h>
    
    extern SoftwareSerial UartDebug1;
    extern SoftwareSerial UartDebug2;
    
    // cn1
    // FEEDER_UART_INFO(44, &softUartToFd_44), // FD_TX1/D44_CN8_4/数字IO号码 = 44
    extern SoftwareSerial softUartToFd_44;
    
    // FEEDER_UART_INFO(43, &softUartToFd_43), // FD_TX2/D43_CN8_2/数字IO号码 = 43
    extern SoftwareSerial softUartToFd_43;
    
    // FEEDER_UART_INFO(45, &softUartToFd_45), // FD_TX3/D45_CN8_6/数字IO号码 = 45
    extern SoftwareSerial softUartToFd_45;
    
    // FEEDER_UART_INFO(46, &softUartToFd_46), // FD_TX4/D46_CN8_8/数字IO号码 = 46
    extern SoftwareSerial softUartToFd_46;
    
    // FEEDER_UART_INFO(47, &softUartToFd_47), // FD_TX5/D47_CN8_10/数字IO号码 = 47
    extern SoftwareSerial softUartToFd_47;
    
    // FEEDER_UART_INFO(48, &softUartToFd_48), // FD_TX6/D48_CN8_12/数字IO号码 = 48
    extern SoftwareSerial softUartToFd_48;
    
    // FEEDER_UART_INFO(49, &softUartToFd_49), // FD_TX7/D49_CN8_14/数字IO号码 = 49
    extern SoftwareSerial softUartToFd_49;
    
    // FEEDER_UART_INFO(50, &softUartToFd_50), // FD_TX8/D50_CN8_16/数字IO号码 = 50
    extern SoftwareSerial softUartToFd_50;
    
    // FEEDER_UART_INFO(0xC0, &softUartToFd_0xC0), // FD_TX9/A0_CN9_1/数字IO号码 = 0xC0 + 0 = 0xC0
    extern SoftwareSerial softUartToFd_0xC0;
    
    // FEEDER_UART_INFO(51, &softUartToFd_51), // FD_TX10/D51_CN9_2/数字IO号码 = 51
    extern SoftwareSerial softUartToFd_51;
    
    // cn2
    // FD_TX11/A1_CN9/数字IO号码 = 0xC0 + (0xA1 - 0xA0) = 0xC1
    extern SoftwareSerial softUartToFd_0xC1;
    
    // FD_TX12/D52_CN9_4/数字IO号码 = 52
    extern SoftwareSerial softUartToFd_52;
    
    // FD_TX13/A2_CN9_5/数字IO号码 = 0xC0 + (0xA2 - 0xA0) = 0xC2
    extern SoftwareSerial softUartToFd_0xC2;
    
    // FD_TX14/D53_CN9_6/数字IO号码 = 53
    extern SoftwareSerial softUartToFd_53;
    
    // FD_TX15/A3_CN9_7/数字IO号码 = 0xC0 + (0xA3 - 0xA0) = 0xC3
    extern SoftwareSerial softUartToFd_0xC3;
    
    // FD_TX16/D54_CN9_8/数字IO号码 = 54
    extern SoftwareSerial softUartToFd_54;
    
    // FD_TX17/A4_CN9_9/数字IO号码 = 0xC0 + (0xA4 - 0xA0) = 0xC4
    extern SoftwareSerial softUartToFd_0xC4;
    
    // FD_TX18/D55_CN9_10/数字IO号码 = 55
    extern SoftwareSerial softUartToFd_55;
    
    // FD_TX19/A5_CN9_11/数字IO号码 = 0xC0 + (0xA5 - 0xA0) = 0xC5
    extern SoftwareSerial softUartToFd_0xC5;
    
    // FD_TX20/D56_CN9_14/数字IO号码 = 56
    extern SoftwareSerial softUartToFd_56;
    
    // FD_TX21/D57_CN9_16/数字IO号码 = 57
    extern SoftwareSerial softUartToFd_57;
    
    // FD_TX22/D70_CN9_17/数字IO号码 = 70
    extern SoftwareSerial softUartToFd_70;
    
    // FD_TX23/D58_CN9_18/数字IO号码 = 58
    extern SoftwareSerial softUartToFd_58;
    
    // FD_TX24/D69_CN9_19/数字IO号码 = 69
    extern SoftwareSerial softUartToFd_69;
    
    // FD_TX25/D59_CN9_20/数字IO号码 = 59
    extern SoftwareSerial softUartToFd_59;
    
    // FD_TX26/D68_CN9_21/数字IO号码 = 68
    extern SoftwareSerial softUartToFd_68;
    
    // FD_TX27/D60_CN9_22/数字IO号码 = 60
    extern SoftwareSerial softUartToFd_60;
    
    // FD_TX28/D61_CN9_24/数字IO号码 = 61
    extern SoftwareSerial softUartToFd_61;
    
    // FD_TX29/D67_CN9_25/数字IO号码 = 67
    extern SoftwareSerial softUartToFd_67;
    
    // FD_TX30/D62_CN9_26/数字IO号码 = 62
    extern SoftwareSerial softUartToFd_62;
    
    // FD_TX31/D66_CN9_27/数字IO号码 = 66
    extern SoftwareSerial softUartToFd_66;
    
    // FD_TX32/D63_CN9_28/数字IO号码 = 63
    extern SoftwareSerial softUartToFd_63;
    
    // FD_TX33/D65_CN9_29/数字IO号码 = 65
    extern SoftwareSerial softUartToFd_65;
    
    // FD_TX34/D64_CN9_30/数字IO号码 = 64
    extern SoftwareSerial softUartToFd_64;
    
    // FD_TX35/D32_CN10_29/数字IO号码 = 32
    extern SoftwareSerial softUartToFd_32;
    
    // FD_TX36/D37_CN10_30/数字IO号码 = 37
    extern SoftwareSerial softUartToFd_37;
    
    // FD_TX37/D33_CN10_31/数字IO号码 = 33
    extern SoftwareSerial softUartToFd_33;
    
    // FD_TX38/D36_CN10_32/数字IO号码 = 36
    extern SoftwareSerial softUartToFd_36;
    
    // FD_TX39/D34_CN10_33/数字IO号码 = 34
    extern SoftwareSerial softUartToFd_34;
    
    // FD_TX40/D35_CN10_34/数字IO号码 = 35
    extern SoftwareSerial softUartToFd_35;
    
    // FD_TX41/D38_CN10_28/数字IO号码 = 38
    extern SoftwareSerial softUartToFd_38;
    
    // FD_TX42/D31_CN10_25/数字IO号码 = 31
    extern SoftwareSerial softUartToFd_31;
    
    // FD_TX43/D39_CN10_26/数字IO号码 = 39
    extern SoftwareSerial softUartToFd_39;
    
    // FD_TX44/D30_CN10_23/数字IO号码 = 30
    extern SoftwareSerial softUartToFd_30;
    
    // FD_TX45/D40_CN10_24/数字IO号码 = 40
    extern SoftwareSerial softUartToFd_40;
    
    // FD_TX46/D29_CN10_21/数字IO号码 = 29
    extern SoftwareSerial softUartToFd_29;
    
    // FD_TX47/D28_CN10_19/数字IO号码 = 28
    extern SoftwareSerial softUartToFd_28;
    
    // FD_TX48/D41_CN10_20/数字IO号码 = 41
    extern SoftwareSerial softUartToFd_41;
    
    // FD_TX49/D42_CN10_18/数字IO号码 = 42
    extern SoftwareSerial softUartToFd_42;
    
    // FD_TX50/D27_CN10_15/数字IO号码 = 27
    extern SoftwareSerial softUartToFd_27;
    
    // FD_TX51/D0_CN10_16/数字IO号码 = 0
    extern SoftwareSerial softUartToFd_0;
    
    // FD_TX52/D26_CN10_13/数字IO号码 = 26
    extern SoftwareSerial softUartToFd_26;
    
    // FD_TX53/D1_CN10_14/数字IO号码 = 1
    extern SoftwareSerial softUartToFd_1;
    
    // FD_TX54/A8_CN10_11/数字IO号码 = 0xC8
    extern SoftwareSerial softUartToFd_0xC8;
    
    // FD_TX55/D2_CN10_12/数字IO号码 = 2
    extern SoftwareSerial softUartToFd_2;
    
    // FD_TX56/A7_CN10_9/数字IO号码 = 0xC7
    extern SoftwareSerial softUartToFd_0xC7;
    
    // FD_TX57/D3_CN10_10/数字IO号码 = 3
    extern SoftwareSerial softUartToFd_3;
    
    // FD_TX58/A6_CN10_7/数字IO号码 = 0xC6
    extern SoftwareSerial softUartToFd_0xC6;
    
    // FD_TX59/D4_CN10_8/数字IO号码 = 4
    extern SoftwareSerial softUartToFd_4;
    
    // FD_TX60/D5_CN10_6/数字IO号码 = 5
    extern SoftwareSerial softUartToFd_5;
    
    // FD_TX61/D6_CN10_4/数字IO号码 = 6
    extern SoftwareSerial softUartToFd_6;
    
    // FD_TX62/D7_CN10_2/数字IO号码 = 7
    extern SoftwareSerial softUartToFd_7;
    
    // FD_TX63/D25_CN7_19/数字IO号码 = 25
    extern SoftwareSerial softUartToFd_25;
    
    // FD_TX64/D8_CN7_20/数字IO号码 = 8
    extern SoftwareSerial softUartToFd_8;
    
    // FD_TX65/D24_CN7_17/数字IO号码 = 24
    extern SoftwareSerial softUartToFd_24;
    
    // FD_TX66/D9_CN7_18/数字IO号码 = 9
    extern SoftwareSerial softUartToFd_9;
    
    // FD_TX67/D23_CN7_15/数字IO号码 = 23
    extern SoftwareSerial softUartToFd_23;
    
    // FD_TX68/D10_CN7_16/数字IO号码 = 10
    extern SoftwareSerial softUartToFd_10;
    
    // FD_TX69/D22_CN7_13/数字IO号码 = 22
    extern SoftwareSerial softUartToFd_22;
    
    // FD_TX70/D11_CN7_14/数字IO号码 = 11
    extern SoftwareSerial softUartToFd_11;
    
    // FD_TX71/D21_CN7_11/数字IO号码 = 21
    extern SoftwareSerial softUartToFd_21;
    
    // FD_TX72/D12_CN7_12/数字IO号码 = 12
    extern SoftwareSerial softUartToFd_12;
    
    // FD_TX73/D20_CN7_9/数字IO号码 = 20
    extern SoftwareSerial softUartToFd_20;
    
    // FD_TX74/D13_CN7_10/数字IO号码 = 13
    extern SoftwareSerial softUartToFd_13;
    
    int UartDebugPrintf1(uint8_t log_level, const char* format, ...);  // 调试串口1打印提示信息
    int UartDebugPrintf2(uint8_t log_level, const char* format, ...);  // 调试串口2打印提示信息
    
    int MainUartPrintf(const char* format, ...);  // 主串口(Serial, 连接上位机openpnp的那个串口)回包
    
    extern SoftwareSerial SoftUartRecv;
    
    typedef struct FeederUartInfo {
      uint32_t m_tx_dig_port_sn;    // 软串口发送用的数字IO号码, e.g. D19 => 19
      SoftwareSerial* m_pSoftUart;  // 指向全局的软串口实例地址, 该软串口的发送管脚和tx_dig_port_sn相同
      // 定义pSoftUart对应的软串口实例, 是为了在编译期间就确定大致的内存用量
    
      FeederUartInfo(uint32_t tx_dig_port_sn, SoftwareSerial* pSoftUart) {
        this->m_tx_dig_port_sn = tx_dig_port_sn;
        this->m_pSoftUart = pSoftUart;
      }
    } FEEDER_UART_INFO;
    
    class FeederClass {
    public:
      FeederClass();
    
    protected:
    
      //on initialize it gets a number.
      int feederNo = -1;
    
      uint8_t port;              // TX serial port for this feeder
      uint8_t lane;              // lane within port
      uint8_t feederStatus = 0;  // initialized to invalid
    
      /*
           *  Feeder number to TX port mapping (uses D port numbers)
           */
    #if defined(BOARD_NUCLEO_H723ZG)
      const FEEDER_UART_INFO SoftUartToFdAry[FEEDER_CNT] = {
        // 根据航插的线序, 调整从MCU出来的飞达通讯线(TX)
        // 调试时, 前后飞达挂壁只插入唯一一把飞达, 能通讯就说明数字IO管脚对应
    
        // 飞达挂壁, 正对设备前部, 从左到右, 为FD01 ~ FD26
        // --------------------------------------------------------------------------------
        // 插座 - CN1- 左边第1个 - 前挂壁从左往右数第1把线
        // --------------------------------------------------------------------------------
        // FD_TX10/D51_CN9_2/数字IO号码 = 51
        FEEDER_UART_INFO(51, &softUartToFd_51),  // M615 N0\r\n M615N1 \r\n
    
        // FD_TX9/A0_CN9_1/数字IO号码 = 0xC0 + 0 = 0xC0
        FEEDER_UART_INFO(0xC0, &softUartToFd_0xC0),  // M615 N2\r\n M615N3 \r\n
    
        // FD_TX8/D50_CN8_16/数字IO号码 = 50
        FEEDER_UART_INFO(50, &softUartToFd_50),  // M615 N4\r\n M615N5 \r\n
    
        // FD_TX7/D49_CN8_14/数字IO号码 = 49
        FEEDER_UART_INFO(49, &softUartToFd_49),  // M615 N6\r\n M615N7 \r\n
    
        // FD_TX6/D48_CN8_12/数字IO号码 = 48
        FEEDER_UART_INFO(48, &softUartToFd_48),  // M615 N8\r\n M615N9 \r\n
    
        // FD_TX5/D47_CN8_10/数字IO号码 = 47
        FEEDER_UART_INFO(47, &softUartToFd_47),  // M615 N10\r\n M615N11 \r\n
    
        // FD_TX4/D46_CN8_8/数字IO号码 = 46
        FEEDER_UART_INFO(46, &softUartToFd_46),  // M615 N12\r\n M615N13 \r\n
    
        // FD_TX3/D45_CN8_6/数字IO号码 = 45
        FEEDER_UART_INFO(45, &softUartToFd_45),  // M615 N14\r\n M615N15 \r\n
    
        // FD_TX2/D43_CN8_2/数字IO号码 = 43
        FEEDER_UART_INFO(43, &softUartToFd_43),  // M615 N16\r\n M615N17 \r\n
    
        // FD_TX1/D44_CN8_4/数字IO号码 = 44
        FEEDER_UART_INFO(44, &softUartToFd_44),  // M615 N18\r\n M615N19 \r\n
    
        // --------------------------------------------------------------------------------
        // 插座 - CN2 - 左边第2个 - 前挂壁从左往右数第2把线
        // --------------------------------------------------------------------------------
        // FD_TX20/D56_CN9_14/数字IO号码 = 56
        FEEDER_UART_INFO(56, &softUartToFd_56),  // M615 N20\r\n M615N21 \r\n
    
        // FD_TX19/A5_CN9_11/数字IO号码 = 0xC0 + (0xA5 - 0xA0) = 0xC5
        FEEDER_UART_INFO(0xC5, &softUartToFd_0xC5),  // M615 N22\r\n M615N23 \r\n
    
        // FD_TX18/D55_CN9_10/数字IO号码 = 55
        FEEDER_UART_INFO(55, &softUartToFd_55),  // M615 N24\r\n M615N25 \r\n
    
        // --------------------------------------------------------------------------------
        // 插座 - CN3 - 左边第3个 - 前挂壁从左往右数第3把线
        // --------------------------------------------------------------------------------
    
        // FD_TX23/D58_CN9_18/数字IO号码 = 58
        FEEDER_UART_INFO(58, &softUartToFd_58),  // M615 N26\r\n M615N27 \r\n
    
        // FD_TX22/D70_CN9_17/数字IO号码 = 70
        FEEDER_UART_INFO(70, &softUartToFd_70),  // M615 N28\r\n M615N29 \r\n
    
        // FD_TX21/D57_CN9_16/数字IO号码 = 57
        FEEDER_UART_INFO(57, &softUartToFd_57),  // M615 N30\r\n M615N31 \r\n
    
        // --------------------------------------------------------------------------------
        // 插座 - CN4 - 左边第4个 - 前挂壁从左往右数第4把线
        // --------------------------------------------------------------------------------
        // FD_TX40/D35_CN10_34/数字IO号码 = 35
        FEEDER_UART_INFO(35, &softUartToFd_35),  // M615 N32\r\n M615N33 \r\n
    
        // FD_TX39/D34_CN10_33/数字IO号码 = 34
        FEEDER_UART_INFO(34, &softUartToFd_34),  // M615 N34\r\n M615N35 \r\n
    
        // FD_TX38/D36_CN10_32/数字IO号码 = 36
        FEEDER_UART_INFO(36, &softUartToFd_36),  // M615 N36\r\n M615N37 \r\n
    
        // FD_TX37/D33_CN10_31/数字IO号码 = 33
        FEEDER_UART_INFO(33, &softUartToFd_33),  // M615 N38\r\n M615N39 \r\n
    
        // FD_TX36/D37_CN10_30/数字IO号码 = 37
        FEEDER_UART_INFO(37, &softUartToFd_37),  // M615 N40\r\n M615N41 \r\n
    
        // FD_TX35/D32_CN10_29/数字IO号码 = 32
        FEEDER_UART_INFO(32, &softUartToFd_32),  // M615 N42\r\n M615N43 \r\n
    
        // FD_TX34/D64_CN9_30/数字IO号码 = 64
        FEEDER_UART_INFO(64, &softUartToFd_64),  // M615 N44\r\n M615N45 \r\n
    
        // FD_TX33/D65_CN9_29/数字IO号码 = 65
        FEEDER_UART_INFO(65, &softUartToFd_65),  // M615 N46\r\n M615N47 \r\n
    
        // FD_TX32/D63_CN9_28/数字IO号码 = 63
        FEEDER_UART_INFO(63, &softUartToFd_63),  // M615 N48\r\n M615N49 \r\n
    
        // FD_TX31/D66_CN9_27/数字IO号码 = 66
        FEEDER_UART_INFO(66, &softUartToFd_66),  // M615 N50\r\n M615N51 \r\n
    
        // 飞达挂壁, 正对设备后部, 从左到右, 为FD27 ~ FD52
        // --------------------------------------------------------------------------------
        // 插座 - CN5 - 右边第4个 - 后挂壁从左往右数第1把线
        // --------------------------------------------------------------------------------
        // FD_TX50/D27_CN10_15/数字IO号码 = 27
        FEEDER_UART_INFO(27, &softUartToFd_27),  // M615 N52\r\n M615N53 \r\n // 面对设备背面, 从左往右数, 第1个航插
    
        // FD_TX49/D42_CN10_18/数字IO号码 = 42
        FEEDER_UART_INFO(42, &softUartToFd_42),  // M615 N54\r\n M615N55 \r\n // 面对设备背面, 从左往右数, 第2个航插
    
        // FD_TX48/D41_CN10_20/数字IO号码 = 41
        FEEDER_UART_INFO(41, &softUartToFd_41),  // M615 N56\r\n M615N57 \r\n // 面对设备背面, 从左往右数, 第3个航插
    
        // FD_TX47/D28_CN10_19/数字IO号码 = 28
        FEEDER_UART_INFO(28, &softUartToFd_28),  // M615 N58\r\n M615N59 \r\n // 面对设备背面, 从左往右数, 第4个航插
    
        // FD_TX46/D29_CN10_21/数字IO号码 = 29
        FEEDER_UART_INFO(29, &softUartToFd_29),  // M615 N60\r\n M615N61 \r\n // 面对设备背面, 从左往右数, 第5个航插
    
        // FD_TX45/D40_CN10_24/数字IO号码 = 40
        FEEDER_UART_INFO(40, &softUartToFd_40),  // M615 N62\r\n M615N63 \r\n // 面对设备背面, 从左往右数, 第6个航插
    
        // FD_TX44/D30_CN10_23/数字IO号码 = 30
        FEEDER_UART_INFO(30, &softUartToFd_30),  // M615 N64\r\n M615N65 \r\n // 面对设备背面, 从左往右数, 第7个航插
    
        // FD_TX43/D39_CN10_26/数字IO号码 = 39
        FEEDER_UART_INFO(39, &softUartToFd_39),  // M615 N66\r\n M615N67 \r\n // 面对设备背面, 从左往右数, 第8个航插
    
        // FD_TX42/D31_CN10_25/数字IO号码 = 31
        FEEDER_UART_INFO(31, &softUartToFd_31),  // M615 N68\r\n M615N69 \r\n // 面对设备背面, 从左往右数, 第9个航插
    
        // FD_TX41/D38_CN10_28/数字IO号码 = 38
        FEEDER_UART_INFO(38, &softUartToFd_38),  // M615 N70\r\n M615N71 \r\n // 面对设备背面, 从左往右数, 第10个航插
    
        // --------------------------------------------------------------------------------
        // 插座 - CN6 - 右边第3个 - 后挂壁从左往右数第2把线
        // --------------------------------------------------------------------------------
        // FD_TX60/D5_CN10_6/数字IO号码 = 5
        FEEDER_UART_INFO(5, &softUartToFd_5),  // M615 N72\r\n M615N73 \r\n // 面对设备背面, 从左往右数, 第11个航插
    
        // FD_TX59/D4_CN10_8/数字IO号码 = 4
        FEEDER_UART_INFO(4, &softUartToFd_4),  // M615 N74\r\n M615N75 \r\n // 面对设备背面, 从左往右数, 第12个航插
    
        // FD_TX58/A6_CN10_7/数字IO号码 = 0xC6
        FEEDER_UART_INFO(0xC6, &softUartToFd_0xC6),  // M615 N76\r\n M615N77 \r\n // 面对设备背面, 从左往右数, 第13个航插
    
        // 由于插座接线的限制(1对1的连接, 备线方便, 不容易搞错), 只能将设备后部的右边第1把线(10队RX/TX)接到底板CN7(从MCU出来10个数字IO), 这样可以和10个飞达通讯
        // 将面对设备后部右边第2把线(只有3对RX/TX), 接到底板CN8(从MCU出来4根数字IO)
        // 然后通过程序来安排合适的飞达号码.
    
        // --------------------------------------------------------------------------------
        // 插座 - CN8 - 右边第1个 - 后挂壁从左往右数第3把线
        // --------------------------------------------------------------------------------
        // FD_TX73/D20_CN7_9/数字IO号码 = 20
        FEEDER_UART_INFO(20, &softUartToFd_20),  // M615 N78\r\n M615 N79\r\n // 面对设备背面, 从左往右数, 第14个航插
    
        // FD_TX72/D12_CN7_12/数字IO号码 = 12
        FEEDER_UART_INFO(12, &softUartToFd_12),  // M615 N80\r\n M615 N81\r\n // 面对设备背面, 从左往右数, 第15个航插
    
        // FD_TX71/D21_CN7_11/数字IO号码 = 21
        FEEDER_UART_INFO(21, &softUartToFd_21),  // M615 N82\r\nM615 N83\r\n // 面对设备背面, 从左往右数, 第16个航插
    
        // --------------------------------------------------------------------------------
        // 插座 - CN7 - 右边第2个 - 后挂壁从左往右数第4把线
        // --------------------------------------------------------------------------------
        // D22, D11都是一个控制, 现在NUCLEO-144_723ZG上SB33, SB35都断开了,
        // D11引脚上不能接飞达, 没有数字IO来控制
        // D22引脚上接了飞达, 用4个命令控制都好使(M615 N84\r\n M615 N85\r\n M615 N86\r\n M615 N87\r\n)
        // 在openpnp中定义西门子飞达时, 给出的飞达位置号码应为 86, 87.
        // D11对应的飞达位置号码(84, 85), 就不要用了
        // FD_TX70/D11_CN7_14/数字IO号码 = 11 // 面对设备背面, 从左往右数, 第17个航插
        FEEDER_UART_INFO(11, &softUartToFd_11),  // 正对设备背面, 从右往左第10个飞达 M615 N84\r\n M615 N85\r\n // 不好使
        // 估计断开SB35后, 要将SB33给焊接上 否则D11就是悬空的
    
        // FD_TX69/D22_CN7_13/数字IO号码 = 22 // 面对设备背面, 从左往右数, 第18个航插
        // 还是D22, D11的合体, 飞达插在D22上, N87, N86, N85, N84同时好使. 看来D11端子上的飞达不能接入了
        FEEDER_UART_INFO(22, &softUartToFd_22),  // 正对设备背面, 从右往左第9个飞达 M615 N86\r\n M615 N87\r\n // 好使
    
        // 以上2把飞达, 发同一个命令(e.g. M615N84 都可以控制, 查一下是否这2个飞达的RX是否短路了)
        // 查到了, 是开发板的D11, D22短路了
        // 查一下电路板的原理图, 看看是否有跳线将这2个管脚短路了
        // 查了电路图和板子实物, 是这2个管脚之间有跳线连接...
    
        // FD_TX68/D10_CN7_16/数字IO号码 = 10 // 面对设备背面, 从左往右数, 第19个航插
        FEEDER_UART_INFO(10, &softUartToFd_10),  // 正对设备背面, 从右往左第8个飞达 M615 N88\r\n M615 N89\r\n
    
        // FD_TX67/D23_CN7_15/数字IO号码 = 23 // 面对设备背面, 从左往右数, 第20个航插
        FEEDER_UART_INFO(23, &softUartToFd_23),  // 正对设备背面, 从右往左第7个飞达 M615 N90\r\n M615 N91\r\n
    
        // FD_TX66/D9_CN7_18/数字IO号码 = 9 // 面对设备背面, 从左往右数, 第21个航插
        FEEDER_UART_INFO(9, &softUartToFd_9),  // 正对设备背面, 从右往左第6个飞达 M615 N92\r\n M615 N93\r\n
    
        // FD_TX65/D24_CN7_17/数字IO号码 = 24 // 面对设备背面, 从左往右数, 第22个航插
        FEEDER_UART_INFO(24, &softUartToFd_24),  // 正对设备背面, 从右往左第5个飞达 M615 N94\r\n M615 N95\r\n
    
        // FD_TX64/D8_CN7_20/数字IO号码 = 8 // 面对设备背面, 从左往右数, 第23个航插
        FEEDER_UART_INFO(8, &softUartToFd_8),  // 正对设备背面, 从右往左第4个飞达 M615 N96\r\n M615 N97\r\n
    
        // FD_TX63/D25_CN7_19/数字IO号码 = 25 // 面对设备背面, 从左往右数, 第24个航插
        FEEDER_UART_INFO(25, &softUartToFd_25),  // 正对设备背面, 从右往左第3个飞达 M615 N98\r\n M615 N99\r\n
    
        // FD_TX62/D7_CN10_2/数字IO号码 = 7 // 面对设备背面, 从左往右数, 第25个航插
        FEEDER_UART_INFO(7, &softUartToFd_7),  // 正对设备背面, 从右往左第2个飞达 M615 N100\r\n M615 N101\r\n
    
        // FD_TX61/D6_CN10_4/数字IO号码 = 6 // 面对设备背面, 从左往右数, 第26个航插
        FEEDER_UART_INFO(6, &softUartToFd_6),  // 正对设备背面, 从右往左第1个飞达 M615 N102\r\n M615 N103\r\n
    
        // --------------------------------------------------------------------------------
        // 为用到的通讯线都放在后面, 等换了新的航插板子可以来通讯
        // 我的航插挂壁板子, 前后各2个. 每个挂壁板子分别控制13个.
        // 那么这个飞达控制地板, 每2个插座, 就有7跟通讯线是用不到的
        // --------------------------------------------------------------------------------
        // --------------------------------------------------------------------------------
        // CN2 - 左边第2个 - 未用到的7根通讯线
        // --------------------------------------------------------------------------------
        // FD_TX17/A4_CN9_9/数字IO号码 = 0xC0 + (0xA4 - 0xA0) = 0xC4
        FEEDER_UART_INFO(0xC4, &softUartToFd_0xC4),
    
        // FD_TX16/D54_CN9_8/数字IO号码 = 54
        FEEDER_UART_INFO(54, &softUartToFd_54),
    
        // FD_TX15/A3_CN9_7/数字IO号码 = 0xC0 + (0xA3 - 0xA0) = 0xC3
        FEEDER_UART_INFO(0xC3, &softUartToFd_0xC3),
    
        // FD_TX14/D53_CN9_6/数字IO号码 = 53
        FEEDER_UART_INFO(53, &softUartToFd_53),
    
        // FD_TX13/A2_CN9_5/数字IO号码 = 0xC0 + (0xA2 - 0xA0) = 0xC2
        FEEDER_UART_INFO(0xC2, &softUartToFd_0xC2),
    
        // FD_TX12/D52_CN9_4/数字IO号码 = 52
        FEEDER_UART_INFO(52, &softUartToFd_52),
    
        // FD_TX11/A1_CN9/数字IO号码 = 0xC0 + (0xA1 - 0xA0) = 0xC1
        FEEDER_UART_INFO(0xC1, &softUartToFd_0xC1),
    
        // --------------------------------------------------------------------------------
        // 插座 - CN3 - 左边第3个 - 未用到的7根线
        // --------------------------------------------------------------------------------
    
        // FD_TX24/D69_CN9_19/数字IO号码 = 69
        FEEDER_UART_INFO(69, &softUartToFd_69),
    
        // FD_TX25/D59_CN9_20/数字IO号码 = 59
        FEEDER_UART_INFO(59, &softUartToFd_59),
    
        // FD_TX26/D68_CN9_21/数字IO号码 = 68
        FEEDER_UART_INFO(68, &softUartToFd_68),
    
        // FD_TX27/D60_CN9_22/数字IO号码 = 60
        FEEDER_UART_INFO(60, &softUartToFd_60),
    
        // FD_TX28/D61_CN9_24/数字IO号码 = 61
        FEEDER_UART_INFO(61, &softUartToFd_61),
    
        // FD_TX29/D67_CN9_25/数字IO号码 = 67
        FEEDER_UART_INFO(67, &softUartToFd_67),
    
        // FD_TX30/D62_CN9_26/数字IO号码 = 62
        FEEDER_UART_INFO(62, &softUartToFd_62),
    
        // --------------------------------------------------------------------------------
        // 插座 - CN6 - 右边第3个 - 后挂壁从左往右数第2把线 - 没用到的7根线
        // --------------------------------------------------------------------------------
        // FD_TX57/D3_CN10_10/数字IO号码 = 3
        FEEDER_UART_INFO(3, &softUartToFd_3),
    
        // FD_TX56/A7_CN10_9/数字IO号码 = 0xC7
        FEEDER_UART_INFO(0xC7, &softUartToFd_0xC7),
    
        // FD_TX55/D2_CN10_12/数字IO号码 = 2
        FEEDER_UART_INFO(2, &softUartToFd_2),
    
        // FD_TX54/A8_CN10_11/数字IO号码 = 0xC8
        FEEDER_UART_INFO(0xC8, &softUartToFd_0xC8),
    
        // FD_TX53/D1_CN10_14/数字IO号码 = 1
        FEEDER_UART_INFO(1, &softUartToFd_1),
    
        // FD_TX52/D26_CN10_13/数字IO号码 = 26
        FEEDER_UART_INFO(26, &softUartToFd_26),
    
        // FD_TX51/D0_CN10_16/数字IO号码 = 0
        FEEDER_UART_INFO(0, &softUartToFd_0),
    
        // --------------------------------------------------------------------------------
        // 插座 - CN8 - 右边第1个 - 后挂壁从左往右数第3把线 - 没用到的1根线
        // --------------------------------------------------------------------------------
        // FD_TX74/D13_CN7_10/数字IO号码 = 13
        FEEDER_UART_INFO(13, &softUartToFd_13),
      };
    
    #endif
    
      bool inverse_logic = true;  // inverted logic for serial output
    
      SoftwareSerial* m_pSoftUartSend;  // 飞达对应的软串口, 负责发送
    public:
      static SoftwareSerial* m_spSoftUartRecv;  // 飞达对应的软串口, 负责接收, 只有一个软串口用来总接收飞达的回包
    
    protected:
      bool sendCommand(uint8_t command);                                    // sends a simple command
      bool sendCommand(uint8_t command, uint8_t* dataBuf);                  // sends a simple command, gets a response in dataBuf
      bool sendCommand(uint8_t command, uint8_t* dataBuf, uint8_t offset);  // sends a simple command with extra byte (offset) after lane, gets a response in dataBuf
      bool sendCommand(uint8_t command, uint8_t len, uint8_t* dataBuf);     // sends a simple command followed by data in dataBuf
      bool receiveACK();
    
    public:
      void clearRxBuf(void);
    
    protected:
      bool receiveMessage(uint8_t* dataBuf);
    
      /*
           * software serial routines - adapted from SendOnlySoftwareSerial by Nick Gammon 30th December 2016
           */
      // uint8_t _transmitBitMask;
    
      // @bugfix
      // volatile uint32_t *_transmitPortRegister;
      uint32_t m_tx_pin;  // 具体是操作核心板哪个引出的管脚
    
      // Expressed as 4-cycle delays (must never be 0!)
      // uint16_t _tx_delay;
    
      // private methods
      // @bugfix
      void setTX(uint8_t port);  // this is port, not tx pin
    
      // Return num - sub, or 1 if the result would be < 1
      // static uint16_t subtract_cap(uint16_t num, uint16_t sub);
    
      // private static method for timing
      // static inline void tunedDelay(uint16_t delay);
    
    public:
    
      //store last timestamp command was sent for timeout
      unsigned long lastTimeCommandSent;
    
      void setup(uint8_t _feeder_no, uint8_t port, uint8_t lane);
    
      bool sendPrePick();
      bool sendAdvance(bool overrideError);
      bool setPitch(uint8_t pitch);
      bool clearFeedCount();
      bool getFeederStatus();
      bool readEEPROM(uint8_t* buf);
      bool readInfo(uint8_t* buf);
      bool startSelfTest();
      bool stopSelfTest();
      bool setID(int32_t feederID);
    
      String reportStatus();
      String showStatus();
      bool feederIsOk();
    
      //software serial
      void begin(long speed);
      virtual size_t write(uint8_t byte);
    
      // followed by response message
      bool waitForFeederResponseMessageByTimeout(int iTimeoutCnt);
      uint8_t readOneByteFromFeederByTimeout(int iTimeoutCnt, bool& b_read_ok);
    };
    
    #endif
    
    

    备注

    通过移植官方工程(mega2560)到STM32_NUCLEO-144_H723ZG, 有收获.

  • 西门子飞达的通讯速率是9600BPS, 上位机通讯速率为115200, 这样就增加了对上位机的响应速度.
  • 通过查找D22, D11的飞达控制问题, 确认只能在D22接入飞达.
  • 通过加入运行时日志, 影响分析飞达回包, 感觉软串口(用GPIO模拟的串口) 不太好用, 有诸多限制. 不过, 模拟出的串口也就是这个样子了.
    软串口只能单独做发送或接收.
  • 对串口日志有了新认识. 如果只是上报日志, 用模拟串口还可以.
  • 对西门子飞达协议的处理有了新认识.
  • 官方工程虽然是Arduino工程, 但是还是有一些AVR特有的东西. 移植到STM32 Arduino Core, 还是要改一些的.
  • 工程改完后的通讯效率挺好的, 12ms ~ 50ms之内就可以和飞达通讯完成一个命令的发包处理和回包分析. 知道了飞达实际的通讯性能, 对于用openpnp来和飞达控制板通讯有很大帮助. 自己改openpnp源码和优化和西门子飞达驱动的通讯时, 心里就更踏实了.
  • 如果有需求来控制更多的飞达, 更细致的粒度, 更高的效率, 可以自己来画板子写程序(西门子飞达协议的处理 在这个工程中都有, 通过单步调试和串口日志, 对西门子飞达协议处理有了更好的了解).
  • 备注

    将飞达都挂上, 整体测试一遍. 发现还是有一个数字IO控制2个飞达的问题.
    笔记写到这, 太卡了. 另外开一篇笔记(openpnp – 74路西门子飞达控制板(主控板STM32_NUCLEO-144) – 验证)来调试和确认这个问题.

    END

    物联沃分享整理
    物联沃-IOTWORD物联网 » OpenPNP – 使用主控板STM32_NUCLEO-144实现74路西门子飞达控制板

    发表评论