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.
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
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(((abs(current.red-previous.red))>2)||((abs(current.green-previous.green))>2)||((abs(current.blue-previous.blue))>2)||){if((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.