Project

General

Profile

Servidor.py

Código correspondiente el sevidor. - Kary Tudela, 12/24/2025 06:25 PM

Download (3.27 KB)

 
1
# -*- coding: utf-8 -*-
2
#!/usr/bin/env python3
3

    
4
import asyncio
5
import websockets
6
import json
7
import time
8
import random
9

    
10
try:
11
    import grovepi
12
    HAS_GROVE = True
13
except:
14
    HAS_GROVE = False
15

    
16
# ===== CONFIGURACIÓN SENSOR pH =====
17
PH_PIN = 0           # A0
18
VREF = 4.95
19
PH_OFFSET = 365
20

    
21
# ===== CONFIGURACIÓN DHT =====
22
DHT_PORT = 4
23
DHT_TYPE = 0
24

    
25
PORT = 8764
26
connected = set()
27

    
28

    
29
# ===== LECTURA pH CALIBRADA =====
30
def leer_ph_calibrado():
31
    if not HAS_GROVE:
32
        return None
33

    
34
    try:
35
        buf = []
36

    
37
        # Tomar 10 muestras ADC
38
        for _ in range(10):
39
            v = grovepi.analogRead(PH_PIN)
40
            buf.append(v)
41
            time.sleep(0.01)
42

    
43
        # Pin flotante / sensor desconectado
44
        if max(buf) - min(buf) > 300:
45
            return None
46

    
47
        buf.sort()
48

    
49
        # Promedio de las 6 muestras centrales
50
        avg_value = sum(buf[2:8]) / 6
51

    
52
        # ADC inválido
53
        if avg_value < 50 or avg_value > 1000:
54
            return None
55

    
56
        # Fórmula oficial Crowtall
57
        ph = 7 - (1000 * (avg_value - PH_OFFSET) * VREF / 59.16 / 1023)
58

    
59
        # pH imposible
60
        if ph < 0 or ph > 14:
61
            return None
62

    
63
        return round(ph, 2)
64

    
65
    except Exception as e:
66
        print("Error lectura pH:", e)
67
        return None
68

    
69

    
70
# ===== LECTURA DHT =====
71
def leer_dht():
72
    if not HAS_GROVE:
73
        return (
74
            round(20 + random.random() * 10, 2),
75
            round(30 + random.random() * 40, 2)
76
        )
77

    
78
    try:
79
        temp, hum = grovepi.dht(DHT_PORT, DHT_TYPE)
80
        return temp, hum
81
    except:
82
        return None, None
83

    
84

    
85
async def handler(ws, path):
86
    print("Cliente conectado:", ws.remote_address)
87
    connected.add(ws)
88
    try:
89
        async for message in ws:
90
            print("Mensaje recibido:", message)
91
    except websockets.exceptions.ConnectionClosed:
92
        pass
93
    finally:
94
        connected.remove(ws)
95
        print("Cliente desconectado:", ws.remote_address)
96

    
97

    
98
async def broadcast_sensor_values():
99
    while True:
100
        # TEMPERATURA Y HUMEDAD
101
        temp_amb, hum_amb = leer_dht()
102

    
103
        # pH CALIBRADO
104
        ph = leer_ph_calibrado()
105

    
106
        # NPK (aún simulado)
107
        nitro = round(10 + random.random() * 30, 2)
108
        fosfo = round(5 + random.random() * 20, 2)
109
        potas = round(8 + random.random() * 25, 2)
110

    
111
        payload = {
112
            "temp": temp_amb,
113
            "hum": hum_amb,
114
            "valor_adc": ph, 
115
            "n": nitro,
116
            "p": fosfo,
117
            "k": potas,
118
            "ts": time.time()
119
        }
120

    
121
        print("Broadcast ->", payload)
122

    
123
        if connected:
124
            msg = json.dumps(payload)
125
            for ws in list(connected):
126
                try:
127
                    await ws.send(msg)
128
                except:
129
                    connected.discard(ws)
130

    
131
        await asyncio.sleep(5)
132

    
133

    
134
async def main_async():
135
    server = await websockets.serve(handler, "0.0.0.0", PORT)
136
    print(f"Servidor WebSocket escuchando en puerto {PORT}")
137
    await broadcast_sensor_values()
138

    
139

    
140
def main():
141
    try:
142
        asyncio.run(main_async())
143
    except KeyboardInterrupt:
144
        print("Servidor detenido por usuario")
145

    
146

    
147
if __name__ == "__main__":
148
    main()
149

    
150