Python + BGE Python to talk to Arduino

I am trying to interface Arduino to Blender BGE and vice versa.

Before jumping to BGE Python script, I am testing it with Blender Python.

This is what happens so far:
I have Arduino program already running in the background, the Potentiometer is sending message to Serial.

I am using PySerial Library.

Now, I thought that what I need to do is to read the data inside Serial:

import serial
import bpy


arduino = serial.Serial('COM3', 9600)


x = 0


# THIS SCRIPT WILL GENERATE MAXIMUM RECURSION DEPTH ERROR


#define action
def Action(Scene):
    potValue = arduino.readline()
    bpy.data.objects['Cube'].scale[0] = 0.1 * int(potValue)
    bpy.data.objects['Cube'].scale[1] = 0.1 * int(potValue)
    bpy.data.objects['Cube'].scale[2] = 0.1 * int(potValue)
    bpy.data.scenes[0].update()


#register action
bpy.app.handlers.scene_update_pre.append(Action)


"""
while(x  < 1000):
    print(x)
    
    # read arduino Series value
    print( arduino.readline() )
 
    x += 1
"""




However doing it like above, and I am getting “Maximum Recursion Depth Error”. Perhaps because I assigned the scene_update_pre.append() the wrong way?

And I cannot stop the script from running. This is confusing.

On the other hand for BGE, also I found issue. If I use while(True), the PySeries seems to read the Potentiometer correctly, however, BGE came to halt (while still reading Series value) and I need to restart Blender.

BGE Code:

from bge import logic
from bge import events
import bge
import math
import serial


#arduino = 0


# Get the cube controller
cont = logic.getCurrentController()
own = cont.owner
   


if not 'init' in own:
    own['init'] = 1
    print ("initializing ....")
    #arduino = serial.Serial('COM3', 9600, timeout=1)


"""
# move cube using sine movement in Z
own.position.x += 0.1
own.position.z = own.position.z + math.sin(own.position.x)
"""




arduino = serial.Serial('COM3', 9600, timeout=1)


"""
if (arduino.isOpen()):    
    x = arduino.readline()
    #own.position.z = x
    print(x)
    print("reading")


arduino = serial.Serial('COM3', 9600, timeout=1)
"""


arduino.write("it is working")

Any idea to fix this or maybe any thread pointing me in the right direction?

I thought using Serial direct to Arduino will be simple, but not the case.

I am also thinking OSC will be what I need. But I like to see if I could:

  • Control Arduino using BGE
  • Control Blender using Arduino (not so real time is okey)
  • Control BGE using Arduino (real time)

Nice project idea, though I don’t quite know how to help.

@Monster

Before jumping to BGE Python script, I am testing it with Blender Python.
I think we can forgive him!

I suspect that the pre_update handler is called when any scene data is changed, which would include changing an object’s position. If you want to make it work with BPY, make a modal operator.

However, it’s easier to do this in the BGE. You can use While loops in the BGE, with threads, but as monster said, it’s a lot of effort. Instead, run the serial in non blocking mode - set the timeout to 0. You might need to catch an empty exception, or handle no-data.

Something like this (if it is an Exception, try and catch the specific exception you expect to be raised rather than generic “Exception”)

Thanks for the replies, I will study it first.

I am also testing this:

Inside BGE loop… but response is slow on Windows PC. Will have to test it on Mac or Linux.

http://playground.arduino.cc/interfacing/python#.UybnQPmSx8E

from time import sleep
import serial

ser = serial.Serial(‘COM3’, 9600, timeout=0.01) # Establish the connection on a specific port

counter = 0
while (counter<100):
counter +=1
value = ser.readline()
print ( value ) # Read the newest output from the Arduino
sleep(.5) # Delay for one tenth of a second

But the above will halt BGE and Blender, however it does kind of reading the Serial from Arduino.

I’ll let you know if it is working.

@Monster
The BGE Loop explanation notes is very helpful, let me try to digest this first… it will get there.

There are few examples that uses OSC + BGE, but for now I want to make this Serial thing happening first. It’s more old school and doing it the hard way, but simpler. OSC is cool of course.

@Agoose
I think I will try to do it with BGE then.

Would be nice if it is working on the normal Blender environment and it updates in realtime to record.
An easier way to interface with OSC, Serial, etc will be very welcomed. I know it’s all possible via Python.

Anyway, I think I have to look around many places and find the answer. By today, there must be lots of “realtime” interfacing, using Wii, Gloves, and so on.

I am still trying to understand this Serial thing first:

Arduino => Processing (via Serial) seems very simple:

I was reading this and then test it using BPY.

It froze Blender, however, it is also working, in a way it is doing the talk to Arduino and sending/receiving Serial data.

Need to somewhat understand BGE Looping better. My conclusion is that the PySerial really does not work correctly in Windows because it keeps resetting the Arduino when being called to check. — I need to check if there is a difference on Mac.

