Merge branch 'main' into dependabot/npm_and_yarn/quiz-app/webpack-5.76.1

pull/451/head
Anthony Bartolo 1 month ago committed by GitHub
commit a96332e7d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -124,11 +124,11 @@ Python 的强大功能之一是能够安装 [pip 软件包](https://pypi.org)
3. 当 VS Code 被启动,它会激活 Python 虚拟环境。被选择的虚拟环境将在底部状态栏出现:
![ 被选择的虚拟环境将在 VS Code的底部状态栏出现](../../../images/vscode-virtual-env.png)
![ 被选择的虚拟环境将在 VS Code的底部状态栏出现](../../../../images/vscode-virtual-env.png)
4. 如果 VS Code 被启动时 VS Code 终端已经正在运行,虚拟环境不会被激活。这时,最容易做的是用 **Kill the active terminal instance** 的按钮:
![VS Code Kill the active terminal instance 按钮](../../../images/vscode-kill-terminal.png)
![VS Code Kill the active terminal instance 按钮](../../../../images/vscode-kill-terminal.png)
虚拟环境的名字将是终端提示的字首,所以你可以把它用来确认虚拟环境已经被激活。例如,它可能是:

@ -0,0 +1,217 @@
# Interagir avec le monde physique à l'aide de capteurs et d'actionneurs
![Un aperçu de cette leçon sous forme de sketchnote](../../../../sketchnotes/lesson-3.jpg)
> Sketchnote de [Nitya Narasimhan](https://github.com/nitya). Cliquez sur l'image pour l'agrandir.
Cette leçon a été enseignée dans le cadre de la [série Hello IoT](https://youtube.com/playlist?list=PLmsFUfdnGr3xRts0TIwyaHyQuHaNQcb6-) du [Microsoft Reactor](https://developer.microsoft.com/reactor/?WT.mc_id=academic-17441-jabenn). La leçon a été enseignée sous forme de deux vidéos - une leçon d'une heure et une heure de bureau pour approfondir certaines parties de la leçon et répondre aux questions.
[![Leçon 3 : Interagir avec le monde physique à l'aide de capteurs et d'actionneurs](https://img.youtube.com/vi/Lqalu1v6aF4/0.jpg)](https://youtu.be/Lqalu1v6aF4)
[![Leçon 3 : Interagir avec le monde physique à l'aide de capteurs et d'actionneurs - Heures de bureau](https://img.youtube.com/vi/qR3ekcMlLWA/0.jpg)](https://youtu.be/qR3ekcMlLWA)
> 🎥 Cliquez sur les images ci-dessus pour visionner les vidéos
## Quiz préalable
[Quiz préalable](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/5)
## Introduction
Cette leçon présente deux concepts importants pour votre appareil IoT : les capteurs et les actionneurs. Vous en ferez une prise en main en ajoutant un capteur de lumière à votre projet IoT, puis en ajoutant une LED contrôlée par les niveaux de lumière, ce qui vous permettra de développer une veilleuse.
Dans cette leçon, nous aborderons les points suivants
* [Qu'est-ce qu'un capteur?](#quest-ce-quun-capteur)
* [Utiliser un capteur](#utiliser-un-capteur)
* [Les types de capteurs](#les-types-de-capteurs)
* [Qu'est-ce que les actionneurs?](#quest-ce-que-les-actionneurs)
* [Utiliser un actionneur](#utiliser-un-actionneur)
* [Les types d'actionneurs](#les-types-dactionneurs)
## Qu'est-ce qu'un capteur?
Les capteurs sont des dispositifs matériels qui détectent le monde physique, c'est-à-dire qu'ils mesurent une ou plusieurs propriétés autour d'eux et envoient l'information à un dispositif IoT. Les capteurs couvrent une vaste gamme d'appareils car il y a beaucoup de choses qui peuvent être mesurées, des propriétés naturelles telles que la température de l'air aux interactions physiques telles que le mouvement.
Les capteurs les plus courants sont les suivants :
* Capteurs de température - ils détectent la température de l'air ou la température de ce dans quoi ils sont immergés. Pour les amateurs et les développeurs, ces capteurs sont souvent combinés avec la pression atmosphérique et l'humidité dans un seul capteur.
* Boutons - ils détectent le moment où ils ont été pressés.
* Capteurs de lumière : ils détectent les niveaux de lumière et peuvent concerner des couleurs spécifiques, la lumière UV, la lumière IR ou la lumière visible en général.
* Les caméras : elles détectent une représentation visuelle du monde en prenant une photo ou en diffusant une vidéo.
* Accéléromètres : ils détectent les mouvements dans plusieurs directions.
* Microphones - ils détectent les sons, qu'il s'agisse de niveaux sonores généraux ou de sons directionnels.
✅ Faites des recherches. Quels sont les capteurs de votre téléphone ?
Tous les capteurs ont un point commun : ils convertissent ce qu'ils détectent en un signal électrique qui peut être interprété par un appareil IoT. La manière dont ce signal électrique est interprété dépend du capteur, ainsi que du protocole de communication utilisé pour communiquer avec l'appareil IoT.
## Utiliser un capteur
Suivez le guide approprié ci-dessous pour ajouter un capteur à votre appareil IoT :
* [Arduino - Terminal Wio](wio-terminal-sensor.fr.md)
* [Ordinateur monocarte - Raspberry Pi](pi-sensor.fr.md)
* [Ordinateur monocarte - Dispositif virtuel](virtual-device-sensor.fr.md)
## Les types de capteurs
Les capteurs sont soit analogiques, soit numériques.
### Capteurs analogiques
Les capteurs analogiques comptent parmi les capteurs les plus élémentaires. Ces capteurs reçoivent une tension de l'appareil IoT, les composants du capteur ajustent cette tension et la tension renvoyée par le capteur est mesurée pour donner la valeur du capteur.
> 🎓 La tension ("Voltage" en anglais) est une mesure de la force exercée pour déplacer l'électricité d'un endroit à un autre, par exemple de la borne positive d'une pile à la borne négative. Par exemple, une pile AA standard a une tension de 1,5V (V est le symbole des volts) et peut pousser l'électricité avec une force de 1,5V de sa borne positive à sa borne négative. Par exemple, une LED peut s'allumer avec une tension de 2 à 3V, alors qu'une ampoule à filament de 100W nécessite une tension de 240V. Pour en savoir plus sur la tension, consultez la [page sur la tension sur Wikipédia](https://wikipedia.org/wiki/Voltage).
Le potentiomètre en est un exemple. Il s'agit d'un cadran que l'on peut faire tourner entre deux positions et dont le capteur mesure la rotation.
![Un potentiomètre réglé sur un point médian reçoit 5 volts et renvoie 3,8 volts](../../../../images/potentiometer.png).
L'appareil IoT envoie un signal électrique au potentiomètre à une tension, par exemple 5 volts (5V). Lorsque le potentiomètre est ajusté, il modifie la tension qui sort de l'autre côté. Imaginez que vous ayez un potentiomètre étiqueté comme un cadran qui va de 0 à [11](https://wikipedia.org/wiki/Up_to_eleven), comme le bouton de volume d'un amplificateur. Lorsque le potentiomètre est en position complètement désactivée (0), 0V (0 volt) sort. Lorsqu'il est en position d'activation totale (11), 5V (5 volts) sont émis.
> 🎓 Il s'agit d'une simplification excessive, et vous pouvez en savoir plus sur les potentiomètres et les résistances variables sur la [page Wikipédia sur les potentiomètres](https://wikipedia.org/wiki/Potentiometer).
La tension qui sort du capteur est alors lue par l'appareil IoT, qui peut alors y répondre. Selon le capteur, cette tension peut être une valeur arbitraire ou correspondre à une unité standard. Par exemple, un capteur de température analogique basé sur une [thermistance](https://wikipedia.org/wiki/Thermistor) modifie sa résistance en fonction de la température. La tension de sortie peut alors être convertie en température en Kelvin, et donc en °C ou °F, par des calculs en code.
✅ Que pensez-vous qu'il se passe si le capteur renvoie une tension plus élevée que celle qui a été envoyée (par exemple en provenance d'une alimentation externe)? ⛔️ NE TESTEZ PAS cela.
#### Conversion analogique-numérique (CAN)
Les appareils IoT sont numériques - ils ne peuvent pas fonctionner avec des valeurs analogiques, ils ne fonctionnent qu'avec des 0 et des 1. Cela signifie que les valeurs analogiques des capteurs doivent être converties en un signal numérique avant de pouvoir être traitées. De nombreux appareils IoT sont équipés de convertisseurs analogique-numérique (CAN) (ou analog-to-digital converters abrégé en "ADC" en anglais) pour convertir les entrées analogiques en représentations numériques de leur valeur. Les capteurs peuvent également fonctionner avec des convertisseurs analogiques-numériques par l'intermédiaire d'une carte de connexion. Par exemple, dans l'écosystème Seeed Grove avec un Raspberry Pi, les capteurs analogiques se connectent à des ports spécifiques sur un 'chapeau' ('hat' en anglais) qui se trouve sur le Pi connecté aux broches GPIO du Pi, et ce chapeau a un CAN pour convertir la tension en un signal numérique qui peut être envoyé hors des broches GPIO du Pi.
Imaginez que vous ayez un capteur de lumière analogique connecté à un dispositif IoT qui utilise 3,3V et renvoie une valeur de 1V. Cette valeur de 1V ne signifie rien dans le monde numérique et doit donc être convertie. La tension sera convertie en valeur analogique à l'aide d'une échelle qui dépend de l'appareil et du capteur. Le capteur de lumière Seeed Grove, par exemple, émet des valeurs comprises entre 0 et 1023. Pour ce capteur fonctionnant à 3,3V, une sortie de 1V correspondrait à une valeur de 300. Un appareil IoT ne peut pas traiter 300 comme une valeur analogique, donc la valeur serait convertie en `0000000100101100`, la représentation binaire de 300 par le chapeau Grove. Cette valeur serait ensuite traitée par l'appareil IoT.
✅ Si vous ne connaissez pas le binaire, faites quelques recherches pour apprendre comment les nombres sont représentés par des 0 et des 1. La [leçon d'introduction au binaire de BBC Bitesize](https://www.bbc.co.uk/bitesize/guides/zwsbwmn/revision/1) est un excellent point de départ.
Du point de vue du codage, tout cela est généralement géré par les bibliothèques fournies avec les capteurs, de sorte que vous n'avez pas à vous préoccuper de cette conversion vous-même. Pour le capteur de lumière Grove, vous devez utiliser la bibliothèque Python et appeler la propriété `light`, ou utiliser la bibliothèque Arduino et appeler `analogRead` pour obtenir une valeur de 300.
### Capteurs numériques
Les capteurs numériques, comme les capteurs analogiques, détectent le monde qui les entoure en utilisant les changements de tension électrique. La différence est qu'ils émettent un signal numérique, soit en mesurant seulement deux états, soit en utilisant un CAN intégré. Les capteurs numériques sont de plus en plus courants pour éviter d'avoir à utiliser un CAN, que ce soit sur une carte de connexion ou sur l'appareil IoT lui-même.
Le capteur numérique le plus simple est un bouton ou un interrupteur. Il s'agit d'un capteur à deux états, marche ou arrêt.
![Un bouton reçoit 5 volts. Lorsqu'il n'est pas enfoncé, il renvoie 0 volt, lorsqu'il est enfoncé, il renvoie 5 volts.](../../../../images/button.png)
Les broches des appareils IoT, telles que les broches GPIO, peuvent mesurer ce signal directement sous la forme d'un 0 ou d'un 1. Si la tension envoyée est la même que la tension renvoyée, la valeur lue est 1, sinon la valeur lue est 0. Il n'est pas nécessaire de convertir le signal, il ne peut être que 1 ou 0.
> 💁 Les tensions ne sont jamais exactes, en particulier parce que les composants d'un capteur ont une certaine résistance, et il y a donc généralement une tolérance. Par exemple, les broches GPIO d'un Raspberry Pi fonctionnent sur 3,3V et lisent un signal de retour supérieur à 1,8V comme un 1, inférieur à 1,8 V comme un 0.
* 3,3V entrent dans le bouton. Le bouton étant éteint, 0V en sort, ce qui donne une valeur de 0
* 3,3V entrent dans le bouton. Le bouton est allumé, donc 3,3V sort, ce qui donne une valeur de 1
Les capteurs numériques plus avancés lisent les valeurs analogiques, puis les convertissent en signaux numériques à l'aide de convertisseurs analogiques/numériques embarqués. Par exemple, un capteur de température numérique utilise toujours un thermocouple de la même manière qu'un capteur analogique et mesure toujours la variation de tension causée par la résistance du thermocouple à la température actuelle. Au lieu de renvoyer une valeur analogique et de compter sur l'appareil ou la carte de connexion pour la convertir en un signal numérique, un CAN intégré au capteur convertira la valeur et l'enverra sous la forme d'une série de 0 et de 1 à l'appareil IoT. Ces 0 et 1 sont envoyés de la même manière que le signal numérique d'un bouton, 1 étant la pleine tension et 0 étant 0v.
![Un capteur de température numérique convertissant une lecture analogique en données binaires avec 0 comme 0 volt et 1 comme 5 volts avant de l'envoyer à un dispositif IoT.](../../../../images/temperature-as-digital.png)
L'envoi de données numériques permet aux capteurs de devenir plus complexes et d'envoyer des données plus détaillées, voire des données cryptées pour les capteurs sécurisés. L'appareil photo en est un exemple. Il s'agit d'un capteur qui capture une image et l'envoie sous forme de données numériques contenant cette image, généralement dans un format compressé tel que JPEG, pour qu'elle soit lue par l'appareil IoT. Il peut même diffuser de la vidéo en capturant des images et en envoyant soit l'image complète image par image, soit un flux vidéo compressé.
## Qu'est-ce que les actionneurs?
Les actionneurs sont l'opposé des capteurs : ils convertissent un signal électrique provenant de votre appareil IoT en une interaction avec le monde physique, par exemple en émettant une lumière ou un son, ou en faisant bouger un moteur.
Les actionneurs les plus courants sont les suivants
* LED - elles émettent de la lumière lorsqu'elles sont allumées.
* Haut-parleur : il émet un son en fonction du signal qui lui est envoyé, qu'il s'agisse d'un simple buzzer ou d'un haut-parleur audio capable de diffuser de la musique.
* Moteur pas à pas : il convertit un signal en une quantité définie de rotation, par exemple en tournant un cadran de 90°.
* Relais : il s'agit d'interrupteurs qui peuvent être activés ou désactivés par un signal électrique. Ils permettent à une petite tension provenant d'un appareil IoT d'activer des tensions plus importantes.
* Écrans - il s'agit d'actionneurs plus complexes qui affichent des informations sur un écran à segments multiples. Les écrans varient d'un simple affichage LED à des moniteurs vidéo haute résolution.
✅ Faites des recherches. Quels sont les actionneurs de votre téléphone ?
## Utiliser un actionneur
Suivez le guide ci-dessous pour ajouter un actionneur à votre appareil IoT, contrôlé par le capteur, afin de créer une veilleuse IoT. Elle recueillera les niveaux de lumière du capteur de lumière et utilisera un actionneur sous la forme d'une LED pour émettre de la lumière lorsque le niveau de lumière détecté est trop faible.
![Organigramme de la mission montrant la lecture et le contrôle des niveaux d'éclairage, et le début de la commande des LED](../../../../images/assignment-1-flow.png)
* [Arduino - Terminal Wio](wio-terminal-actuator.fr.md)
* [Ordinateur monocarte - Raspberry Pi](pi-actuator.fr.md)
* [Ordinateur monocarte - Dispositif virtuel](virtual-device-actuator.fr.md)
## Les types d'actionneurs
Comme les capteurs, les actionneurs sont soit analogiques, soit numériques.
### Actionneurs analogiques
Les actionneurs analogiques prennent un signal analogique et le convertissent en une sorte d'interaction, où l'interaction change en fonction de la tension fournie.
Un exemple est celui d'une lampe à intensité variable, comme celles que vous avez peut-être dans votre maison. La quantité de tension fournie à la lampe détermine sa luminosité.
![Une lumière atténuée à une basse tension et plus brillante à une tension plus élevée](../../../../images/dimmable-light.png)
Comme pour les capteurs, l'appareil IoT proprement dit fonctionne avec des signaux numériques et non analogiques. Cela signifie que pour envoyer un signal analogique, l'appareil IoT a besoin d'un convertisseur numérique-analogique (CNA), soit directement sur l'appareil IoT, soit sur une carte de connexion. Cela convertira les 0 et les 1 de l'appareil IoT en une tension analogique que l'actionneur peut utiliser.
✅ À votre avis, que se passe-t-il si l'appareil IoT envoie une tension plus élevée que celle que l'actionneur peut gérer ?
⛔️ NE PAS tester cela.
#### Modulation de largeur d'impulsion (ou Pulse-Width Modulation en anglais abrégé en PWM)
Une autre option pour convertir les signaux numériques d'un appareil IoT en un signal analogique est la modulation de largeur d'impulsion. Il s'agit d'envoyer de nombreuses impulsions numériques courtes qui agissent comme s'il s'agissait d'un signal analogique.
Par exemple, vous pouvez utiliser la modulation de largeur d'impulsion pour contrôler la vitesse d'un moteur.
Imaginez que vous contrôliez un moteur avec une alimentation de 5V. Vous envoyez une brève impulsion à votre moteur, en faisant passer la tension à un niveau élevé (5V) pendant deux centièmes de seconde (0,02s). Pendant ce temps, votre moteur peut effectuer un dixième de tour, soit 36°. Le signal s'interrompt ensuite pendant deux centièmes de seconde (0,02 s), envoyant un signal bas (0V). Chaque cycle de marche puis d'arrêt dure 0,04s. Le cycle se répète ensuite.
![Rotation par modulation de largeur d'impulsion d'un moteur à 150 RPM](../../../../images/pwm-motor-150rpm.png)
Cela signifie qu'en une seconde, 25 impulsions de 5V de 0,02s font tourner le moteur, chacune étant suivie d'une pause de 0V de 0,02s qui ne fait pas tourner le moteur. Chaque impulsion fait tourner le moteur d'un dixième de tour, ce qui signifie que le moteur effectue 2,5 rotations par seconde. Vous avez utilisé un signal numérique pour faire tourner le moteur à 2,5 tours par seconde, soit 150 [tours par minute](https://wikipedia.org/wiki/Revolutions_per_minute) (une mesure non standard de la vitesse de rotation).
```sortie
25 pulses per second x 0.1 rotations per pulse = 2.5 rotations per second
2.5 rotations per second x 60 seconds in a minute = 150rpm
```
> 🎓 Lorsqu'un signal PWM est activé pendant la moitié du temps et désactivé pendant l'autre moitié, on parle d'un [cycle de service de 50 %](https://wikipedia.org/wiki/Duty_cycle). Les rapports cycliques sont mesurés en pourcentage du temps pendant lequel le signal est activé par rapport au temps pendant lequel il est désactivé.
![Rotation par modulation de largeur d'impulsion d'un moteur à 75 RPM](../../../../images/pwm-motor-75rpm.png)
Vous pouvez modifier la vitesse du moteur en changeant la taille des impulsions. Par exemple, avec le même moteur, vous pouvez conserver le même temps de cycle de 0,04 s, en réduisant de moitié l'impulsion de marche à 0,01 s et en augmentant l'impulsion d'arrêt à 0,03 s. Vous avez le même nombre d'impulsions par seconde (25), mais chaque impulsion de marche est réduite de moitié. Vous avez le même nombre d'impulsions par seconde (25), mais chaque impulsion de marche est deux fois moins longue. Une impulsion de demi-longueur ne fait tourner le moteur que d'un vingtième de tour, et à 25 impulsions par seconde, le moteur effectue 1,25 rotation par seconde, soit 75 tours par minute. En modifiant la vitesse d'impulsion d'un signal numérique, vous avez réduit de moitié la vitesse d'un moteur analogique.
```sortie
25 pulses per second x 0.05 rotations per pulse = 1.25 rotations per second
1.25 rotations per second x 60 seconds in a minute = 75rpm
```
✅ Comment assurer la fluidité de la rotation du moteur, en particulier à faible vitesse ? Utilisez-vous un petit nombre d'impulsions longues avec de longues pauses ou un grand nombre d'impulsions très courtes avec de très courtes pauses?
> 💁 Certains capteurs utilisent également le PWM pour convertir les signaux analogiques en signaux numériques.
> 🎓 Pour en savoir plus sur la modulation de largeur d'impulsion, consultez la page consacrée à [la modulation de largeur d'impulsion sur Wikipédia](https://wikipedia.org/wiki/Pulse-width_modulation).
### Actionneurs numériques
Les actionneurs numériques, comme les capteurs numériques, ont soit deux états contrôlés par une tension haute ou basse, soit un convertisseur numérique-analogique intégré qui permet de convertir un signal numérique en un signal analogique.
Un actionneur numérique simple est une LED. Lorsqu'un dispositif envoie un signal numérique de 1, une haute tension est envoyée et allume la LED. Lorsqu'un signal numérique de 0 est envoyé, la tension chute à 0V et la LED s'éteint.
![Une LED est éteinte à 0 volt et allumée à 5V](../../../../images/led.png)
✅ À quels autres actionneurs simples à deux états pouvez-vous penser ? Un exemple est le solénoïde, qui est un électro-aimant qui peut être activé pour faire des choses comme déplacer un pêne de porte qui verrouille/déverrouille une porte.
Les actionneurs numériques plus avancés, tels que les écrans, exigent que les données numériques soient envoyées dans certains formats. Ils sont généralement livrés avec des bibliothèques qui facilitent l'envoi des données correctes pour les contrôler.
---
## 🚀 Challenge
Le défi des deux dernières leçons consistait à dresser la liste du plus grand nombre possible d'appareils IoT présents chez vous, à l'école ou sur votre lieu de travail, et de déterminer s'ils sont construits autour de microcontrôleurs ou d'ordinateurs monocartes, ou même d'un mélange des deux.
Pour chaque appareil que vous avez répertorié, à quels capteurs et actionneurs sont-ils connectés? Quelle est l'utilité de chaque capteur et actionneur connecté à ces dispositifs?
## Quiz de validation des connaissances
[Quiz de validation des connaissances](https://black-meadow-040d15503.1.azurestaticapps.net/quiz/6)
## Révision et auto-apprentissage
* En apprendre plus sur l'électricité et les circuits sur [ThingLearn](http://thinglearn.jenlooper.com/curriculum/).
* Pour en savoir plus sur les différents types de capteurs de température, consultez le [guide des capteurs de température de Seeed Studios](https://www.seeedstudio.com/blog/2019/10/14/temperature-sensors-for-arduino-projects/)
* Pour en savoir plus sur les LED, consultez [la page LED de Wikipédia](https://wikipedia.org/wiki/Light-emitting_diode)
## Affectation
[Recherche sur les capteurs et les actionneurs](assignment.fr.md)

@ -0,0 +1,96 @@
# Construire une veilleuse - Raspberry Pi
Dans cette partie de la leçon, vous allez ajouter un capteur de lumière à votre Raspberry Pi.
## Matériel
Le capteur utilisé pour cette leçon est un **capteur de lumière** qui utilise une [photodiode](https://wikipedia.org/wiki/Photodiode) pour convertir la lumière en un signal électrique. Il s'agit d'un capteur analogique qui envoie une valeur entière de 0 à 1000 indiquant une quantité relative de lumière qui ne correspond à aucune unité de mesure standard telle que le [lux](https://fr.wikipedia.org/wiki/Lux_(unit%C3%A9)).
Le capteur de lumière est un capteur Grove et doit être connecté au chapeau de base Grove sur le Raspberry Pi.
### Connecter le capteur de lumière
Le capteur de lumière Grove utilisé pour détecter les niveaux de lumière doit être connecté au Raspberry Pi.
#### Tâche - connecter le capteur de lumière
Connecter le capteur de lumière
![Un capteur de lumière Grove](../../../../images/grove-light-sensor.png)
1. Insérez une extrémité d'un câble Grove dans la prise du module du capteur de lumière. Il ne peut être inséré que dans un seul sens.
1. Le Raspberry Pi étant éteint, connectez l'autre extrémité du câble Grove à la prise analogique marquée **A0** sur le chapeau de base Grove fixé au Pi. Cette prise est la deuxième en partant de la droite, sur la rangée de prises à côté des broches GPIO.
![Le capteur de lumière Grove connecté à la prise A0](../../../../images/pi-light-sensor.png)
## Programmer le capteur de lumière
L'appareil peut maintenant être programmé à l'aide du capteur de lumière Grove.
### Tâche - programmer le capteur de lumière
Programmez l'appareil.
1. Allumez le Pi et attendez qu'il démarre
1. Ouvrez le projet nightlight dans VS Code que vous avez créé dans la partie précédente de ce travail, soit en l'exécutant directement sur le Pi, soit en le connectant à l'aide de l'extension Remote SSH.
1. Ouvrez le fichier `app.py` et supprimez tout le code qu'il contient.
1. Ajoutez le code suivant au fichier `app.py` pour importer certaines bibliothèques nécessaires :
```python
import time
from grove.grove_light_sensor_v1_2 import GroveLightSensor
```
L'instruction `import time` importe le module `time` qui sera utilisé plus tard dans ce travail.
L'instruction `from grove.grove_light_sensor_v1_2 import GroveLightSensor` importe le module `GroveLightSensor` des bibliothèques Grove Python. Cette bibliothèque contient du code pour interagir avec un capteur de lumière Grove, et a été installée globalement lors de l'installation du Pi.
1. Ajoutez le code suivant après le code ci-dessus pour créer une instance de la classe qui gère le capteur de lumière :
```python
light_sensor = GroveLightSensor(0)
```
La ligne `light_sensor = GroveLightSensor(0)` crée une instance de la classe `GroveLightSensor` se connectant à la broche **A0** - la broche analogique Grove à laquelle le capteur de lumière est connecté.
1. Ajoutez une boucle infinie après le code ci-dessus pour interroger la valeur du capteur de lumière et l'imprimer sur la console :
```python
while True:
light = light_sensor.light
print('Light level:', light)
```
Ceci va lire le niveau de lumière actuel sur une échelle de 0-1023 en utilisant la propriété `light` de la classe `GroveLightSensor`. Cette propriété lit la valeur analogique de la broche. Cette valeur est ensuite imprimée sur la console.
1. Ajoutez une petite mise en veille d'une seconde à la fin de la `boucle` (`loop`) car les niveaux de lumière n'ont pas besoin d'être vérifiés en permanence. Une mise en veille réduit la consommation d'énergie de l'appareil.
```python
time.sleep(1)
```
1. Depuis le terminal VS Code, exécutez la commande suivante pour lancer votre application Python :
```sh
python3 app.py
```
Les valeurs lumineuses sont transmises à la console. Couvrez et découvrez le capteur de lumière, et les valeurs changeront :
```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
```
> 💁 Vous trouverez ce code dans le dossier [code-sensor/pi](../code-sensor/pi).
😀 L'ajout d'un capteur à votre programme de veilleuse a été un succès!

@ -0,0 +1,110 @@
# Créer une veilleuse - Matériel IoT virtuel
Dans cette partie de la leçon, vous allez ajouter une LED à votre dispositif IoT virtuel et l'utiliser pour créer une veilleuse.
## Matériel virtuel
La veilleuse a besoin d'un actionneur, créé dans l'application CounterFit.
L'actionneur est une **LED**. Dans un dispositif IoT physique, il s'agirait d'une [diode électroluminescente](https://wikipedia.org/wiki/Light-emitting_diode) qui émet de la lumière lorsqu'elle est traversée par un courant. Il s'agit d'un actionneur numérique qui a deux états : allumé (`on`) et éteint (`off`). L'envoi d'une valeur de 1 allume la diode et celle de 0 l'éteint.
La logique de la veilleuse en pseudo-code est la suivante :
```sortie
Vérifier le niveau de lumière.
Si la lumière est inférieure à 300
Allumer la LED
Sinon
Eteindre la LED
```
### Ajouter l'actionneur à CounterFit
Pour utiliser une LED virtuelle, vous devez l'ajouter à l'application CounterFit.
#### Tâche - ajouter l'actionneur à CounterFit
Ajoutez la LED à l'application CounterFit.
1. Assurez-vous que l'application web CounterFit est en cours d'exécution depuis la partie précédente de ce travail. Si ce n'est pas le cas, démarrez-la et ajoutez à nouveau le capteur de lumière.
1. Créez une LED :
1. Dans la case *Créer un actionneur* du volet *Actionneur*, dérouler la case *Type d'actionneur* et sélectionner *LED*.
1. Réglez la *broche* sur *5*
1. Sélectionnez le bouton **Ajouter** pour créer la LED sur la broche 5.
![Les paramètres de la LED](../../../../images/counterfit-create-led.png)
La LED sera créée et apparaîtra dans la liste des actionneurs.
![La LED créée](../../../../images/counterfit-led.png)
Une fois la LED créée, vous pouvez changer sa couleur en utilisant le sélecteur *Color*. Sélectionnez le bouton **Set** pour modifier la couleur après l'avoir sélectionnée.
### Programmer la veilleuse
La veilleuse peut maintenant être programmée à l'aide du capteur de lumière et de la LED CounterFit.
#### Tâche - programmer la veilleuse
Programmez la veilleuse.
1. Ouvrez le projet de veilleuse dans VS Code que vous avez créé dans la partie précédente de ce travail. Fermez et recréez le terminal pour vous assurer qu'il fonctionne en utilisant l'environnement virtuel si nécessaire.
1. Ouvrez le fichier `app.py`
1. Ajoutez le code suivant au fichier `app.py` pour vous connecter à l'importation d'une bibliothèque requise. Ceci doit être ajouté en haut, en dessous des autres lignes `import`.
```python
from counterfit_shims_grove.grove_led import GroveLed
```
L'instruction `from counterfit_shims_grove.grove_led import GroveLed` importe la `GroveLed` des bibliothèques Python de CounterFit Grove shim. Cette bibliothèque contient du code pour interagir avec une LED créée dans l'application CounterFit.
1. Ajoutez le code suivant après la déclaration `light_sensor` pour créer une instance de la classe qui gère la LED :
```python
led = GroveLed(5)
```
La ligne `led = GroveLed(5)` crée une instance de la classe `GroveLed` se connectant à la broche **5** - la broche CounterFit Grove à laquelle la LED est connectée.
1. Ajoutez une vérification à l'intérieur de la boucle `while`, et avant le `time.sleep` pour vérifier les niveaux de lumière et allumer ou éteindre la LED :
```python
if light < 300:
led.on()
else:
led.off()
```
Ce code vérifie la valeur de `light`. Si cette valeur est inférieure à 300, il appelle la méthode `on` de la classe `GroveLed` qui envoie une valeur numérique de 1 à la LED, l'allumant ainsi. Si la valeur de la lumière est supérieure ou égale à 300, elle appelle la méthode `off`, qui envoie une valeur numérique de 0 à la LED, l'éteignant.
> 💁 Ce code doit être indenté au même niveau que la ligne `print('Light level:', light)` pour être à l'intérieur de la boucle while!
1. Depuis le terminal VS Code, exécutez ce qui suit pour lancer votre application Python :
```sh
python3 app.py
```
Les valeurs de lumière seront affichées sur la console.
```sortie
(.venv) ➜ GroveTest python3 app.py
Light level: 143
Light level: 244
Light level: 246
Light level: 253
```
1. Modifiez les paramètres *Value* ou *Random* pour faire varier le niveau d'éclairage au-dessus et au-dessous de 300. La LED s'allume et s'éteint.
![La LED dans l'application CounterFit s'allume et s'éteint lorsque le niveau de lumière change](../../../../images/virtual-device-running-assignment-1-1.gif)
> 💁 Vous trouverez ce code dans le dossier [code-actuator/virtual-device](../code-actuator/virtual-device).
😀 Votre programme de veilleuse a été réalisé avec succès!

@ -0,0 +1,110 @@
# Créer une veilleuse - Matériel IoT virtuel
Dans cette partie de la leçon, vous allez ajouter un capteur de lumière à votre dispositif IoT virtuel.
## Matériel virtuel
La veilleuse a besoin d'un actionneur, créé dans l'application CounterFit.
Le capteur est un **capteur de lumière**. Dans un dispositif IoT physique, il s'agirait d'une [photodiode](https://wikipedia.org/wiki/Photodiode) qui convertit la lumière en un signal électrique. Les capteurs de lumière sont des capteurs analogiques qui envoient une valeur entière indiquant une quantité relative de lumière, qui ne correspond à aucune unité de mesure standard telle que le [lux](https://wikipedia.org/wiki/Lux).
### Ajouter les capteurs à CounterFit
Pour utiliser un capteur de lumière virtuel, vous devez l'ajouter à l'application CounterFit.
#### Tâche - ajouter les capteurs à CounterFit
Ajoutez le capteur de lumière à l'application CounterFit.
1. Assurez-vous que l'application web CounterFit est en cours d'exécution depuis la partie précédente de ce travail. Si ce n'est pas le cas, démarrez-la.
1. Créez un capteur de lumière :
1. Dans la case *Create sensor* du volet *Sensors*, déroulez la case *Sensor type* et sélectionnez *Light*.
1. Laissez les *Unités* (*Units* en anglais) réglées sur *NoUnits*
1. Assurez-vous que l'option *Pin* est réglée sur *0*.
1. Sélectionnez le bouton **Add** pour créer le capteur de lumière sur la broche 0.
![Les paramètres du capteur de lumière](../../../../images/counterfit-create-light-sensor.png)
Le capteur de lumière sera créé et apparaîtra dans la liste des capteurs.
![Le capteur de lumière créé](../../../../images/counterfit-light-sensor.png)
## Programmer le capteur de lumière
L'appareil peut maintenant être programmé pour utiliser le capteur de lumière intégré.
### Tâche - programmer le capteur de lumière
Programmez l'appareil.
1. Ouvrez le projet nightlight dans VS Code que vous avez créé dans la partie précédente de ce travail. Fermez et recréez le terminal pour vous assurer qu'il fonctionne en utilisant l'environnement virtuel si nécessaire.
1. Ouvrez le fichier `app.py`
1. Ajouter le code suivant au début du fichier `app.py` avec le reste des déclarations `import` pour se connecter à l'importation de certaines bibliothèques requises :
```python
import time
from counterfit_shims_grove.grove_light_sensor_v1_2 import GroveLightSensor
```
L'instruction `import time` importe le module Python `time` qui sera utilisé plus tard dans ce travail.
L'instruction `from counterfit_shims_grove.grove_light_sensor_v1_2 import GroveLightSensor` importe le `GroveLightSensor` des bibliothèques Python CounterFit Grove shim. Cette bibliothèque contient du code pour interagir avec un capteur de lumière créé dans l'application CounterFit.
1. Ajoutez le code suivant au bas du fichier pour créer des instances de classes qui gèrent le capteur de lumière :
```python
light_sensor = GroveLightSensor(0)
```
La ligne `light_sensor = GroveLightSensor(0)` crée une instance de la classe `GroveLightSensor` se connectant au pin **0** - le pin du CounterFit Grove auquel le capteur de lumière est connecté.
1. Ajoutez une boucle infinie après le code ci-dessus pour interroger la valeur du capteur de lumière et l'imprimer sur la console :
```python
while True:
light = light_sensor.light
print('Light level:', light)
```
Ceci permet de lire le niveau de lumière actuel en utilisant la propriété `light` de la classe `GroveLightSensor`. Cette propriété lit la valeur analogique de la broche. Cette valeur est ensuite imprimée sur la console.
1. Ajoutez une petite mise en veille d'une seconde à la fin de la boucle `while` car les niveaux de lumière n'ont pas besoin d'être vérifiés continuellement. Une mise en veille réduit la consommation d'énergie de l'appareil.
```python
time.sleep(1)
```
1. Depuis le terminal VS Code, exécutez les commandes suivantes pour lancer votre application Python :
```sh
python3 app.py
```
Les valeurs lumineuses seront affichées sur la console. Initialement, cette valeur sera de 0.
1. Depuis l'application CounterFit, changez la valeur du capteur de lumière qui sera lue par l'application. Vous pouvez le faire de deux façons :
* Saisissez un nombre dans la case *Value* du capteur de lumière, puis sélectionnez le bouton **Set**. Le nombre que vous saisissez sera la valeur renvoyée par le capteur.
* Cochez la case *Aléatoire* et entrez une valeur *Min* et *Max*, puis cliquez sur le bouton **Set**. Chaque fois que le capteur lira une valeur, il lira un nombre aléatoire entre *Min* et *Max*.
Les valeurs que vous avez définies seront affichées sur la console. Modifiez les paramètres *Value* ou *Random* pour modifier la valeur.
```sortie
(.venv) ➜ GroveTest python3 app.py
Light level: 143
Light level: 244
Light level: 246
Light level: 253
```
> 💁 Vous trouverez ce code dans le dossier [code-sensor/virtual-device](../code-sensor/virtual-device).
😀 Votre programme de veilleuse a été un succès!

@ -0,0 +1,110 @@
# Créer une veilleuse - le terminale Wio
Dans cette partie de la leçon, vous ajouterez une LED à votre terminal Wio et l'utiliserez pour créer une veilleuse.
## Matériel
La veilleuse a maintenant besoin d'un actionneur.
L'actionneur est une **LED**, une [diode électroluminescente](https://wikipedia.org/wiki/Light-emitting_diode) qui émet de la lumière lorsqu'elle est traversée par un courant. Il s'agit d'un actionneur numérique qui a deux états : allumé (`on`) et éteint (`off`). L'envoi d'une valeur de 1 allume la LED et de 0 l'éteint. Il s'agit d'un actionneur Grove externe qui doit être connecté au terminal Wio.
La logique de la veilleuse en pseudo-code est la suivante :
```sortie
Vérifier le niveau de lumière.
Si la lumière est inférieure à 300
Allumer la LED
Sinon
Eteindre la LED
```
### Connecter la LED
La LED Grove est livrée sous forme de module avec une sélection de LED, ce qui vous permet de choisir la couleur.
#### Tâche - connecter la LED
Connecter la LED.
![Une LED grove](../../../../images/grove-led.png)
1. Choisissez votre LED préférée et insérez les pattes dans les deux trous du module LED.
Les LEDs sont des diodes électroluminescentes, et les diodes sont des dispositifs électroniques qui ne peuvent transporter le courant que dans un sens. Cela signifie que la LED doit être connectée dans le bon sens, sinon elle ne fonctionnera pas.
L'une des pattes de la LED est la broche positive, l'autre est la broche négative. La LED n'est pas parfaitement ronde, elle est légèrement plus plate d'un côté. Le côté légèrement plus plat est la broche négative. Lorsque vous connectez la LED au module, assurez-vous que la broche du côté arrondi est connectée à la prise marquée **+** à l'extérieur du module, et que le côté plus plat est connecté à la prise plus proche du milieu du module.
1. Le module LED est doté d'un bouton rotatif qui vous permet de contrôler la luminosité. Pour commencer, réglez-le à fond en le tournant dans le sens inverse des aiguilles d'une montre jusqu'à la butée à l'aide d'un petit tournevis cruciforme.
1. Insérez l'une des extrémités d'un câble Grove dans la prise du module LED. Il ne peut être inséré que dans un seul sens.
1. Le terminal Wio étant déconnecté de votre ordinateur ou de toute autre source d'alimentation, connectez l'autre extrémité du câble Grove à la prise Grove du côté droit du terminal Wio lorsque vous regardez l'écran. Il s'agit de la prise la plus éloignée du bouton d'alimentation.
> 💁 La prise Grove de droite peut être utilisée avec des capteurs et des actionneurs analogiques ou numériques.La prise de gauche est réservée aux capteurs et actionneurs I<sup>2</sup>C et numériques. L'I<sup>2</sup>C sera abordé dans une leçon ultérieure.
![La LED grove connectée à la prise de droite](../../../../images/wio-led.png)
## Programmer la veilleuse
La veilleuse peut maintenant être programmée à l'aide du capteur de lumière intégré et de la LED Grove.
### Tâche - programmer la veilleuse
Programmer la veilleuse.
1. Ouvrez le projet de veilleuse dans VS Code que vous avez créé dans la partie précédente de ce devoir
1. Ajoutez la ligne suivante à la fin de la fonction `setup` :
```cpp
pinMode(D0, OUTPUT);
```
Cette ligne configure la broche utilisée pour communiquer avec la LED via le port Grove.
La broche `D0` est la broche numérique pour la prise Grove de droite. Cette broche est réglée sur `OUTPUT`, ce qui signifie qu'elle se connecte à un actionneur et que des données seront écrites sur la broche.
1. Ajoutez le code suivant immédiatement avant le `delay` dans la fonction loop :
```cpp
if (light < 300)
{
digitalWrite(D0, HIGH);
}
else
{
digitalWrite(D0, LOW);
}
```
Ce code vérifie la valeur `light`. Si cette valeur est inférieure à 300, il envoie une valeur `HIGH` à la broche numérique `D0`. Ce `HIGH` a une valeur de 1, ce qui allume la LED. Si la lumière est supérieure ou égale à 300, une valeur `LOW` de 0 est envoyée à la broche, ce qui éteint la LED.
> 💁 Lors de l'envoi de valeurs numériques à des actionneurs, une valeur BASSE correspond à 0 V et une valeur HAUTE correspond à la tension maximale de l'appareil. Pour le terminal Wio, la tension HAUTE ("HIGH" en anglais) est de 3,3V.
1. Reconnectez le terminal Wio à votre ordinateur et téléchargez le nouveau code comme vous l'avez fait précédemment.
1. Connectez le moniteur série. Les valeurs lumineuses seront transmises au terminal.
```sortie
> Executing task: platformio device monitor <
--- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at http://bit.ly/pio-monitor-filters
--- Miniterm on /dev/cu.usbmodem101 9600,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
Light value: 4
Light value: 5
Light value: 4
Light value: 158
Light value: 343
Light value: 348
Light value: 344
```
1. Couvrez et découvrez le capteur de lumière. Remarquez que la LED s'allume lorsque le niveau de luminosité est inférieur ou égal à 300, et s'éteint lorsque le niveau de luminosité est supérieur à 300.
![La LED connectée au WIO s'allume et s'éteint en fonction de l'intensité lumineuse.](../../../../images/wio-running-assignment-1-1.gif)
> 💁 Vous trouverez ce code dans le dossier [code-actuator/wio-terminal](../code-actuator/wio-terminal).
😀 Votre programme de veilleuse a été un succès!

@ -0,0 +1,73 @@
# Ajouter un capteur - le terminal Wio
Dans cette partie de la leçon, vous utiliserez le capteur de lumière sur votre terminal Wio.
## Matériel
Le capteur de cette leçon est un **capteur de lumière** qui utilise une [photodiode](https://fr.wikipedia.org/wiki/Photodiode) pour convertir la lumière en un signal électrique. Il s'agit d'un capteur analogique qui envoie une valeur entière comprise entre 0 et 1023 indiquant une quantité relative de lumière qui ne correspond à aucune unité de mesure standard telle que le [lux](https://wikipedia.org/wiki/Lux).
Le capteur de lumière est intégré au terminal Wio et est visible à travers la fenêtre en plastique transparent située à l'arrière.
![Le capteur de lumière au dos du terminal Wio](../../../../images/wio-light-sensor.png)
## Programmer le capteur de lumière
L'appareil peut maintenant être programmé pour utiliser le capteur de lumière intégré.
### Tâche
Programmez l'appareil.
1. Ouvrez le projet nightlight dans VS Code que vous avez créé dans la partie précédente de ce travail.
1. Ajoutez la ligne suivante à la fin de la fonction `setup` :
```cpp
pinMode(WIO_LIGHT, INPUT);
```
Cette ligne configure les broches utilisées pour communiquer avec le capteur.
La broche `WIO_LIGHT` est le numéro de la broche GPIO connectée au capteur de lumière embarqué. Cette broche est réglée sur `INPUT`, ce qui signifie qu'elle se connecte à un capteur et que les données seront lues à partir de la broche.
1. Effacez le contenu de la fonction `loop`.
1. Ajoutez le code suivant à la fonction `loop` maintenant vide.
```cpp
int light = analogRead(WIO_LIGHT);
Serial.print("Light value: ");
Serial.println(light);
```
Ce code lit une valeur analogique sur la broche `WIO_LIGHT`. Il lit une valeur comprise entre 0 et 1023 à partir du capteur de lumière embarqué. Cette valeur est ensuite envoyée au port série afin que vous puissiez la lire dans le Serial Monitor lorsque ce code est exécuté. `Serial.print` écrit le texte sans nouvelle ligne à la fin, de sorte que chaque ligne commence par `Light value:` et se termine par la valeur réelle de la lumière.
1. Ajoutez un petit délai d'une seconde (1000 ms) à la fin de la `boucle` (`loop` en anglais), car les niveaux de lumière n'ont pas besoin d'être vérifiés en permanence. Un délai réduit la consommation d'énergie de l'appareil.
```cpp
delay(1000);
```
1. Reconnectez le terminal Wio à votre ordinateur et téléchargez le nouveau code comme vous l'avez fait précédemment.
1. Connectez le moniteur série. Des valeurs lumineuses seront émises vers le terminal. Couvrez et découvrez le capteur de lumière à l'arrière du terminal Wio, et les valeurs changeront.
```sortie
> Executing task: platformio device monitor <
--- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at http://bit.ly/pio-monitor-filters
--- Miniterm on /dev/cu.usbmodem101 9600,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
Light value: 4
Light value: 5
Light value: 4
Light value: 158
Light value: 343
Light value: 348
Light value: 344
```
> 💁 Vous trouverez ce code dans le dossier [code-sensor/wio-terminal](../code-sensor/wio-terminal).
😀 L'ajout d'un capteur à votre programme de veilleuses a été un succès!

@ -8,12 +8,12 @@
## 主题
1. [应用物联网预测植物生长](lessons/1-predict-plant-growth/translations/README.zh-cn.md)
1. [检测土壤湿度](lessons/2-detect-soil-moisture/README.md)
1. [自动给植物浇水](lessons/3-automated-plant-watering/README.md)
1. [将你的植物迁移到云端](lessons/4-migrate-your-plant-to-the-cloud/README.md)
1. [将你的应用程序逻辑迁移到云端](lessons/5-migrate-application-to-the-cloud/README.md)
1. [确保你的植物安全](lessons/6-keep-your-plant-secure/README.md)
1. [应用物联网预测植物生长](../lessons/1-predict-plant-growth/translations/README.zh-cn.md)
1. [检测土壤湿度](../lessons/2-detect-soil-moisture/README.md)
1. [自动给植物浇水](../lessons/3-automated-plant-watering/README.md)
1. [将你的植物迁移到云端](../lessons/4-migrate-your-plant-to-the-cloud/README.md)
1. [将你的应用程序逻辑迁移到云端](../lessons/5-migrate-application-to-the-cloud/README.md)
1. [确保你的植物安全](../lessons/6-keep-your-plant-secure/README.md)
## 作者

@ -15,6 +15,8 @@
[![Korean](https://img.shields.io/badge/-Korean-white)](translations/README.ko.md)
[![Japanese](https://img.shields.io/badge/-Japanese-red)](translations/README.ja.md)
[![](https://dcbadge.vercel.app/api/server/ByRwuEEgH4)](https://discord.gg/zxKYvhSnVp?WT.mc_id=academic-000002-leestott)
# IoT for Beginners - A Curriculum
Azure Cloud Advocates at Microsoft are pleased to offer a 12-week, 24-lesson curriculum all about IoT basics. Each lesson includes pre- and post-lesson quizzes, written instructions to complete the lesson, a solution, an assignment and more. Our project-based pedagogy allows you to learn while building, a proven way for new skills to 'stick'.
@ -55,7 +57,7 @@ By ensuring that the content aligns with projects, the process is made more enga
In addition, a low-stakes quiz before a class sets the intention of the student towards learning a topic, while a second quiz after class ensures further retention. This curriculum was designed to be flexible and fun and can be taken in whole or in part. The projects start small and become increasingly complex by the end of the 12 week cycle.
Each project is be based around real-world hardware available to students and hobbyists. Each project looks into the specific project domain, providing relevant background knowledge. To be a successful developer it helps to understand the domain in which you are solving problems, providing this background knowledge allows students to think about their IoT solutions and learnings in the context of the kind of real-world problem that they might be asked to solve as an IoT developer. Students learn the 'why' of the solutions they are building, and get an appreciation of the end user.
Each project is based around real-world hardware available to students and hobbyists. Each project looks into the specific project domain, providing relevant background knowledge. To be a successful developer it helps to understand the domain in which you are solving problems, providing this background knowledge allows students to think about their IoT solutions and learnings in the context of the kind of real-world problem that they might be asked to solve as an IoT developer. Students learn the 'why' of the solutions they are building, and get an appreciation of the end user.
## Hardware
@ -76,7 +78,7 @@ We have two choices of IoT hardware to use for the projects depending on persona
- assignment
- post-lesson quiz
> **A note about quizzes**: All quizzes are contained [in this app](https://black-meadow-040d15503.1.azurestaticapps.net), for 48 total quizzes of three questions each. They are linked from within the lessons but the quiz app can be run locally; follow the instruction in the `quiz-app` folder. They are gradually being localized.
> **A note about quizzes**: All quizzes are contained in the quiz-app folder, for 48 total quizzes of three questions each. They are linked from within the lessons but the quiz app can be run locally or deployed to Azure; follow the instruction in the `quiz-app` folder. They are gradually being localized.
## Lessons
@ -132,11 +134,20 @@ Would you like to contribute a translation? Please read our [translation guideli
Our team produces other curricula! Check out:
- [Web Dev for Beginners](https://aka.ms/webdev-beginners)
- [ML for Beginners](https://aka.ms/ml-beginners)
- [Data Science for Beginners](https://aka.ms/datascience-beginners)
- [Generative AI for Beginners](https://aka.ms/genai-beginners)
- [Generative AI for Beginners .NET](https://github.com/microsoft/Generative-AI-for-beginners-dotnet)
- [Generative AI with JavaScript](https://github.com/microsoft/generative-ai-with-javascript)
- [Generative AI with Java](https://github.com/microsoft/Generative-AI-for-beginners-java)
- [AI for Beginners](https://aka.ms/ai-beginners)
- [Data Science for Beginners](https://aka.ms/datascience-beginners)
- [ML for Beginners](https://aka.ms/ml-beginners)
- [Cybersecurity for Beginners](https://github.com/microsoft/Security-101)
- [Web Dev for Beginners](https://aka.ms/webdev-beginners)
- [IoT for Beginners](https://aka.ms/iot-beginners)
- [XR Development for Beginners](https://github.com/microsoft/xr-development-for-beginners)
- [Mastering GitHub Copilot for Agentic use](https://github.com/microsoft/Mastering-GitHub-Copilot-for-Paired-Programming)
- [Mastering GitHub Copilot for C#/.NET Developers](https://github.com/microsoft/mastering-github-copilot-for-dotnet-csharp-developers)
- [Choose Your Own Copilot Adventure](https://github.com/microsoft/CopilotAdventures)
## Image attributions

322
package-lock.json generated

@ -576,32 +576,35 @@
"dev": true
},
"node_modules/depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"dev": true,
"engines": {
"node": ">= 0.6"
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
"dev": true
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"dev": true,
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/docsify": {
"version": "4.12.1",
"resolved": "https://registry.npmjs.org/docsify/-/docsify-4.12.1.tgz",
"integrity": "sha512-7v4UlCYLTmb83leJLIlheQlQ8kDTbTxcpMttRg0Uf92Nl//m0AcKFHoLLo5HHS4UhnO0KhDV8SKCdTR279zI9A==",
"version": "4.13.1",
"resolved": "https://registry.npmjs.org/docsify/-/docsify-4.13.1.tgz",
"integrity": "sha512-BsDypTBhw0mfslw9kZgAspCMZSM+sUIIDg5K/t1hNLkvbem9h64ZQc71e1IpY+iWsi/KdeqfazDfg52y2Lmm0A==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"dompurify": "^2.2.6",
"marked": "^1.2.9",
"medium-zoom": "^1.0.6",
"opencollective-postinstall": "^2.0.2",
"prismjs": "^1.23.0",
"prismjs": "^1.27.0",
"strip-indent": "^3.0.0",
"tinydate": "^1.3.0",
"tweezer.js": "^1.4.0"
@ -639,34 +642,39 @@
}
},
"node_modules/docsify-server-renderer": {
"version": "4.12.1",
"resolved": "https://registry.npmjs.org/docsify-server-renderer/-/docsify-server-renderer-4.12.1.tgz",
"integrity": "sha512-IYakkc+UxPS89N/Mq8MF4SKTQ1gtxN5nDEFAnJPf5TvQO+1fuxszHgv/hMprG5z/ms7PJb1w4nMykUfRW36+/A==",
"version": "4.13.1",
"resolved": "https://registry.npmjs.org/docsify-server-renderer/-/docsify-server-renderer-4.13.1.tgz",
"integrity": "sha512-XNJeCK3zp+mVO7JZFn0bH4hNBAMMC1MbuCU7CBsjLHYn4NHrjIgCBGmylzEan3/4Qm6kbSzQx8XzUK5T7GQxHw==",
"deprecated": "docsify-server-renderer 4.x and below is no longer supported while we investigate the future of SSR and SSG for Docsify",
"dev": true,
"dependencies": {
"debug": "^4.3.2",
"docsify": "^4.12.0",
"dompurify": "^2.2.6",
"node-fetch": "^2.6.0",
"debug": "^4.3.3",
"docsify": "^4.12.4",
"node-fetch": "^2.6.6",
"resolve-pathname": "^3.0.0"
}
},
"node_modules/docsify-server-renderer/node_modules/debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dev": true,
"dependencies": {
"ms": "2.1.2"
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/docsify-server-renderer/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
},
"node_modules/docsify-to-pdf": {
@ -691,12 +699,6 @@
"docsify-to-pdf": ".bin/cli.js"
}
},
"node_modules/dompurify": {
"version": "2.2.9",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.9.tgz",
"integrity": "sha512-+9MqacuigMIZ+1+EwoEltogyWGFTJZWU3258Rupxs+2CGs4H914G9er6pZbsme/bvb5L67o2rade9n21e4RW/w==",
"dev": true
},
"node_modules/dot-prop": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
@ -812,7 +814,7 @@
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"dev": true,
"engines": {
"node": ">= 0.6"
@ -902,7 +904,7 @@
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"dev": true,
"engines": {
"node": ">= 0.6"
@ -1086,19 +1088,28 @@
"dev": true
},
"node_modules/http-errors": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
"integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"dev": true,
"dependencies": {
"depd": "~1.1.2",
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.6"
"node": ">= 0.8"
}
},
"node_modules/http-errors/node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"dev": true,
"engines": {
"node": ">= 0.8"
}
},
"node_modules/https-proxy-agent": {
@ -2130,50 +2141,80 @@
}
},
"node_modules/send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"dev": true,
"dependencies": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
"statuses": "2.0.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
},
"node_modules/send/node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dev": true,
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/send/node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"dev": true,
"engines": {
"node": ">= 0.8"
}
},
"node_modules/serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
"dev": true,
"dependencies": {
"encodeurl": "~1.0.2",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
"send": "0.19.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/serve-static/node_modules/encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"dev": true,
"engines": {
"node": ">= 0.8"
}
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
@ -2181,9 +2222,9 @@
"dev": true
},
"node_modules/setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"dev": true
},
"node_modules/signal-exit": {
@ -2351,9 +2392,9 @@
}
},
"node_modules/toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"dev": true,
"engines": {
"node": ">=0.6"
@ -3236,28 +3277,27 @@
"dev": true
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"dev": true
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"dev": true
},
"docsify": {
"version": "4.12.1",
"resolved": "https://registry.npmjs.org/docsify/-/docsify-4.12.1.tgz",
"integrity": "sha512-7v4UlCYLTmb83leJLIlheQlQ8kDTbTxcpMttRg0Uf92Nl//m0AcKFHoLLo5HHS4UhnO0KhDV8SKCdTR279zI9A==",
"version": "4.13.1",
"resolved": "https://registry.npmjs.org/docsify/-/docsify-4.13.1.tgz",
"integrity": "sha512-BsDypTBhw0mfslw9kZgAspCMZSM+sUIIDg5K/t1hNLkvbem9h64ZQc71e1IpY+iWsi/KdeqfazDfg52y2Lmm0A==",
"dev": true,
"requires": {
"dompurify": "^2.2.6",
"marked": "^1.2.9",
"medium-zoom": "^1.0.6",
"opencollective-postinstall": "^2.0.2",
"prismjs": "^1.23.0",
"prismjs": "^1.27.0",
"strip-indent": "^3.0.0",
"tinydate": "^1.3.0",
"tweezer.js": "^1.4.0"
@ -3288,31 +3328,30 @@
}
},
"docsify-server-renderer": {
"version": "4.12.1",
"resolved": "https://registry.npmjs.org/docsify-server-renderer/-/docsify-server-renderer-4.12.1.tgz",
"integrity": "sha512-IYakkc+UxPS89N/Mq8MF4SKTQ1gtxN5nDEFAnJPf5TvQO+1fuxszHgv/hMprG5z/ms7PJb1w4nMykUfRW36+/A==",
"version": "4.13.1",
"resolved": "https://registry.npmjs.org/docsify-server-renderer/-/docsify-server-renderer-4.13.1.tgz",
"integrity": "sha512-XNJeCK3zp+mVO7JZFn0bH4hNBAMMC1MbuCU7CBsjLHYn4NHrjIgCBGmylzEan3/4Qm6kbSzQx8XzUK5T7GQxHw==",
"dev": true,
"requires": {
"debug": "^4.3.2",
"docsify": "^4.12.0",
"dompurify": "^2.2.6",
"node-fetch": "^2.6.0",
"debug": "^4.3.3",
"docsify": "^4.12.4",
"node-fetch": "^2.6.6",
"resolve-pathname": "^3.0.0"
},
"dependencies": {
"debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dev": true,
"requires": {
"ms": "2.1.2"
"ms": "^2.1.3"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
}
}
@ -3335,12 +3374,6 @@
"yesno": "^0.2.0"
}
},
"dompurify": {
"version": "2.2.9",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.9.tgz",
"integrity": "sha512-+9MqacuigMIZ+1+EwoEltogyWGFTJZWU3258Rupxs+2CGs4H914G9er6pZbsme/bvb5L67o2rade9n21e4RW/w==",
"dev": true
},
"dot-prop": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
@ -3434,7 +3467,7 @@
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"dev": true
},
"extract-zip": {
@ -3506,7 +3539,7 @@
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"dev": true
},
"fs-extra": {
@ -3647,16 +3680,24 @@
"dev": true
},
"http-errors": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
"integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"dev": true,
"requires": {
"depd": "~1.1.2",
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"dependencies": {
"statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"dev": true
}
}
},
"https-proxy-agent": {
@ -4459,44 +4500,67 @@
}
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"dev": true,
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
"statuses": "2.0.1"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
},
"on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dev": true,
"requires": {
"ee-first": "1.1.1"
}
},
"statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"dev": true
}
}
},
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
"dev": true,
"requires": {
"encodeurl": "~1.0.2",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
"send": "0.19.0"
},
"dependencies": {
"encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"dev": true
}
}
},
"set-blocking": {
@ -4506,9 +4570,9 @@
"dev": true
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"dev": true
},
"signal-exit": {
@ -4639,9 +4703,9 @@
}
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"dev": true
},
"tr46": {

@ -31,3 +31,83 @@ npm run lint
See [Configuration Reference](https://cli.vuejs.org/config/).
Credits: Thanks to the original version of this quiz app: https://github.com/arpan45/simple-quiz-vue
## Deploying to Azure
Heres a step-by-step guide to help you get started:
1. Fork the a GitHub Repository
Ensure your static web app code is in your GitHub repository. Fork this repository.
2. Create an Azure Static Web App
- Create and [Azure account](http://azure.microsoft.com)
- Go to the [Azure portal](https://portal.azure.com)
- Click on “Create a resource” and search for “Static Web App”.
- Click “Create”.
3. Configure the Static Web App
- Basics: Subscription: Select your Azure subscription.
- Resource Group: Create a new resource group or use an existing one.
- Name: Provide a name for your static web app.
- Region: Choose the region closest to your users.
- #### Deployment Details:
- Source: Select “GitHub”.
- GitHub Account: Authorize Azure to access your GitHub account.
- Organization: Select your GitHub organization.
- Repository: Choose the repository containing your static web app.
- Branch: Select the branch you want to deploy from.
- #### Build Details:
- Build Presets: Choose the framework your app is built with (e.g., React, Angular, Vue, etc.).
- App Location: Specify the folder containing your app code (e.g., / if its in the root).
- API Location: If you have an API, specify its location (optional).
- Output Location: Specify the folder where the build output is generated (e.g., build or dist).
4. Review and Create
Review your settings and click “Create”. Azure will set up the necessary resources and create a GitHub Actions workflow in your repository.
5. GitHub Actions Workflow
Azure will automatically create a GitHub Actions workflow file in your repository (.github/workflows/azure-static-web-apps-<name>.yml). This workflow will handle the build and deployment process.
6. Monitor the Deployment
Go to the “Actions” tab in your GitHub repository.
You should see a workflow running. This workflow will build and deploy your static web app to Azure.
Once the workflow completes, your app will be live on the provided Azure URL.
### Example Workflow File
Heres an example of what the GitHub Actions workflow file might look like:
name: Azure Static Web Apps CI/CD
```
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- main
jobs:
build_and_deploy_job:
runs-on: ubuntu-latest
name: Build and Deploy Job
steps:
- uses: actions/checkout@v2
- name: Build And Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
repo_token: ${{ secrets.GITHUB_TOKEN }}
action: "upload"
app_location: "quiz-app" #App source code path
api_location: ""API source code path optional
output_location: "dist" #Built app content directory - optional
```
### Additional Resources
- [Azure Static Web Apps Documentation](https://learn.microsoft.com/azure/static-web-apps/getting-started)
- [GitHub Actions Documentation](https://docs.github.com/actions/use-cases-and-examples/deploying/deploying-to-azure-static-web-app)

File diff suppressed because it is too large Load Diff

@ -18,8 +18,8 @@
"@vue/cli-plugin-eslint": "~5.0.8",
"@vue/cli-service": "~5.0.8",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"eslint": "^9.33.0",
"eslint-plugin-vue": "^10.4.0",
"vue-template-compiler": "^2.6.11"
},
"eslintConfig": {

Loading…
Cancel
Save