owned this note
owned this note
Published
Linked with GitHub
---
title: Blender & Python
description: Tutoriales y cosas que hago en ratos libres
Autors: Santi Fuentemilla & Adai Suriñach
---
###### tags: `Blender` `Python` `Coding` `Fab Lab Barcelona`
This repository is a continuation of Victor's work that you will find here: https://fablabbcn-projects.gitlab.io/learning/educational-docs/material/extras/week12/blender/
# Websockets

## Blender
```
import bpy
import asyncio
import websockets
def set_text(message):
obj = bpy.data.objects.get("Text")
if obj and obj.type == 'FONT':
obj.data.body = message
async def handler(websocket):
async for message in websocket:
print(f"Recibido: {message}")
def actualizar_texto():
set_text(message)
return None
bpy.app.timers.register(actualizar_texto)
async def listen():
print("Iniciando servidor WebSocket en Blender")
async with websockets.serve(handler, "0.0.0.0", 8765):
await asyncio.Future()
def run_server():
import threading
loop = asyncio.new_event_loop()
def start_loop():
asyncio.set_event_loop(loop)
loop.run_until_complete(listen())
threading.Thread(target=start_loop, daemon=True).start()
run_server()
```
## Arduino (XIAO esp32)
```
#include <WiFi.h>
#include <WebSocketsClient.h>
const char* ssid = "Iaac-Wifi";
const char* password = "EnterIaac22@";
const char* host = "172.16.23.12"; // IP del ordenador con Blender
const int port = 8765;
WebSocketsClient webSocket;
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.print("Conectando a WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConectado a WiFi");
Serial.print("IP local: ");
Serial.println(WiFi.localIP());
webSocket.begin(host, port, "/");
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
static unsigned long lastSend = 0;
if (millis() - lastSend > 2000) {
lastSend = millis();
String msg = String("Hola desde ESP32");
Serial.println("Enviando: " + msg);
Serial.
webSocket.sendTXT(msg);
}
}
void webSocketEvent(WStype_t type, uint8_t* payload, size_t length) {
if (type == WStype_DISCONNECTED) {
Serial.println("WebSocket desconectado");
} else if (type == WStype_CONNECTED) {
Serial.println("WebSocket conectado");
} else if (type == WStype_TEXT) {
Serial.printf("Mensaje recibido: %s\n", payload);
}
}
```
## Python
install Websocat: `brew install websocat`
https://github.com/vi/websocat/releases
Terminal:
`websocat ws://192.168.0.101:8765`
----
# Websocket: ON/OFF LED
## Blender:
```
import bpy
import asyncio
import websockets
import threading
# IP del ESP32
ESP32_IP = "ws://192.168.0.101:8765"
# Umbral de Z
Z_THRESHOLD = 1.0
# Estado actual
estado_anterior = None
async def monitor_posicion(websocket):
global estado_anterior
while True:
obj = bpy.data.objects.get("Cube")
if obj:
z = obj.location.z
estado_actual = "on" if z > Z_THRESHOLD else "off"
if estado_actual != estado_anterior:
print(f"Z = {z}, enviando: {estado_actual}")
await websocket.send(estado_actual)
estado_anterior = estado_actual
await asyncio.sleep(0.5)
async def start_client():
try:
async with websockets.connect(ESP32_IP) as websocket:
print("Conectado al ESP32")
await monitor_posicion(websocket)
except Exception as e:
print(f"Error: {e}")
def run_ws():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(start_client())
threading.Thread(target=run_ws).start()
```
## Arduino (Feather Esp32)
```
#include <WiFi.h>
#include <WebSocketsServer.h>
const char* ssid = "TU_WIFI";
const char* password = "TU_PASSWORD";
WebSocketsServer webSocket = WebSocketsServer(8765);
const int ledPin = 13;
void setup() {
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi conectado");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
webSocket.begin();
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
if (type == WStype_TEXT) {
String msg = String((char*)payload);
Serial.println("Mensaje recibido: " + msg);
if (msg == "on") {
digitalWrite(ledPin, HIGH);
} else if (msg == "off") {
digitalWrite(ledPin, LOW);
}
}
}
```
---
# SerialColor

## Blender:
```
import bpy
import serial
import time
class XiaoNeopixelOperator(bpy.types.Operator):
"""Controla el Neopixel en XIAO RP2040 (GPIO 12)"""
bl_idname = "object.xiao_neopixel"
bl_label = "Control XIAO Neopixel"
_timer = None
_ser = None
_puerto = "" # Se configurará en el panel
_running = False
def execute(self, context):
# Obtener el puerto del panel
self._puerto = context.scene.xiao_puerto_serial
# Conectar al puerto serial
try:
self._ser = serial.Serial(self._puerto, 115200, timeout=1)
time.sleep(2) # Tiempo para establecer conexión
self.report({'INFO'}, f"Conectado a {self._puerto}")
# Activar actualización automática
wm = context.window_manager
self._timer = wm.event_timer_add(0.1, window=context.window)
wm.modal_handler_add(self)
self._running = True
return {'RUNNING_MODAL'}
except Exception as e:
self.report({'ERROR'}, f"Error: {str(e)}")
return {'CANCELLED'}
def modal(self, context, event):
if not self._running:
return {'CANCELLED'}
# Si el usuario cierra el operador
if event.type == 'ESC':
self.cancel(context)
return {'CANCELLED'}
# Actualización por posición del objeto
if event.type == 'TIMER':
if context.object:
x_pos = context.object.location.x
# Determinar color basado en posición X
if x_pos < -12.5:
color = 'R' # Rojo
elif x_pos > 12.5:
color = 'G' # Verde
else:
color = 'B' # Azul
# Enviar comando de color
try:
self._ser.write((color + " ").encode('utf-8'))
self._ser.flush()
except Exception as e:
self.report({'ERROR'}, f"Error en comunicación: {str(e)}")
self.cancel(context)
return {'CANCELLED'}
return {'PASS_THROUGH'}
def cancel(self, context):
# Limpiar
if self._ser and self._ser.is_open:
try:
self._ser.close()
except:
pass
# Detener timer
if self._timer:
wm = context.window_manager
wm.event_timer_remove(self._timer)
self._running = False
self.report({'INFO'}, "Operador detenido")
class XiaoSimpleTestOperator(bpy.types.Operator):
"""Test simple para enviar R, G, B al XIAO RP2040"""
bl_idname = "object.xiao_simple_test"
bl_label = "Test Simple RGB"
color: bpy.props.StringProperty(default="R")
def execute(self, context):
puerto = context.scene.xiao_puerto_serial
try:
ser = serial.Serial(puerto, 115200, timeout=1)
time.sleep(1)
# Enviar comando de color
ser.write((self.color + " ").encode('utf-8'))
ser.flush()
self.report({'INFO'}, f"Color {self.color} enviado")
# Esperar respuesta
time.sleep(0.5)
if ser.in_waiting:
respuesta = ser.read(ser.in_waiting).decode('utf-8')
self.report({'INFO'}, f"Respuesta: {respuesta}")
ser.close()
return {'FINISHED'}
except Exception as e:
self.report({'ERROR'}, f"Error: {str(e)}")
return {'CANCELLED'}
class XiaoPanel(bpy.types.Panel):
"""Panel para controlar XIAO RP2040"""
bl_label = "XIAO RP2040 Control"
bl_idname = "OBJECT_PT_xiao"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'Tool'
def draw(self, context):
layout = self.layout
# Configuración del puerto
layout.prop(context.scene, "xiao_puerto_serial")
# Test de colores simples
box = layout.box()
box.label(text="Test de colores:")
row = box.row()
# Botones de colores
row.operator("object.xiao_simple_test", text="Rojo").color = "R"
row.operator("object.xiao_simple_test", text="Verde").color = "G"
row.operator("object.xiao_simple_test", text="Azul").color = "B"
# Control por objeto
box = layout.box()
box.label(text="Control por objeto:")
box.operator("object.xiao_neopixel", text="Iniciar control")
box.label(text="Mover objeto en X para cambiar color")
box.label(text="ESC para detener")
def register():
# Registrar propiedades
bpy.types.Scene.xiao_puerto_serial = bpy.props.StringProperty(
name="Puerto Serial",
description="Ejemplo: COM3 (Windows), /dev/ttyACM0 (Linux/Mac)",
default="/dev/tty.usbmodem11201"
)
# Registrar clases
bpy.utils.register_class(XiaoSimpleTestOperator)
bpy.utils.register_class(XiaoNeopixelOperator)
bpy.utils.register_class(XiaoPanel)
def unregister():
bpy.utils.unregister_class(XiaoSimpleTestOperator)
bpy.utils.unregister_class(XiaoNeopixelOperator)
bpy.utils.unregister_class(XiaoPanel)
# Limpiar propiedades
del bpy.types.Scene.xiao_puerto_serial
if __name__ == "__main__":
register()
```
## Arduino (XIAO RP2040)
```
#include <Adafruit_NeoPixel.h>
// Usando GPIO 12 para el NeoPixel según lo especificado
int Power = 11;
int PIN = 12;
#define NUMPIXELS 1
// Inicializar NeoPixel
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
void setup() {
// Iniciar comunicación serial
Serial.begin(115200);
// Inicializar NeoPixel
pixels.begin();
pixels.setBrightness(100); // Aumentado a 100 para mejor visibilidad
pinMode(Power,OUTPUT);
digitalWrite(Power, HIGH);
// Iniciar con color rojo
pixels.setPixelColor(0, pixels.Color(255, 0, 0)); // Rojo
pixels.show();
Serial.println("Test XIAO RP2040 iniciado. Pin LED = GPIO 12");
}
void loop() {
if (Serial.available() > 0) {
char color = Serial.read();
// Procesar comandos
if (color == 'R') {
pixels.setPixelColor(0, pixels.Color(255, 0, 0)); // Rojo
Serial.println("Color: Rojo");
}
else if (color == 'G') {
pixels.setPixelColor(0, pixels.Color(0, 255, 0)); // Verde
Serial.println("Color: Verde");
}
else if (color == 'B') {
pixels.setPixelColor(0, pixels.Color(0, 0, 255)); // Azul
Serial.println("Color: Azul");
}
else if (color == 'W') {
pixels.setPixelColor(0, pixels.Color(255, 255, 255)); // Blanco para probar máximo brillo
Serial.println("Color: Blanco");
}
pixels.show();
// Limpiar buffer
while (Serial.available() > 0) {
Serial.read();
}
}
}
```
# SERIAL LED STRIP on/of

