안경잡이개발자

728x90
반응형

※ 선형대수학 관련 계산기 ※

 

1. 기약행 사다리꼴 행렬(RREF)

 

  행렬이 주어졌을 때, 기약행 사다리꼴 행렬(RREF)을 계산해야 하는 일이 정말 많은데요. 기약행 사다리꼴 행렬(RREF)은 대략 다음과 같은 형태를 가지도록 변환된 행렬을 말합니다.

 

 

   ▶ EMathHelp: RREF 온라인 계산기 링크

 

  EMathHelp라는 사이트에서 제공하는 온라인 계산기는 과정까지 세세하게 알려줘서 유용합니다. 대략 다음과 같이 과정을 차근차근 보여주면서, 최종적인 RREF를 계산해줍니다.

 

 

2. Null Space 계산기

 

  선형대수학에서 영공간(Null Space)이란, Ax = 0의 해(Solutions)들이 이루는 공간을 의미합니다. Null Space를 계산해주는 사이트는 Linear Algebra Toolkit 사이트가 좋은 것 같아요.

 

  ▶ Linear Algebra Toolkit 사이트 링크

 

  사이트 접속 이후에는 [Vector Spaces] 탭에 있는 [Finding a basis of the null space of a matrix]에 들어가세요. 이후에 행렬의 크기를 입력하고 값을 입력하면 됩니다.

 

 

  영공간(Null Space)를 이루는 기저(Basis)를 찾는 문제에서는, 위에서 언급한 RREF를 먼저 찾아야 합니다. 본 사이트에서는 다음과 같이 먼저 RREF를 계산해주고, Null Space의 Basis들을 찾아서 알려준답니다.

 

  그리고 행렬의 Rank와 Nullity도 계산해서 알려줍니다. 애초에 M x N 크기의 행렬 A가 있을 때, rank(A) + nullity(A) = n 공식이 성립하므로, 이를 직접 계산하는 것도 간단합니다.

 

 

3. 선형성(Linear)

 

  선형대수학 강의에서 자주 나오는 문제는 선형성(Linearity) 증명입니다. 어떤 함수를 주고, 이 함수가 선형적(Linear)한지 물어보는 문제가 나올 수 있는데요. 어떤 함수가 선형적이라는 말은 다음의 두 조건을 만족한다는 것입니다.

 

  1) Additivity

 

    F(a + b) = F(a) + F(b)

 

  2) Homogeneity

 

    F(ax) = aF(x)

 

  그래서 어떤 함수의 선형성을 판단하라는 문제가 나오면, 위 두 가지 조건이 성립하는지 예시를 넣어 보면서 증명하면 됩니다. 참고로 미분, 적분은 선형적이며 코사인(cos) 함수 등은 비선형적입니다.

 

4. 랭크(Rank)

 

  랭크란 행렬이 주어졌을 때, 선형 독립인 행 혹은 열의 개수를 의미합니다. 행과 열의 랭크는 같은 값을 갖기 때문에 한 쪽의 랭크만 계산해도 됩니다. 하지만 이것도 알아서 계산해주는 사이트가 있습니다.

 

  ▶ Matrix Reshish: Rank 온라인 계산기

 

  접속해서 행렬(Matrix)을 입력하면 다음과 같이 랭크(Rank)와 계산 과정을 모두 보여줍니다.

 

 

5. 커널(Kernel)과 이미지(Image)

 

  커널은 핵이라고도 부르고, 영공간(Null Space)이라고 부르기도 합니다. 그리고 이미지(Image)는 상이라고도 부르고, 치역(Range)이라고 부르기도 합니다. 커널과 이미지를 계산할 때에도 Linear Algebra Toolkit을 이용하면 빠르게 계산할 수 있습니다.

 

  ▶ Linear Algebra Toolkit 사이트 링크

 

  접속 이후에는 다음과 같은 두 가지 기능을 이용하시면 됩니다.

 

 

  예를 들어 이미지(Image)를 계산하는 예시는 다음과 같습니다.

 

 

6. 고유값(Eigenvalue)과 고유벡터(Eigenvector)

 

  행렬 A의 고유벡터란, 선형변환 A에 의한 변형 결과가 자기 자신의 상수배가 되는 0이 아닌 벡터를 의미합니다. 결과적으로 Av = λv 형태에서 v를 고유 벡터, 상수 λ를 고유값이라고 부릅니다. 고유값과 고유벡터 계산은 다음의 EMathHelp 웹 사이트를 이용하면 좋습니다.

 

   ▶ EMathHelp: 고유값과 고유벡터 온라인 계산기 링크

 

  위 사이트를 이용하면 기본적으로 행렬식(Determinant)를 계산한 뒤에, 이를 이용해서 고유값과 고유벡터를 계산해줍니다. 계산 과정을 전체 보여주며, 다음과 같이 답을 알려줘서 유용합니다.

 

 

  참고로 고유벡터의 수가 행렬의 차원보다 작다면, 대각화가 불가능합니다.

 

7. 대각화(Diagonalization)

 

  대각화란 행렬을 대각요소만 가진 대각형렬이 되도록 만드는 작업입니다. 고유값 분해(Eigen Decomposition)이라고도 부릅니다. 대각화 또한 EMathHelp 웹 사이트를 이용하면 좋습니다.

 

  ▶ EMathHelp: 대각화 온라인 계산기 링크

 

  이 사이트는 고유값 분해 과정을 매우 상세하게 알려주기 때문에, 행렬의 대각화를 쉽게 진행할 수 있습니다.

 

 

8. 역행렬(Inverse Matrix)

 

  역행렬을 계산할 때는 Matrix Reshish 사이트를 이용하면 좋습니다.

 

  ▶ Matrix Reshish: 역행렬 온라인 계산기

 

 

9. 특이값 분해(Singular Value Decomposition, SVD)

 

  특이값 분해는 고유값 분해처럼 행렬을 대각화할 수 있는 방법입니다. 행렬의 차원과 상관 없이 분해가 가능하다는 점에서 많이 사용됩니다. A라는 행렬을 다음과 같이 세 개의 행렬의 곱 형태로 바꾸어 줍니다.

 

 

  SVD는 AtoZMath 사이트를 이용하면 구체적인 과정과 SVD 결과를 확인할 수 있습니다.

 

  ▶ AtoZMath: SVD 온라인 계산기 링크

 

  저는 하나의 2 X 2 행렬을 예시로 넣어 보았으며, 그 결과는 다음과 같이 나왔네요.

 

 

  참고로 SVD는 파이썬을 이용해서 계산할 수도 있습니다.

 

# Singular-value decomposition
from numpy import array
from scipy.linalg import svd
from numpy import zeros
from numpy import diag

# Define a matrix
A = array([
  [2, 2],
  [-1, 1]
])
print("[ 초기 행렬 ]")
print(A)

# SVD calculation
U, s, VT = svd(A)
print("[ SVD 라이브러리 결과 ]")
print(U)
print(s)
print(VT)

