Curs 4. 0004990600 Internet de les coses (IoT)

De wikijoan
Salta a la navegació Salta a la cerca

Introducció

jordibinefa@electronics.cat
25/6/2018 - 29/6/2018 (32 hores) de 9:00h a 14:00h
	INS Rambla Prim
C. Cristòbal de Moura, 223 (Barcelona) 
Continguts   	* Plaques amb microcontroladors
- Arduino Nano
- ESP8266
- ESP32
- Preparació de les eines de programació
* Sensors i actuadors
- Traductors bidireccionals dels nivells de senyal
- Sensors digitals d'entrada (teclats, tractament dels rebots)
- Actuadors (relés, motors de contínua, servomotors, motors pas a pas)
- BME280 (sensor de temperatura, humitat, pressió i altímetre)
- Pantalles LCD
- Pantalles OLED
* Protocols de comunicació entre els microcontroladors i els sensors i actuadors
- UART
- I2C (TWI)
- SPI
- One Wire Interface
* Sistemes operatius a Internet de les Coses
- Basats en Linux (Raspbian)
- Mongoose OS
* Comunicació entre dispositius
- Port sèrie
- RS485
- Bluetooth
- Ethernet (per cable i sense fils)
- Freqüències de ràdio lliures (SX1276 - xip LoRa)
* Transport de les dades
- ModBus RTU
- UDP
- TCP (telnet, HTTP, MQTT, ModBus TCP)
- LPWAN (SigFox, LoRaMAC, LoRaWAN)
- Seguretat (encriptació AES)
* Visualització i control
- Node-Red
- TheThingsNetwork
- IFTTT
- Serveis al núvol (AWS, plot.ly, thingspeak)
- Scada Indusoft
- Pantalles Nextion
- PyQt

Enllaços

Jornada 1

[1]

Per instal.lar el mòdul ESP32 a l'Ubuntu, seguim:

A l'arrencar l'Arduino una altra vegada ja tindrem disponible la placa DOIT ESP32 DEVKIT V1 (aquesta és compatible amb la nostra placa (TTGO board), que no vol dir que sigui la única que funcioni).

El github del Jordi Binefa:

esp32_BME280_WeatherStation_04

És la primera prova de compilar la placa ESP32 (amb el sensor de temperatura BME280)

esp32_lorawan_ttnEsp32_ttgo_multichannel_02b

Primera prova per connectar-nos amb un gateway de Lorawan

The Things Network:

creem un usuari: joanillo / jq****

Presa de contacte > Conjunt IoT. Prova de connectivitat a The Things Network

El Jordi Binefa ha portat una antena (gateway a classe) Anem a crear una nova app a TTN

ADD APPLICATION

  • Application ID: primera_app_joanillo
  • Description: Node a Rambla Prim

Application EUI (end user interface) EUI issued by The Things Network -> no posem res Handler registration

70B3D57ED00100ED Hem de registrar un device:

device id: dispositiu-07 (07 és el meu)
65/43/61/74/30/30/31/07 (07 és el meu)
Device EUI: 6543617430303107
Registrem el dispositiu: 
Application ID: primera_app_joanillo
Device ID: dispositiu-07
Activation Method: OTAA -> canviar-ho per ABP a Settings. Frame Counter Checks ho desconnectem
Device EUI: 6543617430303107

Application EUI: 70B3D57ED00100ED
Device Address: 2601149C
Network Session Key: 6CA585F5B6B2E8F9CB40D55504763975 -> { 0x6C, 0xA5, 0x85, 0xF5, 0xB6, 0xB2, 0xE8, 0xF9, 0xCB, 0x40, 0xD5, 0x55, 0x04, 0x76, 0x39, 0x75 }
App Session Key: 048AD239AB661D2D894B7AA5157B195D -> { 0x04, 0x8A, 0xD2, 0x39, 0xAB, 0x66, 0x1D, 0x2D, 0x89, 0x4B, 0x7A, 0xA5, 0x15, 0x7B, 0x19, 0x5D }

