Project

General

Profile

app.py

Ignacio Garrido, 12/11/2024 12:29 AM

Download (25.6 KB)

 
1

    
2
from kivy.app import App
3
from kivy.config import Config
4
from kivy.uix.label import Label
5
from kivy.uix.button import Button
6
from kivy.uix.floatlayout import FloatLayout
7
from kivy.core.window import Window
8
from kivy.graphics import Color, RoundedRectangle, Rectangle , Line
9
from kivy.uix.button import ButtonBehavior
10
from kivy.uix.image import Image
11
from kivy.uix.popup import Popup
12
from kivy.uix.boxlayout import BoxLayout
13
from kivy.uix.textinput import TextInput
14
from kivy.uix.screenmanager import ScreenManager, Screen
15
from kivy.uix.scrollview import ScrollView
16
from kivy.uix.progressbar import ProgressBar
17
from kivy.clock import Clock
18
import random 
19

    
20
# Configuración de la ventana
21
Config.set('graphics', 'width', '360')  # Ancho de la ventana
22
Config.set('graphics', 'height', '640')  # Alto de la ventana
23
Config.set('graphics', 'resizable', True)  # Permitir redimensionar
24
Config.write()
25

    
26
class RoundedButton(Button):
27
    def __init__(self, bg_color=(0.7529, 0.7529, 0.7529, 1), **kwargs):
28
        super().__init__(**kwargs)
29
        self.background_normal = ''
30
        self.background_down = ''
31
        self.background_color = (0, 0, 0, 0)
32

    
33
        # Personaliza el fondo redondeado con el color proporcionado
34
        self.bg_color = bg_color
35
        with self.canvas.before:
36
            Color(*self.bg_color)
37
            self.bg_rect = RoundedRectangle(
38
                size=self.size,
39
                pos=self.pos,
40
                radius=[(20, 20), (20, 20), (20, 20), (20, 20)]
41
            )
42
        self.bind(size=self.update_rect, pos=self.update_rect)
43

    
44
    def update_rect(self, *args):
45
        self.bg_rect.size = self.size
46
        self.bg_rect.pos = self.pos
47

    
48

    
49
class ImageButton(ButtonBehavior, Image):
50
    pass
51

    
52
class MainScreen(Screen):  # Cambié MainScreen a heredar de Screen, no de App
53
    def __init__(self, **kwargs):
54
        super(MainScreen, self).__init__(**kwargs)
55
        Window.clearcolor =  (0.7529, 0.7529, 0.7529, 1)  # Color 
56

    
57
        # Crear un FloatLayout para gestionar las posiciones
58
        layout = FloatLayout()
59

    
60
        # Crear un Label en la parte superior
61
        label = Label(text="¡Bienvenido", font_size='30sp', color=(2, 0, 0, 1), bold= True)  # Color rojo
62
        label.size_hint = None, None
63
        label.size = label.texture_size
64
        label.pos_hint = {'center_x': 0.5, 'top': 0.9}  # Posición en la parte superior
65
        layout.add_widget(label)
66

    
67
        # Crear el contenedor amarillo transparente en la parte inferior (50% de la pantalla)
68
        bottom_half = FloatLayout(size_hint=(1, 0.5))  # 50% de la pantalla de altura
69
        with bottom_half.canvas:
70
            Color(1, 1, 0, 0.8)  # Amarillo semi-transparente (r, g, b, alpha)
71
            self.rect_bottom = Rectangle(size=bottom_half.size, pos=bottom_half.pos)  # Cambiar a Rectangle
72
        bottom_half.bind(size=self.update_rect_bottom, pos=self.update_rect_bottom)
73
        bottom_half.pos_hint = {'x': 0, 'y': 0}  # Colocamos en la parte inferior
74
        layout.add_widget(bottom_half)
75

    
76
        # Crear el contenedor gris con bordes redondeados
77
        gray_box = FloatLayout(size_hint=(None, None), size=("300dp", "350dp"))
78
        gray_box.pos_hint = {'center_x': 0.5, 'y': 0.06}
79
        
80
        # Dibujar un fondo gris con bordes redondeados
81
        with gray_box.canvas:
82
            Color(1, 1, 1, 1)
83
            self.rect_gray = RoundedRectangle(size=gray_box.size, pos=gray_box.pos, radius=[(20, 20), (20, 20), (20, 20), (20, 20)])
84
        gray_box.bind(size=self.update_rect_gray, pos=self.update_rect_gray)
85
        layout.add_widget(gray_box)
86

    
87
        gray_box.pos = (gray_box.pos[0], gray_box.pos[1] - 50)
88

    
89
        # Crear un label para conectar
90
        label2 = Label(text="Establecer Conexión", font_size='22sp', color=(2, 0, 0, 1), bold=True)  # Color rojo
91
        label2.size_hint = None, None
92
        label2.size = label2.texture_size
93
        label2.pos_hint = {'center_x': 0.5, 'top': 0.5}  # Posición en la parte superior
94
        layout.add_widget(label2)
95

    
96
        # Crear el botón de conexión
97
        button_conect = ImageButton(
98
            source="conectar.png",  # Ruta de la imagen para el botón
99
            size_hint=(None, None),
100
            size=("200dp", "210dp")
101
        )
102
        button_conect.pos_hint = {'center_x': 0.5, 'center_y': 0.30}
103
        button_conect.bind(on_press=self.show_popup)
104
        layout.add_widget(button_conect)
105

    
106
        # Crear el logo en la parte superior
107
        image = Image(
108
            source='logo.png',
109
            allow_stretch=True, 
110
            keep_ratio=False, 
111
            size_hint=(None, None),  # Desactiva el tamaño relativo
112
            size=(150, 130)  # Asigna ancho y alto específicos
113
        )
114

    
115
        # Establecer la posición del logo
116
        image.pos_hint = {'center_x': 0.5, 'center_y': 0.75}  # Centrar en X y ajustar altura
117
        layout.add_widget(image, index=len(layout.children))  # Asegúrate de que la imagen esté delante
118

    
119
        self.add_widget(layout)  # Agregar el layout a la pantalla
120

    
121
    def show_popup(self, instance):
122
        # Crear un BoxLayout para el popup
123
        popup_layout = BoxLayout(orientation='vertical', padding=10, spacing=10)
124

    
125
        # Crear un campo de entrada para la IP del servidor
126
        ip_label = Label(text="Dirección IP del servidor:")
127
        ip_input = TextInput(hint_text="Ingrese la dirección IP", multiline=False)
128
        
129
        # Crear un botón de CONECTAR en el popup
130
        connect_button = Button(text="Conectar", size_hint=(None, None), size=("300dp", "50dp"))
131
        connect_button.bind(on_press=lambda x: self.connect_to_server(ip_input.text))  # Llamada al método para conectar
132

    
133
        # Agregar los elementos al layout del popup
134
        popup_layout.add_widget(ip_label)
135
        popup_layout.add_widget(ip_input)
136
        popup_layout.add_widget(connect_button)
137

    
138
        # Crear el Popup y mostrarlo
139
        self.popup = Popup(title="Conectar a servidor", content=popup_layout,
140
                           size_hint=(None, None), size=("340dp", "220dp"))
141
        self.popup.open()
142

    
143
    def show_connected_screen(self):
144
        # Cambiar a la pantalla de conexión exitosa
145
        self.manager.current = "second"  # Asegúrate de que 'second' es el nombre correcto de la pantalla
146

    
147
    def connect_to_server(self, ip_address):
148
        if ip_address == "1":  # Comprobamos si la IP es "1", solo como ejemplo
149
            print(f"Conectado a {ip_address}...")
150
            self.popup.dismiss()  # Cerrar el popup
151
            self.show_connected_screen()  # Cambiar a la pantalla de conexión exitosa
152
        else:
153
            print(f"Dirección IP errónea. Inténtalo de nuevo.")
154
            # Actualizar el campo del popup para que el usuario ingrese de nuevo
155
            self.popup.content.children[1].text = ""  # Limpiar el TextInput (si estás usando Kivy)
156
            # Aquí no terminamos la función con return, solo damos feedback al usuario y seguimos esperando
157
            self.popup.content.children[1].hint_text = "Ingresa una IP válida"  # Añadir texto de sugerencia para el usuario
158

    
159
    # Métodos para actualizar los tamaños y posiciones de los rectángulos
160
    def update_rect_bottom(self, instance, value):
161
        self.rect_bottom.pos = instance.pos
162
        self.rect_bottom.size = instance.size
163

    
164
    def update_rect_gray(self, instance, value):
165
        self.rect_gray.pos = instance.pos
166
        self.rect_gray.size = instance.size