I want to see a simple example of how INITIALIZE (one time looping) and LOOP works. Prof. Monster gave a nice PDF on Looping, but still confusing because we need example.

This kind of work if I want a 1 time only execution, however for the PySerial thing that I am doing, this does not work somewhat…:

if not ‘init’ in own:
own[‘init’] = 1
print (“initializing …”)
arduino = serial.Serial(‘COM3’, 9600, timeout = 0.05)

The “arduino” variable when initialized inside, it just does not initialize.

Note: I am a beginner programmer.

I will switch to OSC (Open Sound Controller) and Python for now. And will try to communicate it with Arduino or other OSC controllers (iPad Touch OSC).

I wrote about this in the past, but last time I was using Scratch, Scratch OSC to talk to Blender. It works, but a bit convoluted.

Would be nice to have a SIMPLE straight examples on BGE. I am reading Mike Pan and Dalai Felinto Book, even though it is a good BGE book however, the Python part is too short.

@agoose77
I tried your example and it is getting somewhere. This is exactly what I wanted.

I have to change the code like this in order to get the correct data. There is probably a better way to convert:

b’255’
=> 255 (that is a number, not string or else)

Anyways, below actually does the job.

And what was interesting is the setup with MODULE and the Python script works. I don’t have performance issue.

Except…

One problem is when there is no data coming in from Serial, I am getting None or 0. In my case I am plugging obj.worldScale directly into the (data, data, data) => which goes from 0 - 255.

Whenever I stop rotating the knob/potentiometer, I am getting 0 and so the object become (0,0,0) in scale for every split second.

There is a need to buffer the last value somewhat… How do I do this… is this “debounce” thing?

from bge import logic
from bge import events

import math
import serial
import struct

def convert(str):
try:
base = 10 # default
if ‘:’ in str:
sstr = str.split(‘:’)
base, str = int(sstr[0]), sstr[1]
val = int(str, base)
except ValueError:
val = 0

return val

def update_arduino(arduino, obj):
try:
data = arduino.readline()
except Exception as err:
return

# Clean up data
data = data.decode('ascii').strip()

# All these below did not work :(
#newdata = data.splitlines()
#newdata = int.from_bytes(data, byteorder='big', signed=True)
#does not like INPUT, freeze BGE
#newdata = input(data)

# Finally... turn data into INTEGER!
data = int(convert(data))

#print(eval(data))

print(data)

obj.worldScale = (data, data, data)

def main(cont):
own = cont.owner

try:
    arduino = own['arduino']
except KeyError:
    arduino = own['arduino'] = serial.Serial('COM3', 9600, timeout=0)

update_arduino(arduino, own)

(grrr… don’t like it when code does not paste properly…)

Is there a suggestion on how we can copy paste code and then keep the spacing properly …

from bge import logicfrom bge import events

import math
import serial
import struct

def convert(str):
try:
base = 10 # default
if ‘:’ in str:
sstr = str.split(‘:’)
base, str = int(sstr[0]), sstr[1]
val = int(str, base)
#lastvalue = val

except ValueError:
    val = 0
return val

def update_arduino(arduino, obj):
try:
data = arduino.readline()
except Exception as err:
return

# Clean up data
data = data.decode('ascii').strip()

# All these below did not work :(
#newdata = data.splitlines()
#newdata = int.from_bytes(data, byteorder='big', signed=True)
#does not like INPUT, freeze BGE
#newdata = input(data)

# Finally... turn data into INTEGER!
data = int(convert(data))

print(data)

obj.worldScale = (data, data, data)

def main(cont):
own = cont.owner

try:
    arduino = own['arduino']
except KeyError:
    arduino = own['arduino'] = serial.Serial('COM3', 9600, timeout=0)

update_arduino(arduino, own)

JUST IN CASE:

You can use code tags.

You probably want to use the string strip method after splitting the values which will remove any line breaks or tabs.

Unless the formatting at the end is actually part of the number, in which case you can convert directly from bytes. Be aware that you must respect the base.

Sent from my Nexus 5 using Tapatalk

Ok, I clean up the code a bit.


from bge import logic
from bge import events
 
import math
import serial




def convert(str):
    try:
        base = 10  # default
        if ':' in str:
            sstr = str.split(':')
            base, str = int(sstr[0]), sstr[1]
        val = int(str, base)
        
    except ValueError:
        val = 1000


    return val


 
def update_arduino(arduino, obj):
    
    try:
        data = arduino.readline()
    except Exception as err:
        return
 
    # Clean up data
    data = data.decode('ascii').strip() # get rids of 

    data = convert(data)
    
    # All these below did not work for many reasons
    #data = float(data) # could not convert string to float
    #data = data.splitlines() #this create list
    #data = int.from_bytes(data, byteorder='big', signed=True)
    #data = input(data) # does not like INPUT, freeze BGE


    obj['originalData'] = data


    # need to buffer good data
    if data != 1000:
        obj['bufferValue'] = data
    
    print(data)
    
    filterdata = obj['bufferValue'] * 0.01
    
    # Pass value to anything, scale object in this case
    obj.worldScale = (filterdata, filterdata, filterdata)
     
 
def main(cont):
    
    # Get the cube controller
    cont = logic.getCurrentController()
    own = cont.owner
 
    try:
        arduino = own['arduino']
    except KeyError:
        arduino = own['arduino'] = serial.Serial('COM3', 9600, timeout=0)
 
    update_arduino(arduino, own)

This is the DEMO version:

Screen capture:

There is this glitchy thing that you see above:

  1. I tried buffering the last good values. What happen is, if I didn’t turn the knob of potentiometer, it seems to be sending nothing to BGE… maybe PySeries flush it?
  2. And sometimes there is that jumpy glitchy thing happening.

So far I am quite happy, even better if that glitch can be fixed. Any idea? Is this because of limitation of Serial? I have issue with delay() or timeout()?

After this one answered, I want to go to OSC. Or, maybe I tried the opposite, like sending Serial data from Blender into Arduino, playing with LED maybe.

If I change this line:
arduino = own[‘arduino’] = serial.Serial(‘COM3’, 9600, timeout=0)

Giving it a timeout value:
arduino = own[‘arduino’] = serial.Serial(‘COM3’, 9600, timeout=0.025)

The result I am getting is very stable, and I don’t get the glitchy value jump anymore. And the ValueError does not seem to appear anymore inside Serial data.

I think I can call this one [SOLVED]. Unless you have something more to add.

I will try to do the opposite, sending from Blender => Serial which Arduino can read.

Thanks again agoose77 and Monster…!!!

I suggest that when you read data from the serial port, you check how much data was actually consumed. If you are sending values that are in the 1000s then you are probably sending 2 bytes from the Arduino to the BGE. It is possible for you to call ‘arduino.readline()’ and only get the first byte of the transmission. If you are expecting 2 byte messages then you should check that you have actually read two bytes before attempting to process it.

Thanks for that advice, that part on bytes is still very new to me.

Currently reading some books of Simon Monk about Arduino. I’ll try to understand all these data bytes things. Converting that BYTES to ASCII to INTEGER took me few hours yesterday. There must be a better way of all these.

b’123’
=> normal int number is so complicated!

So far, I manage to get stable result from Arduino <=> Blender Game Engine (using Agoose77 script), if I play with PySerial TimeOut and delay().

Telling BGE to send “Serial” message is simpler. I just need to tell BGE to write b’MY MESSAGE’ into the Serial, for which the Arduino is already running in the background.

In this case, I have Arduino set to receive alphabet or number, and then turn it into MORSE CODE beeps.

It is the BYTE data thing that I still needs to get familiar with.

from bge import logic
from bge import events


import math
import serial




def update_arduino(arduino, obj):
    
    try:
        print("translating morse code... tap again")
        arduino.write(b'sos')
    except Exception as err:
        return print("hey error man")
 
def main(cont):
    
    # Get the cube controller
    cont = logic.getCurrentController()
    own = cont.owner
 
    try:
        arduino = own['arduino']
    except KeyError:
        arduino = own['arduino'] = serial.Serial('COM3', 9600, timeout = 0.1)
 
    update_arduino(arduino, own)

This is probably a stupid question, but for the buffer, is
obj[‘bufferValue’] = data

something you’re creating, or is that a parameter available in the python api for Blender? If so, where did you find the documentation on that?

Thanks!

This code is running as part of the Blender Game Engine (BGE). The objects are KX_GameObject types that provide a dictionary-like access to the object to attach named values to them. So they act like Python dictionaries but have additional, game-related properties and methods.

Thanks! I did a quick look into Python dictionaries so now I see what he is doing with obj[‘bufferValue’]. He’s printing data whether or not the data is good or bad, and updating the world scale if the data is good. It looks like he could just do,

need to buffer good data

if data != 1000:
    obj.worldScale = (data, data, data)

This would omit printing but I think it does the same thing. I’m working on creating a buffer for the raw serial
data and I thought maybe his reference to ‘bufferValue’ meant there was a buffer available to use for each object.
Thanks for your answer, now I know more about Python.

For this code it would have the same effect. What you would lose with your modification is storing the last valid results. This code does not need it but if you wanted to interpolate between values then you would want to keep it around.

For example, this instantly sets the scale. What if you wanted to animate the scale getting changed so you have a smooth transition? Then you would want to keep the last known good value around.

Dear all,
I am trying to connect blender to arduino, I tried the python code above in the console within Blender, the error says,
‘no module named bge’
Another code I tried also ends up with the same message, what can the error be?
Thanks

I guess you are running he code from Blender. Be aware:

The Blender API is bpy
The BGE API is bge

You should no mix them, as they are two separate applications (the BGE can run embedded inside Blender but this is to provide more comfort towards a game developer rather than a use case).

Tthe above thread is a little confusing as the initial post mentioned both situations (Blender script and BGE script). The followup posts are talking about BGE scripts.