Práctica 5: integración del módulo LTE e implementación del servicio MQTT:

En esta práctica implementaremos un servicio de mqtt que nos permitirá comunicarnos con la raspberry pi por internet mediante una suscripción de un tópico a un servidor.
La ventaja que nos proporciona este método es que no tenemos necesariamente que tener todos los dispositivos dentro de la misma red para que se puedan ver y comunicarse entre sí.
Por lo que podemos usar un módulo LTE de para enlazar la raspberry con una estación base.

¿Qué es MQTT?

MQTT o Message Queuing Telemetry Transport es un protocolo de comunicación que transmite cadenas de texto, es ampliamente utilizado en entornos IOT, servicios de mensajería,... tiene muchas aplicaciones para trabajar como API, ya que al permitir el envió de hipertexto, podemos enviar cadenas JSON, XML, ...

Su funcionamiento se basa en un servidor central dónde se crean brokers que gestionan tópicos por los que fluye la información. Los clientes se subscriben a estos tópicos y pueden hacer pulls obteniendo la información

Kafka:

Kafka es el protocolo hermano, tiene un funcionamiento similar con diferencia de que se crean logs de todos los mensajes y estos son consumidos de forma predeterminada secuencialmente. Todo y que se puede configurar a gusto del desarrollador.
Uno de los beneficios es que a parte de mantener un registro, si por algún motivo la conexión se pierde, al volver a establecerse esta, se puede continuar en el hilo dónde se había dejado. También destacar que tiene una mayor escalabilidad y throughput. Fué desarrollada por LindedIn y es el motor de streaming de Netflix.
Aunque en el proyecto no se ve, me pareció interesante mencionarlo ya que es una opción de cara a futuros cursos.

Configuración del módulo LTE:

Para instalar el módulo es tan simple cómo seguir los siguientes pasos:
una vez conectado el usb, se puede comprobar si este dispositivo se ha instalado correctamente usando al comanda

lusb
Luego insertar estos comandos secuencialmente:
sudo apt install minicom -y
minicom -D /dev/ttyUSB2 -b 115200
            
Luego escribir AT en ese terminal y esperar que aparezca un OK.
Finalmente escribir
AT+QCFG="usbnet",1
y la instalación ya estría.

Podemos comprobar lo anterior empleando cualquiera los comandos

ifcongfig
ip adress show
            
La prueba definitiva consiste en realizar un ping a cualquier servidor, por ejemplo a goolge.com forzando la salida por esa interfície:
ping -I usb0 google.com -c 5

Demostración Tecnológica del servicio MQTT:

Para esta demostración, se implementará un servicio de telemetía de tal forma que seamos capaces de recibir información del estado del rover. El código contará de dos partes: El cliente Rover y el cliente suscrito a ese tópico.

ROVER:

import paho.mqtt.client as mqtt
import time
from datetime import datetime
import dronekit


#####Base de la que se parte para mandar telemetría del rover al PC

connection_string = "tcp:127.0.0.1:5762"
vehicle = dronekit.connect(connection_string, wait_ready=True, baud=115200)
broker = "test.mosquitto.org"
client =mqtt.Client("Sutton")
client.connect(broker)
print("Server connected")
topic = "excedida/mitologica"
while True:
    now = datetime.now()
    tiempo = now.strftime("%H:%M:%S")
    pos = str(vehicle.location.global_frame)
    mag = tiempo + ", " + pos
    print(mag)
    client.publish(topic, mag)
    time.sleep(1)

Cliente suscrito:

import paho.mqtt.client as mqtt
import time

def message(client,userdata,message):
    print("message topic: ", message.topic)
    print("message: ", str(message.payload.decode("utf-8")))

topic = "excedida/mitologica"
broker = "test.mosquitto.org"
client=mqtt.Client("BlingBling")
client.connect(broker)
client.subscribe(topic)
print("broker connected")
client.on_message = message
client.loop_start()

integración del código:

Gracias a las buenas praxis empleadas a lo largo del proyecto y la arquitectura modular, ha sido relativamente sencillo realizar la integración, ya que todas las funciones requeridas ya se encoraban previamente desarrolladas y solo ha sido necesario realizar un cambio en el main cambiando la función controller() por listerner_mqtt_control(), ambas pasándoles como argumento un objeto dronekit.vehichle. El código de esta función es el siguiente:

from control import *
import paho.mqtt.client as mqtt
import signal


def listerner_mqtt_control(vehicle):
    
    def command(client,userdata,cmd):
        cmd = str(cmd.payload.decode('utf-8'))
        print(cmd)
        if cmd == 'arm':
            arm_rover(vehicle)
        elif cmd == 'disarm':
            DISarm_rover(vehicle)
        elif cmd == 'AP':
            controller(vehicle)
    
    topic = "excedida/control"
    broker = "test.mosquitto.org"
    client =mqtt.Client("Sutton")
    client.connect(broker)
    client.subscribe(topic)
    client.on_message = command
    client.loop_forever()
Importante fijarse en el client.loop_forever(), ya que permite estar continuamente consumiendo el tópico de forma óptima.

Estación de control:

Por otro lado, se ha diseñado una aplicación para poder controlar el rover mediante la comunicación MQTT, con una interfaz gráfica sencilla:

import paho.mqtt.client as mqtt
import time
from datetime import datetime
import dronekit
from tkinter import *



broker = "test.mosquitto.org"
client =mqtt.Client("LuzDeGas")
client.connect(broker)
print("Server connected")
topic = "excedida/control"

def ARM():
    print('vehicle armed')
    client.publish(topic,'arm')

def DARM():
    print('vehicle dissarmed')
    client.publish(topic,'disarm')


def AP():
    print('Autopilot engaged')
    client.publish(topic,'AP')

frame = Tk()
frame.title('Panel de control del ROVER')
frame.minsize(300,200)
blank0 = Label(frame, text='\t')
blank0.grid(row=0,column=2)
blank1 = Label(frame, text='\t')
blank1.grid(row=1,column=3)
blank2 = Label(frame, text='\t')
blank2.grid(row=1,column=5)
boton1 = Button(frame, text='ARM', command=ARM, bg='green')
boton1.grid(row=1, column=2)
boton2 = Button(frame, text='D/ARM', command=DARM, bg='red')
boton2.grid(row=1,column=4)
boton3 = Button(frame, text='AP',bg='yellow', command=AP)
boton3.grid(row=1, column=6)
frame.mainloop()

El resultado final del diseño e implementación se puede ver en el vídeo a continuación. No se ha podido probar en la plataforma física ya que estos días son festivos y no tengo acceso físico al ROVER: