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
// 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
Pines importantes:
- AN0: Entrada analógica para el potenciómetro
- TX: Salida serial para transmisión
- Pines GPIO: Para LEDs indicadoresParte 2: Programa del PIC para Envío de Datos
Código Principal del PIC
#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:
Abrir MPLAB X IDE o compilador correspondiente
Crear nuevo proyecto para el PIC específico
Insertar el código fuente
Compilar (Build) el proyecto
Verificar que no hay errores
Modo Grabador/Bootloader:
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 sistemaParte 4: Conexión del Potenciómetro
Esquema de Conexiones:
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:
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:
# 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 decimalesError de Codificación (UTF-8 vs Latin-1):
# 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:
continueManejo de Puertos Seriales:
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:
Verificar conexiones físicas:
Potenciómetro correctamente conectado
PIC alimentado (5V)
Conexión serial estable
Probar comunicación:
# 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()Monitorear datos crudos:
# 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:
Configurar correctamente el ADC en el PIC
Enviar datos en formato consistente (con salto de línea recomendado)
Manejar adecuadamente la codificación y conversión en Python
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
Publicar un comentario