52. Aplicación 3: Envió de datos de PIC (Hola Mundo) hacia PYTHON

 

 Comunicación Serial entre PIC y Python para Lectura de Potenciómetro

Introducción

Este tutorial muestra cómo leer un potenciómetro con un PIC, enviar los datos por comunicación serial y recibirlos en Python para procesamiento. A continuación se explican los pasos clave del proceso.

Parte 1: Configuración del PIC para ADC

Activar el ADC del PIC

c
// Configuración básica del ADC
void ADC_Init() {
    ADCON0 = 0x41;  // ADC ON, Canal AN0, Fosc/8
    ADCON1 = 0x80;  // Justificación derecha, VDD y VSS como referencias
}

int ADC_Read(int channel) {
    ADCON0 = (0x41 | (channel << 3)); // Seleccionar canal
    __delay_ms(2); // Tiempo de adquisición
    GO_nDONE = 1; // Iniciar conversión
    while(GO_nDONE); // Esperar finalización
    return ((ADRESH << 8) + ADRESL); // Retornar valor
}

Configuración de Entradas y Salidas

text
Pines importantes:
- AN0: Entrada analógica para el potenciómetro
- TX: Salida serial para transmisión
- Pines GPIO: Para LEDs indicadores

Parte 2: Programa del PIC para Envío de Datos

Código Principal del PIC

c
#include <xc.h>
#include <stdio.h>

#pragma config FOSC = INTOSC
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF

#define _XTAL_FREQ 4000000

void UART_Init(long baud_rate) {
    // Configurar pines TX/RX
    TRISC6 = 0; // TX como salida
    TRISC7 = 1; // RX como entrada
    
    // Configurar baud rate (9600 baudios para 4MHz)
    SPBRG = 25; // Cálculo: (4000000/(64*9600))-1
    
    // Habilitar transmisión serial
    TXEN = 1;
    SYNC = 0;
    SPEN = 1;
}

void UART_Write(char data) {
    while(!TXIF); // Esperar buffer vacío
    TXREG = data; // Enviar dato
}

void UART_Write_Text(char *text) {
    while(*text) {
        UART_Write(*text++);
    }
}

void main(void) {
    OSCCON = 0x66; // Configurar oscilador a 4MHz
    
    UART_Init(9600); // Inicializar UART
    ADC_Init(); // Inicializar ADC
    
    while(1) {
        int adc_value = ADC_Read(0); // Leer canal AN0
        char buffer[10];
        
        // Convertir valor a string y enviar
        sprintf(buffer, "%d\n", adc_value); // Enviar con salto de línea
        UART_Write_Text(buffer);
        
        // Controlar LEDs según valor
        if(adc_value < 341) {
            // Apagar LEDs o patrón específico
        } else if(adc_value < 682) {
            // Patrón intermedio
        } else {
            // Todos LEDs encendidos
        }
        
        __delay_ms(100); // Retardo entre lecturas
    }
}

Parte 3: Compilación y Grabación del PIC

Pasos para Compilar:

  1. Abrir MPLAB X IDE o compilador correspondiente

  2. Crear nuevo proyecto para el PIC específico

  3. Insertar el código fuente

  4. Compilar (Build) el proyecto

  5. Verificar que no hay errores

Modo Grabador/Bootloader:

text
Para grabar el PIC:
1. Conectar el programador (PICkit, ICD, etc.)
2. Poner el PIC en modo programación
   - Generalmente con un pulsador o secuencia específica
3. Seleccionar el archivo .hex generado
4. Programar el dispositivo
5. Resetear el sistema

Parte 4: Conexión del Potenciómetro

Esquema de Conexiones:

text
Potenciómetro (10kΩ recomendado):
- Terminal 1 → GND (negativo)
- Terminal 2 → AN0 (entrada analógica)
- Terminal 3 → VCC (positivo, 5V)

Parte 5: Programa Python para Recepción

Código Python Mejorado:

python
import serial
import time
import sys