# Populate Sigma with n x n diagonal matrix
Sigma = zeros((A.shape[0], A.shape[1]))
Sigma[:A.shape[0], :A.shape[0]] = diag(s)
print("[ 행렬 형태의 Sigma 확인 ]")
print(Sigma)

# Reconstruct matrix
B = U.dot(Sigma.dot(VT))
print("[ 복구된 행렬 ]")
print(B)

 

  참고로 기본적인 SVD는 Rank-K Approximation이 가능합니다. 행렬의 크기를 줄여서 데이터를 압축할 수 있습니다. 아래 그림 (b)를 보시면 Rank-K Approximation이 이루어지는 원리를 확인할 수 있는데요. 이건 사실 SVD를 계산한 뒤에 행렬에서 일부만 이용하여 행렬 A를 다시 만드는 과정이라고 보시면 됩니다.

 

 

  이는 파이썬을 이용해서도 마찬가지로 쉽게 계산할 수 있습니다.

 

# Singular-value decomposition
from numpy import array
from scipy.linalg import svd
from numpy import zeros
from numpy import diag

# Define a matrix
A = array([
  [3, 2, 2],
  [2, 3, -2]
])
print("[ 초기 행렬 ]")
print(A)

# SVD calculation
U, s, VT = svd(A)
print("[ SVD 라이브러리 결과 ]")
print(U)
print(s)
print(VT)

# Populate Sigma with n x n diagonal matrix
Sigma = zeros((A.shape[0], A.shape[1]))
Sigma[:A.shape[0], :A.shape[0]] = diag(s)
print("[ 행렬 형태의 Sigma 확인 ]")
print(Sigma)

# Reconstruct matrix
B = U.dot(Sigma.dot(VT))
print("[ 복구된 행렬 ]")
print(B)

# Select K
K = 1
Sigma = Sigma[:, :K]
VT = VT[:K, :]

# Reconstruct matrix
B = U.dot(Sigma.dot(VT))
print("[ Best Rank-K Approximation ]")
print(B)

 

※ 미적분 관련 계산기 ※

 

  미적분(Derivative) 계산을 도와주는 웹 사이트로 가장 만족스러웠던 곳은 Derivative Calculator 사이트입니다. 이 사이트는 기본적인 함수들(지수함수, 삼각함수 등)을 지원해주며, 원하는 변수로 효과적이고 빠르게 미분할 수 있습니다. 더불어 전체 미분 과정을 다 보여주기 때문에 매우 유용합니다.

 

  ▶ Derivative Calculator: 미적분 온라인 계산기

 

 

  그러면 다음과 같이 일계 도함수(한 번 미분한 결과)를 알려주는 것을 알 수 있습니다. 과정이 매우 구체적으로 나와 있으므로, 이를 따라가면서 미분 과정을 이해할 수 있습니다.

 

 

  또 다른 예시로 시그모이드(Sigmoid) 함수에 대한 미분도 해보았습니다.

 

 

  마찬가지로 정상적으로 미분 결과를 알려주는 것을 확인할 수 있습니다.

 

 

※ 수열 관련 계산기 ※

 

  수열의 원소를 나열하여 입력하면, 수열의 일반항을 알려주는 사이트로 OEIS가 있습니다. 이 사이트는 특히 알고리즘 문제를 풀 때에도 매우 유용하게 사용할 수 있는데요.

 

  ▶ OEIS: 수열 검색 사이트

 

  사이트에 접속한 이후에 검색창에 수열의 원소를 나열하여 입력하시면 됩니다.

 

 

  예를 들어 피보나치 수열인 {1, 1, 2, 3, 5, 8, 13, ... }을 입력하면 다음과 같이 피보나치 수열(Fibonacci Numbers)이라는 이름으로 결과가 반환됩니다.

 

 

  위 사진을 확인해 보시면, 점화식 F(n) = F(n - 1) + F(n - 2) with F(0) = 0 and F(1) = 1 이라는 내용도 포함되어 있습니다. 필요할 때마다 사용하시면 매우 유용합니다.

 

※ 머신러닝 관련 ※

 

1. PCA (Principal Component Analysis)

 

  PCA 전통적인 차원 축소 (Dimensionality Reduction) 방법 중 하나로, 고차원 데이터를 시각화 (Visualization) 하거나 노이즈 제거 (Noise Removal) 목적으로 사용할 수 있습니다. 예를 들어 픽셀 수가 1,000개가 넘는 (1,000 차원이 넘는) 이미지 데이터 셋을 군집화/시각화하고 싶을 때 사용할 수 있겠죠.

 

  전통적인 PCA의 동작 과정은 다음과 같습니다.

 

  1. 먼저 데이터 셋의 평균이 0이 될 수 있도록 전처리(Preprocessing)합니다.

  2. 전처리 된 데이터에서 공분산(Covariance)을 계산합니다.

  3. 공분산의 고유벡터(Eigen Vectors)를 계산합니다.

  4. 공분산의 고유벡터에 투영할 때 분산이 최대가 되므로, 데이터를 고유벡터로 투영(Projection)합니다.

 

  근데 실제로는 전통적인 PCA를 이용하는 것보다, SVD를 이용할 때 공분산 행렬을 메모리에 저장할 필요가 없어서 더 효율적일 수 있습니다. 실제로 많은 라이브러리는 SVD에 기반하여 PCA 기능을 지원합니다. PCA (Principal Component Analysis)를 사용해야 할 때는, 다음과 같이 간단히 Sklearn 라이브러리를 사용할 수 있습니다. 

 

import numpy as np
from sklearn.decomposition import PCA

# 행렬 X 선언 (Feature Size: 3)
X = np.array([
  [1, 2, 3],
  [3, 4, 5],
  [4, 5, 3],
  [6, 5, 4],
  [6, 7, 5]
])

# PCA 수행 (3차원 → 2차원): 내부적으로 SVD를 이용하는 방식으로 동작
pca = PCA(n_components=2)
pca.fit_transform(X)

print("Singular Values:", pca.singular_values_) # SVD의 U * Sigma * V^T에서 Sigma의 대각 원소들
print("Singular Vector:\n", pca.components_) # SVD의 U * Sigma * V^T에서 V^T에 해당
print('Eigen values:', pca.explained_variance_) # 공분산 행렬의 Eigen Values
print('Explained Variance Ratio:', pca.explained_variance_ratio_) # Eigen Value들의 비율

 

