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.