## Blender
```
import bpy
from bpy.props import IntProperty, FloatProperty
import serial
class ModalOperator(bpy.types.Operator):
"""Move an object with the mouse, example"""
bl_idname = "object.modal_operator"
bl_label = "Led control"
first_mouse_x: IntProperty()
first_value: FloatProperty()
# Setup serial port
ser = serial.Serial('/dev/ttyUSB1', 115200)
def modal(self, context, event):
if event.type == 'MOUSEMOVE':
delta = self.first_mouse_x - event.mouse_x
context.object.location.x = self.first_value - delta * 0.5
if context.object.location.x <= -3:
# context.object.location.x = -3
self.ser.write(b'0 ')
bpy.data.objects['led'].material_slots[0].material = bpy.data.materials['grey']
if context.object.location.x >= 3:
# context.object.location.x = 3
bpy.data.objects['led'].material_slots[0].material = bpy.data.materials['green']
self.ser.write(b'1 ')
elif event.type == 'LEFTMOUSE':
context.object.location.x = self.first_value
return {'FINISHED'}
elif event.type in {'RIGHTMOUSE', 'ESC'}:
context.object.location.x = self.first_value
return {'CANCELLED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
if context.object:
self.first_mouse_x = event.mouse_x
self.first_value = context.object.location.x
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "No active object, could not finish")
return {'CANCELLED'}
def register():
bpy.utils.register_class(ModalOperator)
def unregister():
bpy.utils.unregister_class(ModalOperator)
if __name__ == "__main__":
register()
# test call
bpy.ops.object.modal_operator('INVOKE_DEFAULT')
```
## Arduino
```
#include <Adafruit_NeoPixel.h>
#define PIN 12
#define NUMPIXELS 40
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
int state = 0;
void setup() {
Serial.begin(115200);
pixels.begin();
}
void loop() {
if (Serial.available()) state = Serial.parseInt();
while (Serial.available()) Serial.read();
for(int i=0; i<NUMPIXELS; i++) {
if (state) pixels.setPixelColor(i, pixels.Color(0, 250, 0));
else pixels.setPixelColor(i, pixels.Color(0,0,0));
pixels.show();
}
delay(20);
}
```
# SERIAL LED strip control

