아두이노 우노의 함수(Function) 예제

함수는 아두이노의 기본 예제 중 하나여야 하기 때문에 함수 작성을 위한 예제 코드를 직접 만들기로 했습니다. 네, 함수 사용에 대한 설명서는 있습니다. 그렇지만 컨텍스트를 사용해보려면 Arduino 웹사이트를 방문해야만 하며 예제에 대해 자세히 설명하고 있지도 않습니다. 이 게시글을 나눠서, 함수가 없는 예제 용의 시작 코드와 함수를 사용해 동일한 프로그램을 변환하고, 함수로 수행할 수 있는 확장 예제를 보여준 후, 코드 줄 수가 적으면 더 쉽게 사용할 수 있다는 것을 보여주기 위해 함수가 없는 버전의 코드와 비교할 것입니다. 이 예제에서는 기본적인 아두이노 예제들은 이미 알고 있다고 가정하고 있음을 참고바랍니다.

함수가 없는 시작 코드

/*
 * This code is the starting code for my functionExample, I want to turn pins 13, 12, 11 high; wait for half a second; turn all the pins off; wait for half a second;
 * and turn pins 13 and 11 on while keeping 12 off and wait for another half second.
 */
void setup() {
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(11, OUTPUT);
}
boolean writePin = HIGH; //set a global variable to make it easier to turn things on and off in the function, use !writePin for LOW
void loop() {
  //first, let's see how cumbersome writing code can be when trying to run several LEDs
  //write all output pins high:
  digitalWrite(13, writePin); 
  digitalWrite(12, writePin);
  digitalWrite(11, writePin);
  delay(500); //delay half a second
  //write all pins low
  digitalWrite(13, !writePin); 
  digitalWrite(12, !writePin);
  digitalWrite(11, !writePin);
  delay(500); //delay half a second
  //write 13 and 11 high, keep 12 low
  digitalWrite(13, writePin);
  digitalWrite(12, !writePin);
  digitalWrite(11, writePin);
  delay(500);//delay half a second
  //doing that sequence alone takes 12 lines of code to accomplish something that could probably be done in less
}
// 20 lines of code not including comments

일반적으로 {}를 포함하여 주요 라인의 숫자를 세지만 컴파일러에서 주석은 무시되기 때문에 주석은 세지 않습니다. 이 코드는 투박하고 반복되는 줄이 많으며 다르게 동작 시키고 싶으면 여러 줄을 변경해야만 합니다. 문제를 해결하거나 보다 복잡한 프로그램을 만들 경우 매우 비효율적인 프로세스입니다.

함수를 사용해 다시 만든 시작 코드

/*
 The Arduino home page does
 try to explain funcitons using a math example and reading an analog pin, which is cool, but there are more things one can
 do with functions. The anatomy of a function in Arduino is as follows:

 dataType variableName(dataType parameterVariable1, dataType parameterVariable2, ...){}
 
 All functions have to have a valid Arduino dataType at the beginning of the line of code because we use a variable to reference the entire code that will be run between the {}
 If you want numbers or some sort of result with measurable values, you will have to use a data type that can store those values; examples are
 int, double, unsigned long, long, boolean, float (and all the other types that can store data)
 The only thing to mention is that functions with these dataTypes must have a return statement; Example:

 int addTwoNumb(int one, int two){
 int result = one + two;
 return result;
 }

 The function above returns the calculation of one and two and can be stored in a new variable when called/used in the main loop.
 The values inside the () are called parameters, functions don't always require parameters (most do though), so the () can be empty.
 Parameters are placeholder variables with any data type valid in Arduino. These variables do not have to be assigned values until the function is used/called in the main loop, values are passed as soon as they are called. Any number of new variables can be added into a function and any amount of code can be added to a function, even functions can call other functions! (gets a little confusing at that point)
 */