2. Support Vector Machine (SVM)

 

  SVM는 데이터를 분류하기 위한 전통적인 모델 중 하나입니다. 분류 경계면 f(x) = <w, x> + b라고 설정할 수 있는데요. 이 때 벡터 w는 경계면과 수직을 이루는 법선 벡터입니다. 서포트(Support)란 가장 경계면 근처에 있는 벡터(데이터)들을 의미합니다. (Plus-plane과 Minus-plane 위에 존재하는 벡터들) Binary Classification에서는 x+과 x-라고도 표현합니다. 마진(Margin)은 x+과 x- 사이의 거리를 의미합니다.

 

  기본적인 SVM의 목적 함수(Objective Function)는 제약(Constraints)을 포함하고 있기 때문에, 라그랑지안 승수법(Lagrange Multiplier Method)을 적용하여 목적 함수의 형태를 변형해야 합니다. 이어서 듀얼 문제(Dual Problem)으로 변환한 뒤에 실제로 문제를 해결할 수 있습니다.

 

  이처럼 기본적인 SVM은 선형적(Linear) Decision Boundary를 해결할 수 있도록 해줍니다. 하지만 비선형적(Non-linear) Decision Boundary가 필요하다면, 기존의 방법을 그대로 이용할 수 없습니다. 비선형적인 결정 경계를 만들기 위한 핵심 아이디어는 데이터를 고차원 데이터로 매핑(Mapping)시킨 뒤에 고차원 공간에서 Decision Boundary를 만드는 것입니다. 이 과정에서 커널 트릭(Kernel Trick)이 사용될 수 있습니다.

 

  기본적인 SVM 예제는 파이썬의 Sklearn 라이브러리를 이용해서 실행해 볼 수 있습니다. 먼저 간단하게 2D 상의 데이터 셋을 만들 수 있습니다.

 

from sklearn.datasets import make_blobs

X, Y = make_blobs(n_samples=100, centers=2, cluster_std=0.5, random_state=8)
Y = 2 * Y - 1 # 레이블을 1과 -1로 분리하기

plt.scatter(X[Y==-1,0], X[Y==-1,1], marker='o', label="Positive")
plt.scatter(X[Y==+1,0], X[Y==+1,1], marker='x', label="Negative")

plt.title("Train Data")
plt.xlabel("x1")
plt.ylabel("x2")
plt.legend()
plt.show()

 

  시각화 결과는 다음과 같습니다.

 

 

  간단히 선형(Linear) 커널을 이용하여 선형적으로 데이터를 분류할 수 있습니다. C 값은 Loss 설정을 위한 파라미터 중 하나인데, 일반적으로 매우 큰 값을 설정합니다.

 

from sklearn.svm import SVC

# 선형(Linear) 커널 사용하기
model = SVC(kernel='linear', C=1e10).fit(X, Y)

print("각 클래스당 서포트 개수:", model.n_support_)
print("각 클래스의 서포트의 X 값:\n", model.support_vectors_)
print("SVM의 가중치 벡터(W):", model.coef_)
728x90
반응형

728x90
반응형

※ 줌(Zoom) 설치 방법 ※

 

  줌(Zoom)은 PC로도 사용이 가능하고, 모바일(Mobile)로도 사용이 가능합니다. 모바일의 경우 앱을 설치하기만 하면 곧바로 회의에 참여할 수 있기 때문에 매우 간단합니다. 또한 PC는 웹 브라우저만 있어도 사용하는 데에 큰 무리가 없습니다. PC의 경우 다음의 링크에 접속하면 됩니다.

 

  ▶ 줌(Zoom) 웹 사이트 링크: https://zoom.us/

 

  접속 이후에는 바로 [회의 참가] 링크로 들어가서 회의에 참가할 수 있습니다.

 

 

  모바일의 경우 다음과 같이 플레이 스토어에서 Zoom을 검색하시면 됩니다. 설치하면 곧 바로 사용이 가능합니다.

 

 

※ 회의 참가자용 사용 방법 ※

 

1. 방 번호 및 비밀번호를 받은 경우

 

  회의에 참가하려는 사람들은 간단히 줌(Zoom) 회의 방 번호를 입력하여 접속하면 됩니다. 회의를 생성한 사람에게 미리 회의 방 번호랑 비밀번호를 받았을 텐데요. 그 방 번호와 비밀번호를 입력하면 됩니다. (일반적으로 비밀번호는 그냥 간단히 0으로 설정하거나 합니다.)

 

  모바일의 경우 Zoom을 실행하면, 바로 다음과 같은 화면이 나올 건데요. [회의 참가] 버튼을 눌러주세요.

 

  회의에 참가할 때에는 위 쪽에 방 번호를 넣어 주시고, 그 바로 아래에 자신의 이름을 넣으시면 됩니다.

 

 

  만약 방에 비밀번호가 걸려있는 경우, 다음과 같이 [회의 비밀번호]를 입력하라는 메시지가 나옵니다. 이 경우 비밀번호를 정상적으로 입력해야 회의에 참가가 가능합니다.

 

 

2. 줌(Zoom) 링크를 받은 경우

 

  줌 링크를 받은 경우, 일반적으로 그 링크에 방 번호와 비밀번호 정보가 포함되어 있기 때문에 바로 접속이 가능합니다. 따라서 이 경우는 생략하겠습니다. 일반적으로 호스트(방 주인)이 회의 방을 개설한 다음에, 그 링크를 카카오톡 등으로 공유합니다. 그 링크는 대략 다음과 같은 형태입니다.

 

  ▶ 예시 링크: us04web.zoom.us/j/6217744563?pwd=L09aK1dAAKJD

 

  결과적으로 회의 방에 접속할 수 있는데요. 만약에 호스트(방 주인)이 회의를 시작하지 않은 상태라면, 다음과 같은 대기 메시지가 나옵니다. 호스트가 회의를 시작하면 비로소 회의가 시작됩니다.

 

 

  회의가 시작되면 [비디오 공유]를 통해서 간단히 자신의 카메라로 보이는 화면을 공유할 수 있습니다. 결과적으로 다른 사람과 간단하게 화상 회의를 진행할 수 있습니.

 

728x90
반응형

728x90
반응형

  최근 코로나 사태 등으로 온라인으로 시험을 치러야 하는 일이 많아지고 있습니다. 그래서 온라인 시험을 위한 플랫폼들이 많이 사용되고 있는데요. 오늘은 온라인 시험을 위한 플랫폼 중에서 Exam.net을 설명하고자 합니다. 이름에서부터 알 수 있듯이 온라인 시험을 치를 수 있도록 도와주는 교수님/선생님/학생 전용 서비스입니다.

 

  ▶ Exam.net 사용법: https://exam.net/

 

Exam.net is a secure platform for digital exams.

Exam.net is a secure platform for digital exams.

exam.net

 

