https://techzeero.com/wp-content/upload ... rduino.png
I use pulseIn to read the time between highs and lows and use those numbers to drive the motor at different speeds. I'm making the motor behave differently depending on what the pwm is doing, it'll also react to being turned on from on idle state, or where I'm blocking PWM from being sent. The on/off blocking isn't shown here, but there's a switch involved, though during testing I just controlled this from my PC via USB so this specific thing isn't referenced in the code. With that out of the way, here's the Arduino code:
Code: Select all
byte PWM_PIN = 6; //PWM is coming in here
int motor = 10; //motor is on PWM pin 10
int pulse; //Variable used to represent to the time in millis from pulsein
int highmode; //Boolean variable that allows access to parts of code if (true)
int pwm0_loopcount = -2; //Stores the number of loops when pulsein reads 0, helps prevent unintentional shut off
int high_mode_loop_count = -1; //Stores the number of loops when in Highmode. Helps prevent vibration from going into low-mode too soon. I also tried hysteresis but I found I liked how this behaved better
int LMsafetyLoops = - 3; //Stores the number of loops when in low-mode. Prevents code form getting stuck in low-mode when it shouldn't be, and allows loop to re-enter low-mode when appropriate
void setup() {
TCCR1B = TCCR1B & B11111000 | B00000101; // for PWM frequency of 30.64 Hz. I like how jerky/violent this made the motor feel, that's the only reason for the 30Hz frequency
pinMode(PWM_PIN, INPUT); //PWM_PIN is an input
pinMode(motor, OUTPUT); //motor is an output
Serial.begin(115200); //Another part of this build (not featured here) is interfacing with this via UART(code for that also not featured here) and it's baud rate is 115200
}
void loop() {
pulse = pulseIn(PWM_PIN, HIGH, 100); //Setting up a local variable, though this might also function just as well as a global variable.
while (pulse == 0) //while incoming millis equal 0
{
pwm0_loopcount++; //increment the loopcount for consecutive 0s. This is used as a check to prevent other parts of the code from running
delay (5); //delay for a bit. This is so that the stacking loop counts don't increment too quickly, thus bypassing this gate
pulse = pulseIn(PWM_PIN, HIGH, 100); // check the incoming PWM in millis
if ((pwm0_loopcount >= 50) || (pulse <= 1)) { //First part of a gate. This part prevents progress if the loop got here just after coming from low or high-mode since the PWM in millis often dips down to 0 for a moment even while its not specifically in an "off" state
analogWrite (motor, 0); //turns motor off since it would have needed to loop through the previous section 50 consecutive times in order to get here. Ensures it's actually off
if (pulse >= 1) { //Check if the PWM in millis is above 0. A *special* motor animation plays when starting it from "off" state. This check, combined with the previous ones, makes sure that this animation ONLY plays when waking up from an "off" state
analogWrite (motor, 235); //Motor goes fast
delay (500); //holds for a bit
analogWrite (motor, 40); //Motor goes slower
delay (1000); //holds for a bit
pwm0_loopcount = 0; //Resets the loop count for consecutive 0s.
pulse = pulseIn(PWM_PIN, HIGH, 100); //updates the current reading of PWM in millis
break; //Moves on to low-mode
}
else {
continue; //if it failed to start or move on, it restarts
}
}
else {
continue; //if it failed to start or move on, it restarts
}
}
while ((pulse >= 1) || (pwm0_loopcount == 0)) //when looping through low-mode, it'll check these two variables first.
{
Low_mode: //A label to jump to(not needed but it's still here from when I was testing things.
pwm0_loopcount = 0; //reassures that the loop count for consecutive 0s is reset upon every loop
pulse = pulseIn(PWM_PIN, HIGH, 100); //updates the current reading of PWM in millis
if (pulse >= 20) { //If pulse value is high enough, it'll begin high-mode right away(high mode just takes all the pulse signals and does the same thing with them as this part of the code, only more exaggerated
highmode = true; //A boolean value that acts as a key to prevent high-mode form happening when it's not appropriate
goto High_mode; //Send loop to high-mode loop. Again, not needed but it's still here form testing
}
else {
highmode = false; //Sets the highmode boolean to false. Both of these help to prevent high-mode form happening when it's not appropriate
high_mode_loop_count = 0; //Sets the highmode loop count to 0. Both of these help to prevent high-mode form happening when it's not appropriate
if (pulse >= 2 && pulse <= 4) { //These next 3 "if" sections all check the pulse value from the beginning of the low-mode loop and apply different speeds depending on that value
analogWrite (motor, 20);
}
if (pulse >= 5 && pulse <= 8) {
analogWrite (motor, 25);
}
if (pulse >= 9 && pulse <= 19) {
analogWrite (motor, 35);
}
if (pulse == 0) { //Checks to see if the pulse is 0
LMsafetyLoops++; //if so, increment the count for those by 1
delay (5); //a slight delay to prevent these from counting up too quickly
}
if (LMsafetyLoops >= 15) { //if too many 0s in a row occur
break; //break from here, sending loop back to the previous section, since it won't be allowed into the next section, where it will likely determine the pulse to be in an "off" state
}
}
}
while (highmode == true) //Checks to see if highmode is true, if so, it'll continue through this loop
{
High_mode: //not needed anymore, a label to jump to
high_mode_loop_count++; //increments the count for consecutive highmode loops
highmode = true; //makes sure that high mode remains true for each consecutive highmode loop
pwm0_loopcount = 0; //another measure to prevent an accidental "off" state
pulse = pulseIn(PWM_PIN, HIGH, 100); //just like before, but a tiny bit different. The pulse value is updated and the motors activated at certain speeds accordingly. Occasional delays to exaggerate the durations of the vibrations a bit
if (pulse >= 0 && pulse <= 17) {
analogWrite (motor, 40);
pulse = pulseIn(PWM_PIN, HIGH, 100);
}
if (pulse >= 18 && pulse <= 20) {
analogWrite (motor, 175);
delay (300);
analogWrite (motor, 40);
delay (200);
pulse = pulseIn(PWM_PIN, HIGH, 100);
}
if (pulse >= 21 && pulse <= 26) {
analogWrite (motor, 220);
delay (300);
analogWrite (motor, 40);
delay (200);
pulse = pulseIn(PWM_PIN, HIGH, 100);
}
if (high_mode_loop_count >= 15) { //once the high-mode has looped a certain number of times, it goes back to low mode. This ensures that when it goes high, it stays there long enough to get the effect I want
goto Low_mode;
}
}
}
Code: Select all
import time
import board
import busio
import baudrate
import serial
import pulseio
pwm0_loopcount = -2 ##all the same variables
high_mode_loop_count = -1
l_m_safety_loops = - 3
serial.set_baud_rate(baudrate.BAUD_RATE115200) ##I'm pretty sure this is the way to change the baud rate on the M0 express
uart = busio.UART(board.TX, board.RX, baudrate=115200)
pulse = pulseio.PulseIn(board.D9, maxlen=1, idle_state=False) ##this seemed to be the equivalent function to Arduino's pulseIn
motor_pwm = pulseio.PWMOut(board.D10, duty_cycle=2 ** 15, frequency=30) ##I didn't really think I needed to use a motor library because I'm really just sending out a PWM to a transistor
while True: ##infinite loop
while pulse == 0:
pwm0_loopcount += 1 ##the increment
time.sleep(.005) ##the delay
if pwm0_loopcount >= 50 or pulse <= 1:
motor_pwm.duty_cycle = 0
if pulse >= 1:
motor_pwm.duty_cycle = 60395 ##for the duty cycle numbers, I just took 65535, the max value I can use here, representing always-on, and divided it by 255, the max number I could have used in my Arduino code. 65535/255=257, So I took each PWM output value from the Arduino code and multiplied it by 257, so 235X257=60395. I don't know if this was the correct thing to do, but it made sense to me.
time.sleep(.500)
motor_pwm.duty_cycle = 10280
time.sleep(1)
pwm0_loopcount = 0
break
else:
continue
else:
continue ##again, these keep the code looping down here during a confirmed "off" state
while pulse >= 1 or pwm0_loopcount == 0: ##lowmode begins
pwm0_loopcount = 0
if pulse >= 20: ##my assumption is that circuitpython will just check the pulse whenever it's called upon, whereas in Arduino I had to call it just before it was checked in order to update the value. So you'll see all of the normal pulseIn reads removed.
highmode = True
break ##this SHOULD break it out of lowmode and send it to highmode by default, as its the next part of the code after this
else:
highmode = False
high_mode_loop_count = 0
if pulse >= 2 and pulse <= 4:
motor_pwm.duty_cycle = 5140
if pulse >= 5 and pulse <= 8:
motor_pwm.duty_cycle = 6425
if pulse >= 9 and pulse <= 19:
motor_pwm.duty_cycle = 8995
if pulse == 0:
l_m_safety_loops += 1
time.sleep(.005)
if l_m_safety_loops >= 15:
break
while highmode == (True):
high_mode_loop_count += 1
highmode = True
pwm0_loopcount = 0
if pulse >= 0 and pulse <= 17:
motor_pwm.duty_cycle = 10280
if pulse >= 18 and pulse <= 20:
motor_pwm.duty_cycle = 44675
time.sleep(.300)
motor_pwm.duty_cycle = 10280
time.sleep(.200)
if pulse >= 21 and pulse <= 26:
motor_pwm.duty_cycle = 56540
time.sleep(.300)
motor_pwm.duty_cycle = 10280
time.sleep(.200)
if high_mode_loop_count >= 15:
break ##logically, this should break it out of high mode and it should continue back to low mode as it won't be allowed in "off" mode at the beginning