Close

Testing PWM with Zerynth & issues

A project log for Haptic Sleeve

Sleeve worn on the arm to provide haptic feedback while performing handwriting exercises.

grant-stankaitisGrant Stankaitis 05/07/2020 at 20:260 Comments

In the last log I said that I was going to work on transferring the coder over to Zerynth, so here's the documentation of that process! (And the issues I ran into along the way).

Porting main_PWM.py to Zerynth

See the screenshot below of the ESP32 parsing the inputs sent over BLE in the same manner as the main_PWM.py code.

Now that I have the motor direction and range stored in the right variables, the next step is using these values to perform PWM activation of the motors!

Zerynth PWM

There are some similarities when moving the code over to Zerynth because Zerynth is designed to use a mix of Python and C code. However, Zerynth implements its own PWM module, so I had to convert the code to be compatible with their implementation of PWM. See the code below of the Zerynth implementation of PWM.

# Set up pins as PWM
forward_pin = D18.PWM
left_pin = D19.PWM
back_pin = D22.PWM
right_pin = D23.PWM

# Set pins as output
pinMode(forward_pin, OUTPUT)
pinMode(left_pin, OUTPUT)
pinMode(back_pin, OUTPUT)
pinMode(right_pin, OUTPUT)

# Set all to 0 (disabled until activated)
pwm.write(forward_pin, 0, 0)
pwm.write(left_pin, 0, 0)
pwm.write(back_pin, 0, 0)
pwm.write(right_pin, 0, 0)

The code below is the Zerynth implementation of the PWM activation via the equivalent sub_cb() function from main_PWM.py:

forward = 0
left = 0
back = 0
right = 0

# MICROS so every sec is 1000000 of micros, 1000 = 1kHz
frequency = 1000
period = 1000000//frequency

def set_PWM(status,value):
    # Check incoming commands and write PWM
    global duty
    global forward
    global left
    global back
    global right
    
    # Get values for PWM
    motor_direction = value[0]
    range = value[1]
    
    if range == 0: # Set duty to 0
        duty = 0
    else: # Set duty to period/3, period/2, or 1, backwards from original
        duty = period//range

    if motor_direction == 1: # Forward
        forward = duty
        left = 0
        back = 0
        right = 0
    elif motor_direction == 2: # Left
        forward = 0
        left = duty
        back = 0
        right = 0
    elif motor_direction == 3: # Back
        forward = 0
        left = 0
        back = duty
        right = 0
    elif motor_direction == 4: # Right
        forward = 0
        left = 0
        back = 0
        right = duty
    else: # Set all to 0
        forward = 0
        left = 0
        back = 0
        right = 0
        
    pwm.write(forward_pin, period, forward, MICROS)
    pwm.write(left_pin, period, left, MICROS)
    pwm.write(back_pin, period, back, MICROS)
    pwm.write(right_pin, period, right, MICROS)

To see the similarities/differences more clearly, here is a side-by-side comparison of the bodies of the functions. On the left is the Zerynth implementation and on the right is the main_PWM.py implementation:

Issues with Zerynth PWM

I was having some very strange issues with PWM activation when using Zerynth. I was running the same code as above, however it was activating more than one motor at once. When testing main_PWM.py, it was only activating multiple motors at once. 

To get a better understanding of the issue, here is what was happening. For example, when I activate Motor 1, I can confirm via print statements that only the duty for one motor is being set at a time, and all others are 0. When I first set only the duty for Motor 1, this is the case. However, if I then set the duty for Motor 2, Motor 1 continues working and Motor 2 is activated, even though the duty for Motor 1 should be set to 0 (and I can confirm that it is being set to 0). When I set all motors to 0, they turn off in unison as expected. However, when I try to activate Motor 2 again, Motor 2 and Motor 1 turn on together again. It seems they are somehow tied together even though I can confirm that this portion of the code works in the original main_PWM.py file.

I concluded that the issue is derived from this portion of the code:

if motor_direction == 1: # Forward
        forward = duty
        left = 0
        back = 0
        right = 0
    elif motor_direction == 2: # Left
# ... other options removed for brevity

    pwm.write(forward_pin, period, forward, MICROS)
    pwm.write(left_pin, period, left, MICROS)
    pwm.write(back_pin, period, back, MICROS)
    pwm.write(right_pin, period, right, MICROS)

When the PWM module is writing to the motors, they somehow get tied together in what I suspect to be some form of an overflow. I think this is due to the way that Zerynth abstracts the programming out a layer, so you are not directly interfacing with the board like in uPyCraft IDE. Something must get tied up or stored within this layer of abstraction that causes previously activated motors to turn on. I'm not sure what this "something" is, so this is just a hypothesis.

Determined to figure out this issue, I spent countless hours debugging the code to no avail. I posted on the Zerynth Community support page and a Zerynth team member was unable to help me as well. The Zerynth team member noted that they will be testing this bug and pushing bux fixes in future updates. So, I explored other options!

I returned back to uPyCraft IDE, so I will be documenting that in the next update!

Discussions