commit
3d8756140b
@ -0,0 +1,12 @@
|
|||||||
|
# 마이크로컨트롤러와 싱글 보드 컴퓨터의 비교 및 대조
|
||||||
|
|
||||||
|
## 지침
|
||||||
|
|
||||||
|
이 강의에서 마이크로컨트롤러와 싱글 보드 컴퓨터에 대해 다뤘습니다. 두 가지를 비교하고 대조하는 표를 만들고, 싱글 보드 컴퓨터보다 마이크로컨트롤러를 사용하는 이유와 마이크로컨트롤러보다 싱글 보드 컴퓨터를 사용하는 이유를 각각 최소 2개씩 작성하십시오.
|
||||||
|
|
||||||
|
## 평가 기준
|
||||||
|
|
||||||
|
| 기준 | 모범 답안 | 적절함 | 개선이 필요함 |
|
||||||
|
| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
||||||
|
| 마이크로 컨트롤러와 싱글 보드 컴퓨터를 비교하는 표 만들기 | 여러 항목을 올바르게 비교하고 대조하는 목록을 만듦 | 몇 개의 항목만 있는 목록을 생성 | 하나의 항목만 생각해 낼 수 있거나 비교 및 대조할 항목이 없음 |
|
||||||
|
| 다른 것보다 하나를 사용하는 이유 | 마이크로컨트롤러에 대해 2개 이상의 이유를 제공하고 싱글 보드 컴퓨터에 대해 2개 이상을 제공할 수 있음 | 마이크로컨트롤러에 대한 이유는 1-2개, 싱글 보드 컴퓨터에 대한 이유는 1-2개만 제공할 수 있음 | 마이크로컨트롤러 또는 싱글 보드 컴퓨터에 대해 하나 이상의 이유를 제공할 수 없음 |
|
@ -0,0 +1,222 @@
|
|||||||
|
# 센서 및 액추에이터(actuator)를 통한 물리적 환경과의 상호작용
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> [Nitya Narasimhan](https://github.com/nitya) 의 스케치노트입니다. 이미지를 클릭하여 크게 보세요.
|
||||||
|
|
||||||
|
이 수업은 [Microsoft Reactor](https://developer.microsoft.com/reactor/?WT.mc_id=academic-17441-jabenn) 에서 [Hello IoT series](https://youtube.com/playlist?list=PLmsFUfdnGr3xRts0TIwyaHyQuHaNQcb6-) 시리즈의 일부로 제공되었습니다. . 수업은 2개의 비디오로 진행되었습니다. 1시간의 수업과 1시간의 추가 공부 시간을 통해 수업의 내용을 더 깊이 파고들어 질문에 답하고자 합니다.
|
||||||
|
|
||||||
|
[](https://youtu.be/Lqalu1v6aF4)
|
||||||
|
|
||||||
|
[](https://youtu.be/qR3ekcMlLWA)
|
||||||
|
|
||||||
|
> 🎥 동영상을 보려면 위의 이미지를 클릭하세요
|
||||||
|
|
||||||
|
## 강의 전 퀴즈
|
||||||
|
|
||||||
|
[강의 전 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/5)
|
||||||
|
|
||||||
|
## 소개
|
||||||
|
|
||||||
|
이 수업은 IoT 장치를 위한 두 가지 중요한 개념인 센서와 액추에이터를 소개한다. 당신은 또한 IoT 프로젝트에 광센서를 추가하고, 조도에 의해 제어되는 LED를 추가하여, 효과적으로 야간 조명을 구축할 수 있습니다.
|
||||||
|
|
||||||
|
이 수업에서는 다음을 다룹니다:
|
||||||
|
|
||||||
|
* [센서란 무엇인가](#센서란-무엇인가)
|
||||||
|
* [센서를 사용해보자](#센서를-사용해보자)
|
||||||
|
* [센서의 종류](#센서의-종류)
|
||||||
|
* [액추에이터란 무엇인가](#액추에이터란-무엇인가)
|
||||||
|
* [액추에이터를 사용해보자](#액추에이터를-사용해보자)
|
||||||
|
* [액추에이터의 종류](#액추에이터의-종류)
|
||||||
|
|
||||||
|
|
||||||
|
## 센서란 무엇인가
|
||||||
|
|
||||||
|
센서는 물리적 세계를 감지하는 하드웨어 장치입니다. 즉, 센서는 주변의 하나 이상의 속성을 측정하고 정보를 IoT 장치로 보냅니다. 센서는 대기 온도와 같은 자연적 특성부터 움직임과 같은 물리적 상호 작용까지, 측정할 수 있는 것이 매우 많아 방대한 범위의 장치를 포괄합니다.
|
||||||
|
|
||||||
|
일반적인 센서에는 다음이 포함됩니다.
|
||||||
|
|
||||||
|
- 온도 센서 - 공기 온도 또는 물에 담그는 곳의 온도를 감지합니다. 애호가들과 개발자들에게, 이것들은 종종 단일 센서에서 기압과 습도와 결합됩니다.
|
||||||
|
- 버튼 - 버튼을 눌렀을 때 신호가 감지됩니다.
|
||||||
|
- 광센서 - 광도를 감지하고 특정 색상, 자외선, 적외선 또는 일반적인 가시광선일 수 있습니다.
|
||||||
|
- 카메라 - 사진을 찍거나 비디오를 스트리밍함으로써 세계의 시각적 표현을 감지합니다.
|
||||||
|
- 가속도계 - 여러 방향으로의 움직임을 감지합니다.
|
||||||
|
- 마이크 - 일반적인 소리 수준 또는 방향성 소리를 감지합니다.
|
||||||
|
|
||||||
|
✅ 생각해봅시다. 여러분의 휴대전화에는 어떤 센서가 있나요?
|
||||||
|
|
||||||
|
모든 센서는 감지하는 모든 것을 IoT 장치로 해석할 수 있는 전기 신호로 변환한다는 한 가지 공통점이 있습니다. 이 전기 신호가 어떻게 해석되는지는 IoT 장치와 통신하는 데 사용되는 통신 프로토콜뿐만 아니라 센서에 따라 다릅니다.
|
||||||
|
|
||||||
|
## 센서를 사용해보자
|
||||||
|
|
||||||
|
아래의 관련 안내에 따라 IoT 장치에 센서를 추가하십시오.
|
||||||
|
|
||||||
|
* [아두이노 - Wio Terminal](wio-terminal-sensor.md)
|
||||||
|
* [싱글보드 컴퓨터 - Raspberry Pi](pi-sensor.md)
|
||||||
|
* [싱글보드 컴퓨터 - 가상기기](virtual-device-sensor.md)
|
||||||
|
|
||||||
|
## 센서의 종류
|
||||||
|
|
||||||
|
센서는 아날로그 센서와 디지털 센서가 있습니다.
|
||||||
|
|
||||||
|
### 아날로그 센서
|
||||||
|
|
||||||
|
가장 기본적인 센서 중 하나는 아날로그 센서입니다. 이러한 센서는 IoT 장치로부터 전압을 공급받고, 센서 컴포넌트가 전압을 조정하며, 센서로부터 반환되는 전압을 측정하여 센서 값을 제공합니다.
|
||||||
|
|
||||||
|
> 🎓전압은 배터리의 +극에서 -극 으로 전기를 이동시키기 위해 한 장소에서 다른 장소로 얼마나 밀리는지를 측정하는 것입니다. 예를 들어, 표준 AA 배터리는 1.5V(V는 볼트 기호)이며, 양극 단자에서 음극 단자로 1.5V의 힘으로 전기를 밀어낼 수 있다. 다른 전기 하드웨어가 작동하려면 다른 전압이 필요합니다. 예를 들어 LED 캔은 2~3V 사이에서 켜지지만 100W 필라멘트 전구는 240V가 필요합니다. 전압에 대한 자세한 내용은 [Wikipedia 에서 Voltage 검색](https://wikipedia.org/wiki/Voltage) 시 확인 가능합니다.
|
||||||
|
|
||||||
|
이것의 한 예는 Potentiometer (포텐셔미터)입니다. 이것은 두 위치 사이에서 회전할 수 있고 센서가 회전을 측정하는 다이얼입니다.
|
||||||
|
|
||||||
|
![중간 지점에 설정된 potentiometer가 5V를 전송하여 3.8V를 반환합니다.]
|
||||||
|
(../../../images/potentiometer.png)
|
||||||
|
|
||||||
|
IoT 장치는 포텐셔미터에 5V와 같은 전압으로 전기 신호를 보냅니다. 포텐셔미터가 조정되면 반대쪽에서 나오는 전압이 바뀝니다. 앰프의 볼륨 조정기와 같이 0에서 [11](https://wikipedia.org/wiki/Up_to_eleven)로 이동하는 다이얼로 표시된 포텐셔미터가 있다고 가정해 봅시다. 포텐셔미터가 완전히 꺼진 위치(0)에 있으면 0V(0V)가 나옵니다. 최대 ON 위치(11)에 있으면 5V(5V)가 나옵니다.
|
||||||
|
|
||||||
|
> 🎓 이것은 지나치게 단순화된 것이며, [Wikipedia에서 potentiometer 검색 시](https://wikipedia.org/wiki/Potentiometer) 포텐셔미터 및 가변 저항기에 대해 더 많은 정보를 얻을 수 있습니다..
|
||||||
|
|
||||||
|
센서에서 나오는 전압은 IoT 장치에 의해 읽혀지고, 그 장치는 그것에 반응할 수 있습니다. 센서에 따라 이 전압은 임의 값이거나 표준 장치에 매핑될 수 있습니다. 예를 들어, [서미스터](https://wikipedia.org/wiki/Thermistor) 에 기반한 아날로그 온도 센서는 온도에 따라 저항이 변화합니다. 출력 전압은 코드 계산을 통해 켈빈 단위로, 그에 따라 °C 또는 °F 단위로 변환될 수 있습니다.
|
||||||
|
|
||||||
|
✅ 센서가 전송된 전압보다 높은 전압을 반환할 경우(예: 외부 전원 공급 장치에서 나오는 전압) 어떻게 된다고 생각하십니까?
|
||||||
|
⛔️ 호기심에 실험 해 보진 마십시오.
|
||||||
|
|
||||||
|
#### 아날로그에서 디지털로의 변환
|
||||||
|
|
||||||
|
IoT는 디지털 장치입니다. 아날로그 값으로는 작동할 수 없고 0과 1에서만 작동합니다. 즉, 아날로그 센서 값을 처리하기 전에 디지털 신호로 변환해야 합니다. 많은 IoT 장치들은 아날로그 입력을 디지털 값 표현으로 변환하는 아날로그-디지털 변환기(ADC)를 가지고 있습니다. 센서는 커넥터 보드를 통해 ADC와 함께 작동할 수도 있습니다. 예를 들어, Raspberry Pi가 있는 seeed Grove ecosystem에서 아날로그 센서는 Pi에 있는 'hat'의 특정 포트에 연결됩니다. 이 포트는 Pi의 GPIO 핀에 연결되어 있습니다. 또한 이 “hat”은 전압을 파이의 GPIO 핀에서 보낼 수 있는 디지털 신호로 변환하기 위한 ADC가 있습니다.
|
||||||
|
|
||||||
|
3.3V를 사용하고 1V의 값을 반환하는 IoT 장치에 연결된 아날로그 광 센서가 있다고 가정 해 봅시다. 이 1V는 디지털 세계에서 아무런 의미가 없기 때문에 변환이 필요합니다. 전압은 장치와 센서에 따라 스케일을 사용하여 아날로그 값으로 변환됩니다. 한 가지 예는 0에서 1,023까지의 값을 출력하는 씨드 그로브 광 센서입니다. 3.3V에서 작동하는 이 센서의 경우 1V 출력은 300입니다. IoT 기기는 300을 아날로그 값으로 처리할 수 없기 때문에 300을 Grove hat로 이진법으로 표현한 `0000000100101100`으로 변환됩니다. 그러면 이것은 IoT 장치에 의해 처리될 것 입니다.
|
||||||
|
|
||||||
|
✅ 2진법을 모르면 숫자가 0과 1로 어떻게 표현되는지 알아보기 위해 약간의 공부를 추천합니다. [BBC Bitesize introduction to binary lesson](https://www.bbc.co.uk/bitesize/guides/zwsbwmn/revision/1)는 이진법을 공부하기 좋은 곳입니다.
|
||||||
|
|
||||||
|
코딩의 관점에서 볼 때, 이 모든 것은 보통 센서와 함께 제공되는 라이브러리에 의해 처리되므로, 당신은 이 변환에 대해 스스로 걱정할 필요가 없습니다. Grove 광센서의 경우 파이썬 라이브러리를 사용하여 `light (빛)` 속성을 호출하거나 아두이노 라이브러리를 사용하여 `analogRead` 를 호출하여 300의 값을 얻을 수 있습니다.
|
||||||
|
|
||||||
|
### 디지털 센서
|
||||||
|
|
||||||
|
아날로그 센서와 같은 디지털 센서는 전압의 변화를 이용하여 주변 세계를 감지합니다. 차이점은 두 개의 상태만 측정하거나 내장된 ADC를 사용하여 디지털 신호를 출력한다는 것입니다. 디지털 센서는 커넥터 보드 또는 IoT 장치 자체에서 ADC를 사용할 필요성을 피하기 위해 점점 더 보편화되고 있습니다.
|
||||||
|
|
||||||
|
가장 간단한 디지털 센서는 버튼 또는 스위치입니다. 이것은 켜지거나 꺼지는 두 가지 상태의 센서입니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
GPIO 핀과 같은 IoT 장치의 핀은 이 신호를 직접 0 또는 1로 측정할 수 있습니다. 전송된 전압이 반환된 전압과 같으면 판독값이 1이고, 그렇지 않으면 판독값이 0입니다. 신호를 변환할 필요가 없습니다. 1 또는 0만 가능합니다.
|
||||||
|
|
||||||
|
> 💁 전압은 절대 정확하지 않습니다. 특히 센서의 구성 요소에는 약간의 저항이 있기 때문에 일반적으로 공차가 있습니다. 예를 들어, 라즈베리 파이 상의 GPIO 핀은 3.3V에서 작동하며 1.8V 이상은 1로, 1.8V는 0으로 읽습니다.
|
||||||
|
|
||||||
|
* 3.3V가 버튼에 들어갑니다. 버튼이 꺼져 있으므로 0V가 나오고 값이 0입니다.
|
||||||
|
* 3.3V가 버튼에 들어갑니다. 버튼은 3번으로 되어 있어요.3V가 나오고 값이 1입니다.
|
||||||
|
|
||||||
|
더 발전된 디지털 센서는 아날로그 값을 판독한 다음, 온보드 ADC를 사용하여 디지털 신호로 변환합니다. 예를 들어, 디지털 온도 센서는 아날로그 센서와 동일한 방식으로 열전대를 사용하며, 현재 온도에서 열전대의 저항으로 인한 전압 변화를 계속 측정합니다. 아날로그 값을 반환하고 장치나 커넥터 보드에 의존해 디지털 신호로 변환하는 대신 센서에 내장된 ADC가 값을 변환해 IoT 장치에 0과 1의 직렬로 전송합니다. 이러한 0과 1은 1이 최대 전압이고 0이 0v인 버튼에 대한 디지털 신호와 동일한 방식으로 전송됩니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
디지털 데이터를 전송하면 센서가 더욱 복잡해지고 더 자세한 데이터, 심지어 보안 센서를 위해 암호화된 데이터까지 전송할 수 있습니다. 카메라를 생각해봅시다. 카메라는 이미지를 캡처하여 IoT 장치에서 읽을 수 있도록 보통 JPEG와 같은 압축 형식으로 해당 이미지를 전송합니다. 이미지를 캡처하고 전체 이미지 프레임을 프레임별로 전송하거나 압축된 비디오 스트림을 전송하여 비디오를 스트리밍할 수도 있습니다.
|
||||||
|
|
||||||
|
## 액추에이터란 무엇인가
|
||||||
|
|
||||||
|
액추에이터는 센서의 반대입니다. IoT 장치에서 나오는 전기 신호를 빛이나 소리를 방출하거나 모터를 움직이는 것과 같은 물리적 세계와의 상호 작용으로 변환합니다.
|
||||||
|
|
||||||
|
일반적인 액추에이터에는 다음이 포함됩니다 :
|
||||||
|
|
||||||
|
* LED - 켜지면 빛이 방출됩니다.
|
||||||
|
* 스피커 - 기본 부저에서 음악을 재생할 수 있는 오디오 스피커로 전송된 신호에 따라 소리를 냅니다.
|
||||||
|
* 스테퍼 모터 - 신호를 다이얼 90° 회전과 같이 정의된 회전량으로 변환합니다.
|
||||||
|
* 릴레이 - 전기 신호에 의해 켜지거나 끌 수 있는 스위치입니다. 그것들은 IoT 장치의 작은 전압이 큰 전압을 켜도록 합니다.
|
||||||
|
* 화면 - 보다 복잡한 액추에이터로 멀티 세그먼트 디스플레이에 정보를 표시합니다. 화면은 단순한 LED 디스플레이부터 고해상도 비디오 모니터까지 다양합니다.
|
||||||
|
|
||||||
|
✅ 생각해봅시다. 당신의 휴대전화에는 어떤 액추에이터가 있습니까?
|
||||||
|
|
||||||
|
## 액추에이터를 사용해보자
|
||||||
|
|
||||||
|
아래의 관련 안내에 따라 센서에 의해 제어되는 IoT 장치에 작동기를 추가하여 IoT 야간 조명을 만드십시오. 광센서로부터 광도를 모으고, 검출된 광도가 너무 낮을 때 발광하기 위해 LED 형태의 액추에이터를 사용합니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
* [아두이노 - 위오 터미널](wio-terminal-actuator.md)
|
||||||
|
* [싱글 보드 컴퓨터 - 라즈베리 파이](pi-actuator.md)
|
||||||
|
* [싱글 보드 컴퓨터 - 가상 ](virtual-device-actuator.md)
|
||||||
|
|
||||||
|
## 액추에이터의 종류
|
||||||
|
|
||||||
|
센서와 마찬가지로 액추에이터도 아날로그 또는 디지털입니다.
|
||||||
|
|
||||||
|
### 아날로그 액추에이터(actuator)
|
||||||
|
|
||||||
|
아날로그 액추에이터는 아날로그 신호를 받아 일종의 상호 작용으로 변환하며, 상호 작용은 공급되는 전압에 따라 변화합니다.
|
||||||
|
|
||||||
|
한 가지 예는 여러분이 집에 가지고 있을 수 있는 것과 같은 희미한 불빛입니다. 조명에 공급되는 전압의 양에 따라 조명의 밝기가 결정됩니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
센서와 마찬가지로 실제 IoT 장치는 아날로그가 아닌 디지털 신호에서 작동합니다. 즉, 아날로그 신호를 보내려면 IoT 장치가 직접 IoT 장치 또는 커넥터 보드에 디지털-아날로그 변환기(DAC)가 필요합니다. 그러면 IoT 장치의 0과 1이 액추에이터가 사용할 수 있는 아날로그 전압으로 변환됩니다.
|
||||||
|
|
||||||
|
✅ IoT 장치가 액추에이터가 처리할 수 있는 것보다 높은 전압을 전송하면 어떻게 된다고 생각하십니까?
|
||||||
|
⛔️ 실험 해 보진 마십시오.
|
||||||
|
|
||||||
|
### 펄스 폭 변조 (Pulse-Width Modulation)
|
||||||
|
|
||||||
|
IoT 장치에서 아날로그 신호로 디지털 신호를 변환하는 또 다른 옵션은 펄스 폭 변조입니다. 이것은 마치 아날로그 신호인 것처럼 작동하는 많은 짧은 디지털 펄스를 보내는 것을 포함합니다.
|
||||||
|
|
||||||
|
예를 들어 PWM을 사용하여 모터의 속도를 제어할 수 있습니다.
|
||||||
|
|
||||||
|
5V 전원으로 모터를 제어한다고 가정해 보십시오. 모터에 짧은 펄스를 전송하여 전압을 200분의 2초(0.02초) 동안 High(5V)로 전환합니다. 그 시간 동안 당신의 모터는 10분의 1 또는 36° 회전할 수 있습니다. 그런 다음 신호가 200분의 2초(0.02초) 동안 일시 중지되어 낮은 신호(0V)를 전송합니다. 각 ON/OFF 사이클은 0.04초 동안 지속됩니다. 그런 다음 주기가 반복됩니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
즉, 1초 동안 모터를 회전시키는 0.02초의 255V 펄스가 있고, 그 후 모터를 회전하지 않는 0.02초의 0.02초의 일시 중지 상태가 있음을 의미합니다. 각 펄스는 모터를 회전의 10분의 1로 회전시킵니다. 즉, 모터가 초당 2.5회전을 완료한다는 의미입니다. 디지털 신호를 사용하여 모터를 초당 2.5회전 또는 [분당 150회](https://wikipedia.org/wiki/Revolutions_per_minute) (비표준 회전 속도 측정)으로 회전했습니다.
|
||||||
|
|
||||||
|
```output
|
||||||
|
초당 25펄스 x 펄스당 0.1회전 = 초당 2.5회전
|
||||||
|
초당 2.5회전 x 1분에 60초 = 150rpm
|
||||||
|
```
|
||||||
|
|
||||||
|
> 🎓 PWM 신호가 절반 동안 켜져 있고 절반 동안 꺼져 있는 경우를 [50% duty cycle](https://wikipedia.org/wiki/Duty_cycle) 이라고 합니다. 듀티 사이클은 신호가 꺼진 상태와 비교하여 켜진 상태에 있는 시간의 백분율로 측정됩니다.
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
펄스 크기를 변경하여 모터 속도를 변경할 수 있습니다. 예를 들어, 동일한 모터를 사용하여 0.04초의 동일한 사이클 시간을 유지할 수 있으며, 온 펄스는 0.01초로 절반으로, 오프 펄스는 0.03초로 증가할 수 있습니다. 초당 펄스 수(25)는 동일하지만 각 펄스의 길이는 절반입니다. 1/2 길이의 펄스는 모터를 20분의 1 회전만 돌리게 하며, 25펄스에서는 초당 1.25회전을 완료하거나 75rpm으로 회전합니다. 디지털 신호의 펄스 속도를 변경함으로써 아날로그 모터 속도를 절반으로 줄였습니다.
|
||||||
|
|
||||||
|
```output
|
||||||
|
초당 25펄스 x 펄스당 0.05회전 = 초당 1.25회전
|
||||||
|
초당 1.25회 회전 x 1분 동안 60초 = 75rpm
|
||||||
|
```
|
||||||
|
|
||||||
|
✅ 특히 저속 주행 시 모터 회전을 원활하게 유지하려면 어떻게 해야 합니까? 긴 일시 중지를 사용하는 긴 펄스 수를 사용할 것입니까, 아니면 매우 짧은 일시 중지를 사용하는 짧은 펄스 수를 사용할 것입니까?
|
||||||
|
|
||||||
|
> 💁 일부 센서는 PWM을 사용하여 아날로그 신호를 디지털 신호로 변환하기도 합니다.
|
||||||
|
|
||||||
|
> 🎓 [Wikipedia에서 펄스 폭 변조 검색](https://wikipedia.org/wiki/Pulse-width_modulation) 시 자세한 정보를 얻을 수 있습니다.
|
||||||
|
|
||||||
|
### 디지털 액추에이터(actuator)
|
||||||
|
|
||||||
|
디지털 센서와 같은 디지털 액추에이터는 고전압 또는 저전압에 의해 제어되는 두 가지 상태를 가지고 있거나 디지털 신호를 아날로그 신호로 변환할 수 있도록 DAC가 내장되어 있습니다.
|
||||||
|
|
||||||
|
하나의 간단한 디지털 작동기는 LED입니다. 장치가 1의 디지털 신호를 보내면 LED를 켜는 고전압이 전송됩니다. 0의 디지털 신호가 전송되면 전압이 0V로 떨어지고 LED가 꺼집니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
✅ 다른 간단한 2-state 액추에이터는 무엇입니까? 한 예로 솔레노이드가 있는데, 이는 도어 볼트를 움직이기 위해 작동하거나 도어를 잠금/잠금 해제하는 등의 작업을 수행할 수 있는 전자석입니다.
|
||||||
|
|
||||||
|
화면과 같은 더 발전된 디지털 작동기는 디지털 데이터가 특정 형식으로 전송되어야 합니다. 그들은 보통 그들을 제어하기 위해 정확한 데이터를 더 쉽게 보낼 수 있는 라이브러리와 함께 제공됩니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 도전
|
||||||
|
|
||||||
|
이전 두 강의의 과제는 가정, 학교 또는 직장에 있는 가능한 한 많은 IoT 장치를 나열하고 그것들이 마이크로컨트롤러 또는 단일 보드 컴퓨터 또는 심지어 둘의 혼합으로 구축되었는지 결정하는 것이었습니다.
|
||||||
|
|
||||||
|
생각해 낸 모든 장치들은 어떤 센서와 액추에이터에 연결되어 있습니까? 이러한 장치에 연결된 각 센서와 액추에이터의 용도는 무엇입니까?
|
||||||
|
|
||||||
|
|
||||||
|
## 복습 퀴즈
|
||||||
|
|
||||||
|
[복습 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/6)
|
||||||
|
|
||||||
|
## 리뷰 & 추가 개별학습
|
||||||
|
|
||||||
|
* 전기 및 회로에 대한 정보를 읽어보세요 [ThingLearn](http://thinglearn.jenlooper.com/curriculum/).
|
||||||
|
* 다양한 유형의 온도 센서에 대한 자세한 내용은 [Seeed Studios Temperature Sensors guide](https://www.seeedstudio.com/blog/2019/10/14/temperature-sensors-for-arduino-projects/) 를 참조하세요
|
||||||
|
* LED에 관한 내용은 [Wikipedia LED page](https://wikipedia.org/wiki/Light-emitting_diode) 에서 확인하세요
|
||||||
|
|
||||||
|
## 과제
|
||||||
|
|
||||||
|
[센서와 액추에이터에 대하여 알아보자](assignment.md)
|
@ -0,0 +1,17 @@
|
|||||||
|
# 센서와 액추에이터에 대하여 알아보자
|
||||||
|
|
||||||
|
## 소개
|
||||||
|
|
||||||
|
이 수업에서는 센서와 액추에이터에 대해 설명했습니다. IoT 개발 키트와 함께 사용할 수 있는 센서 하나와 액추에이터 하나를 알아봅시다. 여기에는 다음이 포함됩니다.
|
||||||
|
|
||||||
|
* 무슨 일을 하나요?
|
||||||
|
* 사용되는 전자 장치/하드웨어는 무엇이 있나요?
|
||||||
|
* 아날로그 장치인가요 디지털장치인가요?
|
||||||
|
* 입력 또는 측정의 단위 및 범위는 무엇입니까?
|
||||||
|
|
||||||
|
## 평가기준(Rubric)
|
||||||
|
|
||||||
|
| 기준 | 모범 답안 | 적절함 | 개선이 필요함 |
|
||||||
|
| -------- | --------- | -------- | ----------------- |
|
||||||
|
| 센서 설명 | 위에 나열된 4개 섹션에 대한 세부 정보를 포함하여 센서에 대해 설명합니다. | 센서에 대해 설명했지만 위의 섹션 중 2-3개만 제공할 수 있었습니다. | 센서에 대해 설명했지만 위의 섹션 중 1개만 제공할 수 있었습니다. |
|
||||||
|
| 액추에이터 설명 | 위에 나열된 4개 섹션에 대한 세부 정보를 포함하여 액추에이터에 대해 설명합니다. | 액추에이터에 대해 설명했지만 위의 섹션 중 2-3개만 제공할 수 있었습니다. | 액추에이터에 대해 설명했지만 위의 섹션 중 1개만 제공할 수 있었습니다. |
|
@ -0,0 +1,116 @@
|
|||||||
|
# 야간 조명 만들기 - 라즈베리 파이
|
||||||
|
|
||||||
|
라즈베리 파이에 LED를 추가하여 야간 조명을 만들어봅시다.
|
||||||
|
|
||||||
|
## 하드웨어
|
||||||
|
|
||||||
|
이 야간 조명에는 액추에이터가 필요합니다.
|
||||||
|
|
||||||
|
사용되는 액추에이터는 **LED**로, 전류가 흐를 때 빛을 방출하는 [발광 다이오드](https://wikipedia.org/wiki/Light-emitting_diode) 입니다. LED는 on /off 두 가지 상태를 가진 디지털 액추에이터입니다. 1을 값으로 전송하면 LED가 켜지고 0을 전송하면 꺼집니다. LED는 외부 Grove 액추에이터이며 라즈베리 파이의 Grove Base Hat에 연결해야 합니다
|
||||||
|
|
||||||
|
의사 코드에서 야간 조명의 로직은 다음과 같습니다.:
|
||||||
|
|
||||||
|
```output
|
||||||
|
탐지되는 빛의 정도(level)를 확인합니다
|
||||||
|
빛의 정도가 300 미만일 경우
|
||||||
|
LED를 켭니다
|
||||||
|
그렇지 않을 경우
|
||||||
|
LED를 끕니다
|
||||||
|
```
|
||||||
|
|
||||||
|
### LED 연결하기
|
||||||
|
|
||||||
|
Grove LED는 다양한 LED가 포함된 모듈로 제공되므로 색상을 선택할 수 있습니다.
|
||||||
|
|
||||||
|
#### 할 일 - LED 연결
|
||||||
|
|
||||||
|
LED에 연결 해 봅시다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
1. 좋아하는 LED를 선택하고 LED 모듈의 두 구멍에 다리를 삽입합니다.
|
||||||
|
|
||||||
|
LED는 발광 다이오드이며, 다이오드는 전류를 한 방향으로만 전달할 수 있는 전자 장치입니다. 즉, LED를 올바른 방향으로 연결해야 합니다. 그렇지 않으면 작동하지 않습니다.
|
||||||
|
|
||||||
|
LED 다리 중 하나는 양극 핀이고 다른 하나는 음극 핀입니다. LED는 완벽하게 둥글지 않고 한쪽이 약간 평평합니다. 약간 평평한 면이 음극 핀입니다. LED를 모듈에 연결할 때 둥근 쪽의 핀이 모듈 외부에 +로 표시된 소켓에 연결되고 평평한 쪽이 모듈 중앙에 더 가까운 소켓에 연결되었는지 확인하십시오.
|
||||||
|
|
||||||
|
1. LED 모듈에는 밝기를 제어할 수 있는 회전 버튼이 있습니다. 작은 십자 드라이버를 사용하여 시계 반대 방향으로 끝까지 돌립니다.
|
||||||
|
|
||||||
|
1. Grove 케이블의 한쪽 끝을 LED 모듈의 소켓에 삽입합니다. 이는 한 방향으로만 돌아갈 것 입니다.
|
||||||
|
|
||||||
|
1. Rasberry Pi 전원을 끈 상태에서 Grove 케이블의 다른 쪽 끝을 Pi에 부착된 Grove Base 모자에 D5로 표시된 디지털 소켓에 연결합니다. 이 소켓은 왼쪽에서 두 번째, GPIO 핀 옆에 있는 소켓 줄입니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 야간 조명을 프로그래밍 해봅시다
|
||||||
|
|
||||||
|
Grove 조명 센서와 Grove LED를 사용하여 야간 조명 프로그래밍을 진행할 수 있습니다.
|
||||||
|
|
||||||
|
### 할 일 - 야간 조명 동작에 대한 프로그래밍을 해 봅시다.
|
||||||
|
|
||||||
|
야간 조명에 대한 프로그래밍을 합니다.
|
||||||
|
|
||||||
|
1. 라즈베리 파이 전원을 켜고 부팅될 때 까지 기다립니다.
|
||||||
|
|
||||||
|
1. 이 할당의 이전 부분에서 만든 VS Code에서 야간 조명 프로젝트를 Pi에서 직접 실행하거나 원격 SSH 확장을 사용하여 연결합니다.
|
||||||
|
|
||||||
|
1. 필요한 라이브러리를 가져오려면 아래 코드를 `app.py` 파일에 추가하십시오. 이는 다른`import` 줄 바로 아래 추가되어야 합니다..
|
||||||
|
|
||||||
|
```python
|
||||||
|
from grove.grove_led import GroveLed
|
||||||
|
```
|
||||||
|
|
||||||
|
`from grove.grove_led import GroveLed` 문은 Grove Python 라이브러리에서 `GroveLed` 를 import 한다. 이 라이브러리에는 Grove LED와 상호 작용하는 코드가 있습니다.
|
||||||
|
|
||||||
|
1. `light_sensor` 선언 뒤에 다음 코드를 추가하여 LED를 관리하는 클래스의 인스턴스를 만듭니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
led = GroveLed(5)
|
||||||
|
```
|
||||||
|
|
||||||
|
`led = GroveLed(5)` 코드는 핀 **D5**(LED가 연결된 디지털 Grove 핀)에 연결하는 `GroveLed` 클래스의 인스턴스를 생성합니다.
|
||||||
|
|
||||||
|
> 💁 모든 소켓에 고유한 핀 번호가 있습니다. 핀 0, 2, 4, 6은 아날로그 핀이고 핀 5, 16, 18, 22, 24 및 26은 디지털 핀입니다.
|
||||||
|
|
||||||
|
1. `while` 루프 내부에 확인용 변수를 추가합니다, `time.sleep` 전에 조명 레벨을 확인하고 LED를 켜거나 끌 수 있습니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
if light < 300:
|
||||||
|
led.on()
|
||||||
|
else:
|
||||||
|
led.off()
|
||||||
|
```
|
||||||
|
|
||||||
|
이 코드는 감지되는 `빛` 의값을 확인합니다. 이 값이 300 미만인 경우 `GroveLed` 클래스의 `on` 메서드를 호출하여 LED에 디지털 값 1을 전송하고 LED를 켭니다. 조명 값이 300보다 크거나 같으면 꺼짐 방법을 호출하여 디지털 값 0을 LED로 전송하고 LED를 끕니다.
|
||||||
|
|
||||||
|
> 💁 이 코드는 `print('Light level:', light)` 라인과 동일한 레벨로 들여써야 하며, while 루프 안에 있어야 합니다!
|
||||||
|
|
||||||
|
> 💁 액추에이터로 디지털 값을 전송할 때 0 값은 0V, 1 값은 장치의 최대 전압입니다. Grove 센서 및 액추에이터가 장착된 라즈베리 파이의 경우 1 전압은 3.3V입니다.
|
||||||
|
|
||||||
|
1. VS Code Terminal에서 다음을 실행하여 Python 앱을 실행합니다.:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 app.py
|
||||||
|
```
|
||||||
|
|
||||||
|
조명 값이 콘솔에 출력됩니다.
|
||||||
|
|
||||||
|
```output
|
||||||
|
pi@raspberrypi:~/nightlight $ python3 app.py
|
||||||
|
Light level: 634
|
||||||
|
Light level: 634
|
||||||
|
Light level: 634
|
||||||
|
Light level: 230
|
||||||
|
Light level: 104
|
||||||
|
Light level: 290
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 조명 센서를 가려도 봅시다. 조명 레벨이 300 이하인 경우 LED가 어떻게 켜지고, 조명 레벨이 300 이상인 경우 LED가 꺼지는지 확인합니다.
|
||||||
|
|
||||||
|
> 💁 LED가 동작하지 않는다면 제대로 연결 되어있는지 확인하고 스핀 버튼이 완전히 켜져 있는지 확인 해 보세요
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> 💁 [code-actuator/pi](code-actuator/pi) 폴더 안에서 이 코드를 찾을 수 있습니다..
|
||||||
|
|
||||||
|
😀 여러분이 만든 야간 조명 프로젝트는 성공적으로 동작합니다!
|
@ -0,0 +1,97 @@
|
|||||||
|
# 야간 조명 만들기 - 라즈베리 파이
|
||||||
|
|
||||||
|
이 강의에서 여러분의 라즈베리 파이에 광센서를 적용해봅시다
|
||||||
|
|
||||||
|
## 하드웨어
|
||||||
|
|
||||||
|
본 강의용 센서는 [광다이오드](https://wikipedia.org/wiki/Photodiode)를 사용하여 빛을 전기신호로 변환하는 **광센서** 입니다. 이는 [lux](https://wikipedia.org/wiki/Lux) 와 같은 표준 측정단위에 매핑되지 않는 0부터 1000까지의 빛의 상대적인 양을 나타내는 정수값을 보내는 아날로그 센서입니다.
|
||||||
|
|
||||||
|
광센서는 eternal Grove 센서이며 라즈베리 파이의 Grove base hat에 연결해야 합니다.
|
||||||
|
|
||||||
|
### 광센서와 연결해봅시다
|
||||||
|
|
||||||
|
광도를 감지하는데 사용되는 Grove 광센서는 라즈베리 파이에 연결해야 합니다.
|
||||||
|
|
||||||
|
#### 할 일 - 광센서와 연결 해 봅시다.
|
||||||
|
|
||||||
|
광센서와 연결해봅시다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
1. Grove 케이블의 한쪽 끝을 광센서 모듈의 소켓에 삽입합니다. 그것은 한 방향으로만 돌아갈 것입니다.
|
||||||
|
|
||||||
|
1. Rasberry Pi 전원을 끈 상태에서 Grove 케이블의 다른 쪽 끝을 Pi에 부착된 Grove Base Hat의 **A0** 라고 표시된 아날로그 소켓에 연결합니다. 이 소켓은 오른쪽에서 두 번째, GPIO 핀 옆에 있는 소켓 열입니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 광센서를 프로그래밍 해 봅시다.
|
||||||
|
|
||||||
|
이제 Grove light 센서를 사용하여 장치를 프로그래밍할 수 있습니다.
|
||||||
|
|
||||||
|
### 할 일 - 광센서를 프로그래밍한다.
|
||||||
|
|
||||||
|
구현 해 봅시다.
|
||||||
|
|
||||||
|
1. 라즈베리 파이의 전원은 켜고 부팅 될 때까지 기다립니다.
|
||||||
|
|
||||||
|
1. 이 과제의 이전 부분에서 생성한 VS Code에서 야간 조명 프로젝트를 Pi에서 직접 실행하거나 원격 SSH 확장을 사용하여 연결합니다.
|
||||||
|
|
||||||
|
1. `app.py` 파일을 열고 이 파일의 모든 코드를 지웁니다.
|
||||||
|
|
||||||
|
1. 몇가지 라이브러리 파일을 요청하기 위해 `app.py` 파일에 아래 있는 코드를 추가합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import time
|
||||||
|
from grove.grove_light_sensor_v1_2 import GroveLightSensor
|
||||||
|
```
|
||||||
|
|
||||||
|
`import time` 은 이 과제 이후에 사용될 `time` 모듈을 import 합니다.
|
||||||
|
|
||||||
|
`from grove.grove_light_sensor_v1_2 import GroveLightSensor` 는 Grove Python 라이브러리로부터 `GroveLightSensor` 를 import 합니다. 이 라이브러리는 Grove 광센서와 상호작용 할 수 있는 코드를 가지고 있으며 라즈베리 파이 설정 중에 전역으로 설치되었습니다.
|
||||||
|
|
||||||
|
1. 아래 코드를 위에서 작성한 코드 뒤에 추가하여 광센서를 관리하는 클래스의 인스턴스를 만듭니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
light_sensor = GroveLightSensor(0)
|
||||||
|
```
|
||||||
|
|
||||||
|
`light_sensor = GroveLightSensor(0)`는 핀 **A0**(광센서와 연결되어있는 아날로그 Grove 핀)와 연결되어있는 `GroveLightSensor` class의 인스턴스를 생성합니다.
|
||||||
|
|
||||||
|
|
||||||
|
1. 위에서 작성한 코드 뒤에 무한 루프를 추가하여 광 센서 값을 측정하고 콘솔에 출력합니다 :
|
||||||
|
|
||||||
|
```python
|
||||||
|
while True:
|
||||||
|
light = light_sensor.light
|
||||||
|
print('Light level:', light)
|
||||||
|
```
|
||||||
|
|
||||||
|
이는 `GroveLightSensor` 클래스의 `light` 속성을 사용하여 0-1,023의 척도로 현재 빛의 밝기를 판독합니다. 이 속성은 핀에서 아날로그 값을 읽습니다. 이후 이 값이 콘솔에 출력됩니다.
|
||||||
|
|
||||||
|
1. 계속 밝기를 확인할 필요가 없으므로 `루프` 끝에 1초의 짧은 절전 시간을 추가한다. 절전 모드는 장치의 전력 소비를 줄여줍니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
time.sleep(1)
|
||||||
|
```
|
||||||
|
|
||||||
|
1. VS Code의 터미널에서 아래 코드로 Python 앱을 실행 해 봅시다..
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 app.py
|
||||||
|
```
|
||||||
|
|
||||||
|
밝기 값이 콘솔에 출력될 것이다. 광센서를 손으로 가려도 보면서 값이 어떻게 변하는지 확인 해 봅시다 :
|
||||||
|
|
||||||
|
```output
|
||||||
|
pi@raspberrypi:~/nightlight $ python3 app.py
|
||||||
|
Light level: 634
|
||||||
|
Light level: 634
|
||||||
|
Light level: 634
|
||||||
|
Light level: 230
|
||||||
|
Light level: 104
|
||||||
|
Light level: 290
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💁 [code-sensor/pi](code-sensor/pi) 폴더에서 이 코드를 찾을 수 있습니다.
|
||||||
|
|
||||||
|
😀 여러분의 야간 조명 프로그렘에 성공적으로 센서를 적용했습니다!
|
@ -0,0 +1,472 @@
|
|||||||
|
# 인터넷에 장치 연결하기
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> Nitya Narasimhan의 스케치 노트. 더 큰 버전을 보려면 이미지를 클릭하세요.
|
||||||
|
|
||||||
|
이 수업은 [Microsoft Reactor](https://developer.microsoft.com/reactor/?WT.mc_id=academic-17441-jabenn)의 [Hello IoT series](https://youtube.com/playlist?list=PLmsFUfdnGr3xRts0TIwyaHyQuHaNQcb6-) 의 일부로 진행되었습니다. 이 수업은 2개의 비디오 - 1 시간의 수업, 1시간의 강의에 대한 집중 탐구 및 질의 응답으로 구성되어 있습니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> 🎥 상단의 이미지를 클릭하여 비디오를 시청할 수 있습니다.
|
||||||
|
|
||||||
|
## 강의 전 퀴즈
|
||||||
|
|
||||||
|
[강의 전 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/7)
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
IoT에서 **I**는 **인터넷**을 의미합니다. - 장치에 연결된 센서에서 측정값 수집, 액추에이터 제어를 위한 메세지 전송과 같은 IoT의 많은 기능을 가능하게 하는 클라우드 연결 및 서비스를 의미합니다. IoT 장치는 일반적으로 표준 통신 프로토콜을 사용하여 단일 클라우드 IoT 서비스에 연결되며 해당 서비스는 데이터에 대한 현명한 결정을 내리는 AI 서비스에 제어 또는 보고를 위한 웹 앱에 이르기까지 나머지 IoT 어플리케이션에 연결됩니다.
|
||||||
|
|
||||||
|
> 🎓 센서에서 수집되어 클라우드로 전송되는 데이터를 telemetry라고 합니다.
|
||||||
|
|
||||||
|
IoT 장치는 클라우드에서 메시지를 수신할 수 있습니다. 종종 메시지에는 다음 명령들이 포함되어 있습니다. - 내부적으로 작업 수행하기(예: 재부팅 또는 펌웨어 업데이트) 또는 엑추에이터 사용하기(예: 조명 켜기).
|
||||||
|
|
||||||
|
이 강의에서는 IoT 장치가 클라우드에 연결하는데 사용할 수 있는 몇가지 통신 프로토콜과 송수신할 수 있는 데이터 유형을 소개합니다. 또한 야간 조명에 인터넷 제어를 추가하고, LED 제어 논리를 로컬에서 실행되는 ‘서버’ 코드로 이동하는 실습을 하게 됩니다.
|
||||||
|
|
||||||
|
이 강의에서 다룰 내용은 다음과 같습니다:
|
||||||
|
|
||||||
|
- [통신 프로토콜](#communication-protocols)
|
||||||
|
- [메시지 큐 원격 분석 전송(MQTT)](##message-queueing-telemetry-transport-mqtt)
|
||||||
|
- [텔레메트리(원격 측정)](#telemetry)
|
||||||
|
- [명령](#commands)
|
||||||
|
|
||||||
|
## 통신 프로토콜
|
||||||
|
|
||||||
|
IoT 장치가 인터넷과 통신하는 데 널리 사용하는 통신 프로토콜이 많이 있습니다. 가장 인기있는 것은 일종의 브로커 형태의 발행/구독 메시징을 기반으로 합니다. IoT 장치는 브로커에 연결하고 원격 측정을 게시하고 명령을 구독합니다. 또한 클라우드 서비스는 브로커에 연결하고 모든 원격 측정 메시지를 구독하고 특정 장치, 장치 그룹에 명령을 게시합니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
MQTT는 IoT 장치에 가장 널리 사용되는 통신 프로토콜이며 이 강의에서 다룹니다. 다른 프로토콜에는 AMQP와 HTTP/HTTPS가 있습니다.
|
||||||
|
|
||||||
|
## 메시지 큐 원격 분석 전송 (MQTT)
|
||||||
|
|
||||||
|
[MQTT](http://mqtt.org/) 는 장치 간에 메시지를 보낼 수 있는 가벼운 개방형 표준 메시징 프로토콜 입니다. 1999년에 송유관을 모니터링하도록 설계되었으며 15년 후 IBM에서 공개 표준으로 발표했습니다.
|
||||||
|
|
||||||
|
MQTT 에는 단일 브로커와 여러 클라이언트가 있습니다. 모든 클라이언트는 브로커에 연결되고, 브로커는 메시지를 관련 클라이언트에게 라우팅합니다. 메시지는 개별 클라이언트에게 직접 전송되지 않고 명명된 주제를 사용하여 라우팅됩니다. 클라이언트는 주제를 게시할 수 있으며 해당 주제를 구독하는 모든 클라이언트는 메시지를 받습니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
✅ 조사를 해보십시오. IoT 장치가 많은 경우, MQTT 브로커가 모든 메시지를 처리하려면 어떻게 해야 합니까?
|
||||||
|
|
||||||
|
### IoT장치를 MQTT에 연결하기
|
||||||
|
|
||||||
|
야간 조명에 인터넷 제어를 추가하는 첫 번째 단계는 이를 MQTT 브로커에 연결하는 것입니다.
|
||||||
|
|
||||||
|
### 작업
|
||||||
|
|
||||||
|
장치를 MQTT 브로커에 연결합니다.
|
||||||
|
|
||||||
|
이 수업에서는 IoT 야간 조명을 인터넷에 연결하여 원격으로 제어할 수 있도록 합니다. 수업의 뒷부분에서, IoT 장치는 MQTT를 통해 가벼운 수준의 공용 MQTT 브로커로 원격 분석 메시지를 보내고, 당신이 작성할 서버 코드에 의해 선택될 것입니다.
|
||||||
|
|
||||||
|
이러한 설정의 실제 사용 사례는 경기장과 같이 조명이 많은 위치에서 조명 키는 것을 결정하기 전에 여러 광 센서에서 데이터를 수집하는 것이 있습니다. 이렇게 하면 구름이나 새가 한 센서를 가려도, 다른 센서에서 충분한 빛을 감지한 경우 조명이 켜지지 않을 수 있습니다.
|
||||||
|
|
||||||
|
✅ 명령을 보내기 전에 여러 센서의 데이터를 평가해야 하는 다른 상황은 무엇입니까?
|
||||||
|
|
||||||
|
이 과제의 일부로 MQTT 브로커를 설정하는 복잡성을 처리하는 대신 오픈 소스 MQTT 브로커인 [Eclipse Mosquitto](https://www.mosquitto.org/) 를 실행하는 공개 테스트 서버를 사용할 수 있습니다. 이 테스트 브로커는 [test.mosquitto.org](https://test.mosquitto.org/)에서 공개적으로 사용할 수 있으며, 계정을 설정할 필요가 없으므로 MQTT 클라이언트와 서버를 테스트에 훌륭한 도구 입니다.
|
||||||
|
|
||||||
|
> 💁 이 테스트 브로커는 공개되어 있으며 안전하지 않습니다. 누구나 귀하가 게시한 내용을 들을 수 있으므로, 비공개로 유지해야 하는 데이터와 함께 사용해서는 안됩니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
아래 단계에 따라 장치를 MQTT 브로커에 연결하십시오:
|
||||||
|
|
||||||
|
- [Arduino - Wio Terminal](notion://www.notion.so/wio-terminal-mqtt.md) 아두이노 - Wio 터미널
|
||||||
|
- [Single-board computer - Raspberry Pi/Virtual IoT device](notion://www.notion.so/single-board-computer-mqtt.md) 싱글-보드 컴퓨터 - Raspberry Pi/가상 IoT 장치
|
||||||
|
|
||||||
|
### MQTT 자세히 알아보기
|
||||||
|
|
||||||
|
주제에는 계층이 있을 수 있으며, 클라이언트는 와일드카드를 사용하여 다른 수준의 계층에 가입할 수 있습니다. 예를 들어, `/telemetry/temperature` 주제에 온도 원격 측정 메시지를 보내고 , `/telemetry/humidity` 주제에 습도 메시지를 보낸 뒤, 클라우드 앱에서 `/telemetry/*` 주제를 구독하여 온도 와 습도 원격 측정 메시지를 모두 수신할 수 있습니다.
|
||||||
|
|
||||||
|
메시지는 수신되는 메시지의 보장을 결정하는 서비스의 품질(QoS)과 함께 보낼 수 있습니다.
|
||||||
|
|
||||||
|
- 최대 한 번 - 메세지는 한 번만 전송되고 클라이언트와 브로커는 전송을 확인하기 위해 추가 단계를 수행하지 않습니다(실행 후 잊어버림).
|
||||||
|
- 최소 한 번 - 확인 메시지가 수신될 때까지 보낸 사람이 여러번 다시 시도합니다.(전송 보장).
|
||||||
|
- 정확히 한 번 - 보낸 사람과 받는 사람이 2 단계의 핸드셰이크에 참여하여 메시지 사본을 하나만 수신하도록 합니다(전달 보장).
|
||||||
|
|
||||||
|
✅ 어떤 상황에서 실행 후 잊어버림 메시지보다 전달 보장 메시지가 필요할 수 있습니까?
|
||||||
|
|
||||||
|
이름은 메시지 큐잉(MQTT의 이니셜)이지만, 실제로는 메시지 큐를 지원하지 않습니다. 클라이언트가 연결을 끊었다가, 다시 연결하면 QoS 프로세스를 사용하여 이미 처리를 시작한 메시지를 제외하고 연결이 끊긴 동안 보낸 메시지를 받지 않는다는 의미입니다. 메시지에는 보유 플래그가 설정되어 있을 수 있습니다. 이것이 설정되면 MQTT 브로커는 이 플래그를 사용하여 주제에 대해 보낸 마지막 메시지를 저장하고 나중에 주제를 구독하는 모든 클라이언트에게 이를 보냅니다. 이런 식으로 클라이언트는 항상 최신 메시지를 받습니다.
|
||||||
|
|
||||||
|
또한 MQTT는 메시지 간의 긴 간격 동안 연결이 여젼히 활성 상태인지 확인하는 연결 유지 기능을 지원합니다.
|
||||||
|
|
||||||
|
> 🦟 [Mosquitto from the Eclipse Foundation](https://mosquitto.org/) 에는 [test.mosquitto.org](https://test.mosquitto.org/)에서 호스팅되는 코드 테스트에 사용할 수 있는 공개 MQTT 브로커와 함께 MQTT를 실험하기 위해 직접 실행할 수 있는 무료 MQTT 브로커가 있습니다.
|
||||||
|
|
||||||
|
MQTT 연결은 공개되고 공개되거나 사용자의 이름과 암호 또는 인증서를 사용하여 암호화되고 보호될 수 있습니다.
|
||||||
|
|
||||||
|
> 💁 MQTT 는 HTTP와 동일한 기본 네트워크 프로토콜 이지만 다른 포트에서 TCP/IP를 통해 통신합니다. 또한 웹소켓을 통한 MQTT 를 사용하여 브라우저에서 실행되는 웹 앱과 통신하거나, 방화벽이나 기타 네트워크 규칙이 표준 MQTT 연결을 차단하는 상황에서도 사용할 수 있습니다.
|
||||||
|
|
||||||
|
## 텔레메트리(원격 측정)
|
||||||
|
|
||||||
|
텔레메트리(Telemetry)는 원격 측정이라는 그리스어에서 파생되었습니다. 원격 측정은 센서에서 데이터를 수집하여 클라우드로 보내는 행위입니다..
|
||||||
|
|
||||||
|
> 💁 최초의 원격 측정 장치 중 하나는 1874년 프랑스에서 발명되었으며 실시간으로 날씨와 눈 깊이를 몽블랑에서 파리로 보냈습니다. 당시에는 무선 기술을 사용할 수 없었기 때문에 물리적 와이어를 사용했습니다.
|
||||||
|
|
||||||
|
수업 1의 예시 스마트 온도 조절기를 다시 살펴보겠습니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
온도 조절 장치에는 원격 측정을 수집하는 온도 센서가 있습니다. 하나의 온도 센서가 내장되어 있을 가능성이 높으며 BLE([Bluetooth Low Energy](https://wikipedia.org/wiki/Bluetooth_Low_Energy))와 같은 무선 프로토콜을 통해 여러 외부 온도 센서에 연결할 수 있습니다.
|
||||||
|
|
||||||
|
전송할 원격 측정 데이터의 예는 다음과 같습니다:
|
||||||
|
|
||||||
|
| Name | Value | Description |
|
||||||
|
| ------------------------ | ----- | ---------------------------------------------------------------------- |
|
||||||
|
| `thermostat_temperature` | 18°C | 온도 조절 장치에 내장된 온도 센서로 측정된 온도 |
|
||||||
|
| `livingroom_temperature` | 19°C | `livingroom` 이라고 명명된 방에 있는 원격 온도 센서에 의해 측정된 온도 |
|
||||||
|
| `bedroom_temperature` | 21°C | `bedroom`이라고 명명된 방에 있는 원격 온도 센서에 의해 측정된 온도 |
|
||||||
|
|
||||||
|
클라우드 서비스는 이 원격 측정 데이터를 사용하여 난방을 제어하기 위해 보낼 명령을 결정할 수 있습니다.
|
||||||
|
|
||||||
|
### IoT 장치에서 원격 분석 보내기
|
||||||
|
|
||||||
|
야간 조명에 인터넷 제어를 추가하는 다음 과정은 조명 수준 원격 분석을 MQTT 브로커의 텔레메트리 주제를 보내는 것입니다.
|
||||||
|
|
||||||
|
### 작업 - IoT 장치에서 원격 분석 보내기
|
||||||
|
|
||||||
|
MQTT 브로커에게 가벼운 수준의 원격 분석을 보냅니다.
|
||||||
|
|
||||||
|
데이터는 키/값 쌍을 사용해 텍스트로 데이터를 인코딩하는 표준인 JSON(JavaScriopt Object Notation의 약자)으로 인코딩되어 전송됩니다.
|
||||||
|
|
||||||
|
✅ 이전에 JSON을 접한 적이 없다면, [JSON.org documentation](https://www.json.org/)에서 더 자세히 알아볼 수 있습니다.
|
||||||
|
|
||||||
|
장치에서 MQTT 브로커로 원격 분석을 보내려면 아래 단계를 따르십시오:
|
||||||
|
|
||||||
|
- [아두이노 - Wio 터미널](wio-terminal-telemetry.md)
|
||||||
|
- [싱글 보드 컴퓨터 - Raspberry Pi/가상 IoT 장치](single-board-computer-telemetry.md)
|
||||||
|
|
||||||
|
### MQTT 브로커로부터 텔레메트리 수신
|
||||||
|
|
||||||
|
다른 쪽 끝에 수신할 것이 없으면 원격 분석을 보낼 의미가 없습니다. 광도 원격 측정은 데이터를 처리하기 위해 이를 수신하는 무언가가 필요합니다. 이 '서버' 코드는 더 큰 IoT 애플리케이션의 일부로 클라우드 서비스에 배포할 코드 유형이지만, 여기에서는 이 코드를 컴퓨터에서 로컬로(또는 직접 코딩하는 경우 Pi에서 실행할 것입니다). 서버 코드는 가벼운 수준의 MQTT를 통해 원격 분석 메시지를 수신하는 Python 앱으로 구성됩니다. 이 수업의 뒷부분에서 LED를 켜거나 끄도록 지시하는 명령 메시지로 응답하게 할 것입니다.
|
||||||
|
|
||||||
|
✅ 조사해보기: 리스너가 없으면 MQTT 메시지는 어떻게 됩니까?
|
||||||
|
|
||||||
|
### Python 과 VS Code 설치
|
||||||
|
|
||||||
|
Python 및 VS Code를 로컬에 설치하지 않은 경우 서버를 코딩하려면 둘 다 설치해야 합니다. 가상 IoT 장치를 사용 중이거나 Raspberry Pi에서 작업하는 경우 이미 설치 및 구성되어 있어야 하므로 이 단계를 건너뛸 수 있습니다.
|
||||||
|
|
||||||
|
### 작업 - Python 과 VS Code 설치
|
||||||
|
|
||||||
|
Python 과 VS Code 를 설치합니다.
|
||||||
|
|
||||||
|
1. Python을 설치합니다. 최신 버전의 Python 설치에 대한 지침은 [Python downloads page](https://www.python.org/downloads/) 를 참조하십시오.
|
||||||
|
2. Visual Studio Code (VS Code)를 설치합니다. 이것은 Python으로 가상 장치 코드를 작성하는데 사용할 편집기입니다. VS Code 설치에 대한 지침은 [VS Code documentation](https://code.visualstudio.com/?WT.mc_id=academic-17441-jabenn) 를 참조하십시오.
|
||||||
|
|
||||||
|
> 💁 선호하는 편집기가 있는 경우 이 수업에 IDE 또는 편집기를 자유롭게 사용할 수 있지만, 수업에서는 VS Code 사용을 기반으로 지침을 제공합니다.
|
||||||
|
|
||||||
|
3. VS Code Pylance 확장을 설치합니다. 이것은 Python 언어 지원을 제공하는 VS Code 의 확장입니다. VS Code에서 이 확장을 설치하는 방법은 [Pylance extension documentation](https://marketplace.visualstudio.com/items?WT.mc_id=academic-17441-jabenn&itemName=ms-python.vscode-pylance) 를 참조하십시오.
|
||||||
|
|
||||||
|
### Python 가상 환경 구성
|
||||||
|
|
||||||
|
Python의 강력한 기능 중 하나는 [pip packages](https://pypi.org/)를 설치하는 것입니다. - 이는 다른 사람이 작성하여 인터넷에 게시한 코드 패키지입니다. 하나의 명령으로 컴퓨터에 pip 패키지를 설치한 다음, 코드에서 해당 패키지를 사용할 수 있습니다. pip를 사용하여 MQTT를 통해 통신하는 패키지를 설치합니다.
|
||||||
|
|
||||||
|
기본적으로 패키지를 설치하면 컴퓨터의 모든 곳에서 사용할 수 있으며, 이것은 패키지 버전에 문제를 발생시킬 수 있습니다 - 예를 들어, 패키지의 한 버전에 따라 하나의 응용 프로그램이 다른 응용 프로그램의 새 버전을 설치할 때 중단되는 경우. 이 문제를 해결하기 위해, [Python 가상 환경](https://docs.python.org/3/library/venv.html)을 사용할 수 있습니다, 기본적으로 전용 폴더에 있는 폴더에 있는 Python의 복사본이며 pip 패키지를 설치하면 해당 폴더에만 설치됩니다.
|
||||||
|
|
||||||
|
### 작업 - Python 가상 환경 구성
|
||||||
|
|
||||||
|
Python 가상 환경을 구성하고 MQTT pip 패키지를 설치합니다.
|
||||||
|
|
||||||
|
1. 터미널 또는 명령줄에서 원하는 위치에서 다음을 실행하여 새 디렉터리를 만들고 탐색합니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir nightlight-server
|
||||||
|
cd nightlight-server
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 이제 다음을 실행하여 `.venv` 폴더에 가상 환경을 만듭니다.
|
||||||
|
|
||||||
|
```
|
||||||
|
python3 -m venv .venv
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💁 Python 3 (최신 버전) 외에 Python2가 설치된 경우에만 가상 환경을 생성하기 위해 명시적으로 호출해야 합니다. Python2 가 설치된 경우 python 호출 시 Python 3 대신 Python 2 가 사용됩니다.
|
||||||
|
|
||||||
|
3. 가상 환경 활성화:
|
||||||
|
|
||||||
|
- On Windows:
|
||||||
|
|
||||||
|
- 명령 프롬프트 또는 Windows 터미널을 통한 명령 프롬프트를 사용하는 경우 다음을 실행합니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
.venv\\Scripts\\activate.bat
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- PowerShell을 사용하는 경우 다음을 실행합니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
.\\.venv\\Scripts\\Activate.ps1
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- macOS 또는 Linux에서 다음을 실행합니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
source ./.venv/bin/activate
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💁 이러한 명령은 가상 환경을 만들기 위해 명령을 실행한 동일한 위치에서 실행해야 합니다. .venv 폴더를 탐색할 필요가 없으며, 항상 activate 명령과 아무 명령을 실행하여 패키지를 설치하거나 가상 환경을 만들 때 있떤 폴더에서 코드를 실행해야 합니다.
|
||||||
|
|
||||||
|
4. 가상 환경이 활성화 되면, 기본 `python` 명령은 가상 환경을 만드는 데 사용된 Python 버전을 실행합니다. 다음을 실행하여 버전을 가져옵니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
python --version
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
출력은 다음과 유사합니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
(.venv) ➜ nightlight-server python --version
|
||||||
|
Python 3.9.1
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💁 Python 버전은 다를 수 있습니다 - 버전 3.6 이상이면 좋습니다. 그렇지 않은 경우, 이 폴더를 삭제하고, 최신 버전의 Python을 설치한 후 다시 시도하십시오.
|
||||||
|
|
||||||
|
5. 다음 영령을 실행하여 널리 사용되는 MQTT 라이브러리인 [Paho-MQTT](https://pypi.org/project/paho-mqtt/)용 pip 패키지를 설치하십시오.
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install paho-mqtt
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
이 pip 패키지는 가상 환경에만 설치되며 외부에서는 사용할 수 없습니다.
|
||||||
|
|
||||||
|
### 서버 코드 작성
|
||||||
|
|
||||||
|
이제 서버 코드를 Python으로 작성할 수 있습니다.
|
||||||
|
|
||||||
|
### 작업 - 서버 코드 작성
|
||||||
|
|
||||||
|
서버 코드를 작성합니다.
|
||||||
|
|
||||||
|
1. 터미널 또는 명령줄에서, 가상 환경 내에서 다음을 실행하여 `app.py`라는 Python 파일을 만듭니다:
|
||||||
|
|
||||||
|
- Windows에서 다음을 실행합니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
type nul > app.py
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- macOS 또는 Linux에서 다음을 실행합니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
touch app.py
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
2. VS Code에서 현재 폴더를 엽니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
code .
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
3. VS Code가 시작되면 Python 가상 환경이 활성화됩니다. 이것은 하단 상태 표시줄에 보고됩니다.:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
4. VS Code가 시작될 때 VS Code 터미널이 이미 실행 중이면 가상 환경이 활성화되지 않습니다. 가장 쉬운 방법은 **활성 터미널 인스턴스 종료** 버튼을 사용하여 터미널을 종료하는 것입니다.:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
5. \*Terminal -> New Terminal을 선택하거나, `` CTRL+` ``를 눌러 새 VS Code 터미널을 시작합니다. 새 터미널은 가상 환경을 로드하고 이를 활성화하라는 호출이 터미널에 표시됩니다. 가상 환경의 이름(`.venv`)도 프롬프트에 표시됩니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
➜ nightlight-server source .venv/bin/activate
|
||||||
|
(.venv) ➜ nightlight
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
6. VS Code 탐색기에서 `app.py`파일을 열고 다음 코드를 추가합니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
|
||||||
|
id = '<ID>'
|
||||||
|
|
||||||
|
client_telemetry_topic = id + '/telemetry'
|
||||||
|
client_name = id + 'nightlight_server'
|
||||||
|
|
||||||
|
mqtt_client = mqtt.Client(client_name)
|
||||||
|
mqtt_client.connect('test.mosquitto.org')
|
||||||
|
|
||||||
|
mqtt_client.loop_start()
|
||||||
|
|
||||||
|
def handle_telemetry(client, userdata, message):
|
||||||
|
payload = json.loads(message.payload.decode())
|
||||||
|
print("Message received:", payload)
|
||||||
|
|
||||||
|
mqtt_client.subscribe(client_telemetry_topic)
|
||||||
|
mqtt_client.on_message = handle_telemetry
|
||||||
|
|
||||||
|
while True:
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
6행에서 `<ID>`를 기기 코드를 생성할 때 사용한 고유 ID로 바꿉니다.
|
||||||
|
|
||||||
|
⚠️ 이것은 **반드시** 장치에서 사용한 것과 동일한 ID 여야 합니다, 그렇지 않으면 서버 코드가 올바른 주제를 구독하거나 게시하지 않습니다.
|
||||||
|
|
||||||
|
이 코드는 고유한 이름으로 MQTT 클라이언트를 생성하고 _[test.mosquitto.org](http://test.mosquitto.org/)_ 브로커에 연결합니다. 그런 다음 구독된 주제에 대한 메시지를 수신하는 백그라운드 스레드에서 실행되는 처리 루프를 시작합니다.
|
||||||
|
|
||||||
|
그런 다음 클라이언트는 원격 분석 주제에 대한 메시지를 구독하고, 메시지가 수신될 때 호출되는 함수를 정의합니다. 원격 측정 메시지가 수신되면, `handle_telemetry` 함수가 호출되어, 수신된 메시지를 콘솔에 인쇄합니다.
|
||||||
|
|
||||||
|
마지막으로 무한 루프는 응용 프로그램이 계속 실행되도록 합니다. MQTT 클라이언트는 백그라운드 스레드에서 메시지를 수신하고 있으며 기본 애플리케이션이 실행 중인 동안 항상 실행됩니다.
|
||||||
|
|
||||||
|
7. VS Code 터미널에서, 다음을 실행하여 Python 앱을 실행합니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
python app.py
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
앱이 IoT 장치의 메시지 수신을 시작합니다.
|
||||||
|
|
||||||
|
8. 장치가 실행 중이고 원격 분석 메시지를 보내고 있는지 확인하십시오. 물리적 또는 가상 장치에서 감지한 조명 수준을 조정합니다. 수신 중인 메시지가 터미널에 인쇄됩니다.
|
||||||
|
|
||||||
|
```
|
||||||
|
(.venv) ➜ nightlight-server python app.py
|
||||||
|
Message received: {'light': 0}
|
||||||
|
Message received: {'light': 400}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
nightlight-server 가상 환경의 app.py 파일이 전송되는 메시지를 수신하려면 nightlight 가상 환경의 app.py 파일이 실행 중이어야 합니다.
|
||||||
|
|
||||||
|
> 💁 이 코드는 [code-server/server](code-server/server) 폴더에서 찾을 수 있습니다.
|
||||||
|
|
||||||
|
### 얼마나 자주 원격 분석을 보내야 합니까?
|
||||||
|
|
||||||
|
원격 분석에서 중요한 고려 사항 중 하나는 얼마나 자주 데이터를 측정하고 보내는가? 입니다. 대답은 - 상황에 따라 다르다 입니다. 자주 측정하면 측정 변화에 더 빠르게 대응할 수 있지만, 더 많은 전력, 더 많은 대역폭을 사용하고 더 많은 데이터를 생성하고 처리할 더 많은 클라우드 리소스가 필요합니다. 충분히 자주 측정해야 하지만 너무 자주 측정해서는 안 됩니다.
|
||||||
|
|
||||||
|
온도 조절기의 경우, 온도가 자주 변하지 않기 때문에 몇 분마다 측정하는 것으로 충분합니다. If you only measure once a day then you could end up heating your house for nighttime temperatures in the middle of a sunny day, whereas if you measure every second you will have thousands of unnecessarily duplicated temperature measurements that will eat into the users' Internet speed and bandwidth (a problem for people with limited bandwidth plans), use more power which can be a problem for battery powered devices like remote sensors, and increase the cost of the providers cloud computing resources processing and storing them.
|
||||||
|
|
||||||
|
공장에서 기계가 고장나면 치명적인 피해를 입히고 수백만 달러의 수익 손실을 초래할 수 있는 기계 주변의 데이터를 모니터링하는 경우, 초당 여러 번 측정해야 할 수 있습니다. 기계가 고장나기 전에 중지하고 수정해야 함을 나타내는 원격 측정을 놓치는 것보다 대역폭을 낭비하는 것이 좋습니다.
|
||||||
|
|
||||||
|
> 💁 이 상황에서는, 인터넷에 대한 의존도를 줄이기 위해 원격 분석을 먼저 처리하는 에지 장치를 고려할 수 있습니다.
|
||||||
|
|
||||||
|
### 연결 끊김
|
||||||
|
|
||||||
|
인터넷 연결은 불안정할 수 있으며 일반적으로 중단됩니다. 이러한 상황에서 IoT 장치는 무엇을 해야 합니까 - 데이터가 손실되어야 합니까, 아니면 연결이 복원될 때까지 저장해야 합니까? 다시 말하지만, 대답은 상황에 따라 다릅니다.
|
||||||
|
|
||||||
|
온도 조절기의 경우 새 온도 측정이 수행되자마자 데이터가 손실될 수 있습니다. 난방 시스템은 20분 전에 온도가 19°C인 경우 20.5°C였던 것을 신경 쓰지 않습니다. 난방을 켜야 하는지 여부를 결정하는 것은 지금 온도입니다.
|
||||||
|
|
||||||
|
기계류의 경우 특히 추세를 찾는 데 사용되는 경우 데이터를 유지해야 할 수 있습니다. 정의된 기간(예들 들어, 지난 1시간)의 데이터를 살펴보고 비정상적인 데이터를 찾아냄으로써 데이터 스트림의 이상을 감지할 수 있는 기계 학습 모델이 있습니다. 이것은 종종 예측 유지보수에 사용되며, 곧 고장날 수 있다는 표시를 찾아 그 전에 수리하거나 교체할 수 있습니다. 시스템에 대한 모든 원격 분석을 전송하여 이상 감지를 위해 처리할 수 있기를 원할 수 있으므로, IoT 장치가 다시 연결되면 인터넷 중단 중에 생성된 모든 원격 분석이 전송됩니다.
|
||||||
|
|
||||||
|
IoT 장치 설계자는 인터넷 중단 또는 위치로 인한 신호 손실 중에 IoT 장치를 사용할 수 있는지도 고려해야 합니다. 스마트 온도 조절기는 정전으로 인해 클라우드에 원격 측정을 보낼 수 없는 경우 난방을 제어하기 위해 몇 가지 제한된 결정을 내릴 수 있어야 합니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
MQTT가 연결 손실을 처리하려면, 장치 및 서버 코드가 필요한 경우 메시지 전달을 보장해야 합니다. 예를 들어 전송된 모든 메시지가 응답 주제에 대한 추가 메시지로 응답되도록 요구하고, 그렇지 않은 경우 나중에 재생할 수 있도록 수동으로 대기열에 추가됩니다.
|
||||||
|
|
||||||
|
## 명령
|
||||||
|
|
||||||
|
명령은 클라우드에서 장치로 보내는 메시지로, 작업을 수행하도록 지시합니다. 대부분의 경우 이것은 액츄에이터를 통해 일종의 출력을 제공하는 것과 관련이 있지만, 재부팅하거나 추가 원격 측정을 수집하여 명령에 대한 응답으로 반환하는 것과 같은 장치 자체에 대한 명령일 수 있습니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
온도 조절기는 클라우드에서 난방을 켜라는 명령을 받을 수 있습니다. 모든 센서의 원격 측정 데이터를 기반으로 클라우드 서비스가 난방을 켜야 한다고 결정한 경우 관련 명령을 보냅니다.
|
||||||
|
|
||||||
|
### MQTT 브로커에 명령 보내기
|
||||||
|
|
||||||
|
인터넷 제어 야간 조명의 다음 단계는 서버 코드가 감지하는 조명 수준에 따라 조명을 제어하기 위해 IoT 장치에 명령을 다시 보내는 것입니다
|
||||||
|
|
||||||
|
1. VS Code에서 서버 코드 열기
|
||||||
|
2. `client_telemetry_topic` 명령을 보낼 주제를 정의하는 선언 뒤에 다음 줄을 추가힙니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
server_command_topic = id + '/commands'
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
3. `handle_telemetry` 함수 끝에 다음 코드를 추가합니다:
|
||||||
|
|
||||||
|
```
|
||||||
|
command = { 'led_on' : payload['light'] < 300 }
|
||||||
|
print("Sending message:", command)
|
||||||
|
|
||||||
|
client.publish(server_command_topic, json.dumps(command))
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
이것은 명령 주제에 조명이 300 미만인지 여부에 따라 `led_on` 값이 ture나 false로 설정되도록 JSON 메시지를 보냅니다. 조명이 300보다 작으면, LED를 켜도록 장치에 지시하기 위해 true 가 전송됩니다.
|
||||||
|
|
||||||
|
4. 이전과 같이 코드를 실행
|
||||||
|
5. 물리적 또는 가상 장치에서 감지한 조명 수준을 조정합니다. 수신 중인 메시지와 전송 중인 명령이 터미널에 기록됩니다.:
|
||||||
|
|
||||||
|
```
|
||||||
|
(.venv) ➜ nightlight-server python app.py
|
||||||
|
Message received: {'light': 0}
|
||||||
|
Sending message: {'led_on': True}
|
||||||
|
Message received: {'light': 400}
|
||||||
|
Sending message: {'led_on': False}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💁 원격 측정과 명령은 각각 단일 주제에 대해 전송됩니다. 여러 장치의 원격 분석은 동일한 원격 분석 항목에 표시되고 여러 장치에 대한 명령은 동일한 명령 항목에 나타남을 의미합니다. 특정 장치에 명령을 보내려면, `/commands/device1`, `/commands/device2`와 같은 고유한 장치 id같은 여러 주제를 사용할 수 있습니다. 그런 식으로 장치는 해당 장치에 대한 메시지를 수신할 수 있습니다.
|
||||||
|
|
||||||
|
> 💁 이 코드는 [code-commands/server](code-commands/server) 폴더에서 찾을 수 있습니다.
|
||||||
|
|
||||||
|
### IoT 장치에서 명령 처리
|
||||||
|
|
||||||
|
이제 서버에서 명령이 전송되므로, IoT 장치에 코드를 추가하여 명령을 처리하고 LED를 제어할 수 있습니다.
|
||||||
|
|
||||||
|
MQTT 브로커의 명령을 수신하려면 아래 단계를 따르십시오:
|
||||||
|
|
||||||
|
- [아두이노 - Wio 터미널](notion://www.notion.so/wio-terminal-commands.md)
|
||||||
|
- [싱글 보드 컴퓨터 - Raspberry Pi/가상 IoT 장치](notion://www.notion.so/single-board-computer-commands.md)
|
||||||
|
|
||||||
|
이 코드가 작성되고 실행되면 조명 수준을 변경하는 실험을 하십시오. 서버 및 장치의 출력을 관찰하고 조명 수준을 변경할 때 LED를 관찰합니다.
|
||||||
|
|
||||||
|
### 연결 끊김
|
||||||
|
|
||||||
|
오프라인인 IoT 장치에 명령을 보내야 하는 경우 클라우드 서비스는 어떻게 해야 합니까? 다시 말하지만, 대답은 상황에 따라 다릅니다.
|
||||||
|
|
||||||
|
최신 명령이 이전 명령보다 우선하면 이전 명령은 무시할 수 있습니다. 클라우드 서비스가 난방을 켜라는 명령을 보낸 다음, 난방을 끄라는 명령을 보내면, 온 명령을 무시하고 재전송하지 않을 수 있습니다.
|
||||||
|
|
||||||
|
만약 명령을 순서대로 처리해야 하는 경우, 예를 들어 로봇 팔을 위로 이동한 다음 그래버를 닫는 경우, 연결이 복원되면 순서대로 전송해야 합니다.
|
||||||
|
|
||||||
|
✅ 장치 또는 서버 코드가 필요한 경우 MQTT를 통해 명령이 항상 순서대로 전송되고 처리되도록 하려면 어떻게 해야 합니까?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 도전
|
||||||
|
|
||||||
|
지난 세 수업의 과제는 집, 학교 또는 직장에 있는 IoT 장치를 최대한 많이 나열하고 마이크로컨트롤러 또는 단일 보드 컴퓨터 또는 이 둘을 혼합하여 구축되었는지 여부를 결정하고 어떤 센서와 액추에이터를 사용하고 있는지 생각해 보는 것이었습니다.
|
||||||
|
|
||||||
|
이러한 장치의 경우, 어떤 메시지를 보내거나 받을 수 있는지 생각해 보십시오. 어떤 원격 측정을 보내나요? 어떤 메시지나 명령을 받을 수 있습니까? 이것이 안전하다고 생각합니까?
|
||||||
|
|
||||||
|
## 강의 후 퀴즈
|
||||||
|
|
||||||
|
[강의 후 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/8)
|
||||||
|
|
||||||
|
## 복습 & 독학
|
||||||
|
|
||||||
|
[MQTT Wikipedia page](https://wikipedia.org/wiki/MQTT)에서 MQTT에 대해 자세히 알아보세요.
|
||||||
|
|
||||||
|
[Mosquitto](https://www.mosquitto.org/) 를 사용하여 MQTT 브로커를 직접 실행하고 IoT 장치와 서버 코드를 연결해보세요.
|
||||||
|
|
||||||
|
> 💁 팁 - 기본적으로 Mosquitto는 익명 연결(즉, 사용자 이름과 암호 없이 연결)을 허용하지 않으며, 실행중인 컴퓨터 외부에서 연결을 허용하지 않습니다.
|
||||||
|
> [`mosquitto.conf` 구성 파일](https://www.mosquitto.org/man/mosquitto-conf-5.html)로 이 문제를 해결할 수 있습니다:
|
||||||
|
>
|
||||||
|
> ```
|
||||||
|
> listener 1883 0.0.0.0
|
||||||
|
> allow_anonymous true
|
||||||
|
>
|
||||||
|
> ```
|
||||||
|
|
||||||
|
## 과제
|
||||||
|
|
||||||
|
[MQTT를 다른 통신 프로토콜과 비교 및 대조](assignment.ko.md)
|
@ -0,0 +1,14 @@
|
|||||||
|
# MQTT를 다른 통신 프로토콜과 비교, 대조하기
|
||||||
|
|
||||||
|
## 지침
|
||||||
|
|
||||||
|
이 강의에서는 MQTT 통신 프로토콜을 다루었습니다. 다른 프로토콜에는 AMQP 와 HTTP/HTTPS 가 있습니다.
|
||||||
|
|
||||||
|
이 두가지를 모두 조사하고 MQTT와 비교/대조하십시오. 연결이 끊긴 경우 전력 사용량, 보안, 메시지 지속성애 대해 생각해보세요.
|
||||||
|
|
||||||
|
## 평가 기준
|
||||||
|
|
||||||
|
| 기준 | 모범 답안 | 적절함 | 개선이 필요함 |
|
||||||
|
| ----------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||||
|
| AMQP 와 MQTT 비교 | AMQP와 MQTT를 비교 및 대조할 수 있으며 전원, 보안, 메시지 지속성을 다룹니다. | AMQP와 MQTT를 부분적으로 비교하고 대조할 수 있으며 전력, 보안, 메시지 지속성 중 두가지를 다룹니다. | AMQP와 MQTT를 부분적으로 비교 및 대조할 수 있으며 전원, 보안, 메시지 지속성 중 하나를 다룹니다. |
|
||||||
|
| HTTP/HTTPS 와 MQTT 비교 | HTTP/HTTPS와 MQTT를 비교 및 대조할 수 있으며 전원, 보안, 메시지 지속성을 다룹니다. | HTTP/HTTPS와 MQTT를 부분적으로 비교하고 대조할 수 있으며 기능, 보안, 메시지 지속성 중 두가지를 다룹니다. | HTTP/HTTPS를 MQTT와 부분적으로 비교 및 대조할 수 있으며 전원, 보안, 메시지 지속성 중 하나를 다룹니다. |
|
@ -0,0 +1,53 @@
|
|||||||
|
# 인터넷을 통해 야간 조명 제어하기 - 가상 IoT 하드웨어 및 Raspberry Pi
|
||||||
|
|
||||||
|
강의에서 MQTT 브로커에서 Raspberry Pi 또는 가상 IoT 장치로 전송된 명령을 실행하게 됩니다.
|
||||||
|
|
||||||
|
## 명령 실행하기
|
||||||
|
|
||||||
|
다음 단계는 MQTT 브로커에서 보낸 명령을 실행하고 이에 응답하는 것입니다.
|
||||||
|
|
||||||
|
### 작업
|
||||||
|
|
||||||
|
명령을 실행합니다.
|
||||||
|
|
||||||
|
1. VS Code에서 야간 조명 프로젝트를 엽니다.
|
||||||
|
|
||||||
|
2. 가상 IoT 장치를 사용하는 경우, 터미널이 가상 환경을 실행 중인지 확인하십시오. Raspberry Pi 를 사용하는 경우 가상 환경을 사용하지 않습니다.
|
||||||
|
|
||||||
|
3. `client_telemetry_topic`의 정위 뒤에 다음 코드를 추가합니다.:
|
||||||
|
|
||||||
|
```python
|
||||||
|
server_command_topic = id + '/commands'
|
||||||
|
```
|
||||||
|
|
||||||
|
`server_command_topic` 는 장치가 LED 명령을 수신하기 위해 실행될 MQTT 주제입니다.
|
||||||
|
|
||||||
|
4. main loop 바로 위의 `mqtt_client.loop_start()` 라인 뒤에 다음 코드를 추가합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def handle_command(client, userdata, message):
|
||||||
|
payload = json.loads(message.payload.decode())
|
||||||
|
print("Message received:", payload)
|
||||||
|
|
||||||
|
if payload['led_on']:
|
||||||
|
led.on()
|
||||||
|
else:
|
||||||
|
led.off()
|
||||||
|
|
||||||
|
mqtt_client.subscribe(server_command_topic)
|
||||||
|
mqtt_client.on_message = handle_command
|
||||||
|
```
|
||||||
|
|
||||||
|
이 코드는 `handle_command` 함수를 정의합니다, 이는 메시지를 JSON 문서로 읽고 `led_on` 속성 값을 찾는 함수입니다. `True` 로 설정되면 LED가 켜지고, 그렇지 않다면 LED는 꺼집니다.
|
||||||
|
|
||||||
|
MQTT 클라이언트는 서버가 메시지를 보낼 주제를 실행하고 메시지가 수신될 때 `handle_command` 함수가 호출되도록 설정합니다.
|
||||||
|
|
||||||
|
> 💁 `on_message` 핸들러는 실행된 모든 주제에 대해 호출됩니다. 나중에 여러 주제를 수신하는 코드를 작성하면, 핸들러 함수를 거쳐 보내진 `message` 객체의 주제를 얻을 수 있습니다.
|
||||||
|
|
||||||
|
5. 과제의 이전 부분과 동일한 방식으로 코드를 실행합니다. 가상 IoT 장치를 사용하는 경우, CounterFit 앱이 실행중이고 LED가 올바른 핀에 생성되었는지 확인하십시오.
|
||||||
|
|
||||||
|
6. 물리적 또는 가상 장치에서 감지한 조명 수준을 조정합니다. 수신 중인 메시지와 전송 중인 명령이 터미널에 기록됩니다. LED도 조명 수준에 따라 켜지고 꺼질 것입니다.
|
||||||
|
|
||||||
|
> 💁 이 코드는 [code-commands/virtual-device](../code-commands/virtual-device) 폴더 또는 [code-commands/pi](../code-commands/pi) 폴더에서 찾을 수 있습니다.
|
||||||
|
|
||||||
|
😀 장치가 MQTT 브로커의 명령에 응답하도록 성공적으로 코딩했습니다.
|
@ -0,0 +1,60 @@
|
|||||||
|
# 인터넷을 통해 야간 조명 제어하기 - 가상 IoT 하드웨어 및 Raspberry Pi
|
||||||
|
|
||||||
|
이 단원에서는 Rasberry Pi나 가상 IoT 장치로 부터 MQTT 브로커로 조명 레벨을 포함한 telemetry를 전송합니다.
|
||||||
|
|
||||||
|
## telemetry 게시
|
||||||
|
|
||||||
|
다음 단계는 telemetry를 포함한 JSON 문서를 생성하고 MQTT 브로커에게 전송하는 것입니다.
|
||||||
|
|
||||||
|
### 작업
|
||||||
|
|
||||||
|
MQTT 브로커에게 telemetry를 게시합니다.
|
||||||
|
|
||||||
|
1. VS Code에서 야간 조명 프로젝트를 엽니다.
|
||||||
|
|
||||||
|
1. 가상 IoT 기기를 사용한다면 터미널이 가상 환경에서 돌아가는지 확인합니다. Raspberry Pi를 사용한다면 가상 환경을 사용하지 않습니다.
|
||||||
|
|
||||||
|
1. `app.py` 파일의 상단에 다음을 추가합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import json
|
||||||
|
```
|
||||||
|
|
||||||
|
`json` 라이브러리는 telemetry를 JSON 문서로 인코딩 하는데 사용됩니다.
|
||||||
|
|
||||||
|
1. `client_name`의 선언 뒷부분에 다음을 추가합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
client_telemetry_topic = id + '/telemetry'
|
||||||
|
```
|
||||||
|
|
||||||
|
`client_telemetry_topic`은 장치가 조명 레벨을 게시할 MQTT 항목입니다.
|
||||||
|
|
||||||
|
1. 파일 끝에 있는 `while True:` loop의 내용을 다음으로 바꿉니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
while True:
|
||||||
|
light = light_sensor.light
|
||||||
|
telemetry = json.dumps({'light' : light})
|
||||||
|
|
||||||
|
print("Sending telemetry ", telemetry)
|
||||||
|
|
||||||
|
mqtt_client.publish(client_telemetry_topic, telemetry)
|
||||||
|
|
||||||
|
time.sleep(5)
|
||||||
|
```
|
||||||
|
|
||||||
|
이 코드는 조명 레벨을 JSON 문서로 패키지하고 MQTT 브로커에 게시합니다. 그런 다음 메시지를 보내는 빈도를 줄이기 위해 sleep 합니다.
|
||||||
|
|
||||||
|
1. 과제의 이전 부분에서 돌렸던 것과 동일한 방법으로 코드를 실행합니다. 가상 IoT 장치를 사용한다면 CounterFit 앱이 실행 중이고 올바른 핀에 광 센서와 LED가 생성되었는지 확인하십시오.
|
||||||
|
|
||||||
|
```output
|
||||||
|
(.venv) ➜ nightlight python app.py
|
||||||
|
MQTT connected!
|
||||||
|
Sending telemetry {"light": 0}
|
||||||
|
Sending telemetry {"light": 0}
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💁 해당 코드는 [code-telemetry/virtual-device](../code-telemetry/virtual-device) 폴더 또는 [code-telemetry/pi](../code-telemetry/pi) 폴더에서 찾으실 수 있습니다.
|
||||||
|
|
||||||
|
😀 장치에서 성공적으로 telemetry를 전송했습니다.
|
@ -0,0 +1,352 @@
|
|||||||
|
# 위치 데이터 시각화
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> [Nitya Narasimhan](https://github.com/nitya)의 스케치 노트. 더 큰 버전을 보려면 이미지를 클릭하십시오.
|
||||||
|
|
||||||
|
이 비디오는 이번 단원에서 다룰 Azure Maps의 IoT에 대한 개요를 제공합니다.
|
||||||
|
|
||||||
|
[](https://www.youtube.com/watch?v=P5i2GFTtb2s)
|
||||||
|
|
||||||
|
> 🎥 위 이미지를 클릭하면 영상을 볼 수 있습니다.
|
||||||
|
|
||||||
|
## 강의 전 퀴즈
|
||||||
|
|
||||||
|
[강의 전 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/25)
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
지난 수업에서는 서버리스 코드를 사용하여 스토리지 컨테이너의 클라우드에 저장하기 위해 센서에서 GPS 데이터를 가져오는 방법을 배웠습니다. 이제 Azure 맵에서 해당 지점을 시각화하는 방법을 알아봅니다. 웹 페이지에서 지도를 만드는 방법, GeoJSON 데이터 형식 및 이를 사용하여 지도에 캡처된 모든 GPS 지점을 표시하는 방법을 배우게 됩니다.
|
||||||
|
|
||||||
|
이 강의에서는 다음을 다룰 것입니다:
|
||||||
|
|
||||||
|
- [데이터 시각화란](#what-is-data-visualization)
|
||||||
|
- [지도 서비스](#map-services)
|
||||||
|
- [Azure Maps 리소스 만들기](#create-an-azure-maps-resource)
|
||||||
|
- [웹 페이지에 지도 표시](#show-a-map-on-a-web-page)
|
||||||
|
- [GeoJSON 형식](#the-geojson-format)
|
||||||
|
- [GeoJSON을 사용하여 지도에 GPS 데이터 표시하기](#plot-gps-data-on-a-map-using-geojson)
|
||||||
|
|
||||||
|
> 💁 This lesson will involve a small amount of HTML and JavaScript. If you would like to learn more about web development using HTML and JavaScript, check out [Web development for beginners](https://github.com/microsoft/Web-Dev-For-Beginners).
|
||||||
|
|
||||||
|
## 데이터 시각화란
|
||||||
|
|
||||||
|
데이터 시각화는 이름에서 알 수 있듯이 사람이 이해하기 쉽게 데이터를 시각화하는 것입니다. 일반적으로 차트 및 그래프와 연관되지만 사람이 데이터를 더 잘 이해할 수 있을 뿐만 아니라 의사 결정을 내리는 데 도움이 되도록 데이터를 그림으로 나타내는 모든 방법을 말합니다.
|
||||||
|
|
||||||
|
간단한 예를 들자면, 지난 농장 프로젝트에서 토양 수분 설정을 캡처했습니다. 2021년 6월 1일에 매시간 수집된 토양 수분 데이터 테이블은 다음과 같을 수 있습니다:
|
||||||
|
|
||||||
|
| Date | Reading |
|
||||||
|
| ---------------- | ------: |
|
||||||
|
| 01/06/2021 00:00 | 257 |
|
||||||
|
| 01/06/2021 01:00 | 268 |
|
||||||
|
| 01/06/2021 02:00 | 295 |
|
||||||
|
| 01/06/2021 03:00 | 305 |
|
||||||
|
| 01/06/2021 04:00 | 325 |
|
||||||
|
| 01/06/2021 05:00 | 359 |
|
||||||
|
| 01/06/2021 06:00 | 398 |
|
||||||
|
| 01/06/2021 07:00 | 410 |
|
||||||
|
| 01/06/2021 08:00 | 429 |
|
||||||
|
| 01/06/2021 09:00 | 451 |
|
||||||
|
| 01/06/2021 10:00 | 460 |
|
||||||
|
| 01/06/2021 11:00 | 452 |
|
||||||
|
| 01/06/2021 12:00 | 420 |
|
||||||
|
| 01/06/2021 13:00 | 408 |
|
||||||
|
| 01/06/2021 14:00 | 431 |
|
||||||
|
| 01/06/2021 15:00 | 462 |
|
||||||
|
| 01/06/2021 16:00 | 432 |
|
||||||
|
| 01/06/2021 17:00 | 402 |
|
||||||
|
| 01/06/2021 18:00 | 387 |
|
||||||
|
| 01/06/2021 19:00 | 360 |
|
||||||
|
| 01/06/2021 20:00 | 358 |
|
||||||
|
| 01/06/2021 21:00 | 354 |
|
||||||
|
| 01/06/2021 22:00 | 356 |
|
||||||
|
| 01/06/2021 23:00 | 362 |
|
||||||
|
|
||||||
|
인간으로서, 데이터를 이해하는 것은 어려울 수 있습니다. 이는 아무런 의미가 없는 숫자입니다. 이 데이터를 시각화하는 첫 번째 단계는 꺾은선형 차트에 그리는 것입니다:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
이는 언제 자동 급수 시스템이 토양 수분 판독값 450을 읽어 켜지는지를 나타내는 선을 추가하여 더욱 발전시킬 수 있습니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
이 차트는 토양 수분 수준 뿐만 아니라, 급수 시스템이 켜진 지점을 매우 빠르게 보여줍니다.
|
||||||
|
|
||||||
|
차트는 데이터를 시작화하는 유일한 도구가 아닙니다. 날씨를 추적하는 IoT 장치에는 흐린 날의 구름 기호, 비 오는 날의 비구름 등과 같은 기호를 사용하여 날씨 상태를 시각화하는 웹 앱, 또는 모바일 앱이 있을 수 있습니다. 데이터를 시각화하는 방법은 무수히 많고, 진지하고 재미있는 경우도 많습니다.
|
||||||
|
|
||||||
|
✅ 시각화된 데이터를 보는 방법에 대해 생각해 보십시오. 어떤 방법이 가장 명확했고 가장 빠르게 결정을 내릴 수 있었습니까?
|
||||||
|
|
||||||
|
최고의 시각화를 통해 사람과 사람이 빠르게 의사 결정을 내릴 수 있습니다. 예를 들어 산업용 기계의 모든 판독값을 표시하는 게이지를 갖는 것은 처리하기 어렵지만 무언가 잘못되었을 때 빨간색 표시등이 깜박이면 사람이 결정을 내릴 수 있습니다. 때때로 최고의 시각화는 번쩍이는 불빛입니다!
|
||||||
|
|
||||||
|
GPS 데이터로 작업할 때 가장 명확한 시각화는 지도에 데이터를 표시하는 것입니다. 예를 들어 배달 트럭을 표시하는 지도는 가공 공장의 작업자가 트럭이 도착하는 시기를 확인하는 데 도움이 될 수 있습니다. 이 지도가 현재 위치에 있는 트럭 사진보다 더 많은 것을 보여주고 트럭의 내용물에 대한 아이디어를 제공한다면 공장의 작업자는 그에 따라 계획을 세울 수 있습니다. 만약 근처에 냉장 트럭이 보이면 냉장고에 공간을 마련해야 한다는 것을 압니다.
|
||||||
|
|
||||||
|
## 지도 서비스
|
||||||
|
|
||||||
|
지도로 작업하는 것은 흥미롭습니다. Bing Maps, Leaflet, Open Street Maps 및 Google Maps와 같이 선택할 수 있는 것이 많습니다. 이 단원에서는 [Azure Maps](https://azure.microsoft.com/services/azure-maps/?WT.mc_id=academic-17441-jabenn) 과 GPS 데이터를 표시하는 방법에 대해 알아봅니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Azure Maps는 "새로운 매핑 데이터를 사용하여 웹 및 모바일 애플리케이션에 지리학적 환경을 제공하는 지리 공간 서비스 및 SDK 모음"입니다. 개발자에게는 권장 교통 경로 제공, 교통 사고에 대한 정보 제공, 실내 내비게이션, 검색 기능, 고도 정보, 날씨 서비스 등을 수행할 수 있는 아름다운 대화형 지도를 만드는 도구가 제공됩니다.
|
||||||
|
|
||||||
|
✅ [매핑 코드 샘플](https://docs.microsoft.com/samples/browse?WT.mc_id=academic-17441-jabenn&products=azure-maps) 로 실험하기
|
||||||
|
|
||||||
|
지도의 빈 캔버스, 타일, 위성 이미지, 도로가 중첩된 위성 이미지, 다양한 유형의 회색조 지도, 고도를 표시하는 음영 부조가 있는 지도, 야경 지도, 고대비 지도로 지도를 표시할 수 있습니다. [Azure Event Grid](https://azure.microsoft.com/services/event-grid/?WT.mc_id=academic-17441-jabenn)와 통합하여 지도에서 실시간 업데이트를 받을 수 있습니다. 핀치, 드래그와 클릭과 같은 이벤트에 지도가 반응할 수 있도록 다양한 제어를 활성화하여 지도의 동작과 모양을 제어할 수 있습니다. 지도의 모양을 제어하기 위해 버블, 라인, 다각형, 히트 맵 등을 포함하는 레이어를 추가할 수 있습니다. 구현하는 지도의 스타일은 선택한 SDK에 따라 다릅니다.
|
||||||
|
|
||||||
|
[REST API](https://docs.microsoft.com/javascript/api/azure-maps-rest/?WT.mc_id=academic-17441-jabenn&view=azure-maps-typescript-latest), [Web SDK](https://docs.microsoft.com/azure/azure-maps/how-to-use-map-control?WT.mc_id=academic-17441-jabenn), 모바일 어플리케이션을 만드는 경우, [Android SDK](https://docs.microsoft.com/azure/azure-maps/how-to-use-android-map-control-library?WT.mc_id=academic-17441-jabenn&pivots=programming-language-java-android)를 활용하여 Azure Maps API에 접근할 수 있습니다.
|
||||||
|
|
||||||
|
이 수업에서는 웹 SDK를 사용하여 지도와 센서의 GPS 위치 경로를 표시합니다.
|
||||||
|
|
||||||
|
## Azure Maps 리소스 만들기
|
||||||
|
|
||||||
|
첫 번째 단계는 Azure Maps 계정을 만드는 것입니다.
|
||||||
|
|
||||||
|
### 작업 - Azure Maps 리소스 만들기
|
||||||
|
|
||||||
|
1. 터미널 또는 명령 프롬프트에서 다음 명령을 실행하여 `gps-sensor` 리소스 그룹에 Azure Maps 리소스를 만듭니다:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
az maps account create --name gps-sensor \
|
||||||
|
--resource-group gps-sensor \
|
||||||
|
--accept-tos \
|
||||||
|
--sku S1
|
||||||
|
```
|
||||||
|
|
||||||
|
이렇게 하면 Azure Maps의 `gps-sensor`라는 리소스가 생성됩니다. 사용 중인 계층은 `S1`으로, 다양한 기능을 포함하는 유료 계층이지만, 무료로 제공되는 부분이 있습니다.
|
||||||
|
|
||||||
|
> 💁 Azure Maps의 사용 비용을 보려면, [Azure Maps 가격 페이지](https://azure.microsoft.com/pricing/details/azure-maps/?WT.mc_id=academic-17441-jabenn)를 확인하세요.
|
||||||
|
|
||||||
|
2. 지도 리소스에 대한 API 키가 필요합니다. 이 키를 얻으려면 다음 명령어를 사용하십시오:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
az maps account keys list --name gps-sensor \
|
||||||
|
--resource-group gps-sensor \
|
||||||
|
--output table
|
||||||
|
```
|
||||||
|
|
||||||
|
`PrimaryKey` 값을 복사해 놓으십시오.
|
||||||
|
|
||||||
|
## 웹 페이지에 지도 표시
|
||||||
|
|
||||||
|
이제 웹 페이지에 지도를 표시하는 다음 단계를 수행할 수 있습니다. 작은 웹 앱에 대해 하나의 `html` 파일만 사용 합니다. 상품이나 팀 환경에서는 웹 앱에 움직이는 부분이 더 많을 가능성이 높습니다!
|
||||||
|
|
||||||
|
### 작업 - 웹 페이지에 지도 표시
|
||||||
|
|
||||||
|
1. 로컬 컴퓨터의 폴더에 index.html 이라는 파일을 만듭니다. 지도를 보관할 HTML 마크업을 추가합니다:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
#myMap {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="init()">
|
||||||
|
<div id="myMap"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
지도에 `myMap` `div` 가 로드됩니다. 몇 가지 스타일을 사용하면 페이지의 너비와 높이를 확장할 수 있습니다.
|
||||||
|
|
||||||
|
> 🎓 `div` 는 웹 페이지의 섹션으로, 이름과 스타일을 지정할 수 있습니다.
|
||||||
|
|
||||||
|
2. 여는 `<head>` 태그 아래에, 외부 스타일시트를 추가하여 지도 표시를 제어하고, 웹 SDK의 외부 스크립트를 추가하여 해당 동작을 관리합니다:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css"
|
||||||
|
type="text/css"
|
||||||
|
/>
|
||||||
|
<script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
이 스타일시트에는 지도 모양에 대한 설정이 포함되어 있으며, 스크립트 파일에는 지도를 로드하는 코드가 포함되어 있습니다. 이 코드를 추가하는 것은 C++ 헤더 파일을 포함하거나 Python 모듈을 가져오는 것과 유사합니다.
|
||||||
|
|
||||||
|
3. 해당 스크립트 아래에, 스크립트 블록을 추가하여 지도를 시작합니다.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
<script type='text/javascript'>
|
||||||
|
function init() {
|
||||||
|
var map = new atlas.Map('myMap', {
|
||||||
|
center: [-122.26473, 47.73444],
|
||||||
|
zoom: 12,
|
||||||
|
authOptions: {
|
||||||
|
authType: "subscriptionKey",
|
||||||
|
subscriptionKey: "<subscription_key>",
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
`<subscription_key>` 를 Azure Maps 계정의 API 키로 변경합니다.
|
||||||
|
|
||||||
|
웹 브라우저에서 `index.html` 페이지를 열면, 지도가 로드되고, 시애틀 지역에 초점이 맞춰져 있어야 합니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
✅ 확대/축소 및 중심 매개변수를 변경하여 지도 표시를 실험합니다. 데이터의 위도와 경도에 해당하는 다른 좌표를 추가하여 지도의 중심을 다시 맞출 수 있습니다.
|
||||||
|
|
||||||
|
> 💁 웹 앱을 로컬에서 사용하는 더 좋은 방법은 [http-server](https://www.npmjs.com/package/http-server)를 설치하는 것입니다. 이 도구를 사용하려면 [node.js](https://nodejs.org/) 와 [npm](https://www.npmjs.com/) 이 설치되어 있어야 합니다. 도구들이 설치되면 `index.html` 과 `http-server` 타입의 위치를 다룰 수 있습니다. 웹 앱은 로컬 웹 서버 [http://127.0.0.1:8080/](http://127.0.0.1:8080/)에서 열립니다.
|
||||||
|
|
||||||
|
## GeoJSON 형식
|
||||||
|
|
||||||
|
이제 지도가 표시되는 웹 앱이 준비되었으므로, 저장소 계정에서 GPS 데이터를 추출하여 지도 위에 있는 마커를 레이어에 표시해야 합니다. 그 전에 Azure Maps에 필요한 [GeoJSON](https://wikipedia.org/wiki/GeoJSON) 형식을 살펴보겠습니다.
|
||||||
|
|
||||||
|
[GeoJSON](https://geojson.org/)은 지리적 특정 데이터를 처리하도록 설계된 특수 형식의 개방형 표준 JSON 사양입니다. GeoJSON 파일을 디버깅하는 데 유용한 도구인 [geojson.io](https://geojson.io) 를 사용하여 샘플 데이터를 테스트하여 이에 대해 알아볼 수 있습니다 .
|
||||||
|
|
||||||
|
샘플 GeoJSON 데이터는 다음과 같습니다:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"type": "Feature",
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [-2.10237979888916, 57.164918677004714]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
특히 흥미로운 점은 데이터가 `FeatureCollection`내에 `Feature`로 중첩되는 방식입니다. 해당 객체 내에서 위도와 경도를 나타내는 `coordinates`가 있는 `geometry`를 찾을 수 있습니다.
|
||||||
|
|
||||||
|
✅ When building your geoJSON, pay attention to the order of `latitude` and `longitude` in the object, or your points will not appear where they should! GeoJSON expects data in the order `lon,lat` for points, not `lat,lon`.
|
||||||
|
|
||||||
|
`Geometry` 는 단일 점 또는 다각형과 같은 다른 유형을 가질 수 있습니다. 이 예시에서는 경도와 위도라는 두 좌표가 지정된 점입니다.
|
||||||
|
|
||||||
|
✅ Azure Maps는 표준 GeoJSON 과 원 및 기타 기하 도형을 그리는 기능을 비롯한 몇 가지 [향상된 기능들](https://docs.microsoft.com/azure/azure-maps/extend-geojson?WT.mc_id=academic-17441-jabenn) 을 제공합니다.
|
||||||
|
|
||||||
|
## GeoJSON을 사용하여 지도에 GPS 데이터 표시하기
|
||||||
|
|
||||||
|
이제 이전 학습에서 빌드한 저장소에서 데이터를 사용할 준비가 되었습니다. 참고로 Blob 저장소에 여러 파일로 저장되므로 Azure Maps에서 데이터를 사용할 수 있도록 파일을 검색하고 구문 분석해야 합니다.
|
||||||
|
|
||||||
|
### 작업 - 웹 페이지에서 액세스할 스토리지 구성
|
||||||
|
|
||||||
|
데이터를 가져오기 위해 저장소를 호출하면 브라우저 콘솔에서 발생하는 오류를 보고 놀랄 수 있습니다. 외부 웹 앱이 해당 데이터를 읽을 수 있도록 하려면 이 저장소에서 [CORS](https://developer.mozilla.org/docs/Web/HTTP/CORS) 에 대한 권한을 설정해야 하기 때문입니다.
|
||||||
|
|
||||||
|
> 🎓 CORS는 "Cross-Origin Resource Sharing"을 나타내며 일반적으로 보안상의 이유로 Azure에서 명시적으로 설정해야 합니다. 이는 데이터에 접근할 수 있는 것으로 예상하지 않는 사이트를 중지합니다.
|
||||||
|
|
||||||
|
1. 다음 명령을 실행하여 CORS를 활성화합니다:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
az storage cors add --methods GET \
|
||||||
|
--origins "*" \
|
||||||
|
--services b \
|
||||||
|
--account-name <storage_name> \
|
||||||
|
--account-key <key1>
|
||||||
|
```
|
||||||
|
|
||||||
|
`<storage_name>` 을 저장소 계정의 이름으로 바꿉니다. `<key1>` 를 저장소 계정의 키로 바꿉니다.
|
||||||
|
|
||||||
|
이 명령은 모든 웹 사이트(와일드 카드 `*` 는 모두를 의미)가 저장소 계정에서 _GET_ 요청, 즉 데이터 가져오기를 수행할 수 있습니다. `--services b` 는 blobs에만 이 설정을 적용한다는 의미입니다.
|
||||||
|
|
||||||
|
### 작업 - 저장소에서 GPS 데이터 로드
|
||||||
|
|
||||||
|
1. `init` 함수의 전체 내용을 다음 코드로 변경합니다:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
fetch(
|
||||||
|
"https://<storage_name>.blob.core.windows.net/gps-data/?restype=container&comp=list"
|
||||||
|
)
|
||||||
|
.then((response) => response.text())
|
||||||
|
.then((str) => new window.DOMParser().parseFromString(str, "text/xml"))
|
||||||
|
.then((xml) => {
|
||||||
|
let blobList = Array.from(xml.querySelectorAll("Url"));
|
||||||
|
blobList.forEach(async (blobUrl) => {
|
||||||
|
loadJSON(blobUrl.innerHTML);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
map = new atlas.Map("myMap", {
|
||||||
|
center: [-122.26473, 47.73444],
|
||||||
|
zoom: 14,
|
||||||
|
authOptions: {
|
||||||
|
authType: "subscriptionKey",
|
||||||
|
subscriptionKey: "<subscription_key>",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
map.events.add("ready", function () {
|
||||||
|
var source = new atlas.source.DataSource();
|
||||||
|
map.sources.add(source);
|
||||||
|
map.layers.add(new atlas.layer.BubbleLayer(source));
|
||||||
|
source.add(features);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
`<storage_name>` 를 저장소 계정의 이름으로 변경합니다. `<subscription_key>` Azure Maps 계정의 API 키로 변경합니다.
|
||||||
|
|
||||||
|
여기에서 여러 가지 일이 발생합니다. 먼저 코드는 저장소 계정 이름을 사용하여 빌드된 URL 엔드포인트를 사용하여 Blob 컨테이너에서 GPS 데이터를 가져옵니다. 이 URL은 리소스 유형이 컨테이너(`restype=container`) 임을 나타내는 `gps-data`에서 검색하고 모든 Blob 에 대한 정보를 나열합니다. 이 목록은 Blob 자체를 반환하지 않지만 Blob 데이터를 로드하는 데 사용할 수 있는 각 Blob에 대한 URL을 반환합니다.
|
||||||
|
|
||||||
|
> 💁 You can put this URL into your browser to see details of all the blobs in your container. Each item will have a `Url` property that you can also load in your browser to see the contents of the blob. 이 URL을 브라우저에 넣으면 컨테이너의 모든 Blob에 대한 세부 정보를 볼 수 있습니다. 각 항목은 Blob의 콘텐츠를 보기 위해 브라우저에 로드할 수 있는 `Url` 속성이 있습니다.
|
||||||
|
|
||||||
|
이 코드는 다음에 생성될 `loadJSON` 함수를 호출하여 각 Blob을 로드합니다. 그런 다음 지도 제어를 만들고 `ready` 이벤트에 코드를 추가합니다. 이 이벤트는 웹 페이지에 지도가 표시될 때 호출됩니다.
|
||||||
|
|
||||||
|
ready 이벤트는 나중에 채워질 GeoJSON 데이터를 포함하는 컨테이너인 Azure Maps 데이터 원본을 만듭니다. 그런 다음 이 데이터 원본을 사용하여 GeoJSON의 각 지점 중심에 있는 지도의 원 집합인 버블 레이어를 만듭니다.
|
||||||
|
|
||||||
|
2. `init` 함수 아래의 스크립트 블록에 `loadJSON` 함수를 추가합니다:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var map, features;
|
||||||
|
|
||||||
|
function loadJSON(file) {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
features = [];
|
||||||
|
xhr.onreadystatechange = function () {
|
||||||
|
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
gps = JSON.parse(xhr.responseText);
|
||||||
|
features.push(
|
||||||
|
new atlas.data.Feature(
|
||||||
|
new atlas.data.Point([
|
||||||
|
parseFloat(gps.gps.lon),
|
||||||
|
parseFloat(gps.gps.lat),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.open("GET", file, true);
|
||||||
|
xhr.send();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
이 함수는 패치(fetch) 루틴에 의해 호출되어 JSON 데이터를 구문 분석하고 경도 및 위도 좌표를 geoJSON으로 읽도록 변환됩니다. 파싱되면 데이터는 geoJSON의 `Feature`의 일부로 설정됩니다. 지도가 초기화되고 경로 주위에 작은 점들이 나타납니다.
|
||||||
|
|
||||||
|
3. 브라우저에서 HTML 페이지를 로드합니다. 지도를 로드한 다음 저장소에서 모든 GPS 데이터를 로드하고 지도에 표시합니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> 💁 이 코드는 [코드](.././code) 폴더에서 찾을 수 있습니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 도전
|
||||||
|
|
||||||
|
정적 데이터를 지도에 마커로 표시할 수 있다는 점이 좋습니다. 이 웹 앱에 타임스탬프가 있는 json 파일을 사용하여 애니메이션을 추가하고 시간 경과에 따른 마커의 경로를 표시할 수 있습니까? 여기 지동 애니메이션을 사용한 [일부 샘플들](https://azuremapscodesamples.azurewebsites.net/) 이 있습니다.
|
||||||
|
|
||||||
|
## 강의 후 퀴즈
|
||||||
|
|
||||||
|
[강의 후 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/26)
|
||||||
|
|
||||||
|
## 복습 및 독학
|
||||||
|
|
||||||
|
Azure Maps은 특히 IoT 장치 작업에 유용합니다.
|
||||||
|
|
||||||
|
- [Microsoft 문서의 Azure Maps 문서](https://docs.microsoft.com/azure/azure-maps/tutorial-iot-hub-maps?WT.mc_id=academic-17441-jabenn)에서 일부 용도를 조사합니다.
|
||||||
|
- [Microsoft의 Azure Maps의 자기 학습 모듈을 사용하여 첫번째 경로 찾기 앱 만들기](https://docs.microsoft.com/learn/modules/create-your-first-app-with-azure-maps/?WT.mc_id=academic-17441-jabenn)를 통해 지도 작성 및 웨이포인트에 대한 지식을 심화하세요.
|
||||||
|
|
||||||
|
## 과제
|
||||||
|
|
||||||
|
[앱 배포](assignment.md)
|
@ -0,0 +1,79 @@
|
|||||||
|
# 인터넷을 통해 야간 조명을 제어 해봅시다. - 가상 IoT와 Raspberry Pi
|
||||||
|
|
||||||
|
IoT 장치는 MQTT를 사용하여 *test.mosquitto.org*와 통신하도록 코딩됩니다. 이는 광센서 판독값과 함께 원격 측정값을 전송하고 LED를 제어하는 명령을 수신하기 위함입니다.
|
||||||
|
|
||||||
|
이 강의에서는 여러분의 Raspberry Pi나 가상 IoT 장치에 MQTT broker를 연결하고자 합니다.
|
||||||
|
|
||||||
|
## MQTT 사용자 패키지를 설치합니다.
|
||||||
|
|
||||||
|
MQTT broker와 통신하기 위해서, 여러분은 MQTT 라이브러리 pip 패키지를 Pi 혹은 사용하시는 가상환경에 설치합니다.
|
||||||
|
|
||||||
|
### 작업
|
||||||
|
|
||||||
|
pip 패키지를 설치합니다.
|
||||||
|
|
||||||
|
1. 야간조명 프로젝트를 VS code에서 열어줍니다.
|
||||||
|
|
||||||
|
1. 만약 여러분이 가상 IoT device를 사용하고 있다면 가상환경 터미널이 실행중인지 확인해주세요. 여러분이 Raspberry Pi를 사용하고 있다면 가상환경은 사용하지 않습니다.
|
||||||
|
|
||||||
|
1. 아래 코드를 실행하여 MQTT pip 패키지를 설치 해 주세요:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip3 install paho-mqtt
|
||||||
|
```
|
||||||
|
|
||||||
|
## 코딩을 진행합니다.
|
||||||
|
|
||||||
|
이제 코딩을 진행할 준비가 되었습니다.
|
||||||
|
|
||||||
|
### 작업
|
||||||
|
|
||||||
|
코드를 작성 해 주세요
|
||||||
|
|
||||||
|
1. 아래 코드를 `app.py` 맨 위에 import합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
```
|
||||||
|
|
||||||
|
`paho.mqtt.client` 라이브를 사용하여 MQTT와 통신할 수 있습니다.
|
||||||
|
|
||||||
|
1. 광센서 및 LED 정의 뒤에 아래 코드를 추가합니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
id = '<ID>'
|
||||||
|
|
||||||
|
client_name = id + 'nightlight_client'
|
||||||
|
```
|
||||||
|
|
||||||
|
`<ID>`를 고유 ID로 교체합니다. 이는 장치 사용자의 이름으로 사용 될 것이며 이후에 이 장치가 publish하고 subscribe하는 항목에 사용됩니다. *test.mosquitto.org*는 public 이며 이 과제를 수행하는 다른 학생들을 포함한 많은 사람들에게 사용됨니다. 고유한 MQTT 사용자 이름과 주제를 사용하면 다른 사용자와 충돌하지 않습니다. 이 ID는 이번 과제 이후에 서버 코드를 생성할 때 또한 필요합니다.
|
||||||
|
|
||||||
|
> 💁 고유한ID를 생성하기 위해 [GUIDGen](https://www.guidgen.com) 사이트를 이용할 수 있습니다..
|
||||||
|
|
||||||
|
`client_name` 는 broker에서의 MQTT 사용자의 고유한 이름입니다.
|
||||||
|
|
||||||
|
1. 아래 코드를 이 새로운 코드 아래에 추가하여 MQTT 사용자 객체를 생성하고 MQTT broker에 연결합니다.:
|
||||||
|
|
||||||
|
```python
|
||||||
|
mqtt_client = mqtt.Client(client_name)
|
||||||
|
mqtt_client.connect('test.mosquitto.org')
|
||||||
|
|
||||||
|
mqtt_client.loop_start()
|
||||||
|
|
||||||
|
print("MQTT connected!")
|
||||||
|
```
|
||||||
|
|
||||||
|
이 코드는 사용자 객체를 생성하고, public MQTT broker에 연결하며, subscribe된 topic에 대한 메세지를 수신하는 백그라운드 스레드에서 처리 루프를 실행합니다.
|
||||||
|
|
||||||
|
1. 이전 과제에서 실행한 것 과 동일한 방법으로 코드를 실행합니다. 가상 IoT 장치를 사용하는 경우 CouterFit 앱이 실행중인지, 관센서와 LED가 올바른 핀에 생성되었는지 확인해주세요.
|
||||||
|
|
||||||
|
```output
|
||||||
|
(.venv) ➜ nightlight python app.py
|
||||||
|
MQTT connected!
|
||||||
|
Light level: 0
|
||||||
|
Light level: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💁 [code-mqtt/virtual-device](code-mqtt/virtual-device) 폴더나 [code-mqtt/pi](code-mqtt/pi) 폴더에서 코드를 찾을 수 있습니다.
|
||||||
|
|
||||||
|
😀 여러분은 여러분의 장치와 MQTT broker를 성공적으로 연결하였습니다.
|
@ -0,0 +1,213 @@
|
|||||||
|
# 오디오 캡처 - Raspberry Pi
|
||||||
|
|
||||||
|
이 단원에서는 Raspberry Pi로 오디오를 캡처하는 코드를 작성합니다. 오디오 캡처는 버튼으로 제어됩니다.
|
||||||
|
|
||||||
|
## 하드웨어
|
||||||
|
|
||||||
|
Raspberry Pi는 오디오 캡처를 조절하는 버튼을 필요로 합니다.
|
||||||
|
|
||||||
|
사용할 버튼은 Grove 버튼입니다. 이것은 신호를 켜고 끄는 디지털 센서입니다. 버튼이 눌리면 높은 값의 신호를 보내고 누르지 않으면 낮은 값의 신호를 보내도록 구성됩니다. 또는 눌렀을 때 낮은 값, 누르지 않았을 때 높은 값으로도 구성할 수 있습니다.
|
||||||
|
|
||||||
|
ReSpeaker 2-Mics Pi HAT을 마이크로 이용한다면 여기에 이미 장착이 되어있기 때문에 버튼을 연결할 필요가 없습니다. 다음 섹션으로 넘어가십시오.
|
||||||
|
|
||||||
|
### 버튼 연결
|
||||||
|
|
||||||
|
버튼은 Grove base hat에 연결될 수 있습니다.
|
||||||
|
|
||||||
|
#### 작업 - 버튼 연결
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
1. Grove 케이블의 한 쪽 끝을 버튼 모듈의 소켓에 삽입합니다. 한 방향으로만 이루어집니다.
|
||||||
|
|
||||||
|
1. Raspberry Pi의 전원을 끈 상태에서 Grove 케이블의 다른 쪽 끝을 Pi에 부착된 Grove Base hat의 **D5**가 적힌 디지털 소켓에 연결합니다. 이 소켓은 GPIO 핀 옆의 소켓 행에서 왼쪽 두 번째입니다.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 오디오 캡처
|
||||||
|
|
||||||
|
Python 코드를 이용해서 마이크로부터 오디오를 캡처할 수 있습니다.
|
||||||
|
|
||||||
|
### 작업 - 오디오 캡처
|
||||||
|
|
||||||
|
1. Pi의 전원을 켜고 부팅될 때까지 기다립니다.
|
||||||
|
|
||||||
|
1. Pi에서 직접 VS Code를 실행하거나 원격 SSH 확장을 통해 연결합니다.
|
||||||
|
|
||||||
|
1. PyAudio Pip 패키지에는 음성을 녹음하고 재생하는 함수들이 있습니다. 이 패키지는 먼저 설치해야 하는 일부 오디오 라이브러리에 따라 다릅니다. 터미널에서 아래의 명령을 실행하여 이를 설치합니다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install libportaudio0 libportaudio2 libportaudiocpp0 portaudio19-dev libasound2-plugins --yes
|
||||||
|
```
|
||||||
|
|
||||||
|
1. PyAudio Pip 패키지를 설치합니다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip3 install pyaudio
|
||||||
|
```
|
||||||
|
|
||||||
|
1. `smart-timer` 이름의 새 폴더를 생성하고 새로운 파일 `app.py`를 이 폴더에 추가합니다.
|
||||||
|
|
||||||
|
1. 이 파일의 상단에 다음의 import문들을 추가합니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import io
|
||||||
|
import pyaudio
|
||||||
|
import time
|
||||||
|
import wave
|
||||||
|
|
||||||
|
from grove.factory import Factory
|
||||||
|
```
|
||||||
|
|
||||||
|
이것은 `pyaudio` 모듈, 웨이브 파일을 처리하는 일부 표준 Python 모듈과 버튼 클래스를 생성하기 위해 `Factory`를 import 하는 `grove.factory` 모듈을 가져옵니다.
|
||||||
|
|
||||||
|
1. 아래와 같이 코드를 추가하여 Grove 버튼을 추가합니다.
|
||||||
|
|
||||||
|
ReSpeaker 2-Mics Pi HAT를 사용한다면, 다음 코드를 사용하십시오:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# The button on the ReSpeaker 2-Mics Pi HAT
|
||||||
|
button = Factory.getButton("GPIO-LOW", 17)
|
||||||
|
```
|
||||||
|
|
||||||
|
이렇게 하면 ReSpeaker 2-Mics Pi HAT의 버튼이 연결된 포트 **D17**에 버튼이 생성됩니다. 이 버튼은 누르면 낮은 신호를 보내도록 설정되었습니다.
|
||||||
|
|
||||||
|
ReSpeaker 2-Mics Pi HAT를 사용하지 않고 Grove 버튼을 base hat에 연결하는 데 사용하는 경우 이 코드를 사용합니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
button = Factory.getButton("GPIO-HIGH", 5)
|
||||||
|
```
|
||||||
|
|
||||||
|
이는 **D5** 포트에 눌렀을 때 높은 신호를 전송하는 버튼을 생성합니다.
|
||||||
|
|
||||||
|
1. 아래와 같이 오디오를 처리할 PyAudio 클래스의 인스턴스를 생성합니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
audio = pyaudio.PyAudio()
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 마이크와 스피커의 하드웨어 카드 번호를 선언합니다. 이는 이 수업의 앞부분에서 `arecord -l` 와 `aplay -l` 를 실행하여 찾은 카드 번호입니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
microphone_card_number = <microphone card number>
|
||||||
|
speaker_card_number = <speaker card number>
|
||||||
|
```
|
||||||
|
|
||||||
|
`<microphone card number>` 부분을 마이크 카드 번호로 수정합니다.
|
||||||
|
|
||||||
|
`<speaker card number>` 를 `alsa.conf` 파일에서 설정한 숫자와 동일한 스피커 카드 번호로 바꿉니다.
|
||||||
|
|
||||||
|
1. 아래와 같이 오디오를 캡처하고 다시 재생하는 샘플 속도를 선언합니다. 실제로 사용하는 하드웨어에 따라 변경할 수 있습니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
rate = 48000 #48KHz
|
||||||
|
```
|
||||||
|
|
||||||
|
이 코드를 실행하면서 샘플 속도 에러가 발생한다면, 이 값을 `44100` 또는 `16000`로 변경하십시오. 값을 높일 수록 더 좋은 음질을 얻을 수 있습니다.
|
||||||
|
|
||||||
|
1. 아래와 같이 `capture_audio`의 이름을 가진 새 함수를 생성합니다. 이 함수는 마이크에서 오디오를 캡처할 때 호출됩니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def capture_audio():
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 이 함수 안에 다음과 같은 오디오를 캡처하는 부분을 추가합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
stream = audio.open(format = pyaudio.paInt16,
|
||||||
|
rate = rate,
|
||||||
|
channels = 1,
|
||||||
|
input_device_index = microphone_card_number,
|
||||||
|
input = True,
|
||||||
|
frames_per_buffer = 4096)
|
||||||
|
|
||||||
|
frames = []
|
||||||
|
|
||||||
|
while button.is_pressed():
|
||||||
|
frames.append(stream.read(4096))
|
||||||
|
|
||||||
|
stream.stop_stream()
|
||||||
|
stream.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
이 코드는 PyAudio 객체를 사용해 오디오 입력 스트림을 엽니다. 이 스트림은 16KHz에서 마이크의 오디오를 캡처하여 4096바이트 크기의 버퍼에서 캡처합니다.
|
||||||
|
|
||||||
|
그 다음 코드는 버튼이 눌린 동안 4096 바이트의 버퍼를 배열로 읽어들입니다.
|
||||||
|
|
||||||
|
> 💁 `open` 메서드에 전달된 옵션들에 대해서는 [PyAudio documentation](https://people.csail.mit.edu/hubert/pyaudio/docs/)에서 확인할 수 있습니다.
|
||||||
|
|
||||||
|
버튼을 놓으면 스트림은 중지되고 close합니다.
|
||||||
|
|
||||||
|
1. 이 함수의 끝에 다음을 추가합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
wav_buffer = io.BytesIO()
|
||||||
|
with wave.open(wav_buffer, 'wb') as wavefile:
|
||||||
|
wavefile.setnchannels(1)
|
||||||
|
wavefile.setsampwidth(audio.get_sample_size(pyaudio.paInt16))
|
||||||
|
wavefile.setframerate(rate)
|
||||||
|
wavefile.writeframes(b''.join(frames))
|
||||||
|
wav_buffer.seek(0)
|
||||||
|
|
||||||
|
return wav_buffer
|
||||||
|
```
|
||||||
|
|
||||||
|
이 코드는 바이너리 버퍼를 생성하고 캡처한 오디오를 [WAV file](https://wikipedia.org/wiki/WAV)로 작성합니다. 이는 압축되지 않은 오디오를 파일로 저장하는 표준적인 방법입니다. 이 버퍼는 이후 리턴합니다.
|
||||||
|
|
||||||
|
1. 오디오 버퍼를 재생하기 위해 다음과 같은 `play_audio` 함수를 추가합니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def play_audio(buffer):
|
||||||
|
stream = audio.open(format = pyaudio.paInt16,
|
||||||
|
rate = rate,
|
||||||
|
channels = 1,
|
||||||
|
output_device_index = speaker_card_number,
|
||||||
|
output = True)
|
||||||
|
|
||||||
|
with wave.open(buffer, 'rb') as wf:
|
||||||
|
data = wf.readframes(4096)
|
||||||
|
|
||||||
|
while len(data) > 0:
|
||||||
|
stream.write(data)
|
||||||
|
data = wf.readframes(4096)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
이 함수는 이번에는 출력을 위해 오디오를 재생하는 다른 오디오 스트림을 엽니다. 입력 스트림과 같은 방식으로 세팅합니다. 그런 다음 버퍼가 웨이브 파일로 열리고 4096바이트 단위로 출력 스트림에 기록되어 오디오를 재생합니다. 이후 스트림을 close합니다.
|
||||||
|
|
||||||
|
1. `capture_audio` 함수에 버튼을 누를 때까지 loop를 돌도록 아래의 코드를 추가합니다. 버튼이 한 번 눌리면 오디오가 캡처되고 재생됩니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
while True:
|
||||||
|
while not button.is_pressed():
|
||||||
|
time.sleep(.1)
|
||||||
|
|
||||||
|
buffer = capture_audio()
|
||||||
|
play_audio(buffer)
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 코드를 실행합니다. 버튼을 누르고 마이크에 대고 말합니다. 끝나면 버튼에서 손을 떼고 녹음된 내용을 듣습니다.
|
||||||
|
|
||||||
|
PyAudio 인스턴스가 생성될 때 ALSA 오류가 발생할 수 있습니다. 이는 가지고 있지 않은 오디오 장치에 대한 Pi의 구성 때문입니다. 이러한 오류는 무시해도 됩니다.
|
||||||
|
|
||||||
|
```output
|
||||||
|
pi@raspberrypi:~/smart-timer $ python3 app.py
|
||||||
|
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.front
|
||||||
|
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
|
||||||
|
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
|
||||||
|
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
|
||||||
|
```
|
||||||
|
|
||||||
|
만약 다음의 오류가 발생하는 경우:
|
||||||
|
|
||||||
|
```output
|
||||||
|
OSError: [Errno -9997] Invalid sample rate
|
||||||
|
```
|
||||||
|
|
||||||
|
44100 또는 16000으로 `rate` 값을 변경합니다.
|
||||||
|
|
||||||
|
> 💁 이 코드는 [code-record/pi](../code-record/pi) 폴더에서 찾을 수 있습니다.
|
||||||
|
|
||||||
|
😀 오디오 녹음 프로그램이 성공적으로 끝났습니다!
|
@ -0,0 +1,557 @@
|
|||||||
|
# 언어의 이해
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> [Nitya Narasimhan](https://github.com/nitya)의 스케치 노트. 클릭하여 더 크게 보세요.
|
||||||
|
|
||||||
|
## 강의 전 퀴즈
|
||||||
|
|
||||||
|
[강의 전 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/43)
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
지난 수업에서 음성을 텍스트로 변환했습니다. 이것을 스마트 타이머를 프로그래밍하는 데 사용하기 위해선 코드가 말하는 내용을 이해해야 합니다. 사용자가 "3분 타이머 설정해줘"과 같은 정해진 문구를 말하고 해당 표현을 분석해서 타이머가 얼마나 설정되어야 할 지 알아낼 수 있을 것이라고 가정할 수 있지만 이는 사용자 친화적이지 않습니다. 사용자가 "타이머 3분으로 설정해줘"라고 말한다면 당신과 저는 그 말이 의미하는 바를 이해할 수 있지만 코드는 그렇지 않습니다. 코드는 정해진 문구를 기대합니다.
|
||||||
|
|
||||||
|
인공지능 모델을 사용하여 텍스트를 해석하고 필요한 세부 정보를 반환하는 언어 이해가 여기서 나옵니다. 예를들어 "3분 타이머 설정해줘"와 "타이머 3분으로 설정해줘"를 모두 사용할 수 있으며 3분의 타이머를 요구하는 것이라는 것을 이해할 수 있습니다.
|
||||||
|
|
||||||
|
이 수업에서는 언어 이해 모델에 대해 어떻게 그것을 생성하고 학습시키고 사용하는지 배웁니다.
|
||||||
|
|
||||||
|
이 수업에서는 다음을 다룹니다:
|
||||||
|
|
||||||
|
- [언어의 이해](#언어의-이해)
|
||||||
|
- [언어 이해 모델의 생성](#언어-이해-모델의-생성)
|
||||||
|
- [의도와 엔터티](#의도와-엔터티)
|
||||||
|
- [언어 이해 모델의 사용](#언어-이해-모델의-사용)
|
||||||
|
|
||||||
|
## 언어의 이해
|
||||||
|
|
||||||
|
인간은 수십만 년 동안 의사 소통을 위해 언어를 사용해 왔습니다. 우리는 단어, 소리 또는 행동으로 의사소통하고 말한 내용, 단어, 소리 또는 행동의 의미뿐만 아니라 그 맥락도 이해합니다. 우리는 목소리 톤에 따라 단어가 다른 의미를 갖는 것을 허용함으로서 진정성과 빈정거림을 구분합니다.
|
||||||
|
|
||||||
|
✅ 당신이 최근에 한 대화를 생각해보십시오. 컴퓨터가 맥락을 이해하기에 어려울 대화는 얼마나 되나요?
|
||||||
|
|
||||||
|
자연어 이해라고도 불리는 언어의 이해는 단어나 문장의 세부 사항을 이해하려고 노력하는 독해를 다루는 자연어 처리(또는 NLP)라는 인공지능 분야의 일부입니다. 알렉사나 시리와 같은 음성 비서를 사용하는 경우 당신은 언어 이해 서비스를 사용한 적이 있습니다. 이는 'Alexa, 테일러 스위프트 최신 앨범 틀어줘'를 내 딸이 좋아하는 곡에 맞춰 거실에서 춤을 추는 모습으로 바꿔주는 비하인드 AI 서비스들입니다.
|
||||||
|
|
||||||
|
> 💁 컴퓨터는 모든 발전에도 불구하고 텍스트를 정말로 이해하려면 아직 갈 길이 멉니다. 컴퓨터의 언어의 이해를 언급할 때 우리는 인간의 의사소통만큼 발전된 것을 의미하는 것이 아니라 단어를 취하고 주요 세부 사항을 추출하는 것을 의미합니다.
|
||||||
|
|
||||||
|
인간인 우리들은 언어를 깊이 생가하지 않고 이해합니다. 만약 제가 다른 사람에게 "테일러 스위프트의 최신 앨범 틀어줘"라고 한다면 그들은 본능적으로 제가 무슨 뜻으로 말한 것인지 알 것입니다. 컴퓨터에게 이것은 더욱 어렵습니다. 음성에서 텍스트로 변환된 단어를 가져와 다음 정보를 계산해야 합니다:
|
||||||
|
|
||||||
|
- 음악을 재생해야 합니다.
|
||||||
|
- 아티스트 테일러 스위프트가 음악을 맡았습니다.
|
||||||
|
- 특정 음악은 순서대로 여러 트랙의 전체 앨범입니다.
|
||||||
|
- 테일러 스위프트는 많은 앨범을 발매했으므로 시간순으로 정렬하여 가장 최근에 발매된 것이 필요합니다.
|
||||||
|
|
||||||
|
✅ 커피를 주문하거나 가족에게 무언가를 전달해 달라고 요청하는 것과 같이 요청을 할 때 사용했던 다른 문장을 생각해 보십시오. 컴퓨터가 문장을 이해하기 위해 추출해야하는 정보 조각들로 문장을 분리해보십시오.
|
||||||
|
|
||||||
|
언어 이해 모델은 작은 이미지 세트를 사용하여 Custom Vision 모델을 훈련한 것과 같은 방식으로 언어에서 특정 세부 정보를 추출하도록 훈련된 다음 전이 학습을 사용하여 특정 작업에 대해 훈련된 AI 모델입니다. 모델을 선택한 다음 이해하려는 텍스트를 사용하여 모델을 훈련시킬 수 있습니다.
|
||||||
|
|
||||||
|
## 언어 이해 모델의 생성
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Cognitive Services의 일부인 Microsoft의 언어 이해 서비스인 LUIS를 사용하여 언어 이해 모델을 만들 수 있습니다.
|
||||||
|
|
||||||
|
### 작업 - 제작 리소스 생성
|
||||||
|
|
||||||
|
LUIS를 사용하기 위해서는 제작 리소스를 만들어야 합니다.
|
||||||
|
|
||||||
|
1. 다음 명령어를 사용하여 `smart-timer` 그룹에 제작 리소스를 생성합니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
az cognitiveservices account create --name smart-timer-luis-authoring \
|
||||||
|
--resource-group smart-timer \
|
||||||
|
--kind LUIS.Authoring \
|
||||||
|
--sku F0 \
|
||||||
|
--yes \
|
||||||
|
--location <location>
|
||||||
|
```
|
||||||
|
|
||||||
|
`<location>`을 리소스 그룹을 만들 때 사용한 위치로 바꿉니다.
|
||||||
|
|
||||||
|
> ⚠️ LUIS는 모든 지역에서 사용 가능하지는 않습니다. 따라서 다음과 같은 오류 메세지가 뜬다면:
|
||||||
|
>
|
||||||
|
> ```output
|
||||||
|
> InvalidApiSetId: The account type 'LUIS.Authoring' is either invalid or unavailable in given region.
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> 다른 지역을 선택합니다.
|
||||||
|
|
||||||
|
이는 무료 등급의 LUIS 제작 리소스를 생성합니다.
|
||||||
|
|
||||||
|
### 업무 - 언어 이해 앱 만들기
|
||||||
|
|
||||||
|
1. 브라우저에서 [luis.ai](https://luis.ai?WT.mc_id=academic-17441-jabenn)의 LUIS 포털을 열고, Azure에서 사용한 계정과 동일한 계정으로 로그인합니다.
|
||||||
|
|
||||||
|
1. 대화 상자의 지침에 따라 Azure 구독을 선택한 다음, 생성한 `smart-timer-luis-authoring` 리소스를 선택합니다.
|
||||||
|
|
||||||
|
1. _대화 앱_ 목록에서 **새 앱** 버튼을 선택하여 새로운 어플리케이션을 생성합니다. 새 앱의 이름을 `smart-timer`로 명명하고 _Culture_ 를 해당 언어로 설정합니다.
|
||||||
|
|
||||||
|
> 💁 예측 리소스에 대한 필드가 있습니다. 예측을 위한 두 번째 리소스를 만들 수 있지만, 무료 제작 리소스는 한 달에 1,000개의 개발에 충분한 예측을 허용하므로 이 항목을 비워둘 수 있습니다.
|
||||||
|
|
||||||
|
1. 언어 이해 모델을 학습시키기 위해 수행해야 하는 단계를 이해하려면 앱을 만든 후 나타나는 가이드를 읽어보세요. 완료되면 해당 가이드를 닫습니다.
|
||||||
|
|
||||||
|
## 의도와 엔터티
|
||||||
|
|
||||||
|
언어의 이해는 _의도_ 와 _엔터티_ 를 기반으로 합니다. 의도는 단어가 의도하는 바를 말합니다. 예를 들면 음악 재생, 타이머 세팅, 음식 주문 등이 있습니다. 엔티티는 앨범, 타이머의 길이 또는 음식의 종류와 같이 의도가 참조하는 것을 말합니다. 모델이 이해하는 각각의 문장은 적어도 하나의 의도를 가져야 하고, 선택적으로 하나 또는 다수의 엔터티를 가질 수 있습니다.
|
||||||
|
|
||||||
|
예시:
|
||||||
|
|
||||||
|
| 문장 | 의도 | 엔터티 |
|
||||||
|
| ------------------------------------------------- | ------------- | --------------------------------------- |
|
||||||
|
| "Taylor Swift의 최신 앨범 재생해 줘" | _음악 재생_ | _Taylor Swift의 최신 앨범_ |
|
||||||
|
| "3분 타이머 맞춰줘" | _타이머 설정_ | _3분_ |
|
||||||
|
| "타이머 취소해줘" | _타이머 취소_ | 없음 |
|
||||||
|
| "파인애플 피자 라지 3판이랑 시저 샐러드 주문해줘" | _음식 주문_ | _파인애플 피자 라지 3판_, _시저 샐러드_ |
|
||||||
|
|
||||||
|
✅ 이전에 생각해보았던 문장에서 의도와 엔터티는 무엇인가요?
|
||||||
|
|
||||||
|
LUIS를 학습시키기 위해서는 먼저 엔터티를 설정합니다. 이는 용어의 고정된 목록이나 텍스트로부터 배운 것일 수 있습니다. 예를 들어 각 단어의 변형(또는 동의어)과 함께 메뉴에서 사용할 수 있는 고정된 음식 목록을 제공할 수 있습니다. LUIS에는 숫자와 위치와 같이 사용할 수 있는 미리 탑재된 엔터티도 있습니다.
|
||||||
|
|
||||||
|
타이머를 세팅하기 위해서, 시간에 대해 미리 작성된 숫자 엔터티를 사용하는 엔터티와 분이나 초와 같은 단위에 대해 각각의 다른 엔터티를 사용할 수 있습니다. 각 단위에는 minute과 minutes와 같은 단수형 및 복수형을 포함하는 여러 변형이 있습니다.
|
||||||
|
|
||||||
|
엔터티가 정의되면 의도를 생성합니다. 이는 당신이 제공하는 예문(발화)에 기반을 두고 학습합니다. 예를 들어, _타이머 세팅_ 이라는 의도에 대해 다음 문장들을 제공할 수 있습니다.
|
||||||
|
|
||||||
|
- `1초 타이머 설정`
|
||||||
|
- `타이머 1분 12초로 설정해줘`
|
||||||
|
- `타이머 3분으로 설정해줘`
|
||||||
|
- `9분 30초 타이머 설정해줘`
|
||||||
|
|
||||||
|
LUIS에게 이 문장들의 어떤 부분이 엔터티에 매핑되는지 알려줍니다:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
`타이머 1분 12초로 설정해줘`라는 문장은 `타이머 설정`이라는 의도를 가집니다. 또한 각각 2개의 값을 갖는 2개의 엔터티를 가집니다:
|
||||||
|
|
||||||
|
| | 시간 | 단위 |
|
||||||
|
| ---- | ---: | ---- |
|
||||||
|
| 1분 | 1 | 분 |
|
||||||
|
| 12초 | 12 | 초 |
|
||||||
|
|
||||||
|
좋은 모델을 훈련시키기 위해선 누군가가 동일한 것을 요구할 수 있는 여러 가지 방법을 다룰 수 있는 다양한 예문이 필요합니다.
|
||||||
|
|
||||||
|
> 💁 모든 AI 모델과 마찬가지로 훈련에 사용하는 데이터가 많고 정확할수록 모델이 더 좋아집니다.
|
||||||
|
|
||||||
|
✅ 같은 질문을 해서 인간이 이해하기를 기대하는 다른 방법들을 생각해보세요.
|
||||||
|
|
||||||
|
### 업무 - 언어 이해 모델에 엔터티 추가하기
|
||||||
|
|
||||||
|
타이머를 위해서는 2가지 엔터티가 필요합니다. 하나는 시간의 단위(분 또는 초)이고 다른 하나는 분이나 초의 숫자에 대한 것입니다.
|
||||||
|
|
||||||
|
LUIS 포털을 이용해서 [Quickstart: Build your app in LUIS portal documentation on Microsoft docs](https://docs.microsoft.com/azure/cognitive-services/luis/luis-get-started-create-app?WT.mc_id=academic-17441-jabenn)에서 지침을 찾을 수 있습니다.
|
||||||
|
|
||||||
|
1. LUIS 포털에서 _Entities_ 탭을 선택하고 **미리 빌드된 엔터티 추가**를 선택하여 미리 빌드된 엔터티 _number_ 를 추가합니다. 그런 다음 목록에서 _숫자_ 를 선택합니다.
|
||||||
|
|
||||||
|
1. **생성** 버튼을 사용하여 시간 단위를 위한 새 엔터티를 추가합니다. 엔터티의 이름을 `시간 단위`로 짓고 해당 타입을 _목록_ 에 세팅합니다. _정규화된 값_ 목록에 `분`과 `초`의 값을 추가합니다. 단수형과 복수형을 _동의어_ 목록에 추가합니다. 각각의 동의어를 추가한 뒤에 `return`(엔터키)을 누르면 목록에 추가됩니다.
|
||||||
|
|정규화된 값| 동의어|
|
||||||
|
|--------|-----|
|
||||||
|
|분 |minute, minutes|
|
||||||
|
|초 |second, seconds|
|
||||||
|
|
||||||
|
### 업무 - 언어 이해 모델에 의도 추가하기
|
||||||
|
|
||||||
|
1. _의도_ 탭에서 **생성** 버튼을 눌러 새로운 의도를 추가합니다. 이 의도를 `타이머 설정`이라고 이름 짓습니다.
|
||||||
|
|
||||||
|
1. 예시에서 분, 초 및 분과 초를 결합하여 타이머를 설정하는 다양한 방법을 입력합니다. 예를 들면 다음과 같습니다.
|
||||||
|
|
||||||
|
- `1초 타이머 설정`
|
||||||
|
- `4분 타이머 설정`
|
||||||
|
- `타이머 사분 육초로 설정해줘`
|
||||||
|
- `9분 30초 타이머 설정해줘`
|
||||||
|
- `타이머 1분 12초로 설정해줘`
|
||||||
|
- `타이머 3분으로 설정해줘`
|
||||||
|
- `타이머 3분하고 1초로 설정해줘`
|
||||||
|
- `삼분 일초로 타이머 설정해줘`
|
||||||
|
- `1분 1초 타이머 설정`
|
||||||
|
- `타이머 30초로 설정해줘`
|
||||||
|
- `1초 타이머 설정`
|
||||||
|
|
||||||
|
모델이 두 가지 모두를 처리하는 방법을 학습하도록 숫자를 단어와 숫자로 혼합합니다.
|
||||||
|
|
||||||
|
1. 각 예시를 입력하면 LUIS는 엔터티를 검색하기 시작하고 찾은 항목에 밑줄을 긋고 레이블을 지정합니다.
|
||||||
|

|
||||||
|
|
||||||
|
### 업무 - 모델을 학습시키고 테스트하기
|
||||||
|
|
||||||
|
1. 엔터티와 의도가 구성이 되고나면 상단 메뉴의 **Train** 버튼을 이용하여 모델을 학습시킬 수 있습니다. 이 버튼을 선택하면 모델은 몇 초간 학습합니다. 학습 중에는 버튼이 회색으로 표시되며, 학습이 완료되면 다시 활성화됩니다.
|
||||||
|
|
||||||
|
1. 상단 메뉴의 **Test** 버튼을 선택하여 언어 이해 모델을 테스트합니다. `타이머 5분 4초로 설정해줘` 와 같은 텍스트를 입력한 뒤 엔터키를 누릅니다. 입력한 텍스트 상자 아래의 상자에 해당 문장이 나타나며 _상위 의도_ 또는 가장 높은 가능성으로 판별된 의도가 나타납니다. 이것은 `set timer`이어야 합니다. 인텐트 이름 뒤에는 감지된 인텐트가 맞을 확률이 표시됩니다.
|
||||||
|
|
||||||
|
1. 결과의 분석을 보기 위해서는 **점검** 옵션을 선택합니다. 탐지된 엔터티 목록과 함께 백분율 확률과 함께 가장 높은 점수를 얻은 의도가 표시됩니다.
|
||||||
|
|
||||||
|
1. 테스트를 마치면 _Test_ 창을 닫습니다.
|
||||||
|
|
||||||
|
### 업무 - 모델을 게시하기
|
||||||
|
|
||||||
|
코드에서 이 모델을 사용하기 위해서는 이를 게시해야 합니다. LUIS에서 게시할 때 테스트용 스테이징 환경이나 전체 릴리즈용 제품 환경을 게시할 수 있습니다. 이 수업에서는 스테이징 환경이 좋습니다.
|
||||||
|
|
||||||
|
1. LUIS 포털에서 상단 메뉴에서 **게시하기** 버튼을 선택합니다.
|
||||||
|
|
||||||
|
1. _Staging slot_ 이 선택되었는지 확인하고 **완료**를 선택합니다. 앱이 게시되면 알림이 표시됩니다.
|
||||||
|
|
||||||
|
1. curl을 사용해서 이를 테스트할 수 있습니다. curl 명령을 빌드하기위해서는 엔드포인트, 애플리케이션 IP(앱 ID), API 키 3개의 값이 필요합니다. 이는 상단 메뉴에서 선택할 수 있는 **관리** 탭에서 접근할 수 있습니다.
|
||||||
|
|
||||||
|
1. **Settings** 섹션에서 App ID를 복사합니다.
|
||||||
|
|
||||||
|
1. _Azure Resources_ 섹션에서 _Authoring Resource_ 를 선택하고 _기본 키_ 와 _엔드포인트 URL_ 을 복사합니다.
|
||||||
|
|
||||||
|
1. 다음의 curl 명령어를 명령 프롬프트 또는 터미널에서 실행합니다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl "<endpoint url>/luis/prediction/v3.0/apps/<app id>/slots/staging/predict" \
|
||||||
|
--request GET \
|
||||||
|
--get \
|
||||||
|
--data "subscription-key=<primary key>" \
|
||||||
|
--data "verbose=false" \
|
||||||
|
--data "show-all-intents=true" \
|
||||||
|
--data-urlencode "query=<sentence>"
|
||||||
|
```
|
||||||
|
|
||||||
|
`<endpoint url>`를 _Azure Resources_ 섹션에서의 엔드포인트 URL로 바꿉니다.
|
||||||
|
|
||||||
|
`<app id>` 를 _Settings_ 섹션에서의 App ID로 바꿉니다.
|
||||||
|
|
||||||
|
`<primary key>`를 _Azure Resources_ 섹션의 기본 키로 바꿉니다.
|
||||||
|
|
||||||
|
`<sentence>`를 테스트하고자 하는 문장으로 바꿉니다.
|
||||||
|
|
||||||
|
1. 이 호출의 출력은 쿼리, 상위에 있는 의도와 유형별로 분류된 엔터티 목록을 자세히 설명하는 JSON 문서입니다.
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
"query": "set a timer for 45 minutes and 12 seconds",
|
||||||
|
"prediction": {
|
||||||
|
"topIntent": "set timer",
|
||||||
|
"intents": {
|
||||||
|
"set timer": {
|
||||||
|
"score": 0.97031575
|
||||||
|
},
|
||||||
|
"None": {
|
||||||
|
"score": 0.02205793
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"entities": {
|
||||||
|
"number": [
|
||||||
|
45,
|
||||||
|
12
|
||||||
|
],
|
||||||
|
"time-unit": [
|
||||||
|
[
|
||||||
|
"minute"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"second"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
상단의 JSON은 `set a timer for 45 minutes and 12 seconds`을 사용한 쿼리의 결과입니다.
|
||||||
|
|
||||||
|
- `set timer`는 97%의 가능성을 가진 상위 의도입니다.
|
||||||
|
- `45` 와 `12` 두 개의 _숫자_ 엔터티가 감지되었습니다.
|
||||||
|
- `분` 과 `초` 두 개의 _시간-단위_ 엔터티가 감지되었습니다.
|
||||||
|
|
||||||
|
## 언어 이해 모델의 사용
|
||||||
|
|
||||||
|
게시가 되고 나면 LUIS 모델을 코드에서 호출할 수 있습니다. 이전 수업에서 IoT Hub를 사용하여 클라우드 서비스와의 통신을 처리하고 원격 분석을 보내고 명령을 수신했습니다. 이는 매우 비동기적입니다. 일단 원격 측정이 전송되면 코드가 응답을 기다리지 않으며 만약 클라우드 서비스가 다운되더라도 알 수 없습니다.
|
||||||
|
|
||||||
|
스마트 타이머의 경우 사용자에게 타이머가 설정되었음을 알리거나 클라우드 서비스를 사용할 수 없다고 경고할 수 있도록 즉시 응답을 원합니다. 이를 위해 IoT 장치는 IoT Hub에 의존하는 대신 웹의 엔드포인트를 직접 호출합니다.
|
||||||
|
|
||||||
|
IoT 장치에서 LUIS를 호출하는 대신에 다른 유형의 트리거(HTTP 트리거)와 함께 서버리스 코드를 사용할 수 있습니다. 이렇게 하면 함수 앱이 REST 요청을 수신 대기하고 이에 응답할 수 있습니다. 이 함수는 장치가 호출할 수 있는 REST 엔드포인트가 됩니다.
|
||||||
|
|
||||||
|
> 💁 IoT 디바이스에서 직접 LUIS를 호출할 수 있지만 서버리스 코드를 사용하는 것이 좋습니다. 이렇게 하면 호출하는 LUIS 앱을 변경하려는 경우(예: 더 나은 모델을 학습시키거나 다른 언어로 모델을 학습시키는 경우) 잠재적으로 수천 또는 수백만 개의 IoT 장치에 코드를 재배포하지 않고 클라우드 코드만 업데이트하면 됩니다.
|
||||||
|
|
||||||
|
### 업무 - 서버리스 함수 앱 만들기
|
||||||
|
|
||||||
|
1. `smart-timer-trigger`의 이름으로 Azure Function 앱을 생성하고 이를 VS Code에서 엽니다.
|
||||||
|
|
||||||
|
1. VS Code의 터미널에서 아래의 명령을 사용하여 `speech-trigger` HTTP 트리거를 추가합니다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
func new --name text-to-timer --template "HTTP trigger"
|
||||||
|
```
|
||||||
|
|
||||||
|
이는 `text-to-timer`라는 HTTP 트리거를 생성합니다.
|
||||||
|
|
||||||
|
1. functions 앱을 실행하여 HTTP 트리거를 테스트합니다. 실행하면 출력에 나열된 엔드포인트를 볼 수 있습니다.
|
||||||
|
|
||||||
|
```output
|
||||||
|
Functions:
|
||||||
|
|
||||||
|
text-to-timer: [GET,POST] http://localhost:7071/api/text-to-timer
|
||||||
|
```
|
||||||
|
|
||||||
|
URL [http://localhost:7071/api/text-to-timer](http://localhost:7071/api/text-to-timer)을 브라우저에서 로드하여 이를 테스트 합니다.
|
||||||
|
|
||||||
|
```output
|
||||||
|
This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 업무 - 언어 이해 모델 사용하기
|
||||||
|
|
||||||
|
1. LUIS용 SDK는 Pip 패키지를 통해 사용이 가능합니다. `requirements.txt` 파일에 다음 줄을 추가하여 이 패키지에 대한 종속성을 추가합니다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
azure-cognitiveservices-language-luis
|
||||||
|
```
|
||||||
|
|
||||||
|
1. VS Code 터미널에 가상 환경이 활성화되어 있는지 확인하고 다음 명령을 실행하여 Pip 패키지를 설치합니다:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
> 💁 오류가 난다면 다음 명령어를 통해 pip를 업그레이드 해야합니다:
|
||||||
|
>
|
||||||
|
> ```sh
|
||||||
|
> pip install --upgrade pip
|
||||||
|
> ``
|
||||||
|
> ```
|
||||||
|
|
||||||
|
1. LUIS 포털의 **관리** 탭의 LUIS API Key, Endpoint URL, App ID를 위한 새 엔트리를 `local.settings.json` 파일에 추가합니다:
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
"LUIS_KEY": "<primary key>",
|
||||||
|
"LUIS_ENDPOINT_URL": "<endpoint url>",
|
||||||
|
"LUIS_APP_ID": "<app id>"
|
||||||
|
```
|
||||||
|
|
||||||
|
`<endpoint url>` 를 _Azure Resources_ 섹션의 **MANAGE** 탭에서의 앤드포인트 URL로 바꿉니다. 이는 `https://<location>.api.cognitive.microsoft.com/`일 것입니다.
|
||||||
|
|
||||||
|
`<app id>`를 _Settings_ 섹션의 **관리** 탭의 App ID로 변경합니다.
|
||||||
|
|
||||||
|
`<primary key>` 를 _Azure Resources_ 섹션의 **MANAGE** 탭의 기본 키로 변경합니다.
|
||||||
|
|
||||||
|
1. 다음의 import문들을 `__init__.py` 파일에 추가합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from azure.cognitiveservices.language.luis.runtime import LUISRuntimeClient
|
||||||
|
from msrest.authentication import CognitiveServicesCredentials
|
||||||
|
```
|
||||||
|
|
||||||
|
이는 몇몇 LUIS와 상호작용하는 시스템 라이브러리를 import합니다.
|
||||||
|
|
||||||
|
1. `main` 메서드의 내용을 삭제하고 다음 코드를 추가합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
luis_key = os.environ['LUIS_KEY']
|
||||||
|
endpoint_url = os.environ['LUIS_ENDPOINT_URL']
|
||||||
|
app_id = os.environ['LUIS_APP_ID']
|
||||||
|
|
||||||
|
credentials = CognitiveServicesCredentials(luis_key)
|
||||||
|
client = LUISRuntimeClient(endpoint=endpoint_url, credentials=credentials)
|
||||||
|
```
|
||||||
|
|
||||||
|
그러면 LUIS 앱의 `local.settings.json` 파일에 추가한 값이 로드되고 API 키로 자격 증명 개체가 생성된 다음 LUIS 앱과 상호 작용할 LUIS 클라이언트 개체가 생성됩니다.
|
||||||
|
|
||||||
|
1. 이 HTTP 트리거는 텍스트를 JSON으로 이해하기 위해 `text`라는 속성에 있는 텍스트를 전달하는 것으로 호출됩니다. 다음 코드는 HTTP 요청의 본문에서 값을 추출하여 콘솔에 기록합니다. 다음 코드를 `main` 함수에 추가합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
req_body = req.get_json()
|
||||||
|
text = req_body['text']
|
||||||
|
logging.info(f'Request - {text}')
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 예측 요청(예측할 텍스트가 포함된 JSON 문서)을 전송하여 LUIS에서 예측을 요청합니다. 다음 코드를 사용하여 생성합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
prediction_request = { 'query' : text }
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 앱이 게시된 스테이징 슬롯을 사용하여 이 요청을 LUIS로 보낼 수 있습니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
prediction_response = client.prediction.get_slot_prediction(app_id, 'Staging', prediction_request)
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 예측 응답에는 엔터티와 함께 최상위 의도(예측 점수가 가장 높은 의도)가 포함됩니다. 최상위 의도가 `set timer`인 경우 엔티티를 읽어 타이머에
|
||||||
|
필요한 시간을 얻을 수 있습니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
if prediction_response.prediction.top_intent == 'set timer':
|
||||||
|
numbers = prediction_response.prediction.entities['number']
|
||||||
|
time_units = prediction_response.prediction.entities['time unit']
|
||||||
|
total_seconds = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
`number` 엔터티는 숫자 배열입니다. 예를 들어 _"Set a four minute 17 second timer."_ 라고 말한다면 `number` 배열은 4와 17 2개의 단일 값을 포함할 것입니다.
|
||||||
|
|
||||||
|
`time unit` 배열은 문자열 배열이며 각 시간 단위는 배열 내부의 문자열 배열입니다. 예를 들어 _"Set a four minute 17 second timer."_ 라고 말한다면 `time unit` 배열은 각각 `['minute']` 과 `['second']` 의 단일 값을 가지는 두 배열을 포함할 것입니다.
|
||||||
|
|
||||||
|
_"Set a four minute 17 second timer."_ 에 대한 이러한 엔티티의 JSON 버전은 다음과 같습니다:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"number": [4, 17],
|
||||||
|
"time unit": [["minute"], ["second"]]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
이 코드는 또한 타이머의 총 시간을 초 단위로 정의합니다. 이는 엔터티의 값으로 채워집니다.
|
||||||
|
|
||||||
|
1. 엔터티는 연결되어 있지 않지만 우리는 이에 대해 몇 가지 가정을 할 수 있습니다. 말한 순서대로 나열되기 때문에 배열의 위치를 사용하여 어떤 숫자가 어떤 시간 단위와 일치하는지 확인할 수 있습니다. 예를 들어:
|
||||||
|
|
||||||
|
- _"Set a 30 second timer"_ - 이는 하나의 숫자 `30`을 가지고 하나의 시간 단위 `second`을 가집니다, 따라서 하나의 숫자가 하나의 시간 단위와 짝을 이룹니다.
|
||||||
|
- _"Set a 2 minute and 30 second timer"_ - 이는 `2` 와 `30` 2개의 숫자를 가집니다. 또한 `minute`과 `second`의 두 개의 시간 단위를 가집니다. 따라서 첫 번째 숫자는 첫 번째 시간 단위와 짝을 이루고(2분), 두 번째 숫자는 두 번째 시간 단위와 짝을 이룰 것입니다(30초).
|
||||||
|
|
||||||
|
다음 코드는 숫자 엔터티의 항목 수를 가져오고 이를 사용하여 각 배열에서 첫 번째 항목을 추출한 다음 두 번째 항목을 추출하는 코드입니다. 이를 `if` 블록 안에 추가합니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
for i in range(0, len(numbers)):
|
||||||
|
number = numbers[i]
|
||||||
|
time_unit = time_units[i][0]
|
||||||
|
```
|
||||||
|
|
||||||
|
_"Set a four minute 17 second timer."_ 에 대하여 이 코드는 루프를 두번 돌아 다음과 같은 값을 제공합니다:
|
||||||
|
| 반복 횟수 | `number` | `time_unit` |
|
||||||
|
| ---------: | -------: | ----------- |
|
||||||
|
| 0 | 4 | minute |
|
||||||
|
| 1 | 17 | second |
|
||||||
|
|
||||||
|
1. 이 루프 내에서 숫자와 시간 단위를 사용하여 타이머의 총 시간(분당 60초, 초당 초 수)을 계산합니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
if time_unit == 'minute':
|
||||||
|
total_seconds += number * 60
|
||||||
|
else:
|
||||||
|
total_seconds += number
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 엔티티를 통과하는 이 루프 외부에서 타이머의 총 시간을 로그로 남깁니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
logging.info(f'Timer required for {total_seconds} seconds')
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 함수에서 HTTP 응답으로 반환해야 하는 시간(초)입니다. `if` 블럭의 끝 부분에 다음을 추가합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
payload = {
|
||||||
|
'seconds': total_seconds
|
||||||
|
}
|
||||||
|
return func.HttpResponse(json.dumps(payload), status_code=200)
|
||||||
|
```
|
||||||
|
|
||||||
|
이 코드는 타이머의 총 시간(초)이 포함된 페이로드를 생성하고, 이를 JSON 문자열로 변환한 다음 호출이 성공했음을 의미하는 상태 코드 200과 함께 HTTP 결과로 반환합니다.
|
||||||
|
|
||||||
|
1. 마지막으로 `if` 블럭의 밖에서 오류 코드를 반환하여 의도가 인식되지 않은 경우를 처리합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
return func.HttpResponse(status_code=404)
|
||||||
|
```
|
||||||
|
|
||||||
|
404는 _not found_ 에 대한 상태 코드입니다.
|
||||||
|
|
||||||
|
1. function app을 실행하고 curl을 사용하여 이를 테스트합니다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl --request POST 'http://localhost:7071/api/text-to-timer' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--include \
|
||||||
|
--data '{"text":"<text>"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
`<text>` 를 `set a 2 minutes 27 second timer`와 같은 요청한 텍스트로 바꿉니다.
|
||||||
|
|
||||||
|
function app에서 다음과 같은 결과를 볼 수 있습니다:
|
||||||
|
|
||||||
|
```output
|
||||||
|
Functions:
|
||||||
|
|
||||||
|
text-to-timer: [GET,POST] http://localhost:7071/api/text-to-timer
|
||||||
|
|
||||||
|
For detailed output, run func with --verbose flag.
|
||||||
|
[2021-06-26T19:45:14.502Z] Worker process started and initialized.
|
||||||
|
[2021-06-26T19:45:19.338Z] Host lock lease acquired by instance ID '000000000000000000000000951CAE4E'.
|
||||||
|
[2021-06-26T19:45:52.059Z] Executing 'Functions.text-to-timer' (Reason='This function was programmatically called via the host APIs.', Id=f68bfb90-30e4-47a5-99da-126b66218e81)
|
||||||
|
[2021-06-26T19:45:53.577Z] Timer required for 147 seconds
|
||||||
|
[2021-06-26T19:45:53.746Z] Executed 'Functions.text-to-timer' (Succeeded, Id=f68bfb90-30e4-47a5-99da-126b66218e81, Duration=1750ms)
|
||||||
|
```
|
||||||
|
|
||||||
|
curl 호출은 다음을 반환합니다:
|
||||||
|
|
||||||
|
```output
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Date: Tue, 29 Jun 2021 01:14:11 GMT
|
||||||
|
Content-Type: text/plain; charset=utf-8
|
||||||
|
Server: Kestrel
|
||||||
|
Transfer-Encoding: chunked
|
||||||
|
|
||||||
|
{"seconds": 147}
|
||||||
|
```
|
||||||
|
|
||||||
|
타이머가 설정되어야 하는 시간은 `"seconds"` 단위입니다.
|
||||||
|
|
||||||
|
> 💁 이 코드는 [code/functions](../code/functions) 폴더에서 찾을 수 있습니다.
|
||||||
|
|
||||||
|
### 업무 - function을 IoT 장치에서 사용할 수 있도록 하기
|
||||||
|
|
||||||
|
1. IoT 장치가 REST 엔드포인트를 호출하려면 URL을 알아야 합니다. 이전에 이에 액세스 했을 때에는 로컬 시스템에서 REST 끝점에 액세스하기 위한 바로 가기인 `localhost`을 사용했습니다. IoT 장치가 액세스할 수 있도록 하려면 클라우드에 게시하거나 IP 주소를 가져와 로컬에서 액세스해야 합니다.
|
||||||
|
|
||||||
|
> ⚠️ Wio Terminal을 사용하는 경우 이전과 동일한 방식으로 함수 앱을 배포할 수 없음을 의미하는 라이브러리에 대한 종속성이 있습니다. 따라서 로컬에서 함수 앱을 실행하는 것이 더 쉽습니다. function 앱을 로컬에서 실행하고 컴퓨터의 IP 주소를 통해 액세스합니다. 클라우드에 배포하려는 경우 이를 수행하는 방법에 대한 이후 학습에서 정보가 제공됩니다.
|
||||||
|
|
||||||
|
- 기능 앱을 게시하기 - 이전 레슨의 지침에 따라 기능 앱을 클라우드에 게시합니다. 게시되고 나면 URL은 `https://<APP_NAME>.azurewebsites.net/api/text-to-timer`이 되고 `<APP_NAME>`은 당신의 기능 앱 이름을 의미합니다. 로컬 설정에서도 게시되었는지 확인하십시오.
|
||||||
|
|
||||||
|
HTTP 트리거를 사용하여 작업할 때 기본적으로 기능 앱 키로 보호됩니다. 이 키를 가져오기 위해선 다음 명령을 실행합니다:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
az functionapp keys list --resource-group smart-timer \
|
||||||
|
--name <APP_NAME>
|
||||||
|
```
|
||||||
|
|
||||||
|
`functionKeys`에서 `default` 엔트리의 값을 복사합니다.
|
||||||
|
|
||||||
|
```output
|
||||||
|
{
|
||||||
|
"functionKeys": {
|
||||||
|
"default": "sQO1LQaeK9N1qYD6SXeb/TctCmwQEkToLJU6Dw8TthNeUH8VA45hlA=="
|
||||||
|
},
|
||||||
|
"masterKey": "RSKOAIlyvvQEQt9dfpabJT018scaLpQu9p1poHIMCxx5LYrIQZyQ/g==",
|
||||||
|
"systemKeys": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
이 키가 쿼리 매개변수에 추가되어야 하므로 최종 URL은 `https://<APP_NAME>.azurewebsites.net/api/text-to-timer?code=<FUNCTION_KEY>`입니다. 여기서 `<APP_NAME>`이 functions 앱의 이름이 되고 `<FUNCTION_KEY>`가 기본 기능 키가 됩니다.
|
||||||
|
|
||||||
|
> 💁 `function.json` 파일의 `authlevel` 설정을 사용하여 HTTP 트리거의 인증 유형을 변경할 수 있습니다. 이에 대해 더 자세히 알기 위해선 [configuration section of the Azure Functions HTTP trigger documentation on Microsoft docs](https://docs.microsoft.com/azure/azure-functions/functions-bindings-http-webhook-trigger?WT.mc_id=academic-17441-jabenn&tabs=python#configuration)를 참고하십시오.
|
||||||
|
|
||||||
|
- functions 앱을 로컬에서 실행하고 IP 주소를 사용하여 액세스합니다. 로컬 네트워크에서 IP 주소를 가져와 URL을 만드는 데 사용할 수 있습니다.
|
||||||
|
|
||||||
|
IP 주소 찾기:
|
||||||
|
|
||||||
|
- Windows 10에서는 [find your IP address guide](https://support.microsoft.com/windows/find-your-ip-address-f21a9bbc-c582-55cd-35e0-73431160a1b9?WT.mc_id=academic-17441-jabenn)를 따릅니다.
|
||||||
|
|
||||||
|
- macOS에서는 [how to find you IP address on a Mac guide](https://www.hellotech.com/guide/for/how-to-find-ip-address-on-mac)를 따릅니다.
|
||||||
|
|
||||||
|
- Linux에서는 [how to find your IP address in Linux guide](https://opensource.com/article/18/5/how-find-ip-address-linux)에서 개인 IP 주소 찾기 섹션을 따릅니다.
|
||||||
|
|
||||||
|
IP 주소가 있으면, `http://<IP_ADDRESS>:7071/api/text-to-timer`에 있는 함수에 접근할 수 있습니다. `<IP_ADDRESS>` 는 당신의 IP 주소입니다. 예를 들면 `http://192.168.1.10:7071/api/text-to-timer`같이 사용할 수 있습니다.
|
||||||
|
|
||||||
|
> 💁 7071 포트를 사용하는 것이 아니라, IP 주소 뒤에`:7071`이 필요합니다.
|
||||||
|
|
||||||
|
> 💁 이것은 IoT 장치가 컴퓨터와 동일한 네트워크에 있는 경우에만 작동합니다.
|
||||||
|
|
||||||
|
1. curl을 사용하여 엔드포인트에 액세스하여 엔드포인트를 테스트합니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 도전 과제
|
||||||
|
|
||||||
|
타이머를 세팅하는 것과 같이 동일한 것을 요청하는 데는 많은 방법이 있습니다. 이를 수행하는 다양한 방법을 생각하고 LUIS 앱에서 예로 사용해봅시다. 이를 테스트하여 모델이 타이머를 요청하는 여러 방법에 얼마나 잘 대처할 수 있는지 확인합니다.
|
||||||
|
|
||||||
|
## 강의 후 퀴즈
|
||||||
|
|
||||||
|
[강의 후 퀴즈](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/44)
|
||||||
|
|
||||||
|
## 복습 & 자습
|
||||||
|
|
||||||
|
- LUIS 및 해당 기능에 대해 [Language Understanding (LUIS) documentation page on Microsoft docs](https://docs.microsoft.com/azure/cognitive-services/luis/?WT.mc_id=academic-17441-jabenn)에서 더 자세히 읽어보세요.
|
||||||
|
- [natural-language understanding page on Wikipedia](https://wikipedia.org/wiki/Natural-language_understanding)에서 언어 이해에 대해 더 알아보세요.
|
||||||
|
- HTTP 트리거에 대해 [Azure Functions HTTP trigger documentation on Microsoft docs](https://docs.microsoft.com/azure/azure-functions/functions-bindings-http-webhook-trigger?WT.mc_id=academic-17441-jabenn&tabs=python)에서 더 알아보세요.
|
||||||
|
|
||||||
|
## 과제
|
||||||
|
|
||||||
|
[타이머 취소](assignment.ko.md)
|
@ -0,0 +1,14 @@
|
|||||||
|
# 타이머 취소
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
지금까지 이 단원에서는 타이머 설정을 이해하도록 모델을 훈련했습니다. 또 다른 유용한 기능은 타이머를 취소하는 것입니다. 빵이 준비가 되었다면 타이머가 경과하기 전에 오븐에서 꺼낼 수 있습니다.
|
||||||
|
|
||||||
|
LUIS 앱에 새 의도를 추가하여 타이머를 취소합니다. 엔터티가 필요하지 않지만 몇 가지 예문이 필요합니다. 최상위 의도인 경우 서버리스 코드에서 이를 처리하고 의도가 인식되었음을 기록하고 적절한 응답을 반환합니다.
|
||||||
|
|
||||||
|
## 평가 기준
|
||||||
|
|
||||||
|
| 기준 | 모범 답안 | 적절함 | 개선이 필요함 |
|
||||||
|
| ------------------------------- | --------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------- |
|
||||||
|
| LUIS 앱에 취소 타이머 의도 추가 | 의도를 추가하고 모델을 교육할 수 있었습니다. | 인텐트를 추가할 수 있었지만 모델을 교육할 수 없었습니다. | 인텐트를 추가하고 모델을 교육할 수 없습니다. |
|
||||||
|
| 서버리스 앱에서 인텐트 처리 | 의도를 최상위 의도로 감지하고 기록할 수 있었습니다. | 의도를 최상위 인텐트로 감지할 수 있었습니다. | 의도를 최상위 의도로 감지할 수 없습니다. |
|
@ -0,0 +1,17 @@
|
|||||||
|
# 범용 번역기 구축하기
|
||||||
|
|
||||||
|
## 지침
|
||||||
|
|
||||||
|
범용 번역기는 여러 언어를 번역할 수 있는 장치로, 다른 언어를 사용하는 사람들이 의사소통할 수 있도록 합니다. 지난 수업에서 배운 내용을 사용하여 2개의 IoT 장치를 사용하여 범용 번역기를 구축하십시오.
|
||||||
|
|
||||||
|
> 2개의 장치가 없는 경우, 이전 수업의 단계에 따라 가상 IoT 장치를 하나의 IoT 장치처럼 설정합니다.
|
||||||
|
|
||||||
|
하나의 언어에 대해 하나의 장치를 구성하고 다른 언어에 대해 또다른 장치 하나를 구성해야 합니다. 각각의 장치는 음성을 받아들이고, 텍스트로 변환하고, IoT Hub 와 Functions 앱을 통해 다른 장치로 보낸 다음, 그것을 번역하여 음성으로 재생합니다.
|
||||||
|
|
||||||
|
> 💁 팁: 장치에서 다른 장치로 음성을 보낼 때, 사용중인 언어도 함께 보내어 번역하기 쉽도록 합니다. 먼저 IoT Hub 와 Functions 앱을 사용하여 각 장치를 등록하고, Azure Storage에 저장히기 위해 지원하는 언어를 전달할 수도 있습니다. 그런 다음 번역을 위해 Functions 앱을 사용하고, IoT 장치로 번역된 텍스트를 전송할 수 있습니다.
|
||||||
|
|
||||||
|
## 평가 기준
|
||||||
|
|
||||||
|
| 기준 | 모범 답안 | 적절함 | 개선이 필요함 |
|
||||||
|
| ------------------ | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | -------------------------------------- |
|
||||||
|
| 범용 번역기 만들기 | 한 장치에서 감지된 음성을 다른 장치에서 다른 언어로 변환되어 재생되는 범용 번역기를 구축할 수 있음 | 캡처된 음성 또는 번역과 같은 일부 구성 요소는 번역할 수 있지만, 모든 문제를 해결할 수 없음 | 범용 번역기의 어떤 부분도 만들 수 없음 |
|
@ -0,0 +1,112 @@
|
|||||||
|
# 음성을 텍스트로 변환하기 - Raspberry Pi
|
||||||
|
|
||||||
|
이 단원에서는 음성 서비스를 사용하여 캡처한 오디오의 음성을 텍스트로 변환하는 코드를 작성합니다.
|
||||||
|
|
||||||
|
## 음성 서비스로 오디오 보내기
|
||||||
|
|
||||||
|
REST API를 사용하여 음성 서비스를 오디오로 보낼 수 있습니다. 음성 서비스를 사용하려면, 먼저 접근 토큰을 요청한 다음, 해당 토큰을 사용하여 REST API에 접근합니다. 이러한 접근 토큰은 10분 후에 만료되므로, 항상 최신의 상태인지 확인하기 위해 코드에서 정기적으로 요청해야 합니다.
|
||||||
|
|
||||||
|
### 작업 - 접근 토큰 가져오기
|
||||||
|
|
||||||
|
1. Pi에서 `smart-timer` 프로젝트를 엽니다.
|
||||||
|
|
||||||
|
2. `play_audio` 함수를 제거합니다. 스마트 타이머가 당신이 말한 것을 다시 반복하는 것을 원하지 않기 때문에 더이상 필요하지 않습니다.
|
||||||
|
|
||||||
|
3. `app.py` 파일의 맨 위에 다음 import 를 추가합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 음성 서비스에 대한 일부 설정을 위해 `while True` 루프 위에 다음 코드를 추가합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
speech_api_key = '<key>'
|
||||||
|
location = '<location>'
|
||||||
|
language = '<language>'
|
||||||
|
```
|
||||||
|
|
||||||
|
`<key>` 를 음성 서비스 리소스의 API 키로 바꿉니다. `<location>` 를 음성 서비스 리소스를 만들 때 사용한 위치로 바꿉니다.
|
||||||
|
|
||||||
|
`<language>` 를 당신이 사용할 언어의 지역 이름으로 바꿉니다. 예를 들어 영어는 `en-GB`, 광둥어는 `zn-HK`로 표기합니다. 지원되는 언어 목록과 해당 지역 이름은 [Language and voice support documentation on Microsoft docs](https://docs.microsoft.com/azure/cognitive-services/speech-service/language-support?WT.mc_id=academic-17441-jabenn#speech-to-text) 에서 찾을 수 있습니다.
|
||||||
|
|
||||||
|
5. 이 아래에, 다음 함수를 추가하여 접근 토큰을 가져옵니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_access_token():
|
||||||
|
headers = {
|
||||||
|
'Ocp-Apim-Subscription-Key': speech_api_key
|
||||||
|
}
|
||||||
|
|
||||||
|
token_endpoint = f'https://{location}.api.cognitive.microsoft.com/sts/v1.0/issuetoken'
|
||||||
|
response = requests.post(token_endpoint, headers=headers)
|
||||||
|
return str(response.text)
|
||||||
|
```
|
||||||
|
|
||||||
|
이것은 토큰 isuuing endpoint 를 호출해, API 키를 헤더처럼 전달합니다. 이 호출은 음성 서비스를 호출하는데 사용되는 접근 토큰을 반환합니다.
|
||||||
|
|
||||||
|
6. 이 아래에, REST API를 사용해 캡쳐된 오디오의 음성을 텍스트로 변환하는 함수를 선언합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def convert_speech_to_text(buffer):
|
||||||
|
```
|
||||||
|
|
||||||
|
7. 이 함수 내에서, REST API URL과 헤더를 설정합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
url = f'https://{location}.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1'
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'Authorization': 'Bearer ' + get_access_token(),
|
||||||
|
'Content-Type': f'audio/wav; codecs=audio/pcm; samplerate={rate}',
|
||||||
|
'Accept': 'application/json;text/xml'
|
||||||
|
}
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'language': language
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
이것은 음성 서비스 리소스의 위치를 사용하여 URL을 빌드합니다. 그런 다음 `get_access_token` 함수의 접근 토큰과 오디오 캡처에 사용되는 샘플 rate으로 헤더를 채웁니다. 마지막으로 URL과 함께 전달할 오디오의 언어가 포함된 일부 매개변수를 정의합니다.
|
||||||
|
|
||||||
|
8. 이 아래에, 다음 코드를 추가하여 REST API를 호출하고 텍스트를 다시 가져옵니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
response = requests.post(url, headers=headers, params=params, data=buffer)
|
||||||
|
response_json = response.json()
|
||||||
|
|
||||||
|
if response_json['RecognitionStatus'] == 'Success':
|
||||||
|
return response_json['DisplayText']
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
```
|
||||||
|
|
||||||
|
이는 URL을 호출하고 응답으로 들어오는 JSON 값을 디코딩합니다. 응답의 `RecognitionStatus` 값은 호출이 성공적으로 음성을 텍스트로 추출할 수 있는지 나타냅니다. `Success`인 경우 함수는 텍스트를 반환하고 아닌 경우, 빈 문자열이 반환됩니다.
|
||||||
|
|
||||||
|
9. `while True:` 루프 위에, 음성을 텍스트로 변환하는 서비스에서 반환되는 텍스트를 처리하는 함수를 정의합니다. 이 함수는 콘솔에 텍스트를 텍스트를 콘솔에 출력합니다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def process_text(text):
|
||||||
|
print(text)
|
||||||
|
```
|
||||||
|
|
||||||
|
10. 마지막으로 `while True` 루프 안의 호출을 `play_audio`를 `convert_speech_to_text` 함수로 바꾸고, 텍스트를 `process_text` 함수에 전달합니다:
|
||||||
|
|
||||||
|
```python
|
||||||
|
text = convert_speech_to_text(buffer)
|
||||||
|
process_text(text)
|
||||||
|
```
|
||||||
|
|
||||||
|
11. 코드를 실행합니다. 버튼을 누르고 마이크에 대고 말합니다. 완료되면 버튼을 놓고, 오디오가 텍스트로 변환되어 콘솔에 출력될 것입니다.
|
||||||
|
|
||||||
|
```output
|
||||||
|
pi@raspberrypi:~/smart-timer $ python3 app.py
|
||||||
|
Hello world.
|
||||||
|
Welcome to IoT for beginners.
|
||||||
|
```
|
||||||
|
|
||||||
|
동음이의어를 포함한, 다른 유형의 문장을 시도합니다. 예를 들어, 영어로 말할 때, 'I want to buy two bananas and an apple too' 라고 말하고, 어떻게 올바르게 사용되는지 주목하세요. two 와 too 는 소리 뿐만 아니라, 단어의 문맥에 기반합니다.
|
||||||
|
|
||||||
|
> 💁 이 코드는 [code-speech-to-text/pi](code-speech-to-text/pi) 폴더에서 찾을 수 있습니다.
|
||||||
|
|
||||||
|
😀 음성을 텍스트로 변환하는 프로그램이 성공적으로 완료되었습니다!
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,41 @@
|
|||||||
|
# Nettoyez votre projet
|
||||||
|
|
||||||
|
Après avoir terminé chaque projet, il est bon de supprimer vos ressources cloud.
|
||||||
|
|
||||||
|
Dans les leçons de chaque projet, vous avez peut-être créé certains des éléments suivants:
|
||||||
|
|
||||||
|
* Un groupe de ressources
|
||||||
|
* Un hub IoT
|
||||||
|
* Des enregistrements d'appareils IoT
|
||||||
|
* Un compte de stockage
|
||||||
|
* Une application de fonctions
|
||||||
|
* Un compte Azure Maps
|
||||||
|
* Un projet de vision sur mesure
|
||||||
|
* Un registre de conteneurs Azure
|
||||||
|
* Une ressource de services cognitifs
|
||||||
|
|
||||||
|
La plupart de ces ressources n'auront aucun coût - soit elles sont entièrement gratuites, soit vous utilisez un plan gratuit. Pour les services qui nécessitent un plan payant, vous les auriez utilisés à un niveau inclus dans l'allocation gratuite, ou ne coûterait que quelques centimes.
|
||||||
|
|
||||||
|
Même avec des coûts relativement bas, cela vaut la peine de supprimer ces ressources lorsque vous avez terminé. Vous ne pouvez avoir qu'un seul hub IoT utilisant le plan gratuit par exemple, donc si vous souhaitez en créer un autre, vous devrez utiliser un plan payant.
|
||||||
|
|
||||||
|
Tous vos services ont été créés dans des groupes de ressources, ce qui facilite leur gestion. Vous pouvez supprimer le groupe de ressources et tous les services de ce groupe de ressources seront également supprimés.
|
||||||
|
|
||||||
|
Pour supprimer le groupe de ressources, exécutez la commande suivante dans votre terminal ou invite de commande:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
az group delete --name <resource-group-name>
|
||||||
|
```
|
||||||
|
|
||||||
|
Remplacez `<resource-group-name>` par le nom du groupe de ressources qui vous est propre.
|
||||||
|
|
||||||
|
Une confirmation apparaîtra:
|
||||||
|
|
||||||
|
```output
|
||||||
|
Are you sure you want to perform this operation? (y/n):
|
||||||
|
```
|
||||||
|
|
||||||
|
Entrez `y` pour confirmer et supprimer le groupe de ressources.
|
||||||
|
|
||||||
|
Il faudra un certain temps pour supprimer tous les services.
|
||||||
|
|
||||||
|
> 💁 Vous pouvez en savoir plus sur la suppression des groupes de ressources dans la [documentation sur la suppression des groupes de ressources et des ressources Azure Resource Manager sur Microsoft Docs](https://docs.microsoft.com/azure/azure-resource-manager/management/delete-resource-group?WT.mc_id=academic-17441-jabenn&tabs=azure-cli)
|
Loading…
Reference in new issue