※ 교수님/선생님을 위한 설명 ※

 

  일단 이 글을 보시는 분이 교수님이나 선생님이라면, Exam.net에서 선생님(Teacher) 모드로 가입하셔야 합니다. 웹 사이트에 접속하신 뒤에 [Teacher] 계정으로 회원가입/로그인을 진행해주세요.

 

 

  선생님 계정으로 회원가입(Register)을 할 때에는 다음과 같이 국가명 및 도시명을 기입해야 합니다. 그리고 이어서 학교가 어디인지 기입해야 합니다.

 

 

  만약에 본인의 학교가 존재하지 않는다면, [Click here to add a new school] 버튼을 눌러서 학교를 새롭게 등록할 수도 있습니다.

 

 

  저는 예시로 다음처럼 한 번 회원가입을 해보았습니다. 사실 학교 이름이나 이메일 계정을 따로 검사하지는 않기 때문에, 아무렇게나 넣어도 선생님 계정을 만들 수는 있습니다.

 

 

  회원가입이 끝나고 나면 다음과 같이 새로운 시험(New Exam)을 만들 수 있습니다.

 

 

  새롭게 시험을 등록하는 과정은 매우 간단한데요. 먼저 시험용 문제(Exam Questions)를 등록하실 수 있습니다. PDF 파일로 문제를 올리셔도 되고, 그냥 단순히 글을 보여주려고 한다면 [Write exam questions] 버튼을 눌러서 문제를 바로 글로 작성하실 수도 있습니다.

 

 

  저는 한 번 다음과 같이 문제를 출제해 보았습니다.

 

 

  이후에 학생으로부터 어떤 정보를 받을지 기입하는 부분(Student Information)이 나오는데요. 일반적으로 동명이인이 없다면 그냥 First Name과 Last Name 정도만 받아도 될 것 같네요. 학교에서의 시험이라면 학번(Student ID)까지 받도록 하시면 좋습니다.

 

 

  그리고 학생이 답안지를 작성하는 화면(Student Workspace) 대한 내용인데요. 학생이 어떤식으로 답안을 작성하도록 할 것인지를 설정할 수 있습니다. [Writing area]를 체크해서, 학생이 문장을 입력해 답안을 작성할 수 있도록 하는 것이 일반적입니다.

 

 

   그리고 보안(Security) 설정을 하실 수 있습니다. [Require high-security mode]를 이용하시면, 학생들이 별도의 정해진 앱으로만 시험을 보도록 하며 부정행위를 더 잘 방지할 수 있습니다. 반면에 [Allow any browser]를 이용하면, 학생이 아무 브라우저나 이용해서 시험을 볼 수 있습니다.

 

  아래 쪽의 [Settings for the lower security mode]는 아무 브라우저에서나 시험을 볼 수 있도록 설정했을 때 추가적으로 설정할 수 있는 내용인데요. 학생이 웹 브라우저를 건드리려고 하는 경우, 시험 화면을 잠시 잠금(Lock)한 뒤에, 학생이 어떤 이유 때문에 웹 브라우저를 나갔는지 물어보는 란이 나옵니다. 학생은 거기에 '잠시 컴퓨터 오류가 생겼어요.'나 '실수로 탭 키를 눌러서 내려갔어요.' 등으로 답을 해야겠죠. 학생이 그렇게 답을 했을 때 잠금 해제(Unlock)을 바로 진행해 줄 지 등을 설정할 수 있습니다.

 


  자 이제 [Create the exam] 버튼을 눌러서 시험이 만들어지면 다음과 같은 화면이 등장합니다. 사용자는 [Exam key]를 이용해서 시험장에 접속할 수 있습니다. 저는 "ZpDaUx"로 만들어졌네요.

 

 

  그리고 다음과 같이 [Open/closed] 스위치를 눌러서 시험 시작/종료 설정을 할 수 있습니다.

 

 

  이제 학생 입장에서 시험이 어떻게 치뤄질 지 궁금하다면, [Preview exam] 버튼을 눌러서 학생 입장으로 시험을 한 번 치루어 볼 수 있습니다.

 

 

  이제 시험이 진행 중이라면 선생님 입장에서 감독(Surveilance) 하거나 결과 확인을 할 수 있습니다. 다음과 같이 [Surveilance / Results] 페이지로 이동해보세요.

 

 

  그러면 어떤 학생들이 시험을 치르고 있는지 확인할 수 있습니다. 만약에 시험 시간이 종료되었다면, 시험 종료를 위해 [Force submission for students] 버튼을 누르실 수 있습니다.

 

 

  또한 모든 학생의 시험지를 한 번에 다운로드 받아서 확인할 수 있는데요. [Download PDF] 버튼을 누르시면 모든 학생의 시험 결과가 나오게 됩니다.

 

 

  그러면 다음과 같이 학생별로 어떤 답을 넣었는지에 대한 내용이 담긴 PDF 문서를 확인할 수 있습니다.

 

 

  또한 개별 학생에 대한 정보도 다음과 같이 확인할 수 있습니다.

 

 

※ 학생을 위한 설명 ※

 

  교수님/선생님들을 위한 설명은 끝났으니, 이제 학생을 위한 설명도 진행하겠습니다. 학생 입장에서는 먼저 Exam.net에 접속한 뒤에 시험 코드를 입력합니다. 이후에 신상정보를 입력하고 [FETCH THE EXAM] 버튼을 눌러서 시험장으로 접속할 수 있습니다.

 

 

  만약 교수님/선생님이 아직 시험을 시작하지 않은 상태라면, 다음과 같은 화면이 등장합니다.

 

 

  교수님/선생님이 시험을 시작하도록 설정하면, 학생은 다음과 같이 [Begin exam] 버튼을 눌러 시험을 치를 수 있게 됩니다.

 

 

  결과적으로 학생은 다음과 같이 답안을 작성할 수 있습니다. 참고로 아래 그림에서는 보이지 않지만, 선생님이 프로그래밍, 계산기, 그림 그리기 등을 허용했다면 그러한 추가적인 기능을 이용할 수 있습니다.

 

 

  만약에 시험을 치르는 도중에 브라우저 탭을 내리거나 하면, 다음과 같이 "자리를 왜 비웠는지"에 대한 사유를 작성해야 하고, 그 이후에 잠금 해제(Unlock)을 진행할 수 있습니다.

 

 

  결과적으로 시험을 다 치르고 나면 [Submit exam] 버튼을 눌러서 답안지를 제출할 수 있습니다.

 

 

※ 참고 사항 ※ 

 

1. 고수준 보안 모드

 

  기본적으로 교수님/선생님들은 부정 행위를 더 엄격히 방지하기 위하여, [Require high-security mode]를 선택할 수 있는데요. 이 경우에는 학생들이 지정된 소프트웨어를 사용해야 합니다.

 

 

  대표적으로 SEB를 이용하도록 설정했다면, 학생들은 SEB를 설치해야 합니다. SEB 설치 경로는 다음과 같으니, 학생에게 공지해주시면 되겠습니다.

 

  ▶ SEB 설치 경로: https://support.exam.net/#/seb

 

2. 프로그래밍 이용 가능

 

    또한 필요하다면 프로그래밍(Programming) 문제도 출제할 수 있습니다. [Programming] 옵션을 학생들이 사용할 수 있도록 설정한다면, 학생은 다음과 같이 JavaScript 혹은 Python을 이용할 수 있습니다. 자신이 작성한 소스코드 또한 저장을 해서, 선생님이 채점할 때 이를 보도록 할 수도 있답니다.

 

 