void setup() {
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(11, OUTPUT);
}
boolean writePin = HIGH; //set a global variable to make it easier to turn things on and off in the function, use !writePin for LOW
void loop() {
  sequenceLights(13, 12, 11, writePin, writePin, writePin, 500); //call/use sequence lights on pins 13, 12, 11; use the default value for writePin (HIGH), delay for half a second (500 mS)
  sequenceLights(13, 12, 11, !writePin, !writePin, !writePin, 500);
  sequenceLights(13, 12, 11, writePin, !writePin, writePin, 500);
}
/* Functions are typically defined or written below the loop statement for clarity*/
//notice that the function below uses the exact same code but uses different variables and that we don't use a datatype (void), we aren't calculating anything, so there doesn't need to be a datatype
//the function takes 3 integers to begin with: pin13, pin12, pin11; these represent the values for the pins we want to control
//then the next three values are writeFunc1, writeFunc2, writeFunc3 which are boolean values used to control the pins (HIGH or LOW), we want to be able to use unique instructions per light as that is how the original sequence worked
//finaly delayPeriod is used in place of 500, because what if we want to change the timing, why not?
void sequenceLights(int pin13, int pin12, int pin11, boolean writeFunc1, boolean writeFunc2, boolean writeFunc3, int delayPeriod){
  digitalWrite(pin13, writeFunc1);
  digitalWrite(pin12, writeFunc2);
  digitalWrite(pin11, writeFunc3);
  delay(delayPeriod);
}
//17 lines of code not including comments

전체 코드가 단지 3줄만 줄어들었지만 여러 지연 값과 핀 출력을 하이로 할 지 아니면 로우로 할 지를 실험하고자 하는 경우 값을 빠르고 쉽게 조정할 수 있습니다. 만약 함수가 없었다면, 값을 조정하고 싶을 때마다 4줄의 코드를 수동으로 추가해야만 합니다.

초기 코드 배선도

funcExSimp

함수를 사용한 확장 코드

void setup() {
  //Serial.begin(9600); //I often use the serial monitor to debug my program or to do some testing, I then comment the line of code to increase some runtime speed (counted this line of commented code as it was essential to my process)
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(8, OUTPUT);
}
int countRndSeq = 0; //set initial variable for counting variable for the random sequence to "reset"
int randPin = random(8, 14); //set the random Pin variable to a random number between 8 and 13 (14 for including 13 as a choice); we only need 8-13 as those are the pins I use
void loop() {
  randSequenceLightSlow(5, 125, 1200); //run function for a random sequence, set counting interval at 5, set minimum delay at 125, set the max delay to 1200 (1.2 seconds) (I decided to move the randMode into the function because it was easier to accomplish)
  countRndSeq = 0; //reset the counter for the next sequence
  delay(3000); //wait three seconds then turn off all the lights (made a function for that)
  allPinsWrite(13, 12, 11, 10, 9, 8 , LOW);
  randSequenceLightSlow(7, 125, 1450); //run function for random sequence using counting interval at 7, minimum delay at 125, set max delay to 1450 (1.450 seconds)
  countRndSeq = 0; //reset counter for next sequence
  delay(3000); //wait three seconds and turn off all the lights 
  allPinsWrite(13, 12, 11, 10, 9, 8, LOW);
} 
/*
 * The following uses a while loop for control, it does random lights and slows down delay by a set interval between steps (larger interval will make it count and slow down much quicker)
 * As long as that counting number (countRndSeq) is less than the max delay (maxDel), it will do the following in order:
 * 1. chose a random pin 8-13 and set it equal to a variable
 * 2. Pick the random Mode value each time the while loop runs again (increases chance to get modes 0-2)
 * 2. turn off the pin associated to that number using the delay amount of the same number that countrRndSeq is currently at
 * 3. Increase countRndSeq by the interval amount set by calling the function (can be any integer)
 * 4. Check if the current count is greater than the minimum delay and then check which mode (remember I set it to random above); there are 3 modes 0, 1, 2
 * NOTE: For #4, 0 has a high chance of adding less to countRndSeq, 1 has a medium chance of adding a bit more to countRndSeq, 2 has a low chance of adding quite a bit to countRndSeq
 * 5. Set the final pin after the sequence has finished to high to simulate that is the final pin the program landed on
 * See calcThreshChance for an explanation of how I made weighted random events for the chance for the lights to slow faster
 */ 
