2

I have a very basic setup with a potentiometer hooked up to my Arduino analog 0 pin with it spamming output to the serial monitor. I am attempting to get the output in blender to match the arduino output by using the following code:

from time import sleep as wait
import serial

ser = serial.Serial("COM4",9600)
    while 1==1:
    wait(1)
    print(ser.readline())

This code freezes the main window and also outputs whatever the initial value for the potentiometer was despite rotating it so the output gets stuck outputting something like

b'493\r\n'
b'493\r\n'
b'493\r\n'

I realise that these issues are mainly due to the fact that my loop is endless but I'm not really sure how to create a coroutine in python and all the tutorials I've tried either don't work or require extra modules. I'd also like to know why it is outputting differently from the arduino serial monitor and how I can solve that.

This is the arduino code/output

void setup() {
 Serial.begin(9600);
}

void loop() {
  Serial.println(analogRead(0));
}  

574
576
578
Ray Mairlot
  • 29,192
  • 11
  • 103
  • 125

1 Answers1

1

You're reading binary data from the serial port, so that is why you have a bytes string (indicated by the b'' style of quoting). To convert it to a string, use

as_bytes = ser.readline()
line = as_bytes.decode('utf-8')

To get a repeated call to a "loop" function, it's easiest to start a modal operator with a high-frequency timer.

import bpy


class SERIAL_OT_arduino_read(bpy.types.Operator):
    bl_idname = "serial.arduino_read"
    bl_label = "Read From Arduino"

    def execute(self, context):
        as_bytes = self.ser.readline()
        line = as_bytes.decode('utf-8').strip()
        print('Serial line:', line)

        return {'FINISHED'}

    def modal(self, context, event):
        if event.type == 'TIMER':
            self.execute(context)
        elif event.type == 'LEFTMOUSE':  # Confirm
            self._finish()
            return {'FINISHED'}
        elif event.type in ('RIGHTMOUSE', 'ESC'):  # Cancel
            self._finish()
            return {'CANCELLED'}

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        self.ser = serial.Serial("COM4", 9600)
        self.timer = context.window_manager.event_timer_add(0.01, context.window)
        context.window_manager.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def _finish(self):
        context.window_manager.event_timer_remove(self.timer)


bpy.utils.register_class(SERIAL_OT_arduino_read)

# test call
bpy.ops.serial.arduino_read('INVOKE_DEFAULT')

This will read the serial port approximately 100x per second. Blender can still be used while this operator is running.

I haven't tested this code, just copied parts of the documentation and mixed it with your code.

dr. Sybren
  • 7,139
  • 1
  • 23
  • 52