Canvio el codi al fitxer credentials.h:

  static const PROGMEM u1_t NWKSKEY[16] = { 0x6C, 0xA5, 0x85, 0xF5, 0xB6, 0xB2, 0xE8, 0xF9, 0xCB, 0x40, 0xD5, 0x55, 0x04, 0x76, 0x39, 0x75 };
  static const u1_t PROGMEM APPSKEY[16] = { 0x04, 0x8A, 0xD2, 0x39, 0xAB, 0x66, 0x1D, 0x2D, 0x89, 0x4B, 0x7A, 0xA5, 0x15, 0x7B, 0x19, 0x5D };
  static const u4_t DEVADDR = 0x2601149C ;

Aquest codi envia la informació del sensor BME280. Fixar-se que no utilitza la llibreria per al sensor d'Adafruit, sinó que ho fa a pèl.

M'he registrat a TTN, i s'envien les dades del ESP32 a l'antena, i l'antena està connectada al núvol (a TTN). I per tant, veuré les dades del meu sensor a la capa d'aplicació i visualització que representa TTN.

Jornada 2

[2]

  • la meva IP 192.168.1.129
  • IP de la RPi: 192.168.1.103

Ens connectem per SSH

Si comprem una ESP32, és millor ficar-hi una antena una mica més bona extra, una antena 868Mhz (a Europa).

nota: he comprat la TTGO LoRa32 V2.0 868 MHz

El professor ha configurat una xarxa inal·làmbrica ad-hoc que es diu IoT_eCat_RPi (és un punt d'accés basat en raspberry) (fer-ho com a pràctica).

En la Raspberry tenim una petita pantalla OLED que ens diu la IP. Això és molt pràctic. Ens connectem a la xarxa wireless de la RPi, i això és molt pràctic perquè independitzem la xarxa que farem servir de l'institut, no ens barregem amb l'institut. I així no tindrem problemes. Com fer-ho?

Si estiguéssim en Windows instal.laríem el Filezilla per tenir accés als arxius de la raspberry pi. Nosaltres ho fem amb ssh i scp.

PCF8574 (www.ti.com/lit/ds/symlink/pcf8574.pdf): 8-bit input/output (I/O) expander for the two-line bidirectional bus (I2C) is designed for 2.5-V to 6-V.

Es tracta de ficar a la RPi, en la llista de wifis conegudes, la de l'institut (SSID: Robotica)

$ sudo cat /etc/wpa_supplicant/wpa_supplicant.conf

Afegim la xarxa de l'institut

country=ES
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
  ssid="IoT-eCat_RPi"
  psk="clotClot"
  priority=3
}

network={
  ssid="IoT-eCat"
  psk="clotClot"
  priority=5
}

network={
  ssid="Robotica2018"
  psk="1123prim2018"
  priority=10
}

la que té el número més gros de priority la que guanya

En la RPi tinc funcionant dos serveis: nodered i mosquitto:

$ service nodered status
● nodered.service - Node-RED graphical event wiring tool
   Loaded: loaded (/lib/systemd/system/nodered.service; enabled; vendor preset: 
   Active: active (running) since Mon 2018-06-25 12:07:05 UTC; 19h ago

pi@raspberrypi:~ $ service nodered mosquitto
nodered: unrecognized service
$ service mosquitto status
● mosquitto.service - LSB: mosquitto MQTT v3.1 message broker
   Loaded: loaded (/etc/init.d/mosquitto; generated; vendor preset: enabled)
   Active: active (running) since Mon 2018-06-25 12:07:07 UTC; 19h ago

A IoT és útil el programa/servei screen, que pot posar serveis en segon pla, i que són immunes als talls de xarxa. És una manera de què si perdo la connexió (a IoT això pot passar sovint), després recupero el funcionament.

$ screen
$ ping 1.1.1.1 -> fa un ping indefinit
ctrl-A D -> sortim de la sessió de la RPi (o posa el procés en segon pla)
exit
Tornem a entrar a la RPi
$ screen -r -> recuperem el ping que estava fent. El ping ha estat funcionant en segon pla.

El nodered (a la RPi) funciona pel port 1880:

nodered: Flow-based programming for the Internet of Things

Gateway és l'antena. Es diu gateway (passarel.la) perquè de fet passa d'un medi (antena, air) a un altre medi (connexió Ethernet que es connecta al núvol).

Placa witty ESP8266 (val 3-8 e a eBay)

És una placa petiteta. Codi de prova:

Va a 3,3V, i això és important.

A Arduino és habitual fer delays, però en aquesta mena de dispositius connectats per wifi és desaconsellable, i s'utilitza yield().

delayESP8266(3);

void delayESP8266(unsigned long ulMilliseconds){
  unsigned long ulPreviousMillis = millis();

  do{
    yield();
  }while(millis() - ulPreviousMillis <= ulMilliseconds);
}

Per programar la placa witty amb l'Arduino es pot programar amb la placa NodeMCU1.0 (igual que la placa NodeMCU que tinc).

Utilitza com a xip de comunicacions serial el CH340 (no el FT232)

Em connectaré amb el gtkterm (sèrie), i enviaré comandes. Per ex, un tros del codi:

  if(szMsg == "a" || szMsg == "A"){
    Serial.print("LDR level (0..1024): ");
    Serial.println(analogRead(A0));
  }

Em conencto pel gtkterm, i li puc enviar comandes:

$ gtkterm -p /dev/ttyUSB0 -s 9600

ProvesUDP. Escoltarem per exemple pel port 5555

A nodered es fa servir el port 1880 netstat per saber els ports que estan oberts $ sudo netstat -putan en la RPi miro els ports que estan oberts, i escullo un port lliure

tcp        0      0 0.0.0.0:1880            0.0.0.0:*               LISTEN      229/node-red        
tcp        0      0 0.0.0.0:1883            0.0.0.0:*               LISTEN      299/mosquitto       
i altres coses

Posem al nodered un escoltador UDP, i diem que escolti pel port 5555 (ha de ser inferior a 64000) Ouput fico un debug, i els uneixo

També fico un injector (en el payload fico String), i fico hola a tots, que també connecto amb el debug (msg.payload). L'injector té un botonet per injectar i veure com funciona el flux.


També fico un output de udp, per a un port. M'ho puc enviar a mi mateix, o puc enviar a la ip (i port) d'un company. També podem enviar missatges UDP per línia de comandes. Els missatges UDP es poden perdre, no està garantit el rebre'l. Amb TCP sí que està garantida la connexió.

$ echo -n "hello" | nc -4u 192.168.1.103 5555

Anem a fer aquest exemple: esp8266_udpClient_01.ino

En el codi fico la informació correcta:

const char* ssid     = "IoT-eCat";
const char* password = "clotClot";

const char * udpAddress = "192.168.1.103";
const int udpPortTx = 5555;

I per tant el que farem ara és que el ESP8266 enviarà un paquet UDP, i el nostre nodered (a la RPi) tenim un escoltador de UDPs.


El codi que hem pujat al ESP8266 (esp8266_udpClient_01.ino) és el següent:

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>


#ifdef ESP8266
extern "C" {
  #include "user_interface.h"
}
#endif

const char* ssid     = "IoT-eCat";
const char* password = "clotClot";

const char * udpAddress = "192.168.1.103";
const int udpPortTx = 5555;

WiFiUDP Udp;

void setup() {
  Serial.begin(115200);
  delay(10);

  Serial.print("\n\r\n\rConnecting to ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  
  Udp.beginPacket(udpAddress, udpPortTx);  
}

void vGetMac(char *temp){
  static int i = 0;
  
#ifdef ESP8266
  uint8_t macaddr[6];   // array that will receive the MAC address from the chip
  wifi_get_macaddr(0x00, macaddr);
  os_sprintf(temp, "%05d) %02x:%02x:%02x:%02x:%02x:%02x\r\n", i, macaddr[0],
      macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
#endif

  i = (++i >= 0) ? i : 0;
}

void loop() {
  char temp[28];      // buffer for formatting the output (xx:xx:xx:xx:xx:xx\r\n) = 20 bytes

  vGetMac(temp);

  Udp.write(temp);
  Udp.endPacket();

  Serial.print(temp);
  Serial.println(" sent to UDP server");
    
  delay(5000);
}

I el que fa és avariguar la MAC del dispositiu, i enviar-la per UDP (Udp.write(temp);). Aquest missatge el rep el nodered de la RPi (i ho estic visualitzant en el portàtil).

Jornada 3

Jornada 4

Presentació TCP/UDP:

Aplicació d'android: wifi analyzer, per veure el nivell que tenim de les diferents xarxes inal·làmbriques disponibles.

La diferència entre TCP i UDP és que el TCP es preocupa de què la trama hagi arribat, i si no arriba dóna error. A més, en TCP les trames van numerades i es reordenen en el destí. UDP és una sola trama.

Presentació Node-RED:

Exemples Node-RED:

payload vol dir la part útil de la informació.

TODO: fer la pràctica d'Openweathermap i Node-RED. Em connecto a OpenweatherMap, i envio un correu electrònic.

Si el node de OpenWeatherMap no el tenim, s'ha d'instal·lar:

npm install node-red-node-openweathermap

Settings > Pallette > Install (amb el pip no m'ha funcionat). He d'anar a la pàgina web, donar-me d'alta, i aconseguir la API key.

  • API de openweathermap: e2aec1bc0914a482546efb1b491a6f2a

Coses per mirar

Mirar el tema de Node-RED i JSON, switch, dashboards

  • influxdb + grafana
  • modBus
  • Wemos espurna

MQTT

Gauge.png

Per veure les gauges i dashboards he d'anar a:

Mqtt nodered.png

Presentació MQTT:

Exemple mqtt nodered witty.png

Farem aquest exemple: esp8266_MQTT_pub_sub_01.ino, que carreguem en la placa witty, on hi ha un led rgb integrat:

// Based on https://www.baldengineer.com/mqtt-tutorial.html
//
// https://wiki.binefa.cat

#include <EEPROM.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>

#define EXTERNAL_BUTTON 4

// Connect to the WiFi
const char* ssid = "Robotica2018";
const char* password = "1123prim2018";
const char* mqtt_server = "popotamo.binefa.cat";
const int mqtt_port = 1888; // normally 1883
//const char* mqtt_server = "test.mosquitto.org"; //broker
//const int mqtt_port = 1883; // normally 1883

#define TEMA_PUBLICA_ESTAT_BOTO "/joan/witty/boto"
#define TEMA_SUBSCRIPCIO_ESTAT_LEDS "/joan/witty/leds"

WiFiClient espClient;
PubSubClient client(espClient);

const byte ledPin = 2, ledRed = 15, ledGreen = 12, ledBlue = 13;
const byte button = EXTERNAL_BUTTON;

void callback(char* topic, byte* payload, unsigned int length) {
  String szRx = "", szTema(topic);

  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    char receivedChar = (char)payload[i];
    szRx += receivedChar;
  }
  Serial.println(szRx);
  if (szTema == TEMA_SUBSCRIPCIO_ESTAT_LEDS) {
    if (szRx == "2L") digitalWrite(ledPin, LOW);
    if (szRx == "2H") digitalWrite(ledPin, HIGH);
    if (szRx == "12L") digitalWrite(ledBlue, LOW);
    if (szRx == "12H") digitalWrite(ledBlue, HIGH);
    if (szRx == "13L") digitalWrite(ledGreen, LOW);
    if (szRx == "13H") digitalWrite(ledGreen, HIGH);
    if (szRx == "15L") digitalWrite(ledRed, LOW);
    if (szRx == "15H") digitalWrite(ledRed, HIGH);
  }
}


void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    //if (client.connect("ESP8266 Client <- Canvieu aquest nom")) {
    if (client.connect("joan")) {
      Serial.println("connected");
      // ... and subscribe to topic
      client.subscribe(TEMA_SUBSCRIPCIO_ESTAT_LEDS);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  digitalWrite(ledRed, HIGH);
  digitalWrite(ledBlue, LOW);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  digitalWrite(ledRed, LOW);
  digitalWrite(ledBlue, HIGH);

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void setup() {
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);
  pinMode(ledRed, OUTPUT);
  pinMode(ledGreen, OUTPUT);
  pinMode(ledBlue, OUTPUT);
  pinMode(button, INPUT);

  setup_wifi();
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);
}

void loop() {
  boolean bButtonState = !digitalRead(EXTERNAL_BUTTON);
  static boolean bLastButtonState = bButtonState;

  if (!client.connected()) {
    reconnect();
  }

  if (bButtonState != bLastButtonState) {
    bLastButtonState = bButtonState;
    delay(50); //pels rebots
    //quan cliquem el boto farem una publicacio
    if (bButtonState)
      client.publish(TEMA_PUBLICA_ESTAT_BOTO, "Boto GPIO4 NO premut");
    else
      client.publish(TEMA_PUBLICA_ESTAT_BOTO, "Boto GPIO4 ES premut");
  }
  client.loop();
}

Amb aquesta pràctica fem l'exemple de subscriure (llegim l'estat dels botons) i publicar (podem canviar l'estat del led)

També fem la prova amb el MQTT Dash (aplicació del mòbil), per encendre/apagar els leds de la witty.

Ampliació de la pràctica: tenir en el DasH MQTT un gauge del nivell del sensor analògic de la witty. Hem de modificar el codi, similar a:

itoa(analogRead(A0),cstr,10);
if (szRx=="a" || szRx=="A") client.publish(TEMA_PUBLICAT_ESTAT_LDR, cstr);

i aleshores des de la witty publiquem el valor de la LDR, i el podrem recollir en un msg.payload (debug) o una visualització. IDEES: Amb blynk o appInventor podem fer un desenvolupament ràpid d'aplicacions per mòbil, relacionades amb IoT. El blynk també es pot connectar a Node-RED, i el servidor de blynk també el podem instal·lar a la RPi.

Jornada 5

NodeRED: 192.168.1.138

Codi Modbus TCP sobre l'ESP8266 script esp8266_modbus_estatBoto_LDR_i_led_02b_rPrim.ino

/*
  20180523 - wiki.binefa.cat

  Based on Modbus-Arduino Example - Test Holding Register (Modbus IP ESP8266)
  Read Switch Status on pin GPIO0
  Copyright by André Sarmento Barbosa
  http://github.com/andresarmento/modbus-arduino

*/

#include <ESP8266WiFi.h>
#include <Modbus.h>
#include <ModbusIP_ESP8266.h>

#define N_WIFIS 2
#define MAX_STRING_SIZE 15

struct stWifiList {
  String szSSID;
  String szPWD;
};

struct stWifiList stWiFi[N_WIFIS] = {
  {"IoT-eCat" , "clotClot"},
  {"Robotica2018" , "1123prim2018"}
};


//Modbus Registers Offsets (0-9999)
const int SENSOR_IREG = 100;
const int SWITCH_ISTS = 100;
const int LED_COIL = 100;
const int LED_VERMELL_COIL = 101;

//Used Pins
const int switchPin = 4; //GPIO4
const int ledPin = 2; //GPIO2
const int ledVermell = 15; //GPIO15

//ModbusIP object
ModbusIP mb;

long ts;

void vDelayESP8266(unsigned long ulMilliseconds) {
  unsigned long ulPreviousMillis = millis();

  do {
    yield();
  } while (millis() - ulPreviousMillis <= ulMilliseconds);
}

boolean bIsListed(String szSSID,int *nQuina) {
  for (int i = 0; i < N_WIFIS ; i++) {
    if (stWiFi[i].szSSID == szSSID){
      *nQuina = i;
      return true;
      }
  }
  return false;
}

bool bConnectModbus() {
  //mb.config("IoT-eCat", "clotClot");
  char ssid[MAX_STRING_SIZE],  pwd[MAX_STRING_SIZE];
  int n = WiFi.scanNetworks(), nWhichOne;

  Serial.print("*");
  if (n == 0) {
    Serial.println("\nNo networks found");
    vDelayESP8266(1000);
  } else {
    for (int i = 0; i < n; ++i) {
      if (bIsListed(WiFi.SSID(i), &nWhichOne)) {
        String szSsid = stWiFi[nWhichOne].szSSID;
        szSsid.toCharArray(ssid, szSsid.length() + 1);
        String szPwd = stWiFi[nWhichOne].szPWD;
        szPwd.toCharArray(pwd, szPwd.length() + 1);

        mb.config(ssid,pwd);
        return true;
      }
    }
  }
  return false;
}

void setup() {
  int nSsidDetected;
  Serial.begin(115200);


  //Config Modbus IP
  //mb.config("IoT-eCat", "clotClot");
  bConnectModbus();

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  pinMode(ledPin, OUTPUT);
  mb.addCoil(LED_COIL);
  pinMode(ledVermell, OUTPUT);
  mb.addCoil(LED_VERMELL_COIL);

  //Set switchPin mode
  pinMode(switchPin, INPUT);
  // Add SWITCH_ISTS register - Use addIsts() for digital inputs
  mb.addIsts(SWITCH_ISTS);

  // Add SENSOR_IREG register - Use addIreg() for analog Inputs
  mb.addIreg(SENSOR_IREG);

  ts = millis();
}

void loop() {
  //Call once inside loop() - all magic here
  mb.task();

  //Attach switchPin to SWITCH_ISTS register
  mb.Ists(SWITCH_ISTS, digitalRead(switchPin));

  //Read each two seconds
  if (millis() > ts + 2000) {
    ts = millis();
    //Setting raw value (0-1024)
    mb.Ireg(SENSOR_IREG, analogRead(A0));
  }

  //Attach ledPin to LED_COIL register
  digitalWrite(ledPin, mb.Coil(LED_COIL));
  digitalWrite(ledVermell, mb.Coil(LED_VERMELL_COIL));
  //digitalWrite(ledVermell,digitalRead(switchPin));
}

El programa compila i es puja bé, i el Witty agafa la IP de 192.168.1.110.

I el code de Node-RED que hem provat i ha funcionat és:

[
    {
        "id": "41131db2.796494",
        "type": "tab",
        "label": "Flow 1",
        "disabled": false,
        "info": ""
    },
    {
        "id": "77abe6fd.72879",
        "type": "debug",
        "z": "41131db2.796494",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "x": 510,
        "y": 120,
        "wires": []
    },
    {
        "id": "df76097c.8517f8",
        "type": "inject",
        "z": "41131db2.796494",
        "name": "",
        "topic": "",
        "payload": "0",
        "payloadType": "num",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 130,
        "y": 100,
        "wires": [
            [
                "e7add653.1e6668"
            ]
        ]
    },
    {
        "id": "e7add653.1e6668",
        "type": "modbustcp-no-pooling-write",
        "z": "41131db2.796494",
        "name": "Led petit",
        "dataType": "Coil",
        "adr": "100",
        "server": "167db306.72106d",
        "x": 280,
        "y": 80,
        "wires": [
            [
                "77abe6fd.72879"
            ],
            []
        ]
    },
    {
        "id": "4a21dd39.9e228c",
        "type": "inject",
        "z": "41131db2.796494",
        "name": "",
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 130,
        "y": 60,
        "wires": [
            [
                "e7add653.1e6668"
            ]
        ]
    },
    {
        "id": "4c77e299.840d9c",
        "type": "inject",
        "z": "41131db2.796494",
        "name": "",
        "topic": "",
        "payload": "0",
        "payloadType": "num",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 130,
        "y": 160,
        "wires": [
            [
                "5e84f9c7.ca0a3"
            ]
        ]
    },
    {
        "id": "5e84f9c7.ca0a3",
        "type": "modbustcp-no-pooling-write",
        "z": "41131db2.796494",
        "name": "Led vermell",
        "dataType": "Coil",
        "adr": "101",
        "server": "167db306.72106d",
        "x": 290,
        "y": 180,
        "wires": [
            [
                "77abe6fd.72879"
            ],
            []
        ]
    },
    {
        "id": "c523a62e.625cb8",
        "type": "inject",
        "z": "41131db2.796494",
        "name": "",
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 130,
        "y": 200,
        "wires": [
            [
                "5e84f9c7.ca0a3"
            ]
        ]
    },
    {
        "id": "515289c4.4deb78",
        "type": "modbustcp-no-pooling-read",
        "z": "41131db2.796494",
        "name": "Botó GPIO4",
        "dataType": "Input",
        "adr": "100",
        "quantity": "1",
        "server": "167db306.72106d",
        "x": 290,
        "y": 260,
        "wires": [
            [
                "cfebf5e1.18a7b8",
                "388349fa.c3ca66"
            ],
            []
        ]
    },
    {
        "id": "130ac89c.dc3337",
        "type": "inject",
        "z": "41131db2.796494",
        "name": "",
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 130,
        "y": 380,
        "wires": [
            [
                "a150fda4.1ca258"
            ]
        ]
    },
    {
        "id": "a150fda4.1ca258",
        "type": "modbustcp-no-pooling-read",
        "z": "41131db2.796494",
        "name": "LDR",
        "dataType": "InputRegister",
        "adr": "100",
        "quantity": "1",
        "server": "167db306.72106d",
        "x": 290,
        "y": 380,
        "wires": [
            [
                "77abe6fd.72879"
            ],
            []
        ]
    },
    {
        "id": "c0564630.d3673",
        "type": "inject",
        "z": "41131db2.796494",
        "name": "",
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "x": 130,
        "y": 260,
        "wires": [
            [
                "515289c4.4deb78"
            ]
        ]
    },
    {
        "id": "cfebf5e1.18a7b8",
        "type": "delay",
        "z": "41131db2.796494",
        "name": "",
        "pauseType": "delay",
        "timeout": "500",
        "timeoutUnits": "milliseconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "x": 310,
        "y": 320,
        "wires": [
            [
                "a150fda4.1ca258"
            ]
        ]
    },
    {
        "id": "388349fa.c3ca66",
        "type": "function",
        "z": "41131db2.796494",
        "name": "arr(0) -->",
        "func": "msg.payload = msg.payload[0];\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 460,
        "y": 240,
        "wires": [
            [
                "77abe6fd.72879"
            ]
        ]
    },
    {
        "id": "167db306.72106d",
        "type": "modbustcp-no-pooling-server",
        "z": "",
        "host": "192.168.1.110",
        "port": "502",
        "unit_id": "1"
    }
]

Donat un projecte de Node-RED, el podem exportar en aquest format JSON, i de la mateixa manera, també el podem importar.

Hem fet aquesta pràctica i ha funcionat. Controlem els leds de la witty a través d'un script del Node-RED, i els controlem amb el protocol modbus.

El bloc del Node-RED per treballar amb modbus és modbustcp no pooling.

Ara farem el mateix amb el simulador de Indusoft. Es tracta de fer una interfície gràfica amb botons, per encendre i apagar els leds de la witty (ESP8266).

Indusoft

cicles.info/oncloud
user: jclua/jclua
INDUSOFT

Indusoft web Studio is a powerful collection of automation tools that include all the building block needed to develop Human Machine Interfaces (HMIs) , SCADA systems, OEE/Dashboards, embedded applications and IoT solutions.

Drivers de comunicació: MOTCP

Pantalles tàctils Nextion

Objectiu: connectar una pantalla Nextion amb l'Arduino, i comunicar-se com a entrada/sortida. Com a entrada per exemple podem tenin un dashboard/gauge per mesurar el valor d'un sensor; com a sortida podem definir un botó per tancar/obrir un relé.

costen uns 12e a Aliexpress

2.4" 	240 x 320
2.8" 	240 x 320
3.2" 	240 x 400
3.5" 	320 x 480
4.3" 	480 x 272 

Hi ha el programa del Nextion Editor (Windows). És possible que funcioni amb wine. Si no, necessitem una màquina virtual de Windows.

Per programar amb Arduino fem servir la llibre ITEADLIB_Arduino_Nextion

L'Arduino Nano no té ports sèries. S'ha de fer una modificació al fitxer NexConfig.h:

/**
 * Define nexSerial for communicate with Nextion touch panel. 
 */
//#define nexSerial Serial2
#ifndef ESP32
	#include <SoftwareSerial.h>
	extern SoftwareSerial HMISerial;
#else
	extern HardwareSerial HMISerial;
#endif

La capçalera del meu fitxer és:

#include "Nextion.h"

// https://www.reddit.com/r/arduino/comments/4b6jfi/nexconfigh_for_nextion_display_on_arduino_uno/
#define ESP32
HardwareSerial HMISerial(2); // UART2. U2_RXD:GPIO16, U2_TXD:GPIO17

#include "PCF8574.h" // https://github.com/RobTillaart/Arduino/tree/master/libraries/PCF8574
#include <Wire.h>
#include "SSD1306.h" // alias for `#include "SSD1306Wire.h"
...

Com es veu amb la sentència #define ESP32 veig el tros de codi que s'ha d'executar en el NexConfig.h.


Funciona per callbacks (esdeveniments). En un release, deixa anar un event.

Fem aquest codi:

Les pantalles Nextion funcionen amb una microSD, on dins hem de tenir el fitxer compilat del funcionament de la pantalla. Les Nexion tenen un processador propi. La gràcia de tot això és que un sistema com l'Arduino treballa amb aquesta pantalla, que va molt ràpida perquè la potència de processament és independent de l'Arduino.

Primer pas: instal.lar l'editor per Windows,

Segon pas: cerquem a Google Nextion arduino library, on trobarem el codi d'exemple que volem trobar.

Anem a examples > CompGauge. Hi ha tres fitxers: el .ino és el de l'Arduino; el HMI (Human Machine Interface), és el que podem obrir amb l'editor (també està en binari); i el .tft és el compilat. Nosaltres agafem el HMI i l'obrim amb l'editor, el podem modificar, i el compilarem. El compilat l'haurem de ficar en una targeta SSD que ficarem en la pantalla Nextion.

Formatar la targeta SSD en FAT32, per no tenir problemes. Per ex, des d'una màquina de Windows. Un cop tenim el programa tft en la targeta microSSD, la fiquem, connectem la pantalla Nextion, la reconeix, i ho grava a la memòria de forma permanent. Quan hagi acabat ja podem treure la targeta i no la necessitarem més.

En el fitxer CompGauge_v0_32.ino he hagut d'afegir les línies d'abans:

#define ESP32
HardwareSerial HMISerial(2); // UART2. U2_RXD:GPIO16, U2_TXD:GPIO17

Ha compilat i ha funcionat després de comentar en dos llocs la línia:

#include <SoftwareSerial.h>

que deia que no la trobava, però per sort sembla ser que no la necessitava per funcionar.


todo:

salvier@gmail.com
Xavier Gallego

creat per Joan Quintana Compte, juny 2018