Je souhaite partager mon expérience dans le développement d'un appareil intelligent. Dans cette publication, je décrirai le matériel (brièvement) et le logiciel (plus en détail).
Le contrôleur est conçu pour analyser les lectures des capteurs (filaires et sans fil) et maintenir la température réglée (y compris le calendrier, y compris les jours de la semaine) dans chaque zone individuelle en allumant / éteignant la chaudière et en contrôlant les boucles de chauffage du plancher d'eau à l'aide des têtes thermiques du collecteur.
Matériel informatique
En tant que contrôleur, ESP8266 (WeMos D1 mini) a été sélectionné, a le wifi à bord. Au lieu d'ESP8266, tout autre contrôleur ou micro-ordinateur peut être utilisé - les idées générales resteront inchangées, l'essentiel est qu'un serveur WEB prenant en charge les sockets WEB puisse être déployé sur le système sélectionné. Les éléments suivants ont également été utilisés dans le projet:
- RTC: DS3231 - il est nécessaire de déterminer le jour de la semaine et l'heure actuelle. Le projet a été conçu comme un appareil autonome qui peut fonctionner sans Internet, donc NTP ne convient pas.
- Capteurs de température sans fil: NoName, 433 MHz, de la station météo chinoise - une solution clé en main, ils fonctionnent sur piles. De quoi d'autre avez-vous besoin? Mais il faut que la période de transfert des données ne soit pas fixe. Le problème est que la période de transmission est de 35 secondes et ne nage pas beaucoup. Et il y a des situations où les signaux de deux capteurs se chevauchent. Dans ce cas, un ou deux capteurs tombent du système pendant un certain temps. Le problème peut être résolu en utilisant des capteurs similaires, dans lesquels la commutation de canaux modifie également la période de transmission des données.
- Récepteur 433 MHz: Rxb6 - Les critiques sur Internet et l'expérience personnelle ne sont pas un mauvais récepteur.
- Capteurs de température filaires: DS18B20 - très pratique car vous n'avez pas besoin de connaître à l'avance le nombre de capteurs.
- Maître de bus 1Wire: DS2482-100 - Le protocole 1Wire est très sensible aux temporisations, donc toutes les implémentations du maître de bus de programme utilisent le retard, ce qui n'est pas très bon pour le multitâche. En utilisant cette puce, vous pouvez profiter du bus 1Wire et vous débarrasser de ses défauts en diffusant 1Wire <-> i2c. Le protocole i2c a une ligne de synchronisation, grâce à laquelle il n'est pas critique pour les temporisations et est souvent implémenté dans le matériel des contrôleurs.
- Horloge de surveillance: TPL5000DGST - la disponibilité continue n'est pas si importante pour ce projet, mais l'accessibilité est très importante. L'ESP8266 a une minuterie de surveillance intégrée. Mais comme la pratique l'a montré, il existe parfois des situations où il ne peut pas faire face et le système se bloque. Une minuterie de surveillance matérielle externe est conçue pour faire face aux situations d'urgence. Configuré pour un délai de 64 secondes. Attaché à la jambe TX - pendant le fonctionnement, le système écrit constamment des informations de débogage sur Serial et le manque d'activité pendant plus d'une minute indique un blocage du système.
- Extenseur de port: 74HC595 - l'utilisation de cet extenseur nécessite 4 branches du contrôleur - trois pour transmettre l'état et une pour que les relais ne cliquent pas lorsque l'alimentation est appliquée. La prochaine fois, j'utiliserai PCF8574 - le bus i2c est toujours utilisé, c'est-à-dire aucune branche MCU supplémentaire n'est requise et les sorties 1 sont définies lorsque l'alimentation est appliquée.
- Module relais: NoName, 8 canaux, 5V - il n'y a rien à dire, sauf que le relais est activé à un niveau bas aux entrées du module. Les relais statiques ne sont pas autorisés dans ce projet, car Les contacts de la chaudière doivent être commutés par un contact sec - en général, je ne connais pas la tension et le courant continu ou alternatif sur les contacts.
Système d'exploitation
Le système d'exploitation est l'ensemble des logiciels qui assurent l'opérabilité du programme d'application. Le système d'exploitation est également une couche entre le matériel et le programme d'application, fournissant une interface de haut niveau pour accéder aux ressources matérielles. Dans le cas d'ESP, les composants du système d'exploitation peuvent être considérés:
Système de fichiers
Le projet utilise SPIFFS, tout semble clair ici, c'est la manière la plus simple. Pour accéder facilement aux fichiers sur l'appareil depuis l'extérieur, j'utilise la bibliothèque nailbuster / esp8266FTPServer.
Système d'allocation de temps CPU
C'est l'une des principales fonctions du système d'exploitation et ESP ne fera pas exception. Pour l'exécution parallèle de différents flux de l'algorithme, l'objet global (singleton) Timers est responsable. La classe est assez simple et offre les fonctionnalités suivantes:
- Exécution périodique de la fonction, avec un intervalle spécifié. Exemple d'initialisation du minuteur:
Timers.add(doLoop, 6000, F("OneWireSensorsClass::doLoop"));
- Une seule exécution d'une fonction après une période de temps spécifiée. Par exemple, une analyse retardée des réseaux WiFi est effectuée de cette façon:
Timers.once([]() { WiFi.scanNetworks(true);}, 1);
Ainsi, la fonction de boucle ressemble à ceci:
void loop(void) { ESP.wdtFeed(); Timers.doLoop(); CPULoadInfo.doLoop(); }
En pratique, la fonction de boucle contient quelques lignes supplémentaires, qui seront décrites ci-dessous.
Une liste de la classe Timers est jointe.
Comptabilité du temps CPU
Fonction de service sans application pratique. Néanmoins, elle l'est. Implémenté par le singleton CPULoadInfo. Lorsque l'objet est initialisé, le nombre d'itérations de la boucle vide est mesuré pendant une courte période:
void CPULoadInfoClass::init() { uint32_t currTime = millis();
Ensuite, nous comptons le nombre d'appels de procédure de boucle par seconde, nous calculons la charge du processeur en pourcentage et enregistrons les données dans le tampon:
void CPULoadInfoClass::doLoop() { static uint32_t prevTime = 0; uint32_t currTime = millis(); LoopsInSecond++; if ((currTime - prevTime) > 1000) { memmove(CPULoadPercentHistory, &CPULoadPercentHistory[1], sizeof(CPULoadPercentHistory) - 1); int8_t load = ((MaxLoopsInSecond - LoopsInSecond) * 100) / MaxLoopsInSecond; CPULoadPercentHistory[sizeof(CPULoadPercentHistory) - 1] = load; prevTime = currTime; LoopsInSecond = 0; } }
L'utilisation de cette approche vous permet d'obtenir la même utilisation du processeur par chaque thread individuel (si vous connectez ce sous-système à la classe Timers), mais comme je l'ai dit - je ne vois aucune application pratique pour cela.
Système d'entrée-sortie
Pour communiquer avec l'utilisateur, les interfaces UART-USB et WEB sont utilisées. Je pense à UART, je n'ai pas besoin de parler en détail. La seule chose à laquelle il faut faire attention est la commodité et la compatibilité avec les non-ESP, la fonction serialEvent () est implémentée:
void loop(void) {
Avec l'interface WEB, tout est beaucoup plus intéressant. Nous y consacrons une section distincte.
Interface WEB
Dans le cas des appareils intelligents, à mon avis, l'interface WEB est la solution la plus conviviale.
Je considère que l'utilisation d'un écran connecté à l'appareil est une pratique obsolète - il est impossible de créer une interface simple, pratique et belle lorsque vous utilisez un petit écran et un ensemble limité de boutons.
L'utilisation de programmes spécifiques pour contrôler l'appareil impose des restrictions à l'utilisateur, ajoute la nécessité de développer et de prendre en charge ces programmes, et oblige également le développeur à prendre soin de la livraison de ces programmes aux terminaux de l'utilisateur. Dans le bon sens, l'application devrait être publiée dans les magasins d'applications Google, Apple et Windows, et également disponible dans les référentiels Linux sous la forme de packages deb et rpm - sinon, l'accès à l'interface de l'appareil peut être difficile pour une partie de l'audience.
L'accès à l'interface WEB de l'appareil est disponible depuis n'importe quel système d'exploitation - Linux, Windows, Android, MacOS, sur le bureau, ordinateur portable, tablette, smartphone - juste pour avoir un navigateur. Bien sûr, le développeur de l'interface WEB doit prendre en compte les caractéristiques des différents appareils, mais cela concerne principalement la taille et la résolution. L'accès à l'interface WEB d'un appareil intelligent dans une maison / un appartement / un chalet est facilement accessible de l'extérieur via Internet - maintenant, il est difficile d'imaginer une maison / un appartement dans lequel il y a des appareils intelligents et aucun routeur et Internet, et dans le routeur, cet accès est configuré en quelques clics (pour ceux qui complètement hors sujet, les mots-clés aideront - "redirection de port" et "DNS dynamique"). Dans le cas d'une résidence d'été, l'accès peut être fourni via un routeur 3G.
Pour implémenter l'interface WEB, un serveur WEB est requis. J'utilise la bibliothèque me-no-dev / ESPAsyncWebServer. Cette bibliothèque offre les fonctionnalités suivantes:
- Retour de contenu statique, y compris avec support de compression gzip. Les répertoires virtuels sont pris en charge, avec la possibilité de spécifier un fichier principal (qui est généralement index.htm) pour chaque répertoire.
- Affectation de fonctions de rappel à différentes URL en fonction du type de demande (GET, POST, ...)
- Prise en charge des sockets WEB sur le même port (cela est important lors de la redirection de port).
- Autorisation de base. De plus, l'autorisation est définie individuellement pour chaque URL. Ceci est important car par exemple, Google Chrome, lors de la création d'un raccourci de page sur l'écran principal, demande une icône et un fichier manifeste et ne transfère pas les données d'autorisation. Par conséquent, certains fichiers sont placés dans un répertoire virtuel et l'autorisation est désactivée pour ce répertoire.
Système d'exploitation des services HTTP
Dans le projet en cours, tous les paramètres du système d'exploitation sont effectués à l'aide des services HTTP. Le service HTTP est une petite fonctionnalité indépendante de récupération / modification de données disponible via HTTP. Ensuite, considérez une liste de ces services.
Aide
L'implémentation de toute liste de commandes, je pense qu'il est juste de commencer par l'implémentation de l'équipe HELP. Voici le bloc d'initialisation du serveur WEB:
void HTTPserverClass::init() {
Pourquoi ai-je besoin d'un système d'aide, je pense que ça ne vaut pas la peine de le dire. Certains développeurs reportent la mise en œuvre du système d'aide pour plus tard, mais ce «plus tard» ne se produit généralement pas. Il est beaucoup plus facile de le mettre en œuvre au début du projet et de le compléter pendant le développement du projet.
Dans mon projet, une liste de services possibles s'affiche également avec une erreur 404 - la page est introuvable. Actuellement mis en œuvre les services suivants:
http://tc-demo.vehs.ru/help /'doc_hame.ext': load file from server. Allow methods: HTTP_GET /info: get system info. Allow methods: HTTP_GET /time: get time as string (eg: 20140527T123456). Allow methods: HTTP_GET /uptime: get uptime as string (eg: 123D123456). Allow methods: HTTP_GET /rtc: set RTC time. Allow methods: HTTP_GET, HTTP_POST /list ? [dir=...] & [format=json]: get file list as text or json. Allow methods: HTTP_GET /edit: edit files. Allow methods: HTTP_GET, HTTP_PUT, HTTP_DELETE, HTTP_POST /wifi: edit wifi settings. Allow methods: HTTP_GET, HTTP_POST /wifi-scan ? [format=json]: get wifi list as text or json. Allow methods: HTTP_GET /wifi-info ? [format=json]: get wifi info as text or json. Allow methods: HTTP_GET /ap: edit soft ap settings. Allow methods: HTTP_GET, HTTP_POST /user: edit user settings. Allow methods: HTTP_GET, HTTP_POST /user-info ? [format=json]: get user info as text or json. Allow methods: HTTP_GET /update: update flash. Allow methods: HTTP_GET, HTTP_POST /restart: restart system. Allow methods: HTTP_GET, HTTP_POST /ws: web socket url. Allow methods: HTTP_GET /help: list allow URLs. Allow methods: HTTP_GET
Comme vous pouvez le voir, dans la liste des services, il n'y a pas de services d'application. Tous les services HTTP sont liés au système d'exploitation. Chaque service effectue une petite tâche. De plus, si le service nécessite la saisie de données, alors, sur demande, GET renvoie un formulaire de saisie minimaliste:
#ifdef USE_RTC_CLOCK help_info.concat(F("/rtc: set RTC time. Allow methods: HTTP_GET, HTTP_POST\n")); const char* urlNTP = "/rtc"; server.on(urlNTP, HTTP_GET, [](AsyncWebServerRequest *request) { DEBUG_PRINT(F("/rtc")); request->send(200, ContentTypesStrings[ContentTypes::text_html], String(F("<head><title>RTC time</title></head><body><form method=\"post\" action=\"rtc\"><input name=\"newtime\" length=\"15\" placeholder=\"yyyyMMddThhmmss\"><button type=\"submit\">set</button></form></body></html>"))); }); server.on(urlNTP, HTTP_POST, handleSetRTC_time); #endif

Plus tard, ce service est utilisé dans une interface plus jolie:

Logiciels d'application
Enfin, nous sommes arrivés au point pour lequel le système a été créé. À savoir - à la mise en œuvre de la tâche appliquée.
Toute application doit recevoir les données source, les traiter et produire le résultat. Il est également possible que le système signale l'état actuel.
Les données sources du régulateur de chauffage par le sol sont:
- Données de capteur - le système n'est pas lié à des capteurs spécifiques. Un identifiant unique est généré pour chaque capteur. Pour les capteurs radio, leur identifiant est complété par des zéros à 16 bits; pour les capteurs 1Wire, le CRC16 est calculé en fonction de leur identifiant interne et utilisé comme identifiant du capteur. Ainsi, tous les capteurs ont des identifiants d'une longueur de 2 octets.
- Données sur les zones de chauffage - le nombre de zones n'est pas fixe, le nombre maximum est limité par le module relais utilisé. Compte tenu de cette limitation, l'interface WEB a également été développée.
- Température et programme cibles - J'ai essayé de faire les réglages les plus flexibles, vous pouvez créer plusieurs schémas de chauffage et vous pouvez même attribuer votre propre schéma de réglages à chaque zone.
Ainsi, il existe un certain nombre de paramètres qui doivent être définis d'une manière ou d'une autre, et il existe un certain nombre de paramètres que le système signale comme l'état actuel.
Pour la communication entre le contrôleur et le monde extérieur, j'ai implémenté un interpréteur de commandes qui m'a permis de mettre en œuvre à la fois le contrôle du contrôleur et la réception des données d'état. Les commandes sont transmises au contrôleur sous une forme lisible par l'homme et peuvent être transmises via une prise UART ou WEB (si vous le souhaitez, vous pouvez implémenter la prise en charge d'autres protocoles, par exemple telnet).
La ligne de commande commence par le caractère «#» et se termine par un caractère nul ou un caractère de nouvelle ligne. Toutes les commandes se composent d'un nom de commande et d'un opérande, séparés par deux points. Pour certaines commandes, l'opérande est facultatif; dans ce cas, les deux-points et l'opérande ne sont pas spécifiés. Les commandes d'une ligne sont séparées par une virgule. Par exemple:
#ZonesInfo:1,SensorsInfo
Et bien sûr, la liste des commandes commence par la commande Aide, qui affiche une liste de toutes les commandes valides (pour plus de commodité, les commandes transmises commencent par un '>' au lieu de '#'):
>help Help SetZonesCount Zone SetName SetSensor ... LoadCfg SaveCfg #Cmd:Help,CmdRes:Ok
Une caractéristique de l'implémentation de l'interpréteur de commandes est que les informations sur le résultat de l'exécution de la commande sont également émises sous la forme d'une commande ou d'un ensemble de commandes:
>help … #Cmd:Help,CmdRes:Ok >zone:123 #Cmd:Zone,Value:123,CmdRes:Error,Error:Zone 123 not in range 1-5 >SchemasInfo #SchemasCount:2 #Schema:1,Name:,DOWs:0b0000000 #Schema:2,Name:,DOWs:0b0000000 #Cmd:SchemasInfo,CmdRes:Ok
Côté client WEB, un shell est également implémenté qui accepte ces commandes et les convertit en une vue graphique. Par exemple:
>zonesInfo:3 #Zone:3,Name:,Sensor:0x5680,Schema:1,DeltaT:-20 #Cmd:ZonesInfo,CmdRes:Ok
L'interface WEB a envoyé une requête au contrôleur concernant la zone numéro 3, et en réponse a reçu le nom de la zone, l'identifiant du capteur associé à la zone, l'identifiant du circuit affecté à la zone et la correction de température de la zone. La coquille ne comprend pas les nombres fractionnaires, donc la température est transmise en dixièmes de degré, c'est-à-dire 12,3 degrés est de 123 dixièmes.
La caractéristique clé est que le contrôleur répond à tous les clients à la fois pour n'importe quelle commande, quelle que soit la méthode d'entrée de la commande. Cela vous permet d'afficher immédiatement le changement d'état dans toutes les sessions de l'interface WEB. Parce que Le transport d'échange principal entre le contrôleur et l'interface WEB est les sockets WEB, puis le contrôleur peut transmettre des données sans demande, par exemple, lorsque de nouvelles données proviennent de capteurs:
#sensor:0x5A20,type:w433th,battery:1,button_tx:0,channel:0,temperature:228,humidity:34,uptime_label:130308243,time_label:20180521T235126
Ou, par exemple, que ces zones doivent être mises à jour:
#Zone:2,TargetTemp:220,CurrentTemp:228,Error:Ok
L'interface WEB du contrôleur est basée sur l'utilisation de commandes texte. Sur l'un des onglets de l'interface, il y a un terminal avec lequel vous pouvez saisir des commandes sous forme de texte. En outre, cet onglet, à des fins de débogage, vous permet de savoir quelles commandes l'interface WEB envoie et reçoit avec diverses actions utilisateur.
L'interpréteur de commandes facilite la modification et l'augmentation des fonctionnalités de l'appareil en modifiant les commandes existantes et en en ajoutant de nouvelles. Dans le même temps, le débogage d'un tel système est grandement simplifié, car la communication avec le contrôleur s'effectue exclusivement dans un langage lisible par l'homme.
Conclusion
En utilisant une approche similaire, à savoir:
- Séparation du logiciel en système d'exploitation et programme d'application
- Implémentation des paramètres du système d'exploitation sous la forme de services HTTP minimalistes
- Séparation de la logique système de la présentation des données
- Utilisation de protocoles de communication lisibles par l'homme
vous permet de créer des solutions compréhensibles par les utilisateurs et les développeurs. Ces solutions sont faciles à modifier. Sur la base de telles solutions, il est facile de construire de nouveaux appareils avec une logique complètement différente, mais qui fonctionneront sur les mêmes principes. Vous pouvez créer une ligne d'appareils avec le même type d'interface:

Comme vous pouvez le voir, dans ce projet, seules les trois premières pages de l'interface sont directement liées à l'application et les autres sont presque universelles.
Dans cette publication, je ne décris que mon opinion sur la construction d'appareils intelligents, et en aucun cas je ne prétends être la vérité ultime.
Pour qui ce sujet est intéressant - écrivez, peut-être que je me trompe sur quelque chose, mais peut-être que certains détails ont du sens à décrire plus en détail.
Qu'est-il arrivé à la fin:
Fiasco. L'histoire d'un IoT fait maison