3. 기타 기능

 

  이 밖에도 학생들이 그래프/표 등을 그려서 제출하도록 할 수도 있고, 계산기를 사용하도록 설정할 수도 있습니다. 전반적인 기능은 모두 선생님이 컨트롤할 수 있습니다. 따라서 Exam.net의 추가적인 기능도 잘 확인하면서 적절히 시험 환경을 구성하는 것이 중요합니다.

 

4. 시험을 위한 추가적인 팁

 

  본 사이트 Exam.net은 단순히 학생들이 시험을 치르고 답을 제출하도록 해주는 사이트입니다. 온라인 시험이라면, 학생들이 얼굴을 보이도록 카메라(Camera)를 이용하도록 할 수도 있는데요. 이 경우에는 학생에게 PC와 스마트 폰 두 개를 모두 준비하도록 한 뒤에 스마트 폰으로는 Zoom과 같은 화상 채팅 앱을 이용해서 얼굴을 보이도록 하고, PC에서는 Exam.net을 이용하여 시험을 치르도록 요구할 수 있습니다.

728x90
반응형

728x90
반응형

  이번 시간에는 Teensy 보드(Board) 위에서 동작하는 HID 소프트웨어를 개발하는 방법을 소개하고자 합니다. 단, HID 코어(Core) 라이브러리를 처음부터 만드는 방법을 소개합니다. 즉, 자신만의 USB 프로토콜을 만들어 보고 싶은 사람에게 도움이 될 만한 글입니다. 본 예시는 RawHID 소스코드를 기반으로 하지만, 특히 HID가 아닌 본인만의 USB 클래스를 정의하고 싶으신 분들에게도 도움이 될 것입니다.

 

※ Teensy USB 디바이스 개발하기 ※

 

  일단 Teensy의 경우에는 아두이노 IDE 위에서 동작한다는 특징이 있는데요. 사용자 라이브러리는 다음의 경로에 만들어 주면 됩니다.

 

  ▶ 아두이노 사용자 라이브러리(스케치북) 경로: C:\Users\{사용자명}\Documents\Arduino\libraries

 

 

  기본적인 HID 기능을 이용할 수 있도록 MyHID라는 이름의 사용자 라이브러리를 생성하도록 하겠습니다. 폴더 구성은 다음과 같이 해주시면 됩니다. 일반적으로 다음과 같이 하나의 헤더 파일(.h)과 하나의 C++ 소스 코드(.cpp)로 라이브러리를 작성할 수 있습니다. 그리고 자신의 라이브러리를 어떻게 사용할 수 있을지를 간략히 보여주는 examples 폴더를 만드시면 됩니다.

 

 

  먼저 MyHID.h를 작성해 보겠습니다. 기본적으로 2개의 함수가 존재하도록 작성할 수 있는데요. 바로 hid_recv()와 hid_send()입니다. 하나는 HID 프로토콜을 이용해 데이터를 받는 함수, 하나는 보내는 함수입니다.

 

  ▶ hid_recv(): 호스트(Host)로부터 buffer만큼의 데이터를 받아 옵니다.

  ▶ hid_send(): 호스트(Host)로부터 buffer만큼의 데이터를 전송합니다.

 

  USB 디바이스(Device)는 기본적으로 여러 개의 인터페이스(Interface)를 포함할 수 있는데요. 개발 단계에서부터 어떠한 인터페이스를 HID 목적으로 사용할 지 설정할 수 있습니다. 구체적으로 Teensy 쪽의 usb_desc.h에 그러한 기본적인 인터페이스 번호를 포함한 설정 정보가 적혀 있습니다. (조금 있다가 다시 언급하겠습니다.) 아무튼 그러한 usb_desc.h를 불러와 이용하는 방식으로 소스코드가 작성됩니다.

 

#ifndef MyHID_H
#define MyHID_H

#include "usb_desc.h" // for contants (RAWHID_INTERFACE, RAWHID_RX_ENDPOINT, RAWHID_TX_ENDPOINT, ...)

#if defined(RAWHID_INTERFACE)

#include <inttypes.h>

int available(void);
int hid_recv(void *buffer, uint32_t timeout);
int hid_send(const void *buffer, uint32_t timeout);

#endif // RAWHID_INTERFACE
#endif // MyHID_H

 

  이제 MyHID.cpp를 작성할 수 있습니다. hid_recv() 함수와 hid_send() 함수를 실제로 구현하는 부분입니다. 구현을 위해서 usb_dev.h를 이용하는데요, 기본적으로 usb_dev.h 헤더 파일에는 usb_rx()나 usb_tx()와 같은 기본적인 기능들이 구현되어 있습니다. 그러한 코어 USB 기능을 이용하여 실제로 HID 관련 라이브러리를 구현하게 됩니다. 확인해 보시면 전송을 위한 timeout이 있어서, 너무 오랫동안 전송이 안 되는 데이터는 버리게 됩니다.

 

  데이터를 주고 받는 usb_rx()나 usb_tx()를 확인해 보시면, 특정한 엔드포인트(Endpoint)를 이용하는 것을 알 수 있습니다. 엔드포인트데이터를 주고 받기 위한 주소와 같은 것이라고 보시면 됩니다.

 

#include "usb_dev.h" // for USB core functions (usb_rx, usb_tx, ...)
#include "core_pins.h" // for yield(), millis()
#include <string.h> // for memcpy()
#include "MyHID.h"

#ifdef RAWHID_INTERFACE
#if F_CPU >= 20000000

int available(void)
{
	uint32_t count;

	if (!usb_configuration) return 0;
	count = usb_rx_byte_count(RAWHID_RX_ENDPOINT);
	return count;
}

int hid_recv(void *buffer, uint32_t timeout)
{
	usb_packet_t *rx_packet;
	uint32_t begin = millis();

	while (1) {
		if (!usb_configuration) return -1;
		rx_packet = usb_rx(RAWHID_RX_ENDPOINT);
		if (rx_packet) break;
		if (millis() - begin > timeout || !timeout) return 0;
		yield();
	}
	memcpy(buffer, rx_packet->buf, RAWHID_RX_SIZE);
	usb_free(rx_packet);
	return RAWHID_RX_SIZE;
}

// Maximum number of transmit packets to queue so we don't starve other endpoints for memory
#define TX_PACKET_LIMIT 4

int hid_send(const void *buffer, uint32_t timeout)
{
	usb_packet_t *tx_packet;
	uint32_t begin = millis();

	while (1) {
		if (!usb_configuration) return -1;
		if (usb_tx_packet_count(RAWHID_TX_ENDPOINT) < TX_PACKET_LIMIT) {
			tx_packet = usb_malloc();
			if (tx_packet) break;
		}
		if (millis() - begin > timeout) return 0;
		yield();
	}
	memcpy(tx_packet->buf, buffer, RAWHID_TX_SIZE);
	tx_packet->len = RAWHID_TX_SIZE;
	usb_tx(RAWHID_TX_ENDPOINT, tx_packet);
	return RAWHID_TX_SIZE;
}

