I'm trying to measure the time between two pulses with a ESP32 Wroom with interrupts. I have seen some examples but all use the TomerOne.h library and that one I cant use with an ESP32. So I get a bit stuck on how to fix this. Previously I did the same with a MEGA with some help here on the forum but with the MEGA I used a pin read to see which pin had changed and computed the time between change. But now I would like to do it with interrupts, could someone help me a bit to figure out how I can do this? I created one interrupt (eventually I need two) I will use it for speed measurement of a motor.
#include <CommandHandler.h>
#define PWM_A 14
#define PWM_A_Chan 0
#define AI_1 12
#define AI_2 13
#define PWM_B 25
#define PWM_B_Chan 1
#define BI_1 26
#define BI_2 27
#define PWM_Res 8
#define PWM_Freq 1000
int PWM_DutyCycle = 100;
float diskslots = 20.00; //Total slots on motor disk
const int RightMotorSpeedSens = 18; //Right motor Interrupt pin for speed sensing
unsigned int RightSpeedCount = 0; //Pulse counter for right motor
CommandHandler<> SerialCmds;
void IRAM_ATTR isr()
{
RightSpeedCount++;
}
void setup() {
Serial.begin(115200);
Serial.println(F("Program starts....."));
SerialCmds.AddCommand(F("MotorSpeed"), Cmd_DriveForwards); //Command to communicate with Megunolink
pinMode(AI_1, OUTPUT); //A motor setup input channels
pinMode(AI_2, OUTPUT);
pinMode(BI_1, OUTPUT); //B motor setup input channels
pinMode(BI_2, OUTPUT);
pinMode(RightMotorSpeedSens, INPUT_PULLUP);
ledcAttachPin(PWM_A, PWM_A_Chan); //Setup A motor PWM channel
ledcAttachPin(PWM_B, PWM_B_Chan); //Setup B motor PWM channel
ledcSetup(PWM_A_Chan, PWM_Freq, PWM_Res);
ledcSetup(PWM_B_Chan, PWM_Freq, PWM_Res);
attachInterrupt(digitalPinToInterrupt(RightMotorSpeedSens), isr, RISING);
}
void loop() {
SerialCmds.Process(); //Monitor serial commands
}
void Cmd_DriveForwards(CommandParameter& p) //Function for driving forward
{
int DutyCycle = p.NextParameterAsInteger();
digitalWrite(AI_1, LOW);
digitalWrite(AI_2, HIGH);
digitalWrite(BI_1, LOW);
digitalWrite(BI_2, HIGH);
ledcWrite(PWM_A_Chan, DutyCycle);
ledcWrite(PWM_B_Chan, DutyCycle);
Serial.println(DutyCycle);
}
The idea is record the current time when a rising edge occurs when the following rising edge occurs I could compute the difference and than I could compute the speed of the motor in RPM or m/s.
I hope someone could pont me out in the good direction, thanks in advance.
looks like you're trying to compute the frequency of events -- # of interrupts over some period of time -- and from that frequency control adjust some PWM value to maintain some speed.
but i don't the the count incremented in the interrupt being used anywhere, nor do i see any time being measured (e.g. using millis()).
and after seeing a similar thread on using PWM to control motor speed i think the PID algorithm is needed because the rate of change of the PWM signal probably needs to be accounted for when attempting to maintain a constant speed
Thanks for your example I'll dive into it seems like this will help. Millis() wont do the job I need microsecond the time difference between pulses will be very small.
One thing I noticed from your code is that you use Serial.print(). But I thought this is not "allowed" with interrupts as it is a void function.
You measure the pulse width between rising and faling edges. I could measure between two rising edges but could I store those values in an array for example in the interrupt function?
A little bit more explanation would be appreciated.
Note that I'm using a infrared sensor that counts pulses, I need to measure the time between those pulses in order to compute the speed of the motor. The picture below shows what I'm using.
Nevertheless I just added the code as suggested before post#7 and this works decent. I wont get a real stable value yet. And need to fix it so I can use this for two motors. But I'm on my way to nice results.
Hmm that sounds interesting this means I could go a complete other direction with good mesurements. I'll have a look in to this and see if I can make it work.
i think i have the same robot platform you show in the photo with ~2in wheels and a block disc with ~20 openings for use as an encoder (usually with 2 optical sensors)
not sure you need such fine resolution (usec/msec) for likely speeds
i believe for such a platform, controlling the amount of rotation is more important than speed. the amount of rotation will determine how straight a platform moves and how to turn by incrementally moving each wheel a specific # of tics
The ESP32 cycle counters is a pico second cycle counter from which all other cycle counts are derived from. Using millis() on a ESP32 incurs the overhead of the ESP32's Arduino Core.
Instead of using millis() on a ESP32, I use esp_timer_get_time(); which returns uSeconds and overflows after 200+ years.
Example of use of esp_timer_get_time();.
void fDoMoistureDetector1( void * parameter )
{
//wait for a mqtt connection
while ( !MQTTclient.connected() )
{
vTaskDelay( 250 );
}
int TimeToPublish = 5000000; //5000000uS
int TimeForADreading = 100 * 1000; // 100mS
uint64_t TimePastPublish = esp_timer_get_time(); // used by publish
uint64_t TimeADreading = esp_timer_get_time();
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = 10; //delay for 10mS
float RemainingMoisture = 100.0f; //prevents pump turn on during start up
bool pumpOn = false;
uint64_t PumpOnTime = esp_timer_get_time();
int PumpRunTime = 11000000;
uint64_t PumpOffWait = esp_timer_get_time();
uint64_t PumpOffWaitFor = 60000000; //one minute
float lowMoisture = 23.0f;
float highMoisture = 40.0f;
for (;;)
{
xSemaphoreTake( sema_WaterCactus, portMAX_DELAY );
//read AD values every 100mS.
if ( (esp_timer_get_time() - TimeADreading) >= TimeForADreading )
{
xEventGroupSetBits( eg, evtADCreading0 );
TimeADreading = esp_timer_get_time();
}
xQueueReceive(xQ_RM, &RemainingMoisture, 0 ); //receive queue stuff no waiting
//read gpio 0 is water level good. Yes: OK to run pump : no pump off. remaining moisture good, denergize water pump otherwise energize water pump.
if ( RemainingMoisture >= highMoisture )
{
WaterPump0_off();
}
if ( !pumpOn )
{
log_i( "not pump on ");
if ( gpio_get_level( GPIO_NUM_0 ) )
{
if ( RemainingMoisture <= lowMoisture )
{
//has one minute passed since last pump energize, if so then allow motor to run
if ( (esp_timer_get_time() - PumpOffWait) >= PumpOffWaitFor )
{
gpio_set_level( GPIO_NUM_5, HIGH); //open valve
WaterPump0_on();
log_i( "pump on " );
pumpOn = !pumpOn;
PumpOnTime = esp_timer_get_time();
}
}
//xSemaphoreGive( sema_RemainingMoisture );
} else {
log_i( "water level bad " );
WaterPump0_off();
gpio_set_level( GPIO_NUM_5, LOW); //denergize/close valve
PumpOffWait = esp_timer_get_time();
}
} else {
/*
pump goes on runs for X seconds then turn off, then wait PumpOffWaitTime before being allowed to energize again
*/
if ( (esp_timer_get_time() - PumpOnTime) >= PumpRunTime )
{
log_i( "pump off " );
WaterPump0_off(); // after 5 seconds turn pump off
gpio_set_level( GPIO_NUM_5, LOW); //denergize/close valve
pumpOn = !pumpOn;
PumpOffWait = esp_timer_get_time();
}
}
// publish to MQTT every 5000000uS
if ( (esp_timer_get_time() - TimePastPublish) >= TimeToPublish )
{
xQueueOverwrite( xQ_RemainingMoistureMQTT, (void *) &RemainingMoisture );// data for mqtt publish
TimePastPublish = esp_timer_get_time(); // get next publish time
}
xSemaphoreGive( sema_WaterCactus );
xLastWakeTime = xTaskGetTickCount();
vTaskDelayUntil( &xLastWakeTime, xFrequency );
}
vTaskDelete( NULL );
}// end fDoMoistureDetector1()
A bit late reply sorry about that but I was not at home during the week.
Controlling the speed or rotation is basically the same. I could choos for speed control or rpm control. Important is that both wheels are the same.
The previous suggested code from @dlloyd seemed to work. But Im not there yet, the best IMHO is to take readings and start to take a moving average on them. This to filter out "weird" values. Besides of that I need the reading of two sensors and im not quite sure how to do that with interrupts.
The example was quickly thrown together and didn't deal with max or min rpm limits.
This update is somewhat improved. Future update could detect when there's no interrupts happening (0 rpm) and use float rpm for improved resolution.