--- tags: fabacademy, blender --- # Blender IO ![](https://www.blender.org/wp-content/uploads/2015/03/blender_logo_socket-1-1280x391.png) [TOC] ## Intro Blender has a python interface that you can use to interface with the external world, you can connect networked sensors to blender and use the inputs in real-time, you can control some leds connected to your arduino, or plan the path your robot is going to follow. ## Scripting interface ### Python console First of all you need a place to **interact with blender via python**, for that we use the [Python Console](https://docs.blender.org/manual/en/latest/editors/python_console.html): ![](https://i.imgur.com/fHXU4mV.png) ### Interacting As a start point you can start playing with the default cube: ~~~python cube = bpy.data.objects['Cube'] cube.location.x cube.location = (2,3,4.5) ~~~ Remember that the [dir()](https://docs.python.org/3.8/library/functions.html#dir) python function can give a lot of information about what on object can do and hit properties. ![](https://i.imgur.com/VkGMkkG.png) You can use **auto-completion**, if you hit the _Tab_ key on an incomplete line Blender will ofer you the possible options to complete your sentence: ![](https://i.imgur.com/ikxroPz.png) You can copy blender operators from the interface: if you open the add menu and hover your mouse over some object, you can type _Ctrl-c_ to copy the python operator ![](https://i.imgur.com/xCpuNeC.png =300x) and then got to the console or text editor and hit _Ctrl-v_ ![](https://i.imgur.com/QjVBboB.png) If you want to know more about the function you just copied you can go to the [Blender Python API Documentation](https://docs.blender.org/api/current/index.html) and in the search field type _Ctrl-V_ ![](https://i.imgur.com/i4joMrh.png) :::info **Enable _python tooltips_** In the preferences window _Interface_ tabyou can enable _Python Tooltips_, this will give you extra python information on normal blender tooltips. ![](https://i.imgur.com/bM28E2I.png =400x) ::: ## The blender python API The blender [documentation](https://docs.blender.org/api/current/) has a lot of information on python scripting inside blender. The **bpy** python object will give you access to all the data inside blender. >Python accesses Blender’s data in the same way as the animation system and user interface; this implies that any setting that can be changed via a button can also be changed from Python. ### Data Access (bpy.data) > Accessing data from the currently loaded blend file is done with the module bpy.data. This gives access to library data. ~~~python >>> bpy.data.objects['Cube'] ~~~ ### Context Access (bpy.context) While it’s useful to be able to access data directly by name or as a list, it’s more common to operate on the user’s selection. The context is always available from bpy.context and can be used to get the active object, scene, tool settings along with many other attributes. ~~~python >>> bpy.context.object >>> bpy.context.selected_objects >>> bpy.context.visible_bones ~~~ ### Operators (bpy.ops) Operators are **tools** generally accessed by the user from buttons, menu items or key shortcuts. From the user perspective they are a tool but Python can run these with its own settings through the bpy.ops module. ~~~python >>> bpy.ops.mesh.flip_normals() {'FINISHED'} >>> bpy.ops.mesh.hide(unselected=False) {'FINISHED'} >>> bpy.ops.object.subdivision_set(3) {'FINISHED'} ~~~ # Hands on ## Reading mobile phone sensors » To send SmartPhone sensor data via UDP packets to our computer in order to use it inside blender we can use any of the available apps, for this example we are going to use [Sensor stream IMU+GPS](https://play.google.com/store/apps/details?id=de.lorenz_fenster.sensorstreamgps) android app. ### Phone side On the phone side we only need to set the ip address of our computer (in my case is 192.168.0.12), select a port (5555) and check **UDP Stream**. On the _Toggle Sensors_ tab we turn on the desired sensors (the first tree are always enabled) in our case we are going to use **Orientation**, we also need to check the _Include User-Checked..._ option. Testing this app, we get the best results using the _Fast_ speed option. To start sending data just click the _Switch Stream ON_ button. ![](https://i.imgur.com/9Oa9H74.png)![](https://i.imgur.com/h2pX9Tu.png) ### Blender side Using an already written template for a python blender operator called _Operator Modal Timmer_ is the simplest option. ![](https://i.imgur.com/GhlqAp1.png) This script is designed to run any python instructions based on a timer until the user press de _Esc_ key or the right mouse button. We only need to add the code that allow us to connect to the UDP stream and some code to parse the data we receive. On the network side we use the [socket](https://docs.python.org/3/library/socket.html) python library, we need to add a line for importing this library: ~~~python import bpy import socket ~~~ Some code to setup the connection, this goes inside the _Class_ but outside the defined methods, just under the `timer = None` line. ~~~python # Setup UDP socket host = '192.168.0.12' # The same address of your computer port = 5555 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) s.bind((host, port)) s.settimeout(1) ~~~ And inside the _modal_ method under _TIMER_ event we substitute the default code with our code to parse the received data. ~~~python # Receive data try: message, address = self.s.recvfrom(8192) # Check if the data is complete (sometimes the app sends only some of the sensors) if len(message.split(b',')) == 17: x = float(message.split(b',')[-2]) * 0.01745 y = float(message.split(b',')[-1]) * 0.01745 z = float(message.split(b',')[-3]) * 0.01745 # print(x,y,z) context.object.rotation_euler.x = x context.object.rotation_euler.y = y context.object.rotation_euler.z = z except: # Stop if no more data is received self.cancel(context) return {'CANCELLED'} ~~~ The data is received in the form of a long text string comma separated. ![](https://i.imgur.com/J65T2DV.png) So we split the readings, take the last 3 and convert them to radians. Then we update the rotation of the selected object. To make it more responsive we can make the timer to execute more often, just change the value under the _execute(self, context)_ to something smaller, 0.001 has worked nicely in our case. ~~~python def execute(self, context): wm = context.window_manager self._timer = wm.event_timer_add(0.001, window=context.window) wm.modal_handler_add(self) return {'RUNNING_MODAL'} ~~~ Now just run the script and if everything is Ok you will see Suzanne rotating! To stop the script you can click the right mouse button , hit the _esc_ key, or simply stop the data stream on your phone.gg ## Controlling arduino built in led « To start lets control the built in led on an arduino uno with a simple on/off switch ![](https://i.imgur.com/EE03yLe.png =300x) ### Arduino side ~~~arduino int state = 0; void setup() { pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); Serial.begin(115200); } void loop() { if (Serial.available()) state = Serial.parseInt(); // Get the value while (Serial.available()) Serial.read(); // Clean the Serial port if (state > 0) digitalWrite(LED_BUILTIN, HIGH); // Turn on the led else digitalWrite(LED_BUILTIN, LOW); // or off delay(200); } ~~~ The green box has movement constrains so it can only move in X axis and in limited amount. In the python script we check his position and when it arrives to one size we send a value via the serial port to the arduino. ~~~python 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.01 if context.object.location.x <= -3: context.object.location.x = -3 self.ser.write(b'0 ') if context.object.location.x > 3: context.object.location.x = 3 self.ser.write(b'1 ') ~~~ ## Led strip fun Moving an arrow in blender we control a moving light through the led strip ![](https://i.imgur.com/AAXld9A.png) ![](https://i.imgur.com/UlPLbgG.gif) ### Arduino code Turns on the leds depending on the value it receives on the serial port. ~~~arduino #include <Adafruit_NeoPixel.h> #define PIN 6 #define NUMPIXELS 20 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); } ~~~ ### Blender python script Based on _Operator Modal_ template (you can load this templates in blender text editor via the _Templates_ menu). The added lines are indicated with **# «** This script sends a value from 0 to 20 depending on the position of the selected object (the arrow in this case), it only sends data when it changes. The result is pretty responsive and robust. ~~~python 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/ttyUSB1', 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.1 if context.object.location.x < -50: context.object.location.x = -50 # « elif context.object.location.x > 50: context.object.location.x = 50 # « 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 # « ~~~ ## More ideas for trying * Object reacting to microphone data (a Smartcitizen kit via serial port can be used for this). Try to link different data properties: scale, rotation, moving faces along normals, etc. ![](https://i.imgur.com/BFSICSN.gif) * Servo motor that copies the rotation of blender object. --- :::info **References** * [Blender 2.82a Python API Documentation](https://docs.blender.org/api/current/) * [Python Scripting Superpowers](https://cgcookie.com/articles/blender-2-8-python-scripting-superpowers-for-non-programmers) on cgcookie ::: :::warning A lot of content has been borrowed from different sources, mainly from Blender project documentation, thanks to all of them! :::