''' NANO ESP32 ____ ____|____|____ |R P20 (D14) | +------|---------> |G P21 (D15) LED -+| D13 _|_ D12 |+- P13 |B P22 (D16) <-+| 3V3 RGB D11 |+- P12 -+| B0 D10 |+- P11 P14 -+| D17(A0) D9 |+- P10 P15 -+| D18(A1) D8 |+- P9 P16 -+| D19(A2) D7 |+- P8 P17 -+| D20(A3) D6 |+- P7 P18 -+| D21(SDA) D5 |+- P6 P19 -+| D22(SCL) D4 |+- P5 P23/P24 -+| D23(A6) D3 |+- P4 -+| D24(A7) D2 |+- P3 <-+| 5V GND |+- -+| B1 -RST |+- -+| GND D0 |+- P2 >-+| VIN D1 |+- P1 |______________| +=======+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+ | pinID | 1| 2| 3| 4| 5| 6| 7| 8| 9| 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20| 21| 22| 23| 24| +=======+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+===+ | GPIO | 43| 44| 5| 6| 7| 8| 9| 10| 17| 18| 21| 38| 47| 1| 2| 3| 4| 11| 12| 46| 0| 45| 13| 13| | DX | 1| 0| 2| 3| 4| 5| 6| 7| 8| 9| 10| 11| 12| 17| 18| 19| 20| 21| 22| 14| 15| 16| 23| 23| | AX | -| -| -| -| -| -| -| -| -| -| -| -| -| 0| 1| 2| 3| 6| 7| -| -| -| 6| 6| +-------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | Tipo | O| O| O| O| O| O| O| O| O| O| O| O| O| O| O| O| O| O| O| O*| O*| O*| -| -| | | I| I| I| I| I| I| I| I| I| I| I| I| I| I| I| I| I| I| I| -| -| -| -| -| | | -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| A| A| A| A| -| -| -| A*| A*| | | P| P| P| P| P| P| P| P| P| P| P| P| P| P| P| P| P| P| P| -| -| -| -| -| | | S| S| S| S| S| S| S| S| S| S| S| S| S| S| S| S| S| S| S| -| -| -| -| -| +-------+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ O* Salidas internas al led RGB: Pin20=RED, Pin21=GREEN, Pin22=BLUE A* Entrada analógica asociada a un sensor DHT22 (DX=23, AX=6, GPIO=13). pinID = 23 -> sensor de temperatura DHT22. pinID = 24 -> sensor de humedad DHT22. ''' from binascii import crc32 import dht from machine import ADC, idle, Pin, PWM, Timer, reset import network import requests import socket from time import sleep # Modelo del módulo. MODULE_MODEL = 'NanoESP32' # Número de pines. NUM_OF_PINS = 24 # Revisión. SKETCH_REVISION = '1.0.0/150725' # Credenciales de la red wifi. SSID_NAME = 'DeviceControlSystem' SSID_PASSWORD = 'lavidaesbella2018' # IP asignada al servidor de comandos. IP_SERVER = '0.0.0.0' # Indicador wifi. Se enciende cuando se recibe un comando, y se apaga cuando se envía su respuesta. wifiLed = Pin(48, Pin.OUT, value=0) # Socket para la comunicación con DCS. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Px = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 gpio = (-1,43,44,5,6,7,8,9,10,17,18,21,38,47, 1, 2, 3, 4,11,12,46, 0,45,-1,-1) # Se indican los pines asignados al sensor DHT22. DHT22TempPin = 23 DHT22HumiPin = 24 # Almacenan el valor de la temperatura y la humedad relativa del sensor DHT22. DHT = dht.DHT22(Pin(13)) DHT22Temperature = 0 DHT22Humidity = 0 # Almacena 24 objetos Pin para ser programados en función del tipo de dispositivo. px = [] # Almacena el tipo de pin que se va programando. pType = [] # Almacenará hasta 4 objetos de la clase ADC para ser utilizados con los pines analógicos. adc = [0,0,0,0] # Almacenará los nombres de los dispositivos asociados a cada pin. dName = [] # Se inicializan las variables y los pines. for x in gpio: pType.append('F') dName.append('') if x == -1: px.append(0) elif x in [0,45,46]: px.append(Pin(x, Pin.OUT, value=1)) else: px.append(Pin(x, Pin.IN)) # Indica si DCS ha enviado el comando 's:init'. isDCSInit = False # Número total de pines del tipo 'P' más 'S' definidos. No pueden ser más de 5 en total. numOfPWMPins = 0 # Mensajes de error. EXECUTED = '0' COMMAND_UNKNOWN = '1! Command unknown.' MODULE_NO_INIT = '2! No-initialized module.' PIN_BUSY = '3! Pin busy.' PIN_OUT_OF_RANGE = '4! Pin out of range.' SYNTAX_ERROR = '5! Syntax error.' VALUE_OUT_OF_RANGE = '6! Value out of range.' WRONG_PIN_TYPE = '7! Wrong pin type.' WRONG_VALUE = '8! Wrong value.' FUNCTION_BUSY = '9| Function busy.' # Ya hay definidos 5 pines entre pines del tipo 'P' y 'S'. #--------------------------------------------------------------------------------------------------- def read_DHT22(dummy): """Obtiene la temperatura y la humedad del sensor DHT22 conectado al GPIO14.""" global DHT22Temperature global DHT22Humidity DHT.measure() DHT22Temperature = DHT.temperature() DHT22Humidity = DHT.humidity() def execute_command(command): """Selector de comandos.""" if not isDCSInit: return MODULE_NO_INIT if command.startswith('s:'): return set_pin(command) if command.startswith('w:'): return write_pin(command) if command == 'r:all': return read_all_pins() if command == 'r:cfg': return read_config() return COMMAND_UNKNOWN def set_pin(command): """Ejecuta el comando 's:pinID,type,data,devName'""" # Se obtienen los diferentes parámetros. if command.count(',') != 3: return SYNTAX_ERROR pinID, pinType, data, devName = command[2:].split(',') # Se comprueba el ID del pin. pinID = pinID.strip() if not pinID.isdigit(): return WRONG_VALUE pinID = int(pinID) if pinID == 0 or pinID > NUM_OF_PINS: return PIN_OUT_OF_RANGE # Se comprueba el tipo del pin. pinType = pinType.strip() if len(pinType) != 1: return SYNTAX_ERROR if not pinType in 'OIAPS': return WRONG_PIN_TYPE # Se obtiene el valor de 'data'. data = data.strip() if len(data) == 0: return SYNTAX_ERROR # Se comprueba el nombre del dispositivo. if len(devName) < 3: return WRONG_VALUE # Se comprueba que el pin no esté ya ocupado. if pType[pinID] == pinType: if dName[pinID] != devName: return PIN_BUSY else: return EXECUTED + "Executed." # Tipo OUTPUT. if pinType == 'O': if len(data) != 1 or not data in '01': return WRONG_VALUE px[pinID].init(Pin.OUT, value=int(data)) pType[pinID] = 'O' # Tipo INPUT. elif pinType == 'I': if pinID > 19: return PIN_OUT_OF_RANGE if len(data) != 1 or not data in '012': return WRONG_VALUE if data == '0': px[pinID].init(Pin.IN, pull=None) elif data == '1': px[pinID].init(Pin.IN, pull=Pin.PULL_UP) else: px[pinID].init(Pin.IN, pull=Pin.PULL_DOWN) pType[pinID] = 'I' # Tipo ANALOG. elif pinType == 'A': if pinID == DHT22TempPin or pinID == DHT22HumiPin: ... else: if pinID < 16 or pinID > 19: return PIN_OUT_OF_RANGE if data != '0': return SYNTAX_ERROR adc[pinID - 16] = ADC(px[pinID]) adc[pinID - 16].atten(ADC.ATTN_11DB) pType[pinID] = 'A' # Tipo PWM. elif pinType == 'P': global numOfPWMPins if numOfPWMPins == 5: return FUNCTION_BUSY if len(data) > 4 or not data.isdigit() or int(data) > 1023: return WRONG_VALUE px[pinID] = PWM(Pin(gpio[pinID]), freq=1000, duty=int(data)) pType[pinID] = 'P' numOfPWMPins += 1 # Tipo SERVO. elif pinType == 'S': global numOfPWMPins if numOfPWMPins == 5: return FUNCTION_BUSY if len(data) > 3 or not data.isdigit() or int(data) < 51 or int(data) > 102: return WRONG_VALUE px[pinID] = PWM(Pin(gpio[pinID]), freq=50, duty=int(data)) pType[pinID] = 'S' numOfPWMPins += 1 else: return WRONG_PIN_TYPE dName[pinID] = devName return EXECUTED + "Executed." def write_pin(command): """Ejecuta el comando 'w:pinID,value'""" # Se obtienen los diferentes parámetros. if command.count(',') != 1: return SYNTAX_ERROR pinID, value = command[2:].split(',') # Se comprueba el ID del pin. pinID = pinID.strip() if not pinID.isdigit(): return WRONG_VALUE pinID = int(pinID) if pinID == 0 or pinID > NUM_OF_PINS: return PIN_OUT_OF_RANGE # Se comprueba el valor a establecer. value = value.strip() if len(value) == 0 or not value.isdigit(): return WRONG_VALUE # Tipo OUTPUT. if pType[pinID] == 'O': if not value in '01': return VALUE_OUT_OF_RANGE px[pinID].value(int(value)) # Tipo PWM. elif pType[pinID] == 'P': if len(value) > 4 or int(value) > 1023: return VALUE_OUT_OF_RANGE px[pinID].duty(int(value)) # Tipo SERVO. elif pType[pinID] == 'S': if len(value) > 3 or int(value) < 51 or int(value) > 102: return VALUE_OUT_OF_RANGE px[pinID].duty(int(value)) else: return WRONG_PIN_TYPE return EXECUTED + "Executed." def read_all_pins(): """Lee las muestras presentes en los pines.""" response = '0X,' for pinID in range(1, NUM_OF_PINS + 1): if pType[pinID] == 'I': response += str(px[pinID].value()) + ',' elif pType[pinID] == 'A': if pinID == DHT22TempPin: response += str(DHT22Temperature) + ',' elif pinID == DHT22HumiPin: response += str(DHT22Humidity) + ',' else: response += str(get_analog_sample(adc[pinID - 16])) + ',' else: response += '-,' response = response[:-1] return response def read_config(): """Obtiene la programación actual de los pines.""" response = EXECUTED for pinID in range(1, NUM_OF_PINS + 1): if pType[pinID] == 'F': continue response += 'P' + str(pinID) + \ '(GPIO' + str(gpio[pinID]) + '):' + \ pType[pinID] + ':' + \ dName[pinID] + '\n' if len(response) > 1: response = response.strip() else: response += 'No devices found.' return response def get_analog_sample(adc): """Obtiene una muestra analógica.""" maxValue = 0 minValue = 3300 totalSamples = 0 for i in range(0, 22): sample = adc.read_uv() // 1000 if sample > maxValue: maxValue = sample if sample < minValue: minValue = sample totalSamples += sample totalSamples -= (maxValue + minValue) return totalSamples // 20 #--------------------------------------------------------------------------------------------------- def wifi_connection(): """Conecta con la red wifi.""" wifiLed.on() wlan = network.WLAN() wlan.active(True) if not wlan.isconnected(): print('Connecting to network (wait please).') wlan.connect(SSID_NAME, SSID_PASSWORD) while not wlan.isconnected(): idle() wifiLed.off() global IP_SERVER IP_SERVER = wlan.ipconfig('addr4')[0] print('Module IP address:', IP_SERVER) #--------------------------------------------------------------------------------------------------- def check_crc32(data): """Comprueba el CRC32 del comando recibid desde DCS. El formato es 'crc32.command'.""" dotPos = data.find('.') if dotPos == -1: return False else: crc = data[0:dotPos] if crc32(data[dotPos + 1:]) == int(crc): return data[dotPos + 1:] else: return False def start_UDP_server(): """Inicia el servidor de comandos.""" sockaddr = socket.getaddrinfo(IP_SERVER, 50000)[0][-1] sock.bind(sockaddr) print("UDP server up and listening.") def get_command(): """Obtiene el comando recibido desde DCS. Retorna la tupla (command, clientAddress).""" dataFromClient = sock.recvfrom(1024) wifiLed.on() data = dataFromClient[0].decode() if check_crc32(data): return (data[data.find('.') + 1:], dataFromClient[1]) else: return ('! Communication error', '') def send_response(response, clientAddress): """Envía la respuesta de la ejecución del comando al cliente.""" dataToClient = str(crc32(response)).encode() + b'.' + response.encode() sock.sendto(dataToClient, clientAddress) wifiLed.off() #--------------------------------------------------------------------------------------------------- def check_update(): """Comprueba si hay una actualización del sketch.""" response = requests.get('http://192.168.2.85/var/www/html/updates/NanoESP32/getNewRevision.php') newRevision = response.text if (newRevision != SKETCH_REVISION): return True else: return False def start_dcm(): """ Programa principal. Se ejecuta continuamente a no ser que haya disponible una actualización. En este caso se retorna a 'main.py' para que se actualice y se reinicie. """ wifi_connection() start_UDP_server() read_DHT22(0) # Se invoca para actualizar las lecturas del sensor DHT22. # Lectura periódica del sensor DHT22. DHT22Timer = Timer(0) DHT22Timer.init(period=3000, mode=Timer.PERIODIC, callback=read_DHT22) # Se enciendo el led rojo durante un segundo para indicar el reinicio del módulo. px[20].value(0) sleep(1) px[20].value(1) while True: command, clientAddress = get_command() if command == 'update': if check_update(): send_response('0Updating.', clientAddress) DHT22Timer.deinit() return else: send_response('0Nothing to update.', clientAddress) continue if command == 's:reset': send_response('0Ordered', clientAddress) reset() if command == 's:init': send_response(EXECUTED + MODULE_MODEL + '-' + str(NUM_OF_PINS) + '-' + SKETCH_REVISION, clientAddress) global isDCSInit; isDCSInit = True continue send_response(execute_command(command), clientAddress)