首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >霍尔效应传感器的换档指示器& 8x8 LED显示器

霍尔效应传感器的换档指示器& 8x8 LED显示器
EN

Code Review用户
提问于 2021-10-04 06:43:02
回答 1查看 386关注 0票数 4

我为我爸爸的车安装了一个齿轮换档指示器,以显示它目前使用的齿轮,并显示随动画而发生的变化(适当的上下滑动,并在车展上提供一些额外的奖励)。

安全性并不是一个真正的关注,因为它的本地化性质,但我想发布代码供其他人使用在他们自己的项目,所以我希望代码是防弹之前,这样做。我很少或根本没有最佳编码实践等方面的经验,除了我沿途学到的东西之外,我希望从比我更了解的人那里得到一些输入。

我的主要关切领域(按优先次序排列)如下:

  1. 可靠性
    • 应用程序对车辆的功能并不重要;在最坏的情况下,齿轮指示器没有显示,但是我想确保任何奇怪的边缘情况都不会破坏代码。
      • 例如,我了解到在启动/启动时默认到已知状态(在本例中为“Park”位置)是一种很好的做法,可以帮助排除故障,但我没有真正使用任何异常捕获,甚至不确定是否有必要。
      • 我在串行输出中添加了一个调试函数,它输出来自hall效果传感器的原始输入以及缓冲区内容,以检查一切是否正常工作。我为此目的编写的代码合适吗?我使它依赖于一个全局变量,而不是任何类型的输入,因为Arduino的处理能力有限,这很好地引出了下一个主题…

  2. 性能
    • 内存的使用和代码的运行速度非常重要,因为我使用的Arduino Uno在这些方面非常有限。
      • 一些代码被注释掉了(例如,允许由电位器控制LED亮度和动画速度的代码),因为它不用于我的特定用例,尽管这将在文档中解释。我是否正确地认为这有助于释放内存并保持代码的性能,因为它没有运行不必要的检查(也就是说,与检查一个变量(比如调试函数中的变量)相反)?还是我最好有多个版本的代码,并在必要时包含或排除不同的功能?
      • 我的代码有效率吗?在我使用嵌套的IF语句的地方,而不是像切换这样的语句,因为我在网上看到它们通常比其他语句更有效。这是真的吗?

  3. Best编码实践
    • 正如我前面提到的,我对一般的最佳实践没有经验,尽管我遇到的一些做法由于Arduino平台的性质而不适用,后者通常优先考虑效率和性能。有什么我可以做的,以更好地遵守一般准则,或使代码更易读吗?
      • 这些可能的例外可能是注释的样式和行长--大多数注释都是这样格式化的,这样我就可以使用code生成文档,或者使代码对于一般不熟悉代码的人来说具有超级可读性。有什么是我可以做得更好的吗,或者我忽略了的任何有助于实现最终目标的做法?

我很感激你对我的代码的任何输入,我能做的任何事情都将毫无疑问地帮助那些想要使用这个项目的人。

守则如下:

代码语言:javascript
复制
/**
 * @file
 * @author Ryan Jolliffe (ryanjolliffe@hotmail.co.uk)
 * @brief Use hall effect sensors to determine and display vehicle gear selection
 * on an 8x8 LED display.
 * @version v0.7
 * @copyright Copyright (c) 2021
 */

/** @mainpage Introduction and Installation
 *
 * @section intro_sec Introduction
 *
 * This is the documentation for GearShift6_8x8.ino - named as such due
 * to it being code that handles Gear Shifting using 6 (by default) hall
 * effect sensors which determines the current gear position then displays
 * the result on an 8x8 LED display matrix with relevant (and not so
 * relevant) animations. 
 *
 * @subsection note_sub Please Note
 * This code was originally developed for my Dad's scratch-built Locost 7.
 * As such, whilst this code was designed with flexibility and customisation
 * in mind, it of course comes with a few caveats. For example:
 * - Whilst the gear/sensor numbers and values can be adjusted,
 * it is still assumed that there are only 2 directions for gear changes - Up and Down.
 *     + Other configurations will still work, but the animations rely on
 *     this structure and as such will still slide up or down only.
 *     This is on my TODO list for future changes!
 * - To enable potentiometer-driven values for scrolling speed and display brightness,
 * uncomment the appropriate lines of code and set relevant pins in the settings section.
 *     + These values are set only once at startup to reduce the number of analogReads,
 *     which would otherwise slow down the speed with which the program updates.
 * 
 *
 * @section install_sec Software Installation
 *
 * @subsection step1 Step 1: Install Arduino IDE and relevant libraries.
 * 3 additional libraries are used in this code and need to be installed
 * via Tools > Manage Libraries in the Arduino IDE. Searching for their
 * names should allow you to find them with ease.
 * Library          | Used For:
 * -------------    | -------------
 * MD_MAX72xx.h     | Interacting with display(s) 
 * MD_Parola.h      | Text and Sprite Animation 
 * CircularBuffer.h | Tracking gear change history 
 *
 * @subsection step2 Step 2: Adjust variables.
 * Most variables are able to be changed to suit your particular setup.
 * Take care to read the documentation for each, as some must meet
 * particular conditions to work as expected (in particular; pin numbers).
 * If using potentiometers for animation speed and/or display brightness
 * make sure to uncomment the appropriate lines as explained in their
 * comments.
 *
 * @subsection step3 Step 3: Verify changes.
 * Use the Verify button (looks like a check/tick underneath the "File" menu heading)
 * to verify that any changes have been implemented correctly. The IDE will
 * warn of any errors it encounters when compiling the code.
 * For additional information, ensure the "Show verbose output" checkboxes
 * are marked in the IDE Preferences (File > Preferences, Settings Tab).
 * - This step is obviously not necessary if no changes are made to the code.
 *
 * @subsection step4 Step 4: Connect Arduino and upload.
 * If connecting via USB, the appropriate port and board should be automatically
 * selected by the IDE, but can be confirmed or manually adjusted under
 * the Tools menu heading (see "Board" and "Port" subsections).
 *
 * @subsection step5 Step 5: Wire up Arduino and test!
 * Currently, no instructions are available from us but this will be changed
 * in the future. Ensure the pins used match what is written in the code's
 * variables.
 *
 * @section anim_sec Animations
 *
 * As the [MD_Parola library](https://github.com/MajicDesigns/MD_Parola) is used,
 * animations using the [relevant sprites](https://arduinoplusplus.wordpress.com/2018/04/19/parola-a-to-z-sprite-text-effects/)
 * can be used if desired and a few select ones are already implemented.
 */


/* Necessary libraries, ensure they are
  installed in Arduino IDE before uploading */
#include                                                                 // should be included in default Arduino IDE
#include 
#include 
#include 

/** Change if using PAROLA_HW or other LED hardware,
* incorrect setting can cause orientation issues */
#define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW

/* Settings: */

// GEAR SETTINGS
/** How many gears are used - must match the number of gear characters in GearChars array */
const byte    NUM_GEARS                        = 7;
/** Used for loop counting etc when starting with 0 */
const int8_t  NUM_LOOPS                        = NUM_GEARS - 1;
/** Set number of stored previous gear states - 4 used here used to detect 'sequence' such as 1-2-1-2, which can then be acted upon */
const uint8_t BUFFER_SIZE                      = 4;
/** Layout here from left to right should match gear order on vehicle from top to bottom/start to finish */
const char    GearChars[NUM_GEARS]             = {'P', 'R', 'N', 'D', '3', '2', '1'};
// DISPLAY AND SENSOR SETTINGS
/** Hall Effect sensor pins in the same order as GearChars array - 'Park' position is assumed to not have a sensor and so the first pin represents "R" */
const uint8_t Hall[NUM_LOOPS]                  = {      3,   4,   5,   6,   7};   
/** Array for storing relevant LED pins; (DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES) */
const uint8_t LED[]                            = { 11,  13,  10,  1};
/** Remove comment ('//') to enable potentiometer-driven scroll speed */
//const uint8_t POT_PIN_SPEED                    = 8;
/** Remove comment ('//') to enable potentiometer-driven LED display brightness */
//const uint8_t POT_PIN_SPEED                    = 9;

// CUSTOMISATION
/** Text scrolled upon boot */
const char    StartupText[]                    = {"Startup Text Goes Here"};
/** Sequence of gears necessary to display and loop animations (i.e. D-N-D-N), must be same length as BUFFER_SIZE */
const char    ANIM_SEQUENCE[BUFFER_SIZE]       = {'D', 'N', 'D', 'N'};
/** Sequence of gears necessary to display and loop the startup text (i.e. R-N-R-N), must be same length as BUFFER_SIZE */
const char    SCROLLTEXT_SEQUENCE[BUFFER_SIZE] = {'R', 'N', 'R', 'N'};
/** Speed that StartupText and animations are scrolled, number is milliseconds between frame updates */
const uint8_t SCROLL_SPEED                     = 75;
/** Set brightness of LEDs (using range 0-15) - comment out ('//') when using potentiometer-derived value */
const byte    BRIGHTNESS                       = 4;

// DEBUGGING
/** Set to 1 to enable debugging via Serial (baud) */
const byte    DEBUG_MODE                       = 0;
/** Number of readings in debug mode, recommended to match buffer size or larger */
const uint8_t DEBUG_READS                      = BUFFER_SIZE;
/** Delay between debug readings in milliseconds (3 seconds by default) */
const int     DEBUG_DELAY                      = 3000;
/** Set baud rate here for Serial communication */
const int     BAUD_SPEED                       = 9600;

// HARDWARE SPI
/** Creates display instance using given settings (HARDWARE_TYPE, CS_PIN, MAX_DEVICES) */
MD_Parola Parola = MD_Parola(HARDWARE_TYPE, LED[2], LED[3]);
// SOFTWARE SPI
//MD_Parola P = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
    

/* Sprite definitions - stored in RAM/PROGMEM to save memory */

const uint8_t F_PMAN = 6;
const uint8_t W_PMAN = 8;
const uint8_t PROGMEM pacman[F_PMAN * W_PMAN] = {
  0x00, 0x81, 0xc3, 0xe7, 0xff, 0x7e, 0x7e, 0x3c,
  0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c,
  0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c,
  0x3c, 0x7e, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x3c,
  0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c,
  0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c,
};
/**< Sprite definition for gobbling pacman animation */

const uint8_t F_PMANGHOST = 6;
const uint8_t W_PMANGHOST = 18;
static const uint8_t PROGMEM pacmanghost[F_PMANGHOST * W_PMANGHOST] = {
  0x00, 0x81, 0xc3, 0xe7, 0xff, 0x7e, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x7b, 0xf3, 0x7f, 0xfb, 0x73, 0xfe,
  0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x7b, 0xf3, 0x7f, 0xfb, 0x73, 0xfe,
  0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x7b, 0xf3, 0x7f, 0xfb, 0x73, 0xfe,
  0x3c, 0x7e, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x73, 0xfb, 0x7f, 0xf3, 0x7b, 0xfe,
  0x24, 0x66, 0xe7, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x73, 0xfb, 0x7f, 0xf3, 0x7b, 0xfe,
  0x00, 0x42, 0xe7, 0xe7, 0xff, 0xff, 0x7e, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0x73, 0xfb, 0x7f, 0xf3, 0x7b, 0xfe,
};
/**< Sprite definition for ghost pursued by pacman */

const uint8_t F_SAILBOAT = 1;
const uint8_t W_SAILBOAT = 11;
const uint8_t PROGMEM sailboat[F_SAILBOAT * W_SAILBOAT] = {
  0x10, 0x30, 0x58, 0x94, 0x92, 0x9f, 0x92, 0x94, 0x98, 0x50, 0x30,
};
/**< Sprite definition for sail boat */