## Blender
```
import bpy
from bpy.props import IntProperty, FloatProperty
import serial
class ModalOperator(bpy.types.Operator):
"""Move an object with the mouse, example"""
bl_idname = "object.modal_operator"
bl_label = "Led Strip control"
first_mouse_x: IntProperty()
first_value: FloatProperty()
led = 0
prevLed = 0
ser = serial.Serial('/dev/ttyUSB3', 115200) # Setup serial port
def modal(self, context, event):
if event.type == 'MOUSEMOVE':
delta = self.first_mouse_x - event.mouse_x
context.object.location.x = self.first_value - delta * 0.2
if context.object.location.x < -50: context.object.location.x = -50
elif context.object.location.x > 155: context.object.location.x = 155
self.led = int(((context.object.location.x + 50) * 2) /10)
if self.led != self.prevLed:
send = str(self.led) + ' '
self.ser.write(send.encode('utf8'))
print(self.led)
self.prevLed = self.led
elif event.type == 'LEFTMOUSE':
# context.object.location.x = self.first_value
return {'FINISHED'}
elif event.type in {'RIGHTMOUSE', 'ESC'}:
# context.object.location.x = self.first_value
return {'CANCELLED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
if context.object:
self.first_mouse_x = event.mouse_x
self.first_value = context.object.location.x
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "No active object, could not finish")
return {'CANCELLED'}
def register():
bpy.utils.register_class(ModalOperator)
def unregister():
bpy.utils.unregister_class(ModalOperator)
if __name__ == "__main__":
register()
# test call
bpy.ops.object.modal_operator('INVOKE_DEFAULT')
```
## Arduino
```
#include <Adafruit_NeoPixel.h>
#define PIN 12
#define NUMPIXELS 40
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
int value = 0;
void setup() {
Serial.begin(115200);
pixels.begin();
}
void loop() {
if (Serial.available()) value = Serial.parseInt();
while (Serial.available()) Serial.read();
for(int i=0; i<NUMPIXELS; i++) {
if (i == value) {
pixels.setPixelColor(i, pixels.Color(0, 250, 0));
} else if (abs(i - value) == 1) {
pixels.setPixelColor(i, pixels.Color(0,15, 0));
} else if (abs(i - value) == 2) {
pixels.setPixelColor(i, pixels.Color(0, 1, 0));
} else {
pixels.setPixelColor(i, pixels.Color(0, 0, 0));
}
pixels.show();
}
delay(20);
}
```
# WEB socket Phone rotation

## Blender
```
import bpy
from websockets.sync.client import connect
import json
class ModalTimerOperator(bpy.types.Operator):
"""Operator which runs itself from a timer"""
bl_idname = "wm.modal_timer_operator"
bl_label = "Modal Timer Operator"
_timer = None
def modal(self, context, event):
if event.type in {'RIGHTMOUSE', 'ESC'}:
self.cancel(context)
return {'CANCELLED'}
if event.type == 'TIMER':
uri = "ws://172.16.10.93:8080/sensor/connect?type=android.sensor.orientation"
with connect(uri) as websocket:
data = json.loads(websocket.recv())
bpy.context.object.rotation_euler.x = float(data['values'][-1]) * 0.01745
bpy.context.object.rotation_euler.y = float(data['values'][2]) * 0.01745
bpy.context.object.rotation_euler.z = float(data['values'][0]) * 0.01745 #degrees to radians
return {'PASS_THROUGH'}
def execute(self, context):
wm = context.window_manager
self._timer = wm.event_timer_add(0.1, window=context.window)
wm.modal_handler_add(self)
return {'RUNNING_MODAL'}
def cancel(self, context):
wm = context.window_manager
wm.event_timer_remove(self._timer)
def menu_func(self, context):
self.layout.operator(ModalTimerOperator.bl_idname, text=ModalTimerOperator.bl_label)
def register():
bpy.utils.register_class(ModalTimerOperator)
bpy.types.VIEW3D_MT_view.append(menu_func)
# Register and add to the "view" menu (required to also use F3 search "Modal Timer Operator" for quick access).
def unregister():
bpy.utils.unregister_class(ModalTimerOperator)
bpy.types.VIEW3D_MT_view.remove(menu_func)
if __name__ == "__main__":
register()
# test call
bpy.ops.wm.modal_timer_operator()
```
## [Sensor Server](https://github.com/umer0586/SensorServer)

asasa
sa
s
a
sas
asasa
asasa
# santi