Pub

vendredi 30 juillet 2021

Radio Signal Monitor / RXperiment (7) Epilogue !

 Encore un tout dernier article sur le sujet ! 😀

J'ai arrêté le Radio Signal Monitor, le montage fait avec le circuit TEA5767 car j'ai constaté des défauts rédhibitoires avec ce circuit :
 - trop peu de sensibilité, même avec une antenne télescopique rallongée.
 - une sélectivité trop mauvaise qui amène à recevoir les signaux indésirables de stations sur des fréquences adjacentes.
 - des mesures de niveau de réception finalement incohérentes par rapport aux observations faites en parallèle sur d'autres récepteurs. Ce qui pose question sur les niveaux qui étaient mesurés.

Je conserve donc maintenant en fonction et en ligne uniquement le montage que j'avais nommé « RXperiment2 ». Ce bricolage est composé d'une radio Panasonic RF2400D dont la LED « Tuning » est connectée à une carte microcontrôleur ESP32-D1-R32 par l'intermédiaire d'un optocoupleur fait-maison (pour éviter d'injecter des parasites dans le récepteur radio) plus une adaptation de niveau pour une entrée analogique sur l'ESP32 (je rajouterai un schéma en fin d'article).

Les mesures de réception de RXperiment2 sont en ligne ici :
 https://io.adafruit.com/guyvano/dashboards/rxperiment2 

RXperiment2 n'est pas parfait non plus, mais néanmoins il fait le job avec une meilleure sensibilité et une meilleure sélectivité. J'ai rallongé l'antenne télescopique du récepteur d'une trentaine de centimètres afin d'avoir environ la dimension d'un quart d'onde sur la bande FM.

Les avantages du récepteur Panasonic RF2400D pour ce bricolage :

 - Son prix à moins de 40 Euros.
 - L'accès facile à la LED « Tuning ».
 - Son fonctionnement sur secteur.
 - La réception FM et MW. Sur la gamme de fréquences MW, outre le monitoring de stations en ondes moyennes, le montage peut aussi servir de détecteur d'orage !

Le principal défaut du récepteur :

 - Un affichage analogique de la fréquence, délicat à régler sur une fréquence précise.

Néanmoins, on peut s'aider d'un autre récepteur à affichage numérique pour repérer une station faible sur laquelle se régler et faire l'écoute en parallèle lors de la syntonisation.

Lorsqu'on ne peut se baser sur la réception faible sur la fréquence écoutée, on peut s'aider d'un kit main libre Bluetooth si ce dernier intègre aussi un petit émetteur FM que l'on peut régler la fréquence souhaitée, la syntonisation précise du récepteur sera alors grandement facilitée. J'utilise cette méthode car je possède un kit main libre Plantronics K100 (l'inconvénient du Plantronics K100 : un incrément de fréquence par pas de 200 kHz avec la décimale des MHz toujours impaire).

Autres défauts du montage RXperiment :

- Comme on se base sur l'allumage de la LED 
« Tuning » du récepteur radio, le principe de la mesure du niveau de réception n'est pas la mesure du niveau linéaire et progressif du signal reçu mais une mesure élaborée à partir d'une information binaire : un signal reçu dépasse un certain seuil (ou pas). La mesure qui est publiée sur Internet correspond au pourcentage d'un nombre de fois où la LED Tuning a été trouvée allumée pendant un intervalle de mesures. C'est donc une mesure de durée de réception d'un signal fort. Mais néanmoins, je trouve que ce principe de mesure se prête finalement assez bien au type d'observations envisagées : la détection des réceptions sporadiques.

- Un autre défaut, lié à l'optocoupleur en parallèle sur la LED Tuning du récepteur, il a été fait-maison avec une LED et une photorésistance trouvée dans un starter-kit Arduino. Il y a un petit temps de réaction pour la photorésistance à changer de valeur lorsqu'elle est illuminée. En conséquence, les impulsions brèves de la LED Tuning du récepteur ne sont pas détectées, à moins qu'elles ne se répètent rapidement. Mais ce défaut n'est pas rédhibitoire.


Schéma de l'interface récepteur / microcontrôleur :



(montage initial)



Edit 29/08/2021 : Le remplacement de la résistance de 220 k par 100 k et 47 k en série permet d'avoir un niveau plus proche de zéro lorsque la photorésistance n'est pas illuminée par la LED de l'optocoupleur. Schéma modifié :


(remplacé 220k par 100k + 47k en série)

Edit 03/09/2021 : publication des valeurs brutes de niveaux

Depuis quelques temps, au lieu de pourcentages, je publie les niveaux bruts mesurés aux entrées analogiques des microcontrôleurs, ou plus précisément les valeurs moyennes de 30 mesures effectuées pendant 30 secondes (une mesure de niveau chaque seconde pour chaque récepteur). Ce ne sont donc plus des pourcentages de durée d'allumage des LED "Tuning" mais des moyennes de niveaux des 30 dernières mesures. Cela permet de voir plus finement des variations sur des niveaux plus faibles. Etant donné que les DAC des ESP32 sont sur 12 bits, la valeur des niveaux sur les entrées analogiques vont de 0 à 4095 au maximum. Dans la pratique, la valeur maxi peut être inférieure à 4095 car suivant le modèle de récepteur, le courant maximum fourni à la LED de l'optocoupleur n'est pas toujours suffisant pour illuminer fortement la photodiode. C'est le cas notamment pour le récepteur Panasonic RF2400D, où la valeur maximale du niveau sur l'entrée analogique du microcontrôleur plafonne vers 3200. Mais en fait, ce n'est pas vraiment gênant et c'est bien suffisant pour apprécier un ordre de grandeur, car ce ne sont pas les valeurs absolues qui importent mais plutôt leurs variations.

Voici un exemple de réception avec trois récepteurs différents en utilisant ce principe :



Pour connaître l'état des réceptions en cours :
consultez le document web : 
https://bit.ly/RXperiment


pour des infos complémentaires ou plus récentes
sur le sujet, 
suivez-moi sur Twitter  : @guyvano
(+ hashtag #RXperiment)


**** Cordiales 73 ! Guy - F8ABX - 30/07/2021, 29/08/2021, 03/09/2021 ****

samedi 26 juin 2021

Radio Signal Monitor / RXperiment (6) suite et fin du sujet

 La version mk4 du Radio Signal Monitor FM, soudée sur un shield de prototypage est en service !





La suppression des fils volants et de la breadboard élimine de nombreux parasites générés par le montage lui-même.
Le niveau plancher de réception est revenu à zéro, alors qu'un était presque toujours à un sur le prototype en fils volants. De l'importance, comme toujours, en radio : des liaisons courtes ! Mais bon, pas toujours évident à respecter lorsqu'on réalise un proto.





Le Radio Signal Monitor FM publie ses données à l'URL suivante :

  https://io.adafruit.com/guyvano/dashboards/rsmfm


Ceci clôt la série d'articles sur le Radio Signal Monitor / RXperiment.

A bientôt, et faites de beaux montages !


****  73 de Guy F8ABX - 26/07/2021 ****
Twitter: @guyvano 
https://twitter.com/guyvano


vendredi 25 juin 2021

Radio signal monitor / RXperiment (5)

 Je suis en train de tester la version mk4 de mon Radio Signal Monitor, comme indiqué précédemment, avec le circuit TEA5767. Il fonctionne beaucoup mieux que le VMA444 (Si4703) : meilleure sensibilité en réception, pas d'effet de main, prises d'antenne et d'écouteurs séparés.
J'ai rallongé l'antenne télescopique fournie avec le circuit avec du fil de fer car d'origine, elle ne fait que 24 cm ! La longueur totale de l'antenne est maintenant d'environ 70 cm.
Le TEA5767 n'a pas de décodeur RDS, c'est dommage, mais on a la possibilité d'avoir la force du signal reçu de 0 à 15, et c'est bien l'essentiel compte tenu de la finalité du montage.

Pour voir la force des signaux reçus, c'est ici : https://io.adafruit.com/guyvano/dashboards/rsmfm

A l'heure où j'écris cet article, c'est la fréquence 106,9 MHz qui est monitorée. Il n'y a pas de station locale sur cette fréquence, le but est de détecter des épisodes de propagation exceptionnelle des ondes radio (sporadiques E, propagation troposphérique exceptionnelle). Lors de ces épisodes, des stations lointaines sont captées.

Avec cette version 4, le niveau de réception est mesuré et transmis à la plateforme IOT d'Adafruit toutes les 30 secondes environ.
Si vous avez un compte sur cette plateforme, connectez-vous car le rafraîchissement des données sera alors automatique, sinon la page web du tableau de bord (dashboard) reste statique et il vous faudra la rafraîchir manuellement.

C'est toujours une carte microcontrôleur ESP32-D1-R32 qui assure le traitement et les communications, avec le TEA5767 en I2C, et en Wifi avec la plateforme IOT d'Adafruit en utilisant le protocole MQTT.
Un ordinateur est seulement requis pour le développement et le téléchargement du logiciel dans le microcontrôleur, après ça se débrouille tout seul. 

Si cette version mk4 fonctionne comme prévu, la fonction de baromètre connecté y sera transférée (circuit BMP280 en I2C) et la version mk3 qui tourne actuellement en parallèle pourra alors être arrêtée. La version mk3 mobilise un récepteur
hacké Sony que je souhaiterais démobiliser !


*** 73 de Guy F8ABX - 25/06/2021 ***


dimanche 6 juin 2021

Radio signal monitor / RXperiment (4)

edit 11/06/2021 : le décodage du RDS est actuellement désactivé en attendant la mise en service d'un autre circuit en remplacement de l'actuel, trop peu performant.

 L'affichage du RDS a été rajouté au projet Radio signal monitor / RXperiment.

voir en bas de la page : https://io.adafruit.com/guyvano/dashboards/rxperiment
ou à la page dédiée au log RDS : https://io.adafruit.com/guyvano/dashboards/rds

Une première version tourne actuellement en test avec un circuit Velleman VMA444 (circuit intégré Si4703) mais le résultat est assez décevant car le décodage du RDS n'est vraiment pas performant, même pour les stations locales puissantes. Il y a donc peu de chances que le circuit décode le RDS des stations reçues dans des conditions plus difficiles.

 



Actuellement, le nom de la station est envoyé au site IO Adafruit entre crochets [ ] selon un cycle de 30 secondes environ et la fréquence est rappelée tous les 10 cycles, sans crochet.

Une prochaine version sera réalisée avec un circuit à base de TEA5767, en espérant que le décodage du RDS sera meilleur et plus rapide.

Actuellement, le RSM* est réglé sur 101,3 MHz. C'est une fréquence sur laquelle il n'y a pas de station locale.
Le but est de détecter les phénomènes de propagation troposphérique exceptionnels, d'afficher un graphique des signaux reçus et si possible d'afficher le nom des stations reçues, dans la mesure où leur RDS est décodable.


(*) RSM : abrégé pour Radio Signal Monitor / RXperiment , le nom du projet ESP32 en cours.


.**** 73 de Guy F8ABX - 06/06/2021 ****

jeudi 20 mai 2021

Radio signal monitor / RXperiment - mon nouveau projet ESP32 (3)


 Attention : RXperiment / Radio signal monitor est désormais accessible via la plateforme IOT Adafruit. Les données sont transmises en MQTT à cette plateforme, les données ne sont plus transmises via APRS-IS.

Voici les nouveaux liens :

Le graphique du monitoring en cours : https://io.adafruit.com/guyvano/dashboards/rxperiment
   ( ou https://bit.ly/radiosignalmonitor )


copie d'écran de la page web :





La relève de la pression atmosphérique (QNH) est aussi transmise à la plateforme Adafruit :
https://io.adafruit.com/guyvano/dashboards/qnh
  ( ou https://bit.ly/barofareins )


copie d'écran de la page web :





Ce changement de plateforme a pour objectifs l'amélioration de la fiabilité dans le transfert des données et une meilleure sécurité.



**** 73 de Guy F8ABX - 20/05/2021 ****

lundi 10 mai 2021

Radio signal monitor / RXperiment - mon nouveau projet ESP32 (2)

Le câblage des composants :




Le raccordement à la carte microcontrôleur :




La version actuelle de RXperiment mk3 :

Les composants sont désormais soudés sur un shield de prototypage pour Arduino (compatible avec la carte ESP32-D1-32). Seul l'optocoupleur a été modifié, sa LED jaune a été remplacée par une LED rouge plus lumineuse pour un meilleur éclairement de la photorésistance.








Télémétrie publiée sur https://aprs.fi/telemetry/a/F8ABX-10 
 
  Graphique 1 : QNH (pression atmosphérique ramenée au niveau de la mer).
  Graphique 2 : Temp (température au niveau du montage).
  Graphique 3 : Hsnsr (H sensor : champ magnétique mesuré par la sonde à effet Hall du microcontrôleur).
  Graphique 4 ; Band (bande de fréquence, 0 : OFF, 1 : LF/LW, 2 : MF/MW, 3 : HF/SW, 4 : VHF/FM).
  Graphique 5 : RXprm (qualité de la réception en %)
     C'est le pourcentage du temps d'allumage de la LED "tune" du récepteur Sony, sur un échantillon de 256 mesures pendant 5 minutes.





réception sur 153 kHz pendant 24 heures.
La nuit, la station roumaine Antena Satelor est reçue. 




Etat de la réception en cours sur https://bit.ly/RXperiment
(pour avoir des précisions sur le monitoring en cours : la fréquence précise, la date et l'heure de début)


La pression atmosphérique est aussi publiée sur
 https://www.wunderground.com/dashboard/pws/IFAREI3


Et un grand merci à Peter Buchegger OE5BPA pour son « APRS-IS library for ESP32 » qui permet de publier simplement des données sur APRS-IS !
  info ici :  
https://www.arduino.cc/reference/en/libraries/aprs-is-lib/


*** 73 de Guy F8ABX - 10/05/2021 ***



jeudi 22 avril 2021

Radio signal monitor / RXperiment - mon nouveau projet ESP32 (1)

 Voici ma nouvelle réalisation autour d'un récepteur multi-bandes Sony, avec un circuit ESP32-D1-R32 avec quelques composants additionnels sur une planche à contacts pour prototypage.


Le but est d'évaluer le niveau relatif d'un signal reçu par la radio et de publier les résultats sous forme de données télémétriques avec d'autres informations via le réseau APRS-IS .
On peut voir ces donnés sur : https://aprs.fi/telemetry/a/F8ABX-10 

Il est transmis : la pression atmosphérique (QNH), la température (Temp) et le champ magnétique (Hsnsr) au niveau du circuit, la bande où se trouve la fréquence écoutée (Band) et bien sûr le niveau de réception (RXprm) puisque c'est ce qui m'intéresse au départ, c'est le 5ème et dernier graphique en bas de la page web dont l'URL est donné plus haut.

Le récepteur Sony ICF-SW7600G a subi un petit "hack" consistant à placer deux fils en parallèle sur sa LED rouge "Tune" afin de récupérer une tension directement liée au niveau de réception HF. A la suite de tests, comme une connexion directe sur une entrée analogique du microcontrôleur injectait trop de parasites dans le récepteur, il a fallu ajouter un couplage indirect. Après des essais non concluants par couplage magnétique avec le capteur à effet Hall intégré au microcontrôleur, j'ai finalement choisi un couplage par optocoupleur... Mais n'ayant pas d'optocoupleur, j'en ai fabriqué un, avec les composants que j'avais sous la main : une LED jaune et une photorésistance, le tout bien isolé de la lumière ambiante avec du chatterton.



avec les moyens du bord...



et voilà un optocoupleur !

A noter que la photorésistance n'est pas trop réactive lors de brèves impulsions. Une amélioration sera apportée prochainement.

Néanmoins pour l'instant, le circuit fait le travail demandé !


tests de réception sur la gamme de fréquences MW.


Méthode de mesure du signal :

Le niveau de réception est donné en pourcentage :
Sur une durée de 5 minutes, on effectue 256 mesures. A la fin de la période de collecte des mesures, on calcule le pourcentage que représente les valeurs qui ont dépassé un certain seuil. Ce seuil a été déterminé expérimentalement et correspond environ à la moitié de la tension mesurée lors d'une réception optimale.

Codification des bandes de fréquences (Band) + bits 1234 dans la télémétrie :

Band # 0 = OFF : pas de réception en cours (bits 0000).
Band # 1 = RxLF : réception en cours en LF/LW (bits 1000).
Band # 2 = RxMF : 
réception en cours en MF/MW (bits 0100).
Band # 3 = RxHF : 
réception en cours en HF/SW (bits 0010).
Band # 4 = RxVHF : réception en cours en VHF/FM (bits 0001).

Le circuit et le logiciel étant actuellement en test, je donnerai plus de détails sur cette réalisation dans un prochain article.
En attendant, vous pouvez voir les résultats publiés sur 
https://aprs.fi/telemetry/a/F8ABX-10 

Suivez aussi l'état de la réception en cours sur bit.ly/RXperiment
Vous saurez ainsi précisément sur quelle fréquence est le récepteur et depuis quand.


**** Cordiales 73 ! Guy F8ABX 22-28/04/2021 ****





samedi 27 mars 2021

Baromètre connecté : schéma et code

La toute première version du 22 mars dernier ne gérait pas correctement les problèmes de communication, mais après une modification du code le lendemain, le baromètre fonctionne depuis en permanence et il transmet correctement ses mesures à Weather Underground. La mesure de la pression atmosphérique ramenée au niveau de la mer (QNH) est transmise toutes les 5 minutes au site. En cas de problème de connexion, un nouvel essai est fait 2 minutes plus tard. Au delà de 5 échecs de connexion, le micro contrôleur redémarrera.

Les mesures sont consultables ici https://bit.ly/barogrelonges 

Faites défiler la page car le graphique est situé presque tout en bas. Par défaut, le site affiche en la pression en pouces de mercure mais l'on peut l'avoir en hPa (bouton réglages en haut à droite, puis °C pour avoir la pression en hecto-Pascal...).

Pour publier ses mesures sur Weather Underground, il faut d'abord s'être créé un compte et au moins un "device".
L'identifiant du device et son mot de passe associé sont indispensables pour la publication des mesures.
En ce qui concerne la connexion à Internet, il faudra rentrer le SSID et le mot de passe de votre connexion Wifi dans le programme.
Il faut de même renseigner correctement l'altitude du capteur (en mètres).
Donc en tout, il y a 5 constantes à personnaliser dans le programme.

Depuis le 23 mars 2021, cette version me donne satisfaction.


Le schéma de câblage des composants :





Le code de la v.2.0.0 du 23 mars 2021 :

/* Program name: esp32-baro-wu-2-0-0.ino
   Author: Guy Vanoverbeke @GuyVano
   Program last update (dd/mm/yyyy) : 23/03/2021 - V.2 R.0 C.0
   Arduino IDE V1.8.13
   Board: ESP-WROOM-32
   Function: Publish the barometric pressure from a BME280 sensor on the Weather Underground web site.
   Disclaimer:
   This program (in other words: this code, this software or this application) is a personal creation
   made as part of a hobby and it is given without guarantee of any kind and no support is provided.
   It is free of rights and can be reused freely as you wish.
*/

#include <Wire.h>
#include <WiFi.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
Adafruit_BME280 bme; // I2C
#include <HTTPClient.h>
//
// Wifi SSID and password
//
const char* ssid       = "my Wifi SSID";
const char* password   = "my Wifi password";

//
// Weather Underground device ID and password
//
const char* wudevid    = "my WU device ID";
const char* wudevpw    = "my WU device password";

//
const float myaltitude = 186; // my sensor altitude in meters
//
const boolean autcnx = true; // autorize the connection in http flag
const boolean spr = false;   // true for serial prints or false for no prints
const unsigned long ndly = 300000; // 300000 for 5 minutes. Normal delay between each data upload
const unsigned long edly = 120000; // 120000 for 2 minutes. Retry delay after error
//
float temp = 0;
float hr = 0;
float qfe = 0;
float myqnh = 0;
float myqnhinches = 0;
String wumsg; // http connection url + data to transmit
boolean error = false; // error flag
boolean rderr = false; // sensor read error flag
boolean cnxok = false; // http connection ok flag
int serr = 0; // sensor error counter
int herr = 0; // internet error counter
int hcode = 0; // returned http code
int i = 0; // loop control
String htxt; // returned http text
unsigned snsrsts; // sensor status
//
void setup() {
  //
  pinMode(19, OUTPUT); // red LED for error
  pinMode(23, OUTPUT); // green LED ok
  //
  do {
    digitalWrite(19, LOW);
    digitalWrite(23, HIGH);
    delay(500);
    digitalWrite(19, HIGH);
    digitalWrite(23, LOW);
    delay(500);
    i++;
  } while (i < 6);
  // start the serial connection
  if (spr) {
    Serial.begin(115200);
    // wait for serial monitor to open
    while (! Serial);
  }
  //
  // Connect to WiFi
  //
  if (spr) {
    Serial.println("Connecting to Wifi...");
  }
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    if (spr) {
      Serial.print(">");
    }
  }
  if (spr) {
    Serial.println();
    Serial.println(" Wifi connected.");
    //
    Serial.println();
    Serial.println("BME280 linking...");
  }
  // default settings
  snsrsts = bme.begin(0x76);
  if (!snsrsts) {
    rderr = true;
    error = true;
    if (spr) {
      Serial.println("BME280 sensor not found.");
    }
  }
}
void loop() {
  //
  HTTPClient http;
  rdSensor();
  if (spr) {
    prtValues();
  }
  bldwuMsg();
  if ((!rderr) & (autcnx)) {
    http.begin(wumsg);
    hcode = http.GET();
    htxt = http.getString();
  }
  if (spr) {
    Serial.println();
    Serial.print("Returned code: ");
    Serial.print(hcode);
    Serial.print(" / ");
    Serial.println(htxt);
  }
  if (hcode == 200) {
    // a return code 200 means success
    herr = 0;
    error = false;
    cnxok = true;
  } else {
    herr = herr + 1;
    cnxok = false;
    error = true;
    if (spr) {
      Serial.println();
      Serial.print("HTTP error #");
      Serial.print(herr);
      Serial.println(".");
    }
  }
  //
  //
  //
  if (error) {
    digitalWrite(19, HIGH);
  } else {
    digitalWrite(19, LOW);
  }
  if (cnxok) {
    digitalWrite(23, HIGH);
  } else {
    digitalWrite(23, LOW);
  }
  if (herr > 5) {
    // too much errors, restart the MCU
    ESP.restart();
  }
  //
  //
  //
  if (error) {
    delay((edly - 2000));
  } else {
    delay((ndly - 2000));
  }
  digitalWrite(19, LOW);
  digitalWrite(23, LOW);
  delay(2000);
  hcode = 0;
  htxt = ' ';
}
void prtValues() {
  //
  // Print Temperature, Humidity and barometric pressure
  //
  Serial.print("Temperature = ");
  Serial.print(temp);
  Serial.println(" °C");
  Serial.print("Humidity = ");
  Serial.print(hr);
  Serial.println(" % ");
  Serial.print("Pressure = ");
  Serial.print(myqnh);
  Serial.println(" hPa");
}
void rdSensor() {
  //
  temp = bme.readTemperature();
  hr = bme.readHumidity();
  qfe = bme.readPressure() / 100.0F;
  // myqnh = qfe + (myaltitude * 0.1205F); // previous used formulae
  myqnh = qfe / pow((1 - (myaltitude * (0.0065 / 288.15))), 5.255); // OACI standard atmosphear formulae
  myqnhinches = myqnh * 0.02953F;
  if ((myqnh < 860) | (myqnh > 1090)) {
    //
    // myqnh is out of range, probably error sensor or wiring
    //
    serr = serr + 1;
    rderr = true;
    error = true;
    if (spr) {
      Serial.println();
      Serial.print("Error reading sensor #");
      Serial.print(serr);
      Serial.println(".");
    }
  } else {
    serr = 0;
    rderr = false;
    error = false;
  }
  //
}
void bldwuMsg() {
  //
  // build the internet message for Weather Underground publishing
  //
  wumsg = "https://weatherstation.wunderground.com/weatherstation/updateweatherstation.php?ID=";
  wumsg = wumsg + wudevid + "&PASSWORD=" + wudevpw + "&dateutc=now&baromin=";
  wumsg = wumsg + myqnhinches + "&action=updateraw";
  //
  if (spr) {
    Serial.println();
    Serial.println(wumsg);
  }
}
// End of program 
esp32-baro-wu-2-0-0.ino - Thanks for watching !


Cordiales 73 !


**** Guy F8ABX - 27/03/2021 ***



lundi 22 mars 2021

Baromètre connecté avec ESP32

 Je viens de mettre à niveau mon circuit baromètre avec une nouvelle version 2.0 connectée. Cette fois-ci, je n'utilise plus d'Arduino Uno mais un ESP32 car ce dernier a l'avantage d'intégrer nativement le Wifi.
Le logiciel est pour l'instant une version de développement un peu frustre mais une version un peu plus présentable devrait voir le jour et je la publierai dans un autre article prochainement.


Le circuit :


Les composants sont réduits au minimum vital : la carte micro contrôleur ESP-WROOM-32, le module de mesure BME280 qui communique avec le micro contrôleur en I2C, deux LED et deux résistances de 220 ohms. Et bien sûr la plaque de prototypage, quelques fils de câblage et une alimentation secteur 5V.

Comme le montage est autonome, c'est-à-dire qu'il fonctionne seul sans le recours d'un pc, j'ai donc souhaité avoir un affichage minimal pour m'assurer de son bon fonctionnement :
La LED verte indique que la dernière connexion au serveur Internet de Weather Underground a réussi. La LED rouge indique qu'il y a eu un problème de connexion Internet ou bien un problème de mesure.

Actuellement, le baromètre publie ses mesures de pression atmosphérique (QNH) toutes les 5 minutes sur Weather Underground et l'on peut les voir ici :

 https://www.wunderground.com/dashboard/pws/IFAREI3/ 

ou via le raccourci https://bit.ly/barogrelonges

Par défaut sur Weather Underground, les pressions barométriques sont indiquées en pouces de mercure, mais on peut les avoir en hecto-Pascal en cliquant tout en haut à droite sur l'engrenage puis °C afin de passer en système métrique.

A bientôt !


**** Guy F8ABX - 22, 25/03/2021 ****


mardi 16 mars 2021

myTinyPLC : nouvelle version 1.3

 Voici la nouvelle version 1.3 de myTinyPLC avec désormais la possibilité de rentrer les instructions de programmation à partir du moniteur série.
Cela permet de programmer ou de reprogrammer myTinyPLC sans devoir modifier le programme dans l'IDE Arduino ni le recompiler !

Il y a maintenant deux modes de fonctionnement : le mode RUN où le programme s'exécute normalement et le mode EDIT où l'exécution est arrêtée et où l'on a alors la possibilité de rentrer les instructions du programme IL via le moniteur série. Compte tenu de la mémoire réduite de l'Arduino Uno, le mode EDIT est assez rudimentaire, j'en conviens.

Le mode EDIT permet néanmoins les actions suivantes :
- Lister le programme avec les numéros des pas, les codes instructions et leurs mnémoniques respectives.
- Saisir le programme en rentrant pour chaque pas de programme, son code instruction.
- Insérer un pas de programme si besoin, et décaler les suivants.
- Si besoin, on peut désactiver un pas de programme en rentrant l'instruction 80 (NOP : no operation) car il n'y a pas de suppression de pas de programme.


Mode opératoire :

myTinyPLC démarre automatiquement en mode RUN. Si des instructions sont déjà pré renseignées dans le programme C++ dans le tableau d'instructions stp[n], myTinyPLC va les exécuter.

Pour modifier ou saisir de nouvelles instructions, on passe en mode EDIT en tapant 0 puis Entrée (zéro et on valide) 
avec le clavier du pc en utilisant le moniteur série de l'IDE. Puis on se laisse guider par l'affichage du moniteur série sur le pc.


Voici un petit schéma de principe de l'arborescence du mode EDIT :


[RUN mode] (RUN is the default mode at start-up)
 |
 |
 +---> 0 <Enter> --->[EDIT mode]
     (zero)            |
                       +-
--> 0 <Enter> -+-> 0 <Enter> Exit EDIT mode ---->[RUN mode]
                       |                |
                       |                +-> 1 <Enter> [List the instructions]
                       |                                     |
                       |                                     +--> 0 <Enter>
                       |                                     |        |     End list
                       |                                     |        +-->[EDIT mode]
                       |                                     |    
                       |                                     +--> 1 <Enter
                       |                                              |     Continue
                       |                                              +-->[List inst]
                       |
                       |
                       +---> n <Enter> --[edit step n]--> op <Enter> ---->[EDIT mode]
                             :                             : 
                         step number                  instruction code
                        n from 1 to 99                 op from 0 to 99
                                                   if 88 then insert a step


Le code de myTinyPLC en version 1.3.0 du 16/03/2021 :

/* Program name: mytinyPLC-1-3-0.ino
   Author: Guy Vanoverbeke @GuyVano
   Program last update (dd/mm/yyyy) : 16/03/2021 - V.1 R.3 C.0
   - Release name "Dare Dary"
   Arduino IDE V1.8.13
   Board: Arduino UNO R3
   Function: PLC simulator with its own IL language.
    Simulateur d'automate programmable doté de son propre langage IL.
   -----------
   Disclaimer:
   This program (in other words: this code, this software or this application) is
   a personal creation made as part of a hobby and it is given without guarantee
   of any kind and no support is provided. It is free of rights and can be reused
   freely as you wish.
*/
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
//
boolean i1 = 0; // used to store input 1 value
boolean i2 = 0; // used to store input 2 value
boolean i3 = 0; // used to store input 3 value
boolean i4 = 0; // used to store input 4 value
//
boolean p[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // potentiometer indexed position 0 to 9
//
boolean m1 = 0; // used to store memory 1 value
boolean m2 = 0; // used to store memory 2 value
boolean m3 = 0; // used to store memory 3 value
boolean m4 = 0; // used to store memory 4 value
boolean m5 = 0; // used to store memory 5 value
boolean m6 = 0; // memory 6 is tm6 starter
boolean m7 = 0; // memory 7 is tm7 starter
boolean m8 = 0; // memory 8 is tm8 starter
boolean m9 = 0; // memory 9 is tm9 starter
//
boolean pm6 = 0; // previous state of memory 6
boolean pm7 = 0; // previous state of memory 7
boolean pm8 = 0; // previous state of memory 8
boolean pm9 = 0; // previous state of memory 9
//
boolean tm6 = 0; // timer 6
boolean tm7 = 0; // timer 7
boolean tm8 = 0; // timer 8
boolean tm9 = 0; // timer 9
//
boolean mx = 0; // temporary memory used for Swap operation
//
boolean o1 = 0; // used to store output 1 value
boolean o2 = 0; // used to store output 2 value
boolean o3 = 0; // used to store output 3 value
boolean o4 = 0; // used to store output 4 value
boolean stack[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // bit Stack array
boolean edtmode = false; // true when edit mode
//
unsigned int ai1 = 0; // analog input 1 value calculated
int j = 0; // loop control
int k = 0; // loop control and temporary variable
long r = 0; // temporary variable
long s = 0; // temporary variable
long t = 0; // temporary variable
int istack0 = 0; //
//
const int nbstp = 99; //number of maximum IL program steps starting from 1
int stp[(nbstp + 1)] = {}; // IL program steps array dim = nbstp + 1
int stc = 0; // step counter
//
long ctrtm6 = 0; // current millis counter when timer 6 rised to High
long ctrtm7 = 0; // current millis counter when timer 7 rised to High
long ctrtm8 = 0; // current millis counter when timer 8 rised to High
long ctrtm9 = 0; // current millis counter when timer 9 rised to High
//
long dtm6 = 8000; // set default delay value for timer 6 in ms
long dtm7 = 2000; // set default delay value for timer 7 in ms
long dtm8 = 6000; // set default delay value for timer 8 in ms
long dtm9 = 4000; // set default delay value for timer 9 in ms
//
String ope = "ab";
//

void setup()
{
  //
  Serial.begin(9600); // open the serial port at 9600 bps:
  //
  pinMode(A1, INPUT); // buton 1 as input 1
  pinMode(A2, INPUT); // buton 2 as input 2
  pinMode(A3, INPUT); // buton 3 as input 3
  pinMode(8, INPUT);  // buton 4 as input 4
  //
  pinMode(10, OUTPUT); // LED 1 (red) as output 1
  pinMode(11, OUTPUT); // LED 2 (yellow) as ouput 2
  pinMode(12, OUTPUT); // LED 3 (green) as output 3
  pinMode(13, OUTPUT); // LED 4 (blue) as output 4
  //
  j = 0;
  do {
    j++;
    stp[j] = 80; // write NOP in all steps
  } while (j <= (nbstp));
  //
  //
  lcd.begin(16, 2);
  lcd.print("mytinyPLC v1-3-0");
  lcd.setCursor(0, 1);
  lcd.print("project @guyvano");
  delay(1000);
  lcd.clear();
  //
  // v---- your IL program steps here / Votre programme IL ici
  //
  //
  // Light on the LEDs following the potentiometer position
  // IL program for myTinyPLC v.1.2 and over.
  //
  stp[1] = 71; // Read p(1)
  stp[2] = 41; // Write output 1
  stp[3] = 72; // Read p(2)
  stp[4] = 42; // Write output 2
  stp[5] = 73; // Read p(3)
  stp[6] = 43; // Write output 3
  stp[7] = 74; // Read p(4)
  stp[8] = 44; // Write output 4
  stp[9] = 99; // END
  //
  //
  // ^---- end of your IL program / fin de votre programme
  //
  // it is a good idea to comment each line with the mnemonic of
  // the instruction, for a better IL program reading and understanding
  //
  //
  Serial.println(' ');
  Serial.println("in Run mode, type 0 <Enter> to enter Edit mode");
  Serial.println(' ');
}

void loop()
{

  //
  // If 0 <Enter> typed from the serial monitor is zero, run the edit module
  //
  if (Serial.available() > 0) {
    r = Serial.read();
    if (r == '0') {
      edtmode = true;
      edit();
    }
  }
  //
  // update IL program instruction counter
  // (one IL step evaluated per loop)
  //
  stc = (stc % nbstp) + 1;
  //
  // digital input acquisition
  // at least for displaying them later
  // even if they are note read by the IL program
  //
  i1 = digitalRead(A1);
  i2 = digitalRead(A2);
  i3 = digitalRead(A3);
  i4 = digitalRead(8);
  //
  // analog input acquisition
  //
  ai1 = analogRead(A0) / 113;      // value 0..9
  //
  // set corresponding p[] array element
  //
  j = 0;
  do {
    // set all to 0
    p[j] = 0;
    j++;
  } while (j <= 9);
  p[ai1] = 1; // set to 1 the corresponding p[] array element
  //
  //
  // Start IL instruction scrutation (one IL step per loop)
  //
  //
  // check if instruction code is a one figure number and
  // if it is, consider it is a number and stack it in the integer stack
  //
  switch (stp[stc]) {
    //
    case 0:
      //
      istack0 = 0;
      break;
    //
    case 1:
      //
      istack0 = 1;
      break;
    //
    case 2:
      //
      istack0 = 2;
      break;
    //
    case 3:
      //
      istack0 = 3;
      break;
    //
    case 4:
      //
      istack0 = 4;
      break;
    //
    case 5:
      //
      istack0 = 5;
      break;
    //
    case 6:
      //
      istack0 = 6;
      break;
    //
    case 7:
      //
      istack0 = 7;
      break;
    //
    case 8:
      //
      istack0 = 8;
      break;
    //
    case 9:
      //
      istack0 = 9;
      break;
    //
    default:
      // no instruction match
      break;
  }
  //
  //
  // check if operation code is Read an input (instruction codes 10 to 14)
  //
  switch (stp[stc]) {
    //
    case 10:
      // read analog input 1
      istack0 = ai1;
      break;
    //
    case 11:
      // read digital input 1
      dwnstack();
      stack[0] = i1;
      break;
    //
    case 12:
      // read digital input 2
      dwnstack();
      stack[0] = i2;
      break;
    //
    case 13:
      // read digital input 3
      dwnstack();
      stack[0] = i3;
      break;
    //
    case 14:
      // read digital input 4
      dwnstack();
      stack[0] = i4;
      break;
    //
    default:
      // no instruction match
      break;
  }
  //
  // check if instruction code is Read a digital memory (instruction codes 21 to 29)
  //
  switch (stp[stc]) {
    //
    case 21:
      // read digital memory 1
      dwnstack();
      stack[0] = m1;
      break;
    //
    case 22:
      // read digital memory 2
      dwnstack();
      stack[0] = m2;
      break;
    //
    case 23:
      // read digital memory 3
      dwnstack();
      stack[0] = m3;
      break;
    //
    case 24:
      // read digital memory 4
      dwnstack();
      stack[0] = m4;
      break;
    //
    case 25:
      // read digital memory 5
      dwnstack();
      stack[0] = m5;
      break;
    //
    case 26:
      // read timer 6 (bit)
      dwnstack();
      stack[0] = tm6;
      break;
    //
    //
    case 27:
      // read timer 7 (bit)
      dwnstack();
      stack[0] = tm7;
      break;
    //
    //
    case 28:
      // read timer 8 (bit)
      dwnstack();
      stack[0] = tm8;
      break;
    //
    case 29:
      // read timer 9 (bit)
      dwnstack();
      stack[0] = tm9;
      break;
    //
    default:
      // no operation match
      break;
  }
  //
  // check if instruction is Read an output (instruction codes 31 to 34)
  //
  switch (stp[stc]) {
    //
    case 31:
      // read digital output 1
      o1 = digitalRead(10);
      dwnstack();
      stack[0] = o1;
      break;
    //
    case 32:
      // read digital output 2
      o2 = digitalRead(11);
      dwnstack();
      stack[0] = o2;
      break;
    //
    case 33:
      // read digital output 3
      o3 = digitalRead(12);
      dwnstack();
      stack[0] = o3;
      break;
    //
    case 34:
      // read digital output 4
      o4 = digitalRead(13);
      dwnstack();
      stack[0] = o4;
      break;
    //
    default:
      // no instruction match
      break;
  }
  //
  // check if instruction is Write a digital output (instruction codes 41 to 44)
  //
  switch (stp[stc]) {
    //
    case 41:
      // write digital output 1
      o1 = stack[0];
      // digitalWrite(10, o1);
      break;
    //
    case 42:
      // write digital output 2
      o2 = stack[0];
      // digitalWrite(11, o2);
      break;
    //
    case 43:
      // write digital output 3
      o3 = stack[0];
      // digitalWrite(12, o3);
      break;
    //
    case 44:
      // write digital output 4
      o4 = stack[0];
      // digitalWrite(13, o4);
      break;
    //
    default:
      // no instruction match
      break;
  }
  //
  // check if instruction is Write a memory (instruction codes 51 to 59)
  //
  switch (stp[stc]) {
    //
    case 51:
      // write digital memory 1
      m1 = stack[0];
      break;
    //
    case 52:
      // write digital memory 2
      m2 = stack[0];
      break;
    //
    case 53:
      // write digital memory 3
      m3 = stack[0];
      break;
    //
    case 54:
      // write digital memory 4
      m4 = stack[0];
      break;
    //
    case 55:
      // write digital memory 5
      m5 = stack[0];
      break;
    //
    case 56:
      // write memory 6
      m6 = stack[0];
      break;
    //
    case 57:
      // write memory 7
      m7 = stack[0];
      break;
    //
    case 58:
      // write memory 8
      m8 = stack[0];
      break;
    case 59:
      // Start timer 9
      m9 = stack[0];
      break;
    //
    default:
      // no instruction match
      break;
  }
  //
  // check if instruction is to Write bit 0 or 1 (instruction codes 60 to 61)
  //
  switch (stp[stc]) {
    //
    case 60:
      // write 0
      dwnstack();
      stack[0] = 0;
      break;
    //
    case 61:
      // write 1
      dwnstack();
      stack[0] = 1;
      break;
    //
    default:
      // no instruction match
      break;
  }
  //
  // check if instruction is Set Timer Delay (instruction codes 66 to 69)
  // value in istack0 must be the delay in seconds.
  //
  switch (stp[stc]) {
    //
    case 66:
      // write timer 6 delay
      dtm6 = 1000 * istack0;
      break;
    //
    case 67:
      // write timer 7 delay
      dtm7 =  1000 * istack0;
      break;
    //
    case 68:
      // write timer 8 delay
      dtm8 =  1000 * istack0;
      break;
    //
    case 69:
      // write timer 9 delay
      dtm9 = 1000 * istack0;
      break;
    //
    default:
      // no instruction match
      break;
  }
  //
  // check if instruction is Read analog index potentiometer position
  //
  switch (stp[stc]) {
    case 70:
      // read p[0]
      dwnstack();
      stack[0] = p[0];
      break;
    //
    case 71:
      // read p[1]
      dwnstack();
      stack[0] = p[1];
      break;
    //
    case 72:
      // read p[2]
      dwnstack();
      stack[0] = p[2];
      break;
    //
    case 73:
      // read p[3]
      dwnstack();
      stack[0] = p[3];
      break;
    //
    case 74:
      // read p[4]
      dwnstack();
      stack[0] = p[4];
      break;
    //
    case 75:
      // read p[5]
      dwnstack();
      stack[0] = p[5];
      break;
    //
    //
    case 76:
      // read p[6]
      dwnstack();
      stack[0] = p[6];
      break;
    //
    case 77:
      // read p[7]
      dwnstack();
      stack[0] = p[7];
      break;
    //
    case 78:
      // read p[8]
      dwnstack();
      stack[0] = p[8];
      break;
    //
    case 79:
      // read p[9]
      dwnstack();
      stack[0] = p[9];
      break;
    //
    default:
      // no instruction match
      break;
  }
  //
  // check if instruction is a boolean operation (instruction codes 81 to 84)
  //
  switch (stp[stc]) {
    //
    case 81:
      // AND
      stack[1] = stack[0] & stack[1];
      upstack();
      break;
    //
    case 82:
      // OR
      stack[1] = stack[0] | stack[1];
      upstack();
      break;
    //
    case 83:
      // NOT
      if (stack[0] == 0) {
        stack[0] = 1;
      }
      else {
        stack[0] = 0;
      }
      break;
    //
    case 84:
      // XOR
      stack[1] = stack[0] ^ stack[1];
      upstack();
      break;
    //
    default:
      // no instruction match
      break;
  }
  //
  // check if instruction is a stack operation
  //
  switch (stp[stc]) {
    //
    case 91:
      // DUP
      dwnstack();
      stack[0] = stack[1];
      break;
    //
    case 92:
      // SWAP
      mx = stack[0];
      stack[0] = stack[1];
      stack[1] = mx;
      break;
    //
    case 93:
      // DROP
      upstack();
      break;
    //
    default:
      // no instruction match
      break;
  }
  //
  // scrutation end of IL program array
  // ----------------------------------
  //
  //
  //     timer 6 evaluation
  //
  //
  if ((m6 == 1) & (pm6 == 0)) {
    if  (tm6 == 0) {
      // tm6 start
      ctrtm6 = millis();
      tm6 = 1;
      pm6 = 1;
    } else {
      // tm6 already started
      pm6 = 1;
    }
  }
  //
  if ((m6 == 0) & (pm6 == 1)) {
    // m6 has just felt to Low
    pm6 = 0;
  }
  if ((tm6 == 1) & ((ctrtm6 + dtm6) <= millis())) {
    // delay over switch off tm6
    tm6 = 0;
  }
  //
  //     timer 7 evaluation
  //
  if ((m7 == 1) & (pm7 == 0)) {
    if  (tm7 == 0) {
      // tm7 start
      ctrtm7 = millis();
      tm7 = 1;
      pm7 = 1;
    } else {
      // tm7 already started
      pm7 = 1;
    }
  }
  //
  if ((m7 == 0) & (pm7 == 1)) {
    // m7 has just felt to Low
    pm7 = 0;
  }
  if ((tm7 == 1) & ((ctrtm7 + dtm7) <= millis())) {
    // delay over switch off tm7
    tm7 = 0;
  }
  //
  //     timer 8 evaluation
  //
  if ((m8 == 1) & (pm8 == 0)) {
    if  (tm8 == 0) {
      // tm8 start
      ctrtm8 = millis();
      tm8 = 1;
      pm8 = 1;
    } else {
      // tm8 already started
      pm8 = 1;
    }
  }
  //
  if ((m8 == 0) & (pm8 == 1)) {
    // m8 has just felt to Low
    pm8 = 0;
  }
  if ((tm8 == 1) & ((ctrtm8 + dtm8) < millis())) {
    // delay over switch off tm8
    tm8 = 0;
  }
  //
  //     timer 9 evaluation
  //
  if ((m9 == 1) & (pm9 == 0)) {
    if  (tm9 == 0) {
      // tm9 start
      ctrtm9 = millis();
      tm9 = 1;
      pm9 = 1;
    } else {
      // tm9 already started
      pm9 = 1;
    }
  }
  //
  if ((m9 == 0) & (pm9 == 1)) {
    // m9 has just felt to Low
    pm9 = 0;
  }
  if ((tm9 == 1) & ((ctrtm9 + dtm9) < millis())) {
    // delay over switch off tm9
    tm9 = 0;
  }
  //
  //   write digital outputs value
  //
  digitalWrite(10, o1);
  digitalWrite(11, o2);
  digitalWrite(12, o3);
  digitalWrite(13, o4);
  //
  // display inputs on LCD line 1
  //
  lcd.setCursor(0, 0);
  lcd.print("i");
  lcd.print(i1);
  lcd.print(i2);
  lcd.print(i3);
  lcd.print(i4);
  //
  lcd.setCursor(5, 0);
  lcd.print("(");
  lcd.print(ai1);
  lcd.print(")");
  //
  //
  lcd.setCursor(12, 0);
  lcd.print("RUN ");

  //
  // display outputs and memories on LCD line 2
  //
  lcd.setCursor(0, 1);
  lcd.print("o");
  lcd.print(o1);
  lcd.print(o2);
  lcd.print(o3);
  lcd.print(o4);
  lcd.print(" m");
  lcd.print(m1);
  lcd.print(m2);
  lcd.print(m3);
  lcd.print(m4);
  lcd.print(m5);
  lcd.print(tm6);
  lcd.print(tm7);
  lcd.print(tm8);
  lcd.print(tm9);
  //
  // Check if End of program
  //
  if (stp[stc] == 99) {
    stc = 0;
  }
}
void dwnstack()
{
  //
  // shift the stack elements down
  //
  stack[9] = stack[8]; // note : previous stack[9] is lost
  stack[8] = stack[7];
  stack[7] = stack[6];
  stack[6] = stack[5];
  stack[5] = stack[4];
  stack[4] = stack[3];
  stack[3] = stack[2];
  stack[2] = stack[1];
  stack[1] = stack[0];
  stack[0] = 0;
}

void upstack()
{
  //
  // shift the stack elements up
  //
  stack[0] = stack[1];
  stack[1] = stack[2];
  stack[2] = stack[3];
  stack[3] = stack[4];
  stack[4] = stack[5];
  stack[5] = stack[6];
  stack[6] = stack[7];
  stack[7] = stack[8];
  stack[8] = stack[9];
  stack[9] = 0;
}

void edit()
{
  Serial.setTimeout(90000);
  do {
    //
    lcd.setCursor(12, 0);
    lcd.print("EDIT");
    //
    Serial.println("  ");
    Serial.println("Edit mode");
    Serial.println("=========");
    Serial.println("  ");
    Serial.println("Type 0 <Enter> to exit Edit mode or to list the IL program.");
    Serial.println("Type a line number <Enter> to modify the program step.");
    Serial.println(' ');
    Serial.println("Command (0/n)?");
    r = Serial.parseInt();
    Serial.print(">");
    Serial.println(r);
    if (r != 0) {
      Serial.println("  ");
      Serial.print("The actual program step ");
      Serial.print(r);
      Serial.println(" is:");
      Serial.print(r);
      Serial.print(':');
      Serial.println(stp[r]);
      Serial.println("  ");
      Serial.println("Please, complete the step with the new instruction code or 88 to insert.");
      Serial.print(r);
      Serial.print(": ?");
      s = Serial.parseInt();
      Serial.print(">");
      Serial.println(s);
      if (s == 88) {
        // shift the next steps
        Serial.println("  ");
        Serial.print("inserting a new step and shifting the following steps...");
        j = nbstp + 2;
        do {
          j--;
          stp[j] = stp[(j - 1)];
        } while (r < j);
        stp[r] = 80; // NOP
        Serial.println("  ");
        Serial.print("The new step ");
        Serial.print(r);
        Serial.println(" has been inserted.");
        Serial.print(r);
        Serial.print(':');
        Serial.println(stp[r]);
      } else {
        stp[r] = s;
        Serial.println("  ");
        Serial.println("The new step is:");
        Serial.print(r);
        Serial.print(':');
        Serial.println(stp[r]);
      }
    } else {
      Serial.println("  ");
      Serial.println("Command (0/1)? 0 <Enter>:Exit / 1 <Enter>:Program list");
      s = Serial.parseInt();
      Serial.print(">");
      Serial.println(s);
      switch (s) {
        case 0:
          edtmode = false;
          //
          break;
        //
        case 1:
          //
          j = 0;
          Serial.println("  ");
          Serial.println("  ");
          Serial.println("Program list:");
          Serial.println("  ");
          do {
            j++;
            Serial.print(j);
            Serial.print(':');
            Serial.print(stp[j]);
            Serial.print("\t");
            switch (stp[j])  {
              case 0:
                ope = "dec nbr 0";
                break;
              case 1:
                ope = "dec nbr 1";
                break;
              case 2:
                ope = "dec nbr 2";
                break;
              case 3:
                ope = "dec nbr 3";
                break;
              case 4:
                ope = "dec nbr 4";
                break;
              case 5:
                ope = "dec nbr 5";
                break;
              case 6:
                ope = "dec nbr 6";
                break;
              case 7:
                ope = "dec nbr 7";
                break;
              case 8:
                ope = "dec nbr 8";
                break;
              case 9:
                ope = "dec nbr 9";
                break;
              case 10:
                ope = "R analog inp.1";
                break;
              case 11:
                ope = "R inp.1";
                break;
              case 12:
                ope = "R inp.2";
                break;
              case 13:
                ope = "R inp.3";
                break;
              case 14:
                ope = "R inp.4";
                break;
              case 21:
                ope = "R mem.1";
                break;
              case 22:
                ope = "R mem.2";
                break;
              case 23:
                ope = "R mem.3";
                break;
              case 24:
                ope = "R mem.4";
                break;
              case 25:
                ope = "R mem.5";
                break;
              case 26:
                ope = "R tmr 6";
                break;
              case 27:
                ope = "R tmr 7";
                break;
              case 28:
                ope = "R tmr 8";
                break;
              case 29:
                ope = "R tmr 9";
                break;
              case 31:
                ope = "R outp.1";
                break;
              case 32:
                ope = "R outp.2";
                break;
              case 33:
                ope = "R outp.3";
                break;
              case 34:
                ope = "R outp.4";
                break;
              case 41:
                ope = "W outp.1";
                break;
              case 42:
                ope = "W outp.2";
                break;
              case 43:
                ope = "W outp.3";
                break;
              case 44:
                ope = "W outp.4";
                break;
              case 51:
                ope = "W mem.1";
                break;
              case 52:
                ope = "W mem.2";
                break;
              case 53:
                ope = "W mem.3";
                break;
              case 54:
                ope = "W mem.4";
                break;
              case 55:
                ope = "W mem.5";
                break;
              case 56:
                ope = "W tmr 6";
                break;
              case 57:
                ope = "W tmr 7";
                break;
              case 58:
                ope = "W tmr 8";
                break;
              case 59:
                ope = "W tmr 9";
                break;
              case 60:
                ope = "bit 0";
                break;
              case 61:
                ope = "bit 1";
                break;
              case 66:
                ope = "tmr 6 delay";
                break;
              case 67:
                ope = "tmr 7 delay";
                break;
              case 68:
                ope = "tmr 8 delay";
                break;
              case 69:
                ope = "tmr 9 delay";
                break;
              case 70:
                ope = "R p(0)";
                break;
              case 71:
                ope = "R p(1)";
                break;
              case 72:
                ope = "R p(2)";
                break;
              case 73:
                ope = "R p(3)";
                break;
              case 74:
                ope = "R p(4)";
                break;
              case 75:
                ope = "R p(5)";
                break;
              case 76:
                ope = "R p(6)";
                break;
              case 77:
                ope = "R p(7)";
                break;
              case 78:
                ope = "R p(8)";
                break;
              case 79:
                ope = "R p(9)";
                break;
              case 80:
                ope = "NOP";
                break;
              case 81:
                ope = "AND";
                break;
              case 82:
                ope = "OR";
                break;
              case 83:
                ope = "NOT";
                break;
              case 84:
                ope = "XOR";
                break;
              case 91:
                ope = "DUP";
                break;
              case 92:
                ope = "SWAP";
                break;
              case 93:
                ope = "DROP";
                break;
              case 99:
                ope = "END";
                break;
              //
              default:
                ope = "(unassigned)";
                break;
            }
            Serial.println(ope);
            if (j % (10) == 0) {
              Serial.println("  ");
              Serial.println("  ");
              Serial.println("Command (0/1)? 0 <Enter> Quit list / 1 <Enter> Continue list");
              t = Serial.parseInt();
              Serial.print(">");
              Serial.println(t);
              if (t == 0) {
                j = nbstp;
              }
            }

          } while ((stp[j] != 99) & ( j < nbstp));
          Serial.println("  ");
          Serial.println("End list.");
      }
    }
  } while (edtmode == true);
  Serial.setTimeout(1000);
  Serial.println("  ");
  Serial.println("End Edit mode, back to normal RUN mode.");
  Serial.println("Type 0 <Enter> for Edit mode.");
  //
  lcd.setCursor(12, 0);
  lcd.print("RUN ");
  //
  loop();
}
// End of myTinyPLC (version 1.3.0 "Dare Dary") - Thanks for watching !


Remarques :

La mémoire interne de l'Arduino Uno commence à être bien remplie ! En l'état de cette version, le compilateur affiche un message d'avertissement : La mémoire disponible faible, des problèmes de stabilité pourraient survenir.

Je souhaitais au départ pouvoir rajouter des servo-moteurs sur myTinyPLC mais j'abandonne cette idée car il faudrait développer la logique pour les calculs et il n'y a clairement plus de place pour cela. Je garde donc le projet myTinyPLC en l'état actuel de sa version 1.3 présentée ci-dessus.

En résumé : myTinyPLC est un simulateur d'automate programmable réalisé avec un Arduino Uno R3 et quelques composants courants d'un starter kit.
 
Il est possède :
 - 
son propre langage de programmation, de type Instruction List, et pouvant se programmer sans recourir à une recompilation dans l'IDE Arduino.
 - une entrée analogique : un potentiomètre, dont on peut connaître la position de 0 à 9 (0 = valeur minimale, en position tournée complètement vers la gauche, et 9  = 
valeur maximale, en position tournée complètement vers la droite). Avec les codes d'instructions de lecture du potentiomètre 70 à 79, il fait fonction de commutateur.
 - 4 entrées binaires (boutons poussoirs).
 - 4 sorties binaires (LEDs).
 - 5 mémoires binaires 0/1 internes (m1 à m5)
 - 4 temporisations réglables de 1 à 9 secondes (tmr6 à tmr9).
 - 2 modes de fonctionnement : un mode Exécution (RUN) et un mode Programmation (EDIT) pour entrer les instructions du programme IL avec le moniteur série.
 - Un afficheur LCD1602 ( 2 lignes de 16 caractères) permet de visualiser l'état des entrées binaires et du potentiomètre, ainsi que l'état des sorties et des mémoires, sans oublier le mode fonctionnement RUN ou EDIT.








Composants et câblage :


Composants :

1 Arduino Uno R3.
1 planche à contacts pour prototypage (breadboard 163 x 54 mm).
1 écran LCD1602.
4 boutons poussoirs.
2 potentiomètres 10 k ohms.
5 résistances 330 ohms 1/8W.
4 résistances 10 k ohms 1/8W.
1 LED rouge.
1 LED jaune.
1 LED verte.
1 LED bleue.
et un certain nombre de fils de câblage.


**** Guy F8ABX - 16/03/2021 ****