const uint8_t F_STEAMBOAT = 2;
const uint8_t W_STEAMBOAT = 11;
const uint8_t PROGMEM steamboat[F_STEAMBOAT * W_STEAMBOAT] = {
  0x10, 0x30, 0x50, 0x9c, 0x9e, 0x90, 0x91, 0x9c, 0x9d, 0x90, 0x71,
  0x10, 0x30, 0x50, 0x9c, 0x9c, 0x91, 0x90, 0x9d, 0x9e, 0x91, 0x70,
};
/**< Sprite definition for steam boat */

const uint8_t F_HEART = 5;
const uint8_t W_HEART = 9;
const uint8_t PROGMEM beatingheart[F_HEART * W_HEART] = {
  0x0e, 0x11, 0x21, 0x42, 0x84, 0x42, 0x21, 0x11, 0x0e,
  0x0e, 0x1f, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x1f, 0x0e,
  0x0e, 0x1f, 0x3f, 0x7e, 0xfc, 0x7e, 0x3f, 0x1f, 0x0e,
  0x0e, 0x1f, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x1f, 0x0e,
  0x0e, 0x11, 0x21, 0x42, 0x84, 0x42, 0x21, 0x11, 0x0e,
};
/**< Sprite definition for beating heart */

const uint8_t F_INVADER = 2;
const uint8_t W_INVADER = 10;
const uint8_t PROGMEM spaceinvader[F_INVADER * W_INVADER] = {
  0x0e, 0x98, 0x7d, 0x36, 0x3c, 0x3c, 0x36, 0x7d, 0x98, 0x0e,
  0x70, 0x18, 0x7d, 0xb6, 0x3c, 0x3c, 0xb6, 0x7d, 0x18, 0x70,
};
/**< Sprite definition for space invader */

const uint8_t F_FIRE = 2;
const uint8_t W_FIRE = 11;
const uint8_t PROGMEM fire[F_FIRE * W_FIRE] = {
  0x7e, 0xab, 0x54, 0x28, 0x52, 0x24, 0x40, 0x18, 0x04, 0x10, 0x08,
  0x7e, 0xd5, 0x2a, 0x14, 0x24, 0x0a, 0x30, 0x04, 0x28, 0x08, 0x10,
};
/**< Sprite definition for fire */

const uint8_t F_WALKER = 5;
const uint8_t W_WALKER = 7;
const uint8_t PROGMEM walker[F_WALKER * W_WALKER] = {
  0x00, 0x48, 0x77, 0x1f, 0x1c, 0x94, 0x68,
  0x00, 0x90, 0xee, 0x3e, 0x38, 0x28, 0xd0,
  0x00, 0x00, 0xae, 0xfe, 0x38, 0x28, 0x40,
  0x00, 0x00, 0x2e, 0xbe, 0xf8, 0x00, 0x00,
  0x00, 0x10, 0x6e, 0x3e, 0xb8, 0xe8, 0x00,
};
/**< Sprite definition for walking stick figure */

/* Struct used for storing/retrieving sprite settings. */
struct
{
  const uint8_t *data;
  uint8_t width;
  uint8_t frames;
}
sprite[] =
{
  { fire, W_FIRE, F_FIRE },
  { pacman, W_PMAN, F_PMAN },
  { walker, W_WALKER, F_WALKER },
  { beatingheart, W_HEART, F_HEART },
  { sailboat, W_SAILBOAT, F_SAILBOAT },
  { spaceinvader, W_INVADER, F_INVADER },
  { steamboat, W_STEAMBOAT, F_STEAMBOAT },
  { pacmanghost, W_PMANGHOST, F_PMANGHOST }
};

/* Variables that will change during runtime: */

/** An integer representing the current gear position
* (as given in the GearChars array). */
uint8_t currentGear;
/** A Circular Buffer (array of length BUFFER_SIZE)
* used to store the previous gear positions. */
CircularBuffer previousGears;
/** Remove comment ('//') to enable potentiometer-driven scroll speed */
uint16_t scrollSpeed = SCROLL_SPEED;
uint8_t displayBrightness = BRIGHTNESS;

/**
 * @brief Initialises sensors and LED display.
 * 
 * Function that runs *once* when Arduino is first
 * booted; initialises sensors and LED display  
 * (brightness and scroll speed set only once during
 * initialization for efficiency).
 * Loads known state (Park position) then checks if 
 * DEBUG_MODE is enabled.
 * Also adds randomness to random() calls via
 * an analogue 'Pin 0' read.
 *
 */
void setup() {
  hallSetup();                                                                  // initialise sensors
  displaySetup();                                                               // initialise display
  currentGear = 0;                                                              // set current gear to 'Parked' position until first sensor read to establish known state
  previousGears.push(currentGear);                                              // push 'Park' {"P"} position to buffer also, which is translated to *char via GearChars[0]
  if (DEBUG_MODE == 1) {                                                        // check if DEBUG_MODE is enabled, and runs debugFunction() if TRUE
    Serial.begin(BAUD_SPEED);
    debugFunction();
  }
  randomSeed(analogRead(0));                                                    // take 'noisy' reading (i.e. hopefully random) as the seed for our random() calls; adds randomness
}

/**
 * @brief Main loop.
 * 
 * The main loop that runs the core components
 * repeatedly until power-off.
 *
 */
void loop() {
  currentGear = getGear();                                                      // read hall effect sensors and calculate current gear position
  displayGear(currentGear);                                                     // display the current gear, with appropriate animation if different from previous gear
  checkHistory();                                                               // checks gear history for defined sequences and calls relevant functions
}

/** @brief Initialize the hall effect sensor pins as inputs. */
void hallSetup() {
  for (int8_t i = 0; i < NUM_LOOPS; i++) {
    pinMode(Hall[i], INPUT);
  }
}

/** @brief Setup LED display*/
void displaySetup() {
  Parola.begin();                                                               // initialise display
  Parola.setIntensity(displayBrightness);                                       // set display intensity/brightness
  Parola.displayClear();
  Parola.displayScroll(StartupText, PA_LEFT, PA_SCROLL_LEFT, scrollSpeed);      // display message on startup
  while (!Parola.displayAnimate())                                              // play animation once until complete
    ;
  Parola.displayReset();
  Parola.displayClear();
  //Parola.setIntensity(static_cast(analogRead(POT_PIN_BRIGHTNESS) / 68.2)); // update display brightness to match potentiometer reading - uncomment to enable
  //scrollSpeed = analogRead(POT_PIN_SPEED);                                      // update scrollSpeed to match potentiometer reading - uncomment to enable
}

/**
 * @brief Loop through sensors until LOW reading detected
 * 
 * @return gear
 * A numeric value representing the current gear,
 * matching the gear's position in the GearChars array.
 */
int8_t getGear() {
  int8_t gear = NUM_LOOPS;
  while ((gear) && (digitalRead(Hall[gear - 1]))) {
    gear--;
  }
  return gear;
}

/**
 * @brief Displays current gear on LED, and checks if animations
 * should be used depending on previous gear value.
 *
 * @param gearValue A numeric value representing the current gear,
 * matching the gear's position in the GearChars array.
 */
void displayGear(int8_t gearValue) {
  char curGearChar[2] = {GearChars[gearValue]};                                 // convert gearValue to c-string character for display purposes by pulling from null terminated array using pointers
  if (gearValue == previousGears.last()) {                                      // if current gear is same as previous, simply print
    Parola.displayText(curGearChar, PA_CENTER, 0, 0, PA_PRINT, PA_NO_EFFECT);   // set display settings
    Parola.displayAnimate();                                                    // display appropriate character
  }
  else if ((previousGears.last() < gearValue)) {                                // if the previous gear is situated to the left of current gear (in char array) then scroll down
    Parola.displayText(
      curGearChar, PA_CENTER, scrollSpeed, 1, PA_SCROLL_DOWN, PA_NO_EFFECT      // set scrolling text settings
    );
    while (!Parola.displayAnimate())                                            // play once animation until complete
      ;
    previousGears.push(gearValue);                                              // push current gear to buffer as it is different
  } else {                                                                      // if the previous gear is not situated left (i.e. is to the right of current gear in char array) then scroll up
    Parola.displayText(
      curGearChar, PA_CENTER, scrollSpeed, 1, PA_SCROLL_UP, PA_NO_EFFECT
    );
    while (!Parola.displayAnimate())
      ;
    previousGears.push(gearValue);                                              // push current gear to buffer as it is different
  }
}