void randSequenceLightSlow(int interval, int minDel, int maxDel){
  while(countRndSeq < maxDel){
    randPin = random(8, 14);
    int mode = random(0, 3);
    digitalWrite(randPin, HIGH);
    delay(countRndSeq);
    digitalWrite(randPin, LOW);
    delay(countRndSeq); 
    countRndSeq += interval;
    if(countRndSeq > minDel && mode == 0){
      countRndSeq += calcThreshChance(mode, interval);
    } else if(countRndSeq > minDel && mode == 1){
      countRndSeq += calcThreshChance(mode, interval);
    } else if(countRndSeq > minDel && mode == 2){
      countRndSeq += calcThreshChance(mode, interval);
    }
  }
  digitalWrite(randPin, HIGH);
} 
/*
 * Simple function that uses integers 8-13 and a boolean value to turn on or off all the LEDs at once
 */ 
void allPinsWrite(int p1, int p2, int p3, int p4, int p5, int p6, boolean writeInst){
  digitalWrite(p1, writeInst);
  digitalWrite(p2, writeInst);
  digitalWrite(p3, writeInst);
  digitalWrite(p4, writeInst);
  digitalWrite(p5, writeInst);
  digitalWrite(p6, writeInst);
} 
/*
 * The following function takes an integer 0-2 and calculates the chance for it to add more to the number countRndSeq
 * First it sets up three variables local to the function (chance for around 50, a chance for around 200, and a chance for around 300)
 * Then the following first check is made: check which mode is selected, 0, 1, or 2 (if someone entered something else or not calculated it will return 0 and count by the regular interval in the randomSequence)
 * If it is mode 0, use a relatively short random range from 45 to 69 and check if that random number falls between 50 and 60 which is a pretty high chance based on the short range, so it will more than likely add 1000 plus that random number to countRndSeq
 * If it is mode 1, use a larger range from 200 to 299 and check if that random number lies between 250 and 255, which is a medium chance, so it may or may not use that random number + 2000
 * If it is mode 2, use a much larger range from 300 to 599 and check if that random number lies between 320 and 324, which is a low chance, so it has less likelihood of using that random number and adding 3000
 * All other cases it will just add the regular interval
 */ 
int calcThreshChance(int mode, int interval){
  int randCh50 = 0;
  int randCh200 = 0;
  int randCh300 = 0;
    if(mode == 0){
      randCh50 = random(45, 70);
      if(randCh50 >= 50 && randCh50 <= 60){
        return randCh50 + 1000;
      } else {
        return interval;
      }
    } else if (mode == 1){
      randCh200 = random(200, 300);
      if(randCh200 >= 250 && randCh200 <= 255){
        return randCh200 + 2000;
      } else {
        return interval;
      }
    } else if (mode == 2){
      randCh300 = random(300, 600);
      if(randCh300 >= 320 && randCh300 <= 324){
        return randCh300 + 3000;
      } else {
        return interval;
      }
    } else {
      return interval;
    }
}
//77 lines of code not including comments

이 코드의 아이디어는 빠르게 구르기 시작해 점진적으로 속도가 줄어드는 스스로 굴러가는 6면 주사위를 시뮬레이션 하는 것과 같습니다. 코드는 (생성 시 포함된)시작 번호와 생성에서 배제된 끝 번호를 사용하는 내장된 랜덤 함수를 사용합니다. 8-13번까지의 핀만 무작위인 것이 아니라, 주사위 굴리기가 그렇듯 속도가 크게 느려질 확률도 무작위입니다. 세 가지 가능성 "모드"를 통해 이를 구현했습니다: 카운팅 변수에 적게 더할 높은 가능성, 카운팅 변수에 조금 더 많이 더할 중간 가능성, 그리고 카운팅 변수에 많이 더할 낮은 가능성(전체 컨텍스트는 코드의 주석을 참고하시길 바랍니다). 이 아이디어를 이해하고 실험하기 더 쉽게 만들기 위한 세 가지 커스텀 함수가 포함되어 있습니다.

커스텀 함수가 없는 확장 코드