167

    
168
class SecondScreen(Screen):  # Pantalla de conexión exitosa
169
    def __init__(self, **kwargs):
170
        super(SecondScreen, self).__init__(**kwargs)
171
        layout = FloatLayout()
172

    
173
        # Imagen del logo
174
        image = Image(
175
            source='logo.png',
176
            allow_stretch=True, 
177
            keep_ratio=False, 
178
            size_hint=(None, None),  
179
            size=(180, 160)
180
        )
181
        image.pos_hint = {'center_x': 0.5, 'center_y': 0.80}
182
        layout.add_widget(image, index=len(layout.children))
183
        
184
        # Contenedor amarillo en la parte inferior
185
        bottom_half = FloatLayout(size_hint=(1, 0.5))
186
        with bottom_half.canvas:
187
            Color(0, 0, 1, 0.8)  # Azul semi-transparente
188
            self.rect_bottom = Rectangle(size=bottom_half.size, pos=bottom_half.pos)
189
        bottom_half.bind(size=self.update_rect_bottom, pos=self.update_rect_bottom)
190
        bottom_half.pos_hint = {'x': 0, 'y': 0}
191
        layout.add_widget(bottom_half)
192

    
193
        # Caja gris flotante
194
        gray_box = FloatLayout(size_hint=(None, None), size=("300dp", "350dp"))
195
        gray_box.pos_hint = {'center_x': 0.5, 'y': 0.06}
196
        with gray_box.canvas:
197
            Color(1, 1, 1, 1)  # Gris claro
198
            self.rect_gray = RoundedRectangle(
199
                size=gray_box.size, pos=gray_box.pos, radius=[(20, 20), (20, 20), (20, 20), (20, 20)]
200
            )
201
        gray_box.bind(size=self.update_rect_gray, pos=self.update_rect_gray)
202
        layout.add_widget(gray_box)
203

    
204
        
205
        # Botón para la pantalla "four"
206
        button_to_four = RoundedButton(
207
            text="Ver Estado Agua",
208
            size_hint=(None, None),
209
            size=("250dp", "60dp"),
210
            color=(0, 0, 0, 1),
211
            bold=True,
212
            font_size='18sp'
213
        )
214
        button_to_four.pos_hint = {'center_x': 0.5, 'center_y': 0.42}
215
        button_to_four.bind(on_press=lambda x: self.change_screen("four"))
216
        layout.add_widget(button_to_four)
217

    
218
        # Botón para la pantalla "five"
219
        button_to_five = RoundedButton(
220
            text="Ver Estado Alimento",
221
            size_hint=(None, None),
222
            size=("250dp", "60dp"),
223
            color=(0, 0, 0, 1),
224
            bold=True,
225
            font_size='18sp'
226
        )
227
        button_to_five.pos_hint = {'center_x': 0.5, 'center_y': 0.28}
228
        button_to_five.bind(on_press=lambda x: self.change_screen("five"))
229
        layout.add_widget(button_to_five)
230

    
231
        self.add_widget(layout)
232

    
233
    def change_screen(self, screen_name):
234
        """
235
        Cambia a la pantalla especificada.
236
        """
237
        self.manager.current = screen_name
238

    
239
    def update_rect_bottom(self, instance, value):
240
        """
241
        Actualiza el tamaño y la posición del fondo azul.
242
        """
243
        self.rect_bottom.pos = instance.pos
244
        self.rect_bottom.size = instance.size
245
        
246
    def update_rect_gray(self, instance, value):
247
        """
248
        Actualiza el tamaño y la posición de la caja gris.
249
        """
250
        self.rect_gray.pos = instance.pos
251
        self.rect_gray.size = instance.size
252

    
253

    
254
from kivy.uix.screenmanager import Screen
255
from kivy.uix.floatlayout import FloatLayout
256
from kivy.uix.label import Label
257
from kivy.uix.image import Image
258
from kivy.uix.button import Button
259
from kivy.graphics import Color, RoundedRectangle, Line
260
import serial
261
import time
262
import RPi.GPIO as GPIO
263

    
264

    
265
class FourScreen(Screen):
266
    def __init__(self, **kwargs):
267
        super(FourScreen, self).__init__(**kwargs)
268
        layout = FloatLayout()
269

    
270
        # Imagen del logo
271
        image = Image(
272
            source='logo.png',
273
            allow_stretch=True,
274
            keep_ratio=False,
275
            size_hint=(None, None),
276
            size=(180, 160)
277
        )