#endif // F_CPU
#endif // RAWHID_INTERFACE

 

  이제 이러한 라이브러리를 이용하는 실질적인 USB 디바이스의 아두이노 코드를 작성할 수 있습니다.

 

 

  소스코드는 다음과 같이 작성할 수 있습니다. 소스코드를 간단히 설명하자면, 앞서 정의한 HID 코어 라이브러리를 이용해서 데이터를 보내고 받도록 작성되어 있습니다. 일단 코드의 앞 부분에서는 데이터를 받아서 이를 바로 출력하는 부분이 작성되어 있으며, 코드의 뒷 부분에서는 2초마다 한 번씩 64바이트의 데이터를 보내는 부분이 작성되어 있습니다.

 

#include <MyHID.h>

void setup() {
}

// RawHID packets are always 64 bytes.
byte buffer[64];
elapsedMillis msUntilNextSend;

void loop() {
  int n;
  n = hid_recv(buffer, 0); // 0 timeout = do not wait
  if (n > 0) {
    String received = String((char*)buffer);
    Serial.println(received);
  }
  // every 2 seconds, send a packet to the computer (Host PC)
  if (msUntilNextSend > 2000) {
    msUntilNextSend = msUntilNextSend - 2000;
    buffer[0] = 0x12;
    buffer[1] = 0x34;
    for (int i = 2; i < 62; i++) {
      buffer[i] = 0;
    }
    buffer[62] = 0x56;
    buffer[63] = 0x78;
    // send the packet
    n = RawHID.send(buffer, 100);
    if (n > 0) {
      Serial.println("Transmit packet ");
    } else {
      Serial.println("Unable to transmit packet");
    }
  }
}

 

  실제로 USB Device의 설정 정보는 Teensy 코어 라이브러리 위치의 usb_desc.h 헤더 파일에 정의되어 있습니다. 우리가 Teensy 장치를 RawHID로 설정하게 되면, 다음과 같이 각각의 Description 정보가 구성됩니다. 확인해 보시면, 기본적으로 Vendor ID로 0x16C0을, Product ID로 0x0486을 사용하도록 되어 있네요.

 

 

  여기에서 핵심이 되는 내용은 Vendor ID, Product ID인데요. 이 값을 통해서 Host OS가 USB Device를 정확히 찾아 접근할 수 있습니다. 또한 Product Name은 "Teensyduino RawHID"인데요, 이게 나중에 Host OS에서 출력될 USB Device의 이름이 됩니다.

 

  또한 HID 통신을 위해서 데이터를 주고 받으려면 데이터를 받는 엔드포인트(Endpoint)와 데이터를 보내는 엔드포인트(Endpoint)를 정확히 정의해야 합니다. 확인해 보시면 송신(Transmit)을 위한 엔드포인트는 3번이고, 수신(Receive)을 위한 엔드포인트는 4번으로 정의된 것을 확인할 수 있습니다. 이러한 정보는 실제로 Description 정보가 되어 Host PC로 전송됩니다. Host는 이후에 이러한 엔드포인트를 이용해서 HID 통신을 진행하게 되는 것이죠.

 

  이제 최종적으로 만들어진 HelloWorld.ino 파일을 Teensy에 업로드 해주세요.

 

 

  그럼 이제 성공적으로 우리의 Teensy는 HID 기능을 위한 목적으로 동작하게 됩니다.

 

※ Host 프로그램 개발하기 ※

 

  Host 프로그램의 소스코드는 다음과 같이 구성됩니다.

 

  ▶ hid_WINDOWS.c: HID 기능을 위한 기본 라이브러리 (윈도우 전용)

  hid_LINUX.c: HID 기능을 위한 기본 라이브러리 (리눅스 전용)

   hid.h: HID 라이브러리를 위한 헤더 파일

   Makefile: 컴파일을 진행하기 위한 설정 파일

   my_hid.c: 메인 함수가 포함된 실제 소스코드

 

  먼저 hid_WINDOWS.c와 hid_LINUX.c의 소스코드는 다음의 경로에서 찾아보실 수 있습니다. (참고로 소스코드를 가져오신 뒤에, printf() 함수를 보이지 않도록 처리한 부분을 제거하시면 디버깅에 도움이 됩니다.)

 

  https://github.com/ndb796/Teensy-RawHID-SD/tree/master/host

 

  먼저 hid.h는 다음과 같이 작성할 수 있습니다. Host OS 쪽에서도 마찬가지로 USB 디바이스로부터 데이터를 받을 수 있어야 하기 때문에, 송신(Send) 함수와 수신(Receive) 함수로 구성됩니다.

 

int rawhid_open(int max, int vid, int pid, int usage_page, int usage);
int rawhid_recv(int num, void *buf, int len, int timeout);
int rawhid_send(int num, void *buf, int len, int timeout);
void rawhid_close(int num);

 

  그리고 Makefile은 다음과 같이 작성할 수 있습니다. 참고로 빌드(Build)는 리눅스 OS에서 진행하셔야 합니다. 리눅스 OS에서 윈도우 전용 실행 파일(.exe)도 만들 수 있습니다. 일단 아래 Makefile은 LINUX 환경에서 실행할 수 있는 실행 파일이 나오도록 작성되어 있습니다. 윈도우용 실행 파일을 만드실 때는 OS의 값으로 WINDOWS를 넣어주시면 됩니다.

 

OS = LINUX
# OS = WINDOWS
PROG = my_hid

ifeq ($(OS), LINUX)
TARGET = $(PROG)
CC = gcc
STRIP = strip
CFLAGS = -Wall -O2 -DOS_$(OS)
LIBS = -lusb
else ifeq ($(OS), WINDOWS)
TARGET = $(PROG).exe
CC = i686-w64-mingw32-gcc
STRIP = i686-w64-mingw32-strip
CFLAGS = -Wall -O2 -DOS_$(OS)
LIBS = -lhid -lsetupapi
endif

OBJS = $(PROG).o hid.o

all: $(TARGET)

$(PROG): $(OBJS)
	$(CC) -o $(PROG) $(OBJS) $(LIBS)
	$(STRIP) $(PROG)

$(PROG).exe: $(PROG)
	cp $(PROG) $(PROG).exe

hid.o: hid_$(OS).c hid.h
	$(CC) $(CFLAGS) -c -o $@ $<

clean:
	rm -f *.o $(PROG) $(PROG).exe $(PROG).dmg
	rm -rf tmp

 

  이제 my_hid.c는 다음과 같이 작성할 수 있습니다. 확인해 보시면 계속해서 데이터를 받고, 보내는 작업이 수행되는 것을 확인할 수 있는데요. 기본적으로 타임아웃(Timeout)이 걸려 있어서 무한정 대기하지는 않기 때문에, 데이터를 잘 주고 받으며 동작하게 되는 것입니다.

 

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#if defined(OS_LINUX)
#include <sys/ioctl.h>
#include <termios.h>
#elif defined(OS_WINDOWS)
#include <conio.h>
#endif

#include "hid.h"

static char get_keystroke(void);