/** @brief Checks for given sequence of gear changes using
* buffer functionality and calls other functions as required. */
void checkHistory() {
  if (previousGears.isFull()) {
    char gearHistory[BUFFER_SIZE];                                              // create new char array from history for comparison
    for (int8_t i = 0; i < BUFFER_SIZE; i++) {                                  // loop to populate array with char equivalents
      gearHistory[i] = GearChars[previousGears[i]];
    }
    if (checkArrays(gearHistory, ANIM_SEQUENCE, BUFFER_SIZE)) {                 // compares the two arrays; if buffer history matches ANIM_SEQUENCE, then display animation
      displayAnimation(random(ARRAY_SIZE(sprite) - 1));                         // selects and displays random animation from struct array
    }
    else if (checkArrays(gearHistory, SCROLLTEXT_SEQUENCE, BUFFER_SIZE)) {
      scrollSpeed = analogRead(POT_PIN_SPEED);                                  // update scrollSpeed to match potentiometer reading
      Parola.displayClear();
      Parola.displayScroll(StartupText, PA_LEFT, PA_SCROLL_LEFT, scrollSpeed);  // scroll StartupText
      while (!Parola.displayAnimate())                                          // play animation once until complete
        ;
      Parola.displayReset();
      Parola.displayClear();
    }
  }
}

/** @brief Compares 2 char arrays and returns boolean result. */
boolean checkArrays(char arrayA[], char arrayB[], long numItems) {
  boolean matchCheck = true;
  long i = 0;
  while (i < numItems && matchCheck) {
    matchCheck = (arrayA[i] == arrayB[i]);
    i++;
  }
  return matchCheck;
}

/** @brief Displays an animation based on the previously
* selected sprite definition from checkHistory function. */
void displayAnimation(byte selection) {
  char curGearChar[2] = {GearChars[previousGears.last()]};
  Parola.displayReset();
  Parola.displayClear();
  Parola.setSpriteData(
    sprite[selection].data, sprite[selection].width, sprite[selection].frames,  // entry sprite
    sprite[selection].data, sprite[selection].width, sprite[selection].frames   // exit sprite
  );
  Parola.displayText(
    curGearChar, PA_CENTER, scrollSpeed, 1, PA_SPRITE, PA_SPRITE                // set animation settings
  );
  while (!Parola.displayAnimate())                                              // play animation once until complete
    ;
}

/** @brief Functions useful for debugging.
*
* Writes DEBUG_READS lots of readings (default:4) from
* all hall sensors to Serial - with a delay to allow changing
* gear - then fills & prints buffer; for debugging purposes. */
void debugFunction() {
  String buf = "";
  String bufChars = "";
  for (int8_t i = 0; i < DEBUG_READS; i++) {
    delay(DEBUG_DELAY);                                                         // wait to allow gear changing/hall sensor/magnet position changes
    for (int8_t x = 0; x < NUM_LOOPS; x++) {                                    // loop through all sensors and print values to Serial
      Serial.println(
        String(x) + ") Digital Reading: " + String(digitalRead(Hall[x])) +
        " | Analogue Reading: " + String(analogRead(Hall[x]))
      );
    }
    previousGears.push(random(NUM_LOOPS));                                      // push pseudorandom GearChar values to buffer
    buf = buf + previousGears.last();                                           // add current gear in numeric form to a string for printing to Serial
    bufChars = bufChars + GearChars[previousGears.last()];                      // add current gear in char form to a string for printing to Serial
  }
  Serial.println("Buffer contents: " + buf + bufChars);                         // ...print buffer contents, to Serial...
  while (true);                                                                 // puts arduino into an infinite loop, reboot to start again
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2021-10-06 16:58:03

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/268638

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档