class PICSerialReader:
    def __init__(self, port='COM21', baudrate=9600):
        self.port = port
        self.baudrate = baudrate
        self.ser = None
        
    def connect(self):
        try:
            self.ser = serial.Serial(
                port=self.port,
                baudrate=self.baudrate,
                timeout=1,
                bytesize=serial.EIGHTBITS,
                parity=serial.PARITY_NONE,
                stopbits=serial.STOPBITS_ONE
            )
            time.sleep(2)  # Esperar inicialización
            print(f"Conectado a {self.port}")
            return True
        except serial.SerialException as e:
            print(f"Error de conexión: {e}")
            return False
    
    def read_data(self, max_readings=1000):
        if not self.ser or not self.ser.is_open:
            print("Puerto no conectado")
            return
            
        reading_count = 0
        print("Leyendo datos... (Presione Ctrl+C para detener)")
        
        try:
            while reading_count < max_readings:
                if self.ser.in_waiting > 0:
                    # Leer línea completa
                    raw_data = self.ser.readline()
                    
                    # Decodificar con manejo de errores
                    try:
                        decoded_data = raw_data.decode('utf-8').strip()
                    except UnicodeDecodeError:
                        # Intentar con latin-1 si utf-8 falla
                        decoded_data = raw_data.decode('latin-1').strip()
                    
                    # Validar y convertir a entero
                    if decoded_data:
                        try:
                            value = int(decoded_data)
                            print(f"Valor recibido: {value}")
                            
                            # Mostrar estado binario aproximado
                            if value < 341:
                                print("Estado: Bajo (00)")
                            elif value < 682:
                                print("Estado: Medio (01)")
                            else:
                                print("Estado: Alto (11)")
                                
                        except ValueError as e:
                            print(f"Error convirtiendo '{decoded_data}': {e}")
                            # Mostrar representación hexadecimal para debug
                            print(f"Raw hex: {raw_data.hex()}")
                    
                    reading_count += 1
                    
        except KeyboardInterrupt:
            print("\nLectura detenida por usuario")
        except Exception as e:
            print(f"Error: {e}")
    
    def close(self):
        if self.ser and self.ser.is_open:
            self.ser.close()
            print("Conexión cerrada")
    
    def __del__(self):
        self.close()

# Uso principal
if __name__ == "__main__":
    # Configurar puerto según sistema operativo
    # Windows: COM21, Linux: /dev/ttyUSB0, Mac: /dev/tty.usbserial
    
    reader = PICSerialReader(port='COM21')  # Cambiar según tu sistema
    
    if reader.connect():
        reader.read_data(max_readings=1000)
        reader.close()
    else:
        print("No se pudo establecer conexión")
        sys.exit(1)

Parte 6: Solución de Problemas Comunes

Error "Invalid literal for int()":

Causa: El PIC envía caracteres no numéricos o formato incorrecto.
Solución:

python
# Antes de convertir, limpiar la cadena
def clean_string(data):
    # Eliminar caracteres no numéricos excepto signo y puntos
    return ''.join(filter(lambda x: x.isdigit() or x == '-' or x == '.', data))

# Uso:
cleaned = clean_string(decoded_data)
if cleaned:  # Verificar que no esté vacío
    value = int(float(cleaned))  # Convertir a float primero si hay decimales

Error de Codificación (UTF-8 vs Latin-1):

python
# Probar múltiples codificaciones
encodings = ['utf-8', 'latin-1', 'ascii', 'cp1252']

for encoding in encodings:
    try:
        decoded_data = raw_data.decode(encoding).strip()
        break  # Salir si una funciona
    except UnicodeDecodeError:
        continue

Manejo de Puertos Seriales:

python
import serial.tools.list_ports

# Listar puertos disponibles
def list_available_ports():
    ports = serial.tools.list_ports.comports()
    available_ports = []
    for port in ports:
        available_ports.append(port.device)
        print(f"Puerto disponible: {port.device} - {port.description}")
    return available_ports

# Seleccionar puerto automáticamente
available = list_available_ports()
if available:
    reader = PICSerialReader(port=available[0])

Parte 7: Prueba y Validación

Pasos de Validación:

  1. Verificar conexiones físicas:

    • Potenciómetro correctamente conectado

    • PIC alimentado (5V)

    • Conexión serial estable

  2. Probar comunicación:

    python
    # Script de prueba mínima
    import serial
    ser = serial.Serial('COM21', 9600, timeout=1)
    ser.write(b'test\n')  # Enviar test si el PIC lo soporta
    response = ser.readline()
    print(f"Respuesta: {response}")
    ser.close()
  3. Monitorear datos crudos:

    python
    # Ver bytes exactos recibidos
    raw_bytes = ser.read(ser.in_waiting)
    print(f"Bytes recibidos: {raw_bytes.hex()}")

Conclusión

Este sistema permite la comunicación básica entre un PIC y Python para lectura de sensores analógicos. La clave está en:

  1. Configurar correctamente el ADC en el PIC

  2. Enviar datos en formato consistente (con salto de línea recomendado)

  3. Manejar adecuadamente la codificación y conversión en Python

  4. Cerrar siempre las conexiones seriales después de usarlas

El código mostrado es un ejemplo mínimo funcional que puede extenderse para aplicaciones más complejas como control de motores, adquisición de datos múltiples, o interfaces gráficas.

Comentarios

Entradas populares de este blog

¿Qué es el Modelo OSI?

bit -El codigo ASCII

38. Tema 1: Protocolos de comunicación.