In the digital age you stumble on concepts analog in their nature.
Modern graphics are created on a computer yet they originate in an analog process.
You constantly change the brush size yet the computer allows you to do it in
an increments. Do you actually care your color is R=53, G=123, B=200
in
numerical values except when moving the data?
Concept of making an analog controller for art creation on the computer was
always interesting for me. An exercise in this was this small controller built
with Arduino. It’s just a 3 potentiometers dividing voltage. Arduino reads this
voltage and sends the numeric values to Blender which can use them in various
ways thanks to built-in Python
environment. I used it to control color values.
Quick and creative soldering...
Below you will find an explanation in a video form.
Blender
has a built-in Python
environment. That means that you can use any Python
module. In
this case I’ve used the pyserial
library to communicate with the Arduino.
In order to be be able to access the pyserial
module I’ve downloaded it, unpacked and copied serial
folder here:
importbpyimportserialimportcolorsystry:port=serial.Serial("\\\\.\\COM5",115200)except:print("Error opening serial port.")ADC_RANGE=1024.0classModalTimerOperator(bpy.types.Operator):"""Operator which runs its self from a timer"""bl_idname="wm.modal_timer_operator"bl_label="Modal Timer Operator"_timer=Nonemode='HSV'defmodal(self,context,event):ifevent.type=='TIMER':ifport.in_waiting>13:line=port.readline()line=line.decode('UTF-8')line=line.strip()red,green,blue=str(line).split(':')red=int(red)/ADC_RANGEgreen=int(green)/ADC_RANGEblue=int(blue)/ADC_RANGEelse:return{'PASS_THROUGH'}ifself.mode=='HSV':red,green,blue=colorsys.hsv_to_rgb(red,green,blue)elifself.mode=='RGB':passbpy.context.object.active_material.diffuse_color=(red,green,blue)elifevent.type=='K':ifself.mode=='HSV':self.mode='RGB'elifself.mode=='RGB':self.mode='HSV'elifevent.typein{'ESC'}:print("Closing serial port.")port.close()return{'FINISHED'}return{'PASS_THROUGH'}defexecute(self,context):wm=context.window_managerself._timer=wm.event_timer_add(0.05,context.window)wm.modal_handler_add(self)return{'RUNNING_MODAL'}defcancel(self,context):wm=context.window_managerwm.event_timer_remove(self._timer)defregister():bpy.utils.register_class(ModalTimerOperator)defunregister():bpy.utils.unregister_class(ModalTimerOperator)if__name__=="__main__":register()bpy.ops.wm.modal_timer_operator()
Please take into consideration that it has been written for Blender 2.79. Things might have change
when it come to the API.
What’s most important in this script is handling the communication without blocking the main
Blender
thread. That has been achieved with the ModalOperator
class. By pressing K
key on your
keyboard you can change between RGB
and HSV
modes.
intred_raw=0;intgreen_raw=0;intblue_raw=0;charbuf[64];typedefstruct_State_t{intred;intgreen;intblue;}State_t;State_tcurrent;State_tprevious;#define SAMPLES_COUNT 3
#define OR_1(x) (x)
#define OR_2(x, y) ((x) || (y))
#define OR_3(x, y, z) ((x) || (y) || (z))
#define OR_4(w, x, y, z) ((w) || (x) || (y) || (z))
// At least 5 arguments. Arguments 6th and the rest are put in the __VA_ARGS__.
#define GET_MACRO(_1,_2,_3,_4,NAME,...) NAME
// If __VA_ARGS__ is 2 arguments then OR_4 becomes _3, OR_3 becomes _4 and NAME becomes OR_2.
#define ANY(...) GET_MACRO(__VA_ARGS__, OR_4, OR_3, OR_2, OR_1)(__VA_ARGS__)
intred_avg[SAMPLES_COUNT];intgreen_avg[SAMPLES_COUNT];intblue_avg[SAMPLES_COUNT];voidsetup(){Serial.begin(115200);}voidloop(){staticintindex=0;if(index>SAMPLES_COUNT-1)index=0;// read the analog in value:
blue_raw=analogRead(A0);green_raw=analogRead(A1);red_raw=analogRead(A2);red_avg[index]=red_raw;green_avg[index]=green_raw;blue_avg[index]=blue_raw;index++;intred_sum=0;intgreen_sum=0;intblue_sum=0;for(inti=0;i<SAMPLES_COUNT;i++){red_sum+=red_avg[i];green_sum+=green_avg[i];blue_sum+=blue_avg[i];}red_raw=red_sum/SAMPLES_COUNT;green_raw=green_sum/SAMPLES_COUNT;blue_raw=blue_sum/SAMPLES_COUNT;// map it to the range of the analog out:
//current.blue = map(blue_raw, 0, 1023, 0, 255);
//current.green = map(green_raw, 0, 1023, 0, 255);
//current.red = map(red_raw, 0, 1023, 0, 255);
current.red=red_raw;current.green=green_raw;current.blue=blue_raw;if(ANY((abs(current.red-previous.red)>2),(abs(current.green-previous.green)>2),(abs(current.blue-previous.blue)>2))){if(ANY(current.red!=previous.red,current.green!=previous.green,current.blue!=previous.blue)){sprintf(buf,"%04d:%04d:%04d\r\n",current.red,current.green,current.blue);Serial.print(buf);}}previous=current;delay(50);}
Nothing fancy here. There is a bit of filtering applied to the values read from the ADC. If the
values corresponding to the specific potentiometers changed by more than 2, the new values get
pushed via the serial port.