void setup() {
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(8, OUTPUT);
}
int countRndSeq = 0;
int randPin = random(8,14);
int minDel1 = 125;
int maxDel1 = 1200;
int interval1 = 5;
int minDel2 = 125;
int maxDel2 = 1450;
int interval2 = 7;
int randCh50 = 0;
int randCh200 = 0;
int randCh300 = 0;
void loop() {
  while(countRndSeq < maxDel1){
    randPin = random(8, 14);
    int randMode = random(0, 3);
    digitalWrite(randPin, HIGH);
    delay(countRndSeq);
    digitalWrite(randPin, LOW);
    delay(countRndSeq);
    countRndSeq += interval1;
    if(countRndSeq > minDel1 && randMode == 0){
      randCh50 = random(45, 70);
      if(randCh50 >= 50 && randCh50 <= 60){
        countRndSeq += randCh50 + 1000;
      } else {
        countRndSeq += interval1;
      }
    } else if(countRndSeq > minDel1 && randMode == 1){
      randCh200 = random(200, 300);
      if(randCh200 >= 250 && randCh200 <= 255){
        countRndSeq += randCh200 + 2000;
      } else {
        countRndSeq += interval1;
      }
    } else if(countRndSeq > minDel1 && randMode == 2){
      randCh200 = random(300, 600);
      if(randCh200 >= 320 && randCh200 <= 324){
        countRndSeq += randCh200 + 3000;
      } else {
        countRndSeq += interval1;
      }
    } else {
      countRndSeq += interval1;
    }
  }
  digitalWrite(randPin, HIGH);
  countRndSeq = 0;
  delay(3000);
  digitalWrite(13, LOW);
  digitalWrite(12, LOW);
  digitalWrite(11, LOW);
  digitalWrite(10, LOW);
  digitalWrite(9, LOW);
  digitalWrite(8, LOW);
  while(countRndSeq < maxDel2){
    randPin = random(8, 14);
    int randMode = random(0, 3);
    digitalWrite(randPin, HIGH);
    delay(countRndSeq);
    digitalWrite(randPin, LOW);
    delay(countRndSeq);
    countRndSeq += interval2;
    if(countRndSeq > minDel2 && randMode == 0){
      randCh50 = random(45, 70);
      if(randCh50 >= 50 && randCh50 <= 60){
        countRndSeq += randCh50 + 1000;
      } else {
        countRndSeq += interval2;
      }
    } else if(countRndSeq > minDel2 && randMode == 1){
      randCh200 = random(200, 300);
      if(randCh200 >= 250 && randCh200 <= 255){
        countRndSeq += randCh200 + 2000;
      } else {
        countRndSeq += interval2;
      }
    } else if(countRndSeq > minDel2 && randMode == 2){
      randCh200 = random(300, 600);
      if(randCh200 >= 320 && randCh200 <= 324){
        countRndSeq += randCh200 + 3000;
      } else {
        countRndSeq += interval2;
      }
    } else {
      countRndSeq += interval2;
    }
  }
  digitalWrite(randPin, HIGH);
  countRndSeq = 0;
  delay(3000);
  digitalWrite(13, LOW);
  digitalWrite(12, LOW);
  digitalWrite(11, LOW);
  digitalWrite(10, LOW);
  digitalWrite(9, LOW);
  digitalWrite(8, LOW);
}
//105 lines of code not including comments

이 코드는 동일한 작업을 수행하기 위한 줄 수가 훨씬 더 많습니다(함수 버전이 28줄이 더 적으며 이는 더 큰 프로그램을 만들 때는 의미가 큽니다). 줄 수만 적은 것이 아니라, 함수를 사용한 방식이 다른 값들을 실험하기에 상당히 용이하도록 해줍니다. 손으로 코딩한(함수가 없는) 버전은 실험을 하려면 라인에서 여러 변수를 변경해야 합니다. 수행해야 할 실험은 더 많은데 원하는 각 작업에 대한 코드를 변경하고자 한다고 가정해 보십시오: 함수는 코드 한 블록을 편집해서 기능을 더 빠르게 테스트할 수 있습니다.

확장 코드 배선도

funcExampExt

자재 명세서

사용한 모든 부품이 담긴 장바구니에 대한 링크입니다.
https://www.digikey.kr/short/0rtq9qmt



영문 원본: Function Example for Arduino Uno