int main()
{
	int i, r, num;
	char c, buf[64];

	// C-based example is 16C0:0480:FFAB:0200
	r = rawhid_open(1, 0x16C0, 0x0480, 0xFFAB, 0x0200);
	if (r <= 0) {
		// Arduino-based example is 16C0:0486:FFAB:0200
		r = rawhid_open(1, 0x16C0, 0x0486, 0xFFAB, 0x0200);
		if (r <= 0) {
			printf("no rawhid device found\n");
			return -1;
		}
	}
	printf("found rawhid device\n");

	while (1) {
		// check if any Raw HID packet has arrived
		num = rawhid_recv(0, buf, 64, 220);
		if (num < 0) {
			printf("\nerror reading, device went offline\n");
			rawhid_close(0);
			return 0;
		}
		if (num > 0) {
			printf("\nrecv %d bytes:\n", num);
			for (i=0; i<num; i++) {
				printf("%02X ", buf[i] & 255);
				if (i % 16 == 15 && i < num-1) printf("\n");
			}
			printf("\n");
		}
		// check if any input on stdin
		while ((c = get_keystroke()) >= 32) {
			printf("\ngot key '%c', sending...\n", c);
			buf[0] = c;
			for (i=1; i<64; i++) {
				buf[i] = 0;
			}
			rawhid_send(0, buf, 64, 100);
		}
	}
}

#if defined(OS_LINUX)
static int _kbhit() {
	static const int STDIN = 0;
	static int initialized = 0;
	int bytesWaiting;

	if (!initialized) {
		// Use termios to turn off line buffering
		struct termios term;
		tcgetattr(STDIN, &term);
		term.c_lflag &= ~ICANON;
		tcsetattr(STDIN, TCSANOW, &term);
		setbuf(stdin, NULL);
		initialized = 1;
	}
	ioctl(STDIN, FIONREAD, &bytesWaiting);
	return bytesWaiting;
}
static char _getch(void) {
	char c;
	if (fread(&c, 1, 1, stdin) < 1) return 0;
	return c;
}
#endif

static char get_keystroke(void)
{
	if (_kbhit()) {
		char c = _getch();
		if (c >= 32) return c;
	}
	return 0;
}

 

  결과적으로 리눅스 Host PC 입장에서 전체 소스코드는 다음과 같이 구성됩니다.

 

 

  이제 Teensy Board를 리눅스 OS에 연결합니다. 저는 가상 머신(Virtual Machine)에서 실습을 진행하고 있으므로, PC에 꽂혀 있는 USB를 가상 머신에서 인식할 수 있도록 USB 장치 설정을 해주었습니다.

 

 

  이제 소스코드를 컴파일하고, my_hid 프로그램을 실행하시면 됩니다. 참고로 리눅스에서는 알 수 없는 USB 장치와 통신하기 위해서는 루트(Root) 권한이 필요합니다. 결과적으로 다음과 같이 프로그램이 실행됩니다.

 

 

  이후에 다음과 같이 2초마다 한 번씩 USB 장치로부터 64 바이트의 데이터가 날라오는 것을 확인할 수 있습니다. 또한 Host 입장에서도 어떠한 알파벳 키를 누르면 해당 데이터가 USB 장치로 전달됩니다. 서로 데이터를 주고 받는 것을 알 수 있습니다.

 

 

  또한 Makefile 코드에서 빌드 대상(Target)을 윈도우로 설정하여 컴파일하면 다음과 같이 윈도우 호스트 실행 파일이 생성됩니다. (만약에 이미 리눅스 버전으로 컴파일을 해서 my_hid 실행 파일이 존재한다면, 이를 모두 삭제한 뒤에 다시 컴파일을 진행하셔야 합니다.)

 

 

  다음과 같이 윈도우 호스트에서 실행을 해도 정상적으로 동작하는 것을 확인할 수 있습니다.

 

 

  최종적으로 호스트(Host)와 USB 장치가 통신하는 내용을 출력한 사진은 다음과 같습니다.

 

 

  이제 이러한 소스코드 예시를 조금씩 수정해서 자신만의 USB 프로토콜 상에서 동작하는 USB 장치를 개발할 수 있게 되었습니다. 현재 예시는 RawHID를 토대로 하여 작성된 예시이지만, HID가 아닌 본인만의 USB 클래스를 정의하고 출시할 때에도 이와 비슷한 과정을 거칠 것이라는 것을 알 수 있습니다.

728x90
반응형

728x90
반응형

  Oracle VM VirtualBox를 이용할 때는 드래그 앤 드롭(Drag And Drop)으로 간단하게 Host와 Guest 사이에 파일을 주고 받을 수 있습니다. 이를 설정하는 방법은 간단한데요. 먼저 Oracle VM VirtualBox에서 [머신] - [설정]으로 이동합니다. 그리고 [일반] - [고급] 탭으로 이동해주세요.

 


  이제 [드래그 앤 드롭] 속성으로 [양방향]을 설정해주세요.

 

 

  이제 호스트(Host) OS와 게스트(Guest) OS 사이에서 파일을 끌어다 놓는 방식으로 간단히 파일을 옮길 수 있습니다.

 

728x90
반응형

728x90
반응형

  아두이노 설치 경로의 하드웨어(Hardware) 폴더는 아두이노 IDE를 이용하는 각기 다른 보드(Board) 하드웨어에 따라서 서로 다른 기능을 정의하고 있는 폴더입니다. 예를 들어 USB 장치 개발용 보드인 Teensy의 경우, 아두이노 IDE에서 개발이 가능하도록 지원하고 있습니다. 이 때 Teensy만의 기능들은 하드웨어 폴더 상에 정의가 되는 방식입니다.

  실제로 아두이노 IDE를 먼저 설치한 뒤에, Teensyduino라는 이름의 플러그인을 설치하면 아두이노 IDE에서 소스코드상으로 Teensy 전용 기능을 불러와 사용할 수 있게 됩니다. Teensyduino를 설치한 뒤의 하드웨어 폴더는 다음과 같이 구성됩니다. 원래 아두이노 IDE를 설치하면 아래 그림에서 보이는 teensy라는 이름의 폴더가 없는데요, 플러그인을 설치한 뒤에 생기는 겁니다.

 

 

  실제로 아두이노 상에서 [툴] - [보드]로 이동하면 하드웨어를 선택할 수 있습니다. 저는 Teensy 하드웨어 플러그 인을 설치했기 때문에, 이와 같이 Teensy를 선택할 수 있는 것을 확인할 수 있습니다.

 

 

  그렇다면 각기 다른 하드웨어(Hardware)에 따른 라이브러리는 어디서 확인할 수 있을까요? 바로 다음과 같이 하드웨어 폴더의 libraries 폴더까지 들어 오시면, 여기에 각종 예제 소스코드와 라이브러리가 포함되어 있습니다.

 

 

  실제로 위 라이브러리들은 아두이노 IDE를 실행했을 때 나오는 [예제] 소스코드들과 동일합니다.

 

 

  또한 [파일] - [예제]에 들어가는 기본 예제 소스코드들은 어디에서 확인할 수 있을까요?

 

 

  이 소스코드들은 예제(examples) 폴더 안에 있습니다.

 