278
        image.pos_hint = {'center_x': 0.5, 'center_y': 0.80}
279
        layout.add_widget(image, index=len(layout.children))
280

    
281
        # Parte inferior amarilla
282
        bottom_half = FloatLayout(size_hint=(1, 0.5))
283
        with bottom_half.canvas:
284
            Color(1, 1, 0, 0.8)
285
            self.rect_bottom = RoundedRectangle(size=bottom_half.size, pos=bottom_half.pos)
286
        bottom_half.bind(size=self.update_rect_bottom, pos=self.update_rect_bottom)
287
        bottom_half.pos_hint = {'x': 0, 'y': 0}
288
        layout.add_widget(bottom_half)
289

    
290
        # Caja gris flotante
291
        gray_box = FloatLayout(size_hint=(None, None), size=("300dp", "350dp"))
292
        gray_box.pos_hint = {'center_x': 0.5, 'y': 0.06}
293
        with gray_box.canvas:
294
            Color(1, 1, 1, 1)
295
            self.rect_gray = RoundedRectangle(
296
                size=gray_box.size,
297
                pos=gray_box.pos,
298
                radius=[(20, 20), (20, 20), (20, 20), (20, 20)]
299
            )
300
        gray_box.bind(size=self.update_rect_gray, pos=self.update_rect_gray)
301
        layout.add_widget(gray_box)
302

    
303
        # Caja de texto para mostrar información
304
        info_box = FloatLayout(size_hint=(None, None), size=("250dp", "200dp"))
305
        info_box.pos_hint = {'center_x': 0.5, 'center_y': 0.55}
306
        with info_box.canvas:
307
            Color(0.9, 0.9, 0.9, 1)
308
            self.info_background = RoundedRectangle(
309
                size=info_box.size,
310
                pos=info_box.pos,
311
                radius=[(10, 10), (10, 10), (10, 10), (10, 10)]
312
            )
313
            Color(0, 0, 0, 1)
314
            self.info_border = Line(
315
                rounded_rectangle=(info_box.x, info_box.y, info_box.width, info_box.height, 10),
316
                width=1.5
317
            )
318
        info_box.bind(size=self.update_info_background, pos=self.update_info_background)
319

    
320
        # Etiqueta para mostrar datos
321
        self.label = Label(
322
            text="Distancia: 0.00 cm\nNivel de Agua: Desconocido\nValor pH: Desconocido",
323
            font_size=18,
324
            size_hint=(None, None),
325
            pos_hint={"center_x": 0.5, "center_y": 0.5},
326
            color=(0, 0, 0, 1)
327
        )
328
        info_box.add_widget(self.label)
329
        layout.add_widget(info_box)
330

    
331
        # Botón para medir nivel de agua y pH
332
        medir_button = Button(
333
            text="Medir Nivel de Agua",
334
            size_hint=(None, None),
335
            size=("250dp", "50dp"),
336
            font_size='18sp'
337
        )
338
        medir_button.pos_hint = {'center_x': 0.5, 'center_y': 0.35}
339
        medir_button.bind(on_press=self.check_water_level)
340
        layout.add_widget(medir_button)
341

    
342
        # Botón para suministrar agua
343
        suministrar_button = Button(
344
            text="Suministrar Agua",
345
            size_hint=(None, None),
346
            size=("250dp", "50dp"),
347
            font_size='18sp'
348
        )
349
        suministrar_button.pos_hint = {'center_x': 0.5, 'center_y': 0.25}
350
        suministrar_button.bind(on_press=self.suministrar_agua)
351
        layout.add_widget(suministrar_button)
352

    
353
        # Botón para limpiar agua
354
        limpiar_button = Button(
355
            text="Limpiar Agua",
356
            size_hint=(None, None),
357
            size=("250dp", "50dp"),
358
            font_size='18sp'
359
        )
360
        limpiar_button.pos_hint = {'center_x': 0.5, 'center_y': 0.15}
361
        limpiar_button.bind(on_press=self.limpiar_agua)
362
        layout.add_widget(limpiar_button)
363

    
364
        # Botón para regresar
365
        back_button = Button(
366
            text="Volver",
367
            size_hint=(None, None),
368
            size=("250dp", "50dp"),
369
            background_color=(1, 0, 0, 1),
370
            font_size='18sp'
371
        )