728x90
반응형

728x90
반응형

  기본적으로 이용할 모든 기능을 스케치 파일(.ino)에 하나의 소스코드로 작성하는 것은 비효율적인 경우가 많습니다. 그래서 자주 사용하는 기능을 분리하여 사용자 라이브러리로 만들어서 관리하면 좋은 점이 많습니다. 특히 라이브러리를 직접 만들게 되면, 내가 만든 라이브러리를 다른 사람에게 쉽게 공유할 수도 있기 때문에, 여러모로 좋은 점이 많습니다.

 

  일반적으로 아두이노 라이브러리를 만들 때는 약속이 있습니다. 바로 다음의 위치(스케치북 위치)에 아두이노 라이브러리를 만든다는 것입니다.


  ▶ 아두이노 사용자 라이브러리 폴더: C:\Users\{사용자명}\Documents\Arduino\libraries

  저는 다음과 같이 MyLibrary라는 이름으로 하나의 라이브러리 폴더를 생성해 보았습니다.

 

 

  일반적으로 라이브러리 폴더는 다음과 같은 형식으로 구성됩니다. 아두이노 라이브러리 사용 경험이 많으신 분들에게는 익숙하실 수 있는데요, 라이브러리 폴더에는 소스코드 파일들을 제외하고도 examples라는 이름의 폴더도 포함하는 경우가 많습니다. examples 폴더는 어떤 라이브러리의 사용 방법 예시를 포함하는 폴더라고 할 수 있습니다. 그래서 보통 Hello World와 같은 코드를 담습니다.

 

 

※ 라이브러리 폴더의 구성 예시

  * MyLibrary.cpp

  * MyLibrary.h

  * examples

    * HelloWorld

        * HelloWorld.ino

 

  그러면 한 번 MyLibrary.h를 작성해 보겠습니다. 여기에서 사용되는 문법은 일반적인 C/C++과 같습니다. 저는 간단히 더하기 함수(add)와 빼기 함수(subtract)를 넣었습니다.

 

  참고로 #ifndef ~ #endif는 C/C++에서 자주 사용되는 전처리문입니다. 일반적으로 헤더 파일이 중복되어 초기화되지 않도록 막기 위해 사용합니다. 아래 코드에서는 MyLibrary_H이 초기화 되어 있지 않을 때에만, MyLibrary_H를 초기화 한 뒤에 두 함수를 선언할 수 있도록 해주고 있습니다.

 

#ifndef MyLibrary_H
#define MyLibrary_H

int add(int a, int b);
int subtract(int a, int b);

#endif

 

  이제 MyLibrary.cpp을 다음과 같이 작성할 수 있습니다. 헤더 파일에서 선언했던 함수를 실제로 구현한 것입니다.

 

#include "Arduino.h"
#include "MyLibrary.h"

int add(int a, int b) {
	return a + b;
}
int subtract(int a, int b) {
	return a - b;
}

 

  참고로 .cpp 파일을 작성할 때는 "Arduino.h" 헤더파일을 추가하도록 합니다. "Arduino.h" 파일은 아두이노의 기본적인 함수, 상수, 표준 타입을 포함하고 있습니다. 그래서 아두이노의 기본적인 기능을 이용하도록 우리의 라이브러리를 작성할 수 있습니다.

  일반적으로 아두이노 IDE를 이용해서 .ino 파일을 작성하여 코딩을 하면, 자동으로 "Arduino.h"가 추가됩니다. 하지만 우리가 만들 사용자 라이브러리 파일에서는 "Arduino.h" 헤더 파일이 자동으로 추가되지 않기 때문에, 소스코드상에서 직접 지정해주어야 합니다. 사실 아두이노의 기본적인 기능을 이용하지 않더라도, 관행적으로 "Arduino.h" 파일은 사용자 라이브러리에 포함될 수 있도록 합니다.

 

  이제 최종적으로 다음과 같이 examples 폴더 안에 하나의 예시 소스코드를 넣어 주도록 합시다. 저는 HelloWorld.ino라는 이름으로 하나의 소스코드를 만들어 주었습니다.

 

 

#include <MyLibrary.h>

void setup() {
  
}

void loop() {
  Serial.println(add(3, 4));
}

 

  이제 실제로 소스코드를 업로드하여 프로그램을 실행해 보면, 다음과 같이 함수를 실행한 결과가 정상적으로 출력되는 것을 확인할 수 있습니다. 만약에 함수를 찾을 수 없다거나 하는 오류가 발생한다면, 라이브러리 소스코드를 다시 한 번 살펴보세요.

 

728x90
반응형

728x90
반응형

  기본적으로 존재하는 [한컴 입력기] 때문에 불편할 때가 있습니다. 이를 삭제하는 방법은 간단한데요, 먼저 [언어 기본 설정]으로 이동하시면 됩니다.

 

 

  이후에 [기본 설정 언어] 탭에 존재하는 [한국어] - [옵션]으로 들어갑니다.

 

 

  이제 여기에서 [한컴 입력기]를 찾으신 뒤에 [제거]를 해주시면 깔끔하게 제거됩니다.

 

728x90
반응형

728x90
반응형

※ 쓰레드(Thread) 사용 방법 ※

 

  Teensy 버전 3과 4에서는 쓰레드(Thread) 기능을 제공한다. 쓰레드를 이용하기 위해서는 아두이노 IDE에서 [스케치] - [라이브러리 포함하기]를 확인하자.

 

 

  여기에서 TeensyThreads를 선택하여 TeensyThread 라이브러리를 불러 올 수 있다.

 

 

  이후에 다음과 같은 예시 소스코드를 작성하여 실행해보자.

 

#include <TeensyThreads.h>

volatile int count = 0;

void thread_function(int data){
  while(1) {
    count += data;
  }
}

void setup() {
  threads.addThread(thread_function, 1);
}

void loop() {
  Serial.println(count);
}

 

  실제로 업로드하여 프로그램을 실행하면, 다음과 같이 count 변수의 값이 증가하며 출력된다.

 

 

※ 라이브러리가 없는 경우 ※

 

  혹시나 TeensyThread 라이브러리가 없다는 오류가 나온다면, 해당 라이브러리를 아두이노 라이브러리로 등록해주면 된다. 라이브러리 경로(https://github.com/ndb796/python-for-coding-test)로 접속하여 프로젝트 소스코드를 다운로드 할 수 있다. 아래와 같이 TeensyThreads.zip을 확인할 수 있다.

 

 

  압축 파일(zip)을 라이브러리로 포함시킬 때에도 마찬가지로 [스케치] - [라이브러리 포함하기]로 이동하면 된다.

 

 

  다음과 같은 경로의 압축 파일을 등록하면 된다.

 

 

728x90
반응형