372
        back_button.pos_hint = {'center_x': 0.5, 'center_y': 0.05}
373
        back_button.bind(on_press=lambda x: self.change_screen("second"))
374
        layout.add_widget(back_button)
375

    
376
        self.add_widget(layout)
377

    
378
    def check_water_level(self, instance):
379
        print("Botón 'Medir Nivel de Agua' presionado")
380
        distancia = self.medir_distancia()
381
        ph_valor = self.leer_ph()
382

    
383
        # Validar y manejar posibles valores nulos
384
        if distancia is None:
385
            distancia_text = "Error"
386
            estado = "Desconocido"
387
        else:
388
            distancia_text = f"{distancia:.2f}"
389
            if 5 <= distancia <= 8:
390
                estado = "Lleno"
391
            elif 8.1 <= distancia <= 13:
392
                estado = "Normal"
393
            else:
394
                estado = "Vacío"
395

    
396
        ph_valor = ph_valor if ph_valor else "Error"
397

    
398
        self.label.text = f"Distancia: {distancia_text} cm\nNivel de Agua: {estado}\nValor pH: {ph_valor}"
399
        print(f"Distancia medida: {distancia_text} cm - Nivel de Agua: {estado} - Valor pH: {ph_valor}")
400

    
401
    def suministrar_agua(self, instance):
402
        try:
403
            puerto_arduino = '/dev/ttyUSB0'
404
            baud_rate = 9600
405
            print("Intentando conectar al Arduino...")
406
            arduino = serial.Serial(puerto_arduino, baud_rate, timeout=1)
407
            time.sleep(2)
408
            print("Abriendo válvula...")
409
            arduino.write(b'1')
410
            time.sleep(30)
411
            print("Cerrando válvula...")
412
            arduino.write(b'0')
413
        except Exception as e:
414
            print(f"Error al suministrar agua: {e}")
415
            self.label.text = "Error al suministrar agua."
416
        finally:
417
            try:
418
                arduino.close()
419
                print("Conexión serial cerrada.")
420
            except:
421
                pass
422

    
423
    def limpiar_agua(self, instance):
424
        print("Función de limpiar agua aún no implementada.")
425

    
426
    def medir_distancia(self):
427
        GPIO.setmode(GPIO.BCM)
428
        TRIG = 3
429
        ECHO = 4
430

    
431
        GPIO.setup(TRIG, GPIO.OUT)
432
        GPIO.setup(ECHO, GPIO.IN)
433

    
434
        try:
435
            GPIO.output(TRIG, False)
436
            time.sleep(2)
437

    
438
            GPIO.output(TRIG, True)
439
            time.sleep(0.00001)
440
            GPIO.output(TRIG, False)
441

    
442
            while GPIO.input(ECHO) == 0:
443
                pulso_inicio = time.time()
444

    
445
            while GPIO.input(ECHO) == 1:
446
                pulso_fin = time.time()
447

    
448
            duracion_pulso = pulso_fin - pulso_inicio
449
            return round(duracion_pulso * 17150, 2)
450
        except Exception as e:
451
            print(f"Error al medir distancia: {e}")
452
            return None
453
        finally:
454
            GPIO.cleanup()
455

    
456
    def leer_ph(self):
457
        try:
458
            arduino = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
459
            time.sleep(2)
460
            if arduino.in_waiting > 0:
461
                return arduino.readline().decode('utf-8').strip()
462
        except Exception as e:
463
            print(f"Error al leer pH: {e}")
464
            return None
465
        finally:
466
            try:
467
                arduino.close()
468
            except:
469
                pass
470

    
471
    def update_info_background(self, instance, value):
472
        self.info_background.size = instance.size
473
        self.info_background.pos = instance.pos
474
        self.info_border.rounded_rectangle = (
475
            instance.x, instance.y, instance.width, instance.height, 10
476
        )
477

    
478
    def update_rect_bottom(self, instance, value):
479
        self.rect_bottom.pos = instance.pos
480
        self.rect_bottom.size = instance.size
481

    
482
    def update_rect_gray(self, instance, value):
483
        self.rect_gray.pos = instance.pos
484
        self.rect_gray.size = instance.size
485

    
486
    def change_screen(self, screen_name):
487
        self.manager.current = screen_name
488

    
489
import RPi.GPIO as GPIO
490
import time
491
from kivy.uix.image import Image
492
from kivy.uix.label import Label
493
from kivy.uix.button import Button
494
from kivy.uix.floatlayout import FloatLayout
495
from kivy.graphics import Color, RoundedRectangle
496

    
497

    
498
class FiveScreen(Screen):  # Pantalla de Ver Estado Alimento
499
    def __init__(self, **kwargs):
500
        super(FiveScreen, self).__init__(**kwargs)
501
        layout = FloatLayout()
502

    
503
        # Imagen del logo
504
        image = Image(
505
            source='logo.png',
506
            allow_stretch=True,
507
            keep_ratio=False,
508
            size_hint=(None, None),
509
            size=("180dp", "160dp")
510
        )
511
        image.pos_hint = {'center_x': 0.5, 'center_y': 0.80}
512
        layout.add_widget(image)
513

    
514
        # Parte inferior amarilla
515
        with layout.canvas.before:
516
            Color(1, 1, 0, 0.8)  # Amarillo semi-transparente
517
            self.rect_bottom = RoundedRectangle(size=(300, 100), pos=(0, 0))
518

    
519
        # Caja gris flotante
520
        gray_box = FloatLayout(size_hint=(None, None), size=("300dp", "350dp"))
521
        gray_box.pos_hint = {'center_x': 0.5, 'y': 0.06}
522
        with gray_box.canvas:
523
            Color(1, 1, 1, 1)  # Blanco
524
            self.rect_gray = RoundedRectangle(
525
                size=gray_box.size,
526
                pos=gray_box.pos,
527
                radius=[(20, 20), (20, 20), (20, 20), (20, 20)]
528
            )
529
        layout.add_widget(gray_box)
530

    
531
        # Etiqueta para mostrar datos
532
        self.label = Label(
533
            text="Estado del Alimento:\nDesconocido",
534
            font_size=18,
535
            size_hint=(1, 1),
536
            pos_hint={"center_x": 0.5, "center_y": 0.5},
537
            color=(0, 0, 0, 1),
538
            halign="center",
539
            valign="middle"
540
        )
541
        layout.add_widget(self.label)
542

    
543
        # Botón para medir nivel de alimento
544
        check_food_button = Button(
545
            text="Ver Nivel de Alimento",
546
            size_hint=(None, None),
547
            size=("250dp", "60dp"),
548
            background_color=(0, 1, 0, 1),
549
            pos_hint={'center_x': 0.5, 'center_y': 0.4}
550
        )
551
        check_food_button.bind(on_press=self.check_food_level)
552
        layout.add_widget(check_food_button)
553

    
554
        # Botón para suministrar alimento
555
        supply_button = Button(
556
            text="Suministrar Alimento",
557
            size_hint=(None, None),
558
            size=("250dp", "60dp"),
559
            background_color=(0, 1, 0, 1),
560
            pos_hint={'center_x': 0.5, 'center_y': 0.35}
561
        )
562
        supply_button.bind(on_press=self.supply_food)
563
        layout.add_widget(supply_button)
564

    
565
        # Botón para regresar
566
        back_button = Button(
567
            text="Volver",
568
            size_hint=(None, None),
569
            size=("250dp", "60dp"),
570
            background_color=(1, 0, 0, 1),
571
            pos_hint={'center_x': 0.5, 'center_y': 0.2}
572
        )
573
        back_button.bind(on_press=lambda x: self.change_screen("second"))
574
        layout.add_widget(back_button)
575

    
576
        self.add_widget(layout)
577

    
578
    def on_enter(self):
579
        """
580
        Configura los GPIO necesarios al entrar a la pantalla.
581
        """
582
        try:
583
            GPIO.setwarnings(False)
584
            GPIO.cleanup()  # Limpia cualquier configuración previa
585
            GPIO.setmode(GPIO.BCM)
586

    
587
            # Configuración del sensor HX711
588
            self.dout = 5
589
            self.pd_sck = 6
590
            GPIO.setup(self.dout, GPIO.IN)
591
            GPIO.setup(self.pd_sck, GPIO.OUT)
592
            GPIO.output(self.pd_sck, False)
593
            self.offset = 0
594
            self.scale = 500  # Ajusta la escala según tu sensor
595
            self.tare()
596

    
597
            # Configuración del servomotor
598
            self.servo_pin = 9
599
            GPIO.setup(self.servo_pin, GPIO.OUT)
600
            self.pwm = GPIO.PWM(self.servo_pin, 50)  # Frecuencia de 50 Hz
601
            self.pwm.start(0)
602
        except Exception as e:
603
            print(f"Error al configurar GPIO en FiveScreen: {e}")
604

    
605
    def on_leave(self):
606
        """
607
        Limpia los recursos GPIO utilizados al salir.
608
        """
609
        try:
610
            self.pwm.stop()  # Detiene el PWM
611
            GPIO.cleanup([self.dout, self.pd_sck, self.servo_pin])  # Limpia solo los pines utilizados por esta pantalla
612
        except Exception as e:
613
            print(f"Error al limpiar GPIO en FiveScreen: {e}")
614

    
615
    def tare(self):
616
        """
617
        Calibra el sensor HX711 para establecer el offset.
618
        """
619
        print("Calibrando... Por favor, no coloque peso.")
620
        time.sleep(5)
621
        self.offset = self.read_raw()
622
        print("Calibración completa. Puede colocar peso.")
623

    
624
    def read_raw(self):
625
        """
626
        Lee el valor bruto del sensor HX711.
627
        """
628
        while GPIO.input(self.dout) == 1:
629
            pass
630
        count = 0
631
        for _ in range(24):
632
            GPIO.output(self.pd_sck, True)
633
            count = count << 1
634
            GPIO.output(self.pd_sck, False)
635
            if GPIO.input(self.dout):
636
                count += 1
637
        GPIO.output(self.pd_sck, True)
638
        GPIO.output(self.pd_sck, False)
639

    
640
        if count & 0x800000:
641
            count -= 0x1000000
642
        return count
643

    
644
    def get_units(self, readings=10):
645
        """
646
        Devuelve el peso en unidades calibradas.
647
        """
648
        total = 0
649
        for _ in range(readings):
650
            total += self.read_raw() - self.offset
651
        average = total / readings
652
        return average / self.scale
653

    
654
    def check_food_level(self, instance):
655
        """
656
        Mide el nivel de alimento y actualiza el texto.
657
        """
658
        try:
659
            weight = self.get_units(10)
660
            if weight <= 0.05:
661
                estado = "Vacío"
662
            else:
663
                estado = "Lleno"
664
            self.label.text = f"Peso: {weight:.2f} g\nEstado del Alimento: {estado}"
665
            print(f"Peso medido: {weight:.2f} g - Estado del Alimento: {estado}")
666
        except Exception as e:
667
            print(f"Error al medir el nivel de alimento: {e}")
668
            self.label.text = "Error al medir el nivel de alimento."
669

    
670
    def set_angle(self, angle):
671
        """
672
        Configura el ángulo del servomotor.
673
        """
674
        duty_cycle = 2 + (angle / 18)
675
        self.pwm.ChangeDutyCycle(duty_cycle)
676
        time.sleep(0.5)
677

    
678
    def supply_food(self, instance):
679
        """
680
        Controla el servomotor para suministrar alimento.
681
        """
682
        try:
683
            self.label.text = "Suministrando alimento..."
684
            print("Moviendo servomotor a 90 grados.")
685
            self.set_angle(90)  # Gira a 90 grados
686
            time.sleep(3)       # Mantiene la posición durante 3 segundos
687
            print("Regresando servomotor a la posición inicial.")
688
            self.set_angle(0)   # Regresa a 0 grados
689
            self.pwm.ChangeDutyCycle(0)  # Detiene el PWM
690
            self.label.text = "Estado del Alimento:\nSuministrado."
691
        except Exception as e:
692
            print(f"Error al suministrar alimento: {e}")
693
            self.label.text = "Error al suministrar alimento."
694

    
695
    def change_screen(self, screen_name):
696
        """
697
        Cambia a la pantalla especificada.
698
        """
699
        self.manager.current = screen_name
700

    
701

    
702
# Inicialización de la aplicación
703
class MyApp(App):
704
    def build(self):
705
        # Crear el ScreenManager
706
        sm = ScreenManager()
707

    
708
        # Agregar las pantallas al ScreenManager
709
        sm.add_widget(MainScreen(name="main"))
710
        sm.add_widget(SecondScreen(name="second"))
711
        sm.add_widget(FourScreen(name="four"))
712
        sm.add_widget(FiveScreen(name="five"))
713

    
714
        return sm
715

    
716

    
717
if __name__ == "__main__":
718
    MyApp().run()