Arduino/ESP8266 Using interrupts to take samples - arduino

I'm trying to measure the discharge time of a capacitor using a NodeMCU board. See complete sketch below. This code works fine, however I want to improve it to get to better time scales, by using the ESP.getCycleCount() function and interrupts for the timing. The part of interest is this:
startTime = micros();
while (digitalRead(capPos) == HIGH) {
delayMicroseconds (1);
}
endTime = micros();
The while loop I want to rewrite into some form of interrupt based function, listening to a falling edge of the capPos pin, replacing the above function with something like this:
startTime = micros();
attachInterrupt(digitalPinToInterrupt(capPos), dischargeInterrupt, FALLING);
}
void dischargeInterrupt() {
endTime = micros();
detachInterrupt(digitalPinToInterrupt(capPos));
After that the original code continues.
Te problem I have is how to take the required 100 samples. When setting the interrupt after the startTime is taken, this routine will finish and do the next of the 100 iterations. Instead it should wait: for the interrupt to come, then for the rest of the routine to finish, as per original sketch. Being rather new to interrupts, I have no idea where to start with that part.
So what has to be done:
- loop() calls getEC()
getEC() takes 100 samples:
charge the cap, set the pins for discharge, set interrupt to measure discharge time.
interrupt comes, time passed is measured. Negative cap cycle is performed and one sampling round complete.
The main purpose of this change is to make the timing more accurate: react instantly on pin dropping to LOW and use a much higher resolution for time. The current microsecond resolution does the job but it's a serious limitation.
Here my complete, working, sketch:
// capacitor based TDS measurement
// pin D5 C+ - 330 ohm resistor----------|------------|
// | |
// cap EC probe or
// | resistor (for simulation)
// pin D6 C- ----------------------------| |
// |
// pin A0 EC -----------------------------------------|
#include <Average.h>
int capPos = D5; //C+
int capNeg = D6; //C-
int EC = D7; //EC
float CAP = 47; // capacity in nF
#define calibration 150 // a calibration factor to link time with EC.
void setup() {
Serial.begin(9600);
}
void loop () {
float EC = getEC(); // get the EC as mS/cm.
Serial.println (", EC: " + String(EC) + " mS/cm");
delay(100);
}
float getEC() {
int samples = 100; // number of EC samples to take and average.
unsigned long startTime; // the time stamp (in microseconds) the measurement starts.
unsigned long endTime; // the time stamp (in microseconds) the measurement is finished.
unsigned int dischargeTime; // the time it took for the capacitor to discharge.
Average<unsigned int> discharge(samples); // Take measurements on both the positive and negative cycles.
unsigned int chargeDelay = 500; // The time (in microseconds) given to the cap to fully charge/discharge - about 10x RC is a good value.
int startLevel; // analog level of the pin.
int endLevel;
pinMode(A0, INPUT);
for(int i=0; i<samples; i++) { // take <samples> measurements of the EC.
// Stage 1: fully charge capacitor for positive cycle.
// C+ high, C- low, EC disconnected.
pinMode (EC, INPUT);
pinMode (capPos,OUTPUT);
digitalWrite (capPos, HIGH);
pinMode (capNeg, OUTPUT);
digitalWrite (capNeg, LOW);
delayMicroseconds(chargeDelay);
// Stage 2: positive side discharge; measure time it takes.
// C+ disconnected, C- low, EC low.
pinMode (capPos,INPUT); //set C+ to input to keep voltage from grounding a discharging thru this output pin
pinMode (EC, OUTPUT);
digitalWrite (EC, LOW);
// Measure time until capPos goes LOW. Can't use pulseIn() here as the pin will be high already.
startTime = micros();
while (digitalRead(capPos) == HIGH) {
delayMicroseconds (1);
}
endTime = micros();
// handle potential overflow of micros() just as we measure, this happens every 70 minutes.
if (endTime < startTime) dischargeTime = 4294967295 - startTime + endTime;
else dischargeTime = endTime - startTime;
discharge.push(dischargeTime);
// Stage 3: fully charge capacitor for negative cycle. C+ low, C- high, EC disconnected.
pinMode (EC, INPUT);
pinMode (capPos,OUTPUT);
digitalWrite (capPos, LOW);
pinMode (capNeg, OUTPUT);
digitalWrite (capNeg, HIGH);
delayMicroseconds(chargeDelay);
// Stage 4: negative side charge; don't measure as we just want to balance it the directions.
// C+ disconnected, C- low, EC low.
pinMode (capPos,INPUT); //set C+ to input to keep voltage from grounding a discharging thru this output pin
pinMode (EC, OUTPUT);
digitalWrite (EC, HIGH);
delayMicroseconds(dischargeTime);
}
float dischargeAverage = discharge.mean();
Serial.print("Discharge time: ");
Serial.print(dischargeAverage);
// Calculate EC from the discharge time.
return dischargeAverage;
}

So, got this answered myself.
For the time resolution: you can get a much more exact time by using ESP.getCycleCount() which counts processor cycles - on my 80 MHz NodeMCU board that's 12.5 ns per cycle or 80 cycles per microsecond. I should have mentioned that in the first part.
Interrupts: this is something I misunderstood. Now I solved it by having the main function wait in a loop until a timeout is reached (set to 1 millisecond, normal expected times are in the 1-100 microseconds range), or until a global variable is set by the interrupt function. So now I'm measuring in the 12.5 nanosecond resolution!
One thing that's missing from this sketch is a correction for program time taken to deal with the timing: the time it takes from dropping the value on the EC pin to starting to count, and the time it takes from receiving the interrupt to stopping the count. If that overhead is say 100 cycles, that would be 1.25 microseconds, which is well within my measurement time.
// capacitor based TDS measurement
// pin D5 C+ - 330 ohm resistor----------|------------|
// | |
// cap EC probe or
// | resistor (for simulation)
// pin D6 C- ----------------------------| |
// |
// pin A0 EC -----------------------------------------|
#include <Average.h>
int capPos = D5; //C+
int capNeg = D6; //C-
int EC = D7; //EC
unsigned long startCycle;
unsigned long endCycle;
#define CYCLETIME 12.5 // the time it takes in nanoseconds to complete one CPU cycle (12.5 ns on a 80 MHz processor)
float CAP = 47; // capacity in nF
#define calibration 150 // a calibration factor to link time with EC.
void setup() {
Serial.begin(9600);
}
void loop () {
float EC = getEC(); // get the EC as mS/cm.
Serial.println (", EC: " + String(EC) + " mS/cm");
delay(500);
}
float getEC() {
int samples = 100; // number of EC samples to take and average.
unsigned long startTime; // the time stamp (in microseconds) the measurement starts.
unsigned long endTime; // the time stamp (in microseconds) the measurement is finished.
unsigned int dischargeTime; // the time it took for the capacitor to discharge.
Average<unsigned int> discharge(samples); // The sampling results.
unsigned int chargeDelay = 500; // The time (in microseconds) given to the cap to fully charge/discharge - about 10x RC is a good value.
unsigned int timeout = 1; // discharge timeout in milliseconds - if not triggered within this time, the EC probe is probably not there.
int startLevel; // analog level of the pin.
int endLevel;
pinMode(A0, INPUT);
for(int i=0; i<samples; i++) { // take <samples> measurements of the EC.
// Stage 1: fully charge capacitor for positive cycle.
// C+ high, C- low, EC disconnected.
pinMode (EC, INPUT);
pinMode (capPos,OUTPUT);
digitalWrite (capPos, HIGH);
pinMode (capNeg, OUTPUT);
digitalWrite (capNeg, LOW);
delayMicroseconds(chargeDelay);
// Stage 2: positive side discharge; measure time it takes.
// C+ disconnected, C- low, EC low.
startCycle = ESP.getCycleCount();
pinMode (capPos,INPUT); //set C+ to input to keep voltage from grounding a discharging thru this output pin
pinMode (EC, OUTPUT);
digitalWrite (EC, LOW);
// Use cycle counts and an interrupt to get a much more precise time measurement, especially for high-EC situations.
endCycle = 0;
startTime = millis();
attachInterrupt(digitalPinToInterrupt(capPos), capDischarged, FALLING);
while (endCycle == 0) {
if (millis() > (startTime + timeout)) break;
}
detachInterrupt(digitalPinToInterrupt(capPos));
if (endCycle == 0) dischargeTime = 0;
else {
// Handle potential overflow of micros() just as we measure, this happens about every 54 seconds
// on a 80-MHz board.
if (endCycle < startCycle) dischargeTime = (4294967295 - startCycle + endCycle) * CYCLETIME;
else dischargeTime = (endCycle - startCycle) * CYCLETIME;
discharge.push(dischargeTime);
}
// Stage 3: fully charge capacitor for negative cycle. C+ low, C- high, EC disconnected.
pinMode (EC, INPUT);
pinMode (capPos,OUTPUT);
digitalWrite (capPos, LOW);
pinMode (capNeg, OUTPUT);
digitalWrite (capNeg, HIGH);
delayMicroseconds(chargeDelay);
// Stage 4: negative side charge; don't measure as we just want to balance it the directions.
// C+ disconnected, C- high, EC high.
pinMode (capPos,INPUT); //set C+ to input to keep voltage from grounding a discharging thru this output pin
pinMode (EC, OUTPUT);
digitalWrite (EC, HIGH);
delayMicroseconds(dischargeTime/1000);
}
float dischargeAverage = discharge.mean();
Serial.print("Discharge time: ");
Serial.print(dischargeAverage);
// Calculate EC from the discharge time.
return dischargeAverage;
}
// Upon interrupt: register the cycle count of when the cap has discharged.
void capDischarged() {
endCycle = ESP.getCycleCount();
detachInterrupt(digitalPinToInterrupt(capPos));
}

Related

Reading Speed with 2 Photoelectric sensors with Arduino and show speed after reading

I have 2 photo electric sensors with NPN output: Looking at the data sheet it might actually be LOW (page 14 NPN Output): https://mouser.com/datasheet/2/307/e3fb_photoelectric-1189416.pdf page 14.
The 2 sensors are placed 152.4 mm apart. Basically I want to start a timer when the first gate is triggered and finish when the second gate is triggered and then divide the set distance (152.4 mm) by the time to travel between each gate and output the speed in meters second. The sensors are powered by a 24 VDC power supply, the NPN output voltage can be as high as 3V DC from the collector. I first tested out the sensor to trigger the onboard LED to blink when an object is present in front of the sensor and was able to trigger the LED:
const int sensorPin = 3; // photoelectric sensor
const int ledPin = 13;
boolean state1 = LOW; // state at startup
void setup() {
pinMode(sensorPin, INPUT_PULLUP); // sensor input with internal pullup
resistor enabled
pinMode(ledPin, OUTPUT); // initialize digital pin 13 as an output.
}
void loop() {
state1 = digitalRead(sensorPin);
if (state1 == HIGH) {
digitalWrite(ledPin, HIGH);
}
else {
digitalWrite(ledPin, LOW);
}
}
So it looks like the the voltage is in the right range to be read as HIGH or LOW.
So next I try to read the speed.
The code is below:
const int PE_01 = 2;
const int PE_02 = 3;
unsigned long start, finish, speed;
float elapsed;
boolean state1 = LOW; //state at startup
boolean state2 = LOW; //state at startup
void setup() {
// Initialize the serial communication at 9600 bits per second:
Serial.begin(9600);
pinMode(PE_01, INPUT_PULLUP); //sensor input with internal pullup
resistor enabled
pinMode(PE_02, INPUT_PULLUP); //sensor input with internal pullup
resistor enabled
}
void loop() {
state1 = digitalRead(PE_01);
state2 = digitalRead(PE_02);
// time = millis ();
if (state1 == HIGH){
start = millis();
}
if (state2 == HIGH) {
finish = millis();
}
elapsed = finish - start;
speed = 76200/elapsed; // unit distance (micro meters) and time (milli
seconds)
Serial.print("initial time (ms) = ");
Serial.println(start);
Serial.print("final time (ms) = ");
Serial.println(finish);
Serial.print("Speed of object (M/s) = ");
Serial.println(speed);
delay(500);
}
what i see in the serial monitor however is:
10:31:07.632 -> initial time (ms) = 15502
10:31:07.666 -> final time (ms) = 15502
10:31:07.666 -> Speed of object (M/s) = 4294967295
10:31:08.109 -> initial time (ms) = 16002
10:31:08.144 -> final time (ms) = 15502
10:31:08.178 -> Speed of object (M/s) = 0
The 4294967295 shows up when there is no object present and when there is it seems to output 0. I've adjusted the equation a few times but I don't seem to get anything that makes sense. Any help is greatly appreciated. Thank you!
The logic of code doesn't seem right.
From your explanation if you sure that trigged state is HIGH my code will be like this.
void loop() {
if (digitalRead(PE_01)== HIGH){// Check if first sensor trigged
unsigned long start = millis();
while(digitalRead(PE_02)== LOW);// Wait until second sensor trigged
unsigned long finish = millis();
float speed = 76200.0f/float(finish - start );
Serial.print("initial time (ms) = ");
Serial.println(start);
Serial.print("final time (ms) = ");
Serial.println(finish);
Serial.print("Speed of object (M/s) = ");
Serial.println(speed);
delay(500);
}
Edit
If your sensor are active low reverse all logic.
void loop() {
if (digitalRead(PE_01)== LOW){// Check if first sensor trigged
unsigned long start = millis();
while(digitalRead(PE_02)== HIGH);// Wait until second sensor trigged
unsigned long finish = millis();
float speed = 76200.0f/float(finish - start );
Serial.print("initial time (ms) = ");
Serial.println(start);
Serial.print("final time (ms) = ");
Serial.println(finish);
Serial.print("Speed of object (M/s) = ");
Serial.println(speed);
delay(500);
}

Arduino Serial.print() if Weight is Steady

I have a code in Arduino that runs a height and weight sensor combination with an ultrasonic sensor and load cells. I need the weights and heights to print to the serial monitor only when the weight has been steady on the scale for three seconds and the same for the height. What I mean by steady is that the weight or height would have to be the same for three seconds. Is this possible?
The code that I have for the circuit is as follows:
#include <LiquidCrystal.h>
#define calibration_factor -13180.00
//This value is obtained using the SparkFun_HX711_Calibration sketch
#define DOUT 7
#define CLK 6
HX711 scale(DOUT, CLK);
LiquidCrystal LCD(10, 9, 5, 4, 3, 2); //Create Liquid Crystal Object
called LCD
int trigPin=13; //Sensor Trip pin connected to Arduino pin 13
int echoPin=11; //Sensor Echo pin connected to Arduino pin 11
int myCounter=0; //declare your variable myCounter and set to 0
int servoControlPin=6; //Servo control line is connected to pin 6
float pingTime; //time for ping to travel from sensor to target and return
float targetDistance; //Distance to Target in inches
float speedOfSound=776.5; //Speed of sound in miles per hour when temp is 77 degrees.
void setup() {
Serial.begin(9600);
Serial.println("HX711 scale demo");
scale.set_scale(calibration_factor); //This value is obtained by
using the SparkFun_HX711_Calibration sketch
scale.tare(); //Assuming there is no weight on the scale at start up, reset the scale to 0
Serial.println("Readings:");
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
LCD.begin(16,2); //Tell Arduino to start your 16 column 2 row LCD
LCD.setCursor(0,0); //Set LCD cursor to upper left corner, column 0, row 0
LCD.print("Target Distance:");
}
void loop() {
Serial.println("Reading: ");
Serial.print(scale.get_units(), 1); //scale.get_units() returns a float
Serial.print(" lbs"); //You can change this to kg but you'll need to refactor the calibration_factor
Serial.println();
digitalWrite(trigPin, LOW); //Set trigger pin low
delayMicroseconds(2000); //Let signal settle
digitalWrite(trigPin, HIGH); //Set trigPin high
delayMicroseconds(15); //Delay in high state
digitalWrite(trigPin, LOW); //ping has now been sent
delayMicroseconds(10); //Delay in high state
pingTime = pulseIn(echoPin, HIGH); //pingTime is presented in microceconds
pingTime=pingTime/1000000; //convert pingTime to seconds by dividing by 1000000 (microseconds in a second)
pingTime=pingTime/3600; //convert pingtime to hourse by dividing by 3600 (seconds in an hour)
targetDistance= speedOfSound * pingTime; //This will be in miles, since speed of sound was miles per hour
targetDistance=targetDistance/2; //Remember ping travels to target and back from target, so you must divide by 2 for actual target distance.
targetDistance= targetDistance*63360; //Convert miles to inches by multipling by 63360 (inches per mile)
LCD.setCursor(0,1); //Set cursor to first column of second row
LCD.print(" "); //Print blanks to clear the row
LCD.setCursor(0,1); //Set Cursor again to first column of second row
LCD.print(targetDistance); //Print measured distance
LCD.print(" inches"); //Print your units.
delay(250); //pause to let things settle
Serial.print(targetDistance);
Serial.println();
Serial.print(" inches ");
delay(250);
}

Controlling DC motor using encoder

I'm trying to control the speed of two DC motors using an Arduino Uno and encoders that are connected to the motors.
I've written a code to check whether there's a change in the position of the encoder and according to that calculate the velocity of the motors.
Ive used this website for the code:
I'm having problems when calculating the difference between the new position of the encoder and the old position of the encoder. For some reason that difference keeps going up even though the speed stays the same.
This is my code so far:
#define pwmLeft 10
#define pwmRight 5
#define in1 9
#define in2 8
#define in3 7
#define in4 6
//MOTOR A
int motorSpeedA = 100;
static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile long encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile long oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile long reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
//MOTOR B
static int pinC = 12; // Our first hardware interrupt pin is digital pin 2
static int pinD = 33; // Our second hardware interrupt pin is digital pin 3
volatile byte cFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte dFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile long encoderPosB = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile long oldEncPosB = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile long readingB = 0;
int tempPos;
long vel;
unsigned long newtime;
unsigned long oldtime = 0;
void setup() {
//MOTOR A
pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
attachInterrupt(0, PinA, RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
attachInterrupt(1, PinB, RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
//MOTOR B
pinMode(pinC, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
pinMode(pinD, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
attachInterrupt(0, PinC, RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
attachInterrupt(1, PinD, RISING);
Serial.begin(9600); // start the serial monitor link
pinMode (in1, OUTPUT);
pinMode (in2, OUTPUT);
pinMode (in3, OUTPUT);
pinMode (in4, OUTPUT);
digitalWrite (8, HIGH);
digitalWrite (9, LOW); //LOW
digitalWrite (7, LOW); //LOW
digitalWrite (6, HIGH);
pinMode (pwmLeft, OUTPUT);
pinMode (pwmRight, OUTPUT);
}
void PinA(){
cli(); //stop interrupts happening before we read pin values
reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
if(reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos --; //decrement the encoder's position count
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
} else if (reading == B00000100) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinB(){
cli(); //stop interrupts happening before we read pin values
reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos ++; //increment the encoder's position count
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
} else if (reading == B00001000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinC(){
cli(); //stop interrupts happening before we read pin values
readingB = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
if(readingB == B00001100 && cFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPosB --; //decrement the encoder's position count
dFlag = 0; //reset flags for the next turn
cFlag = 0; //reset flags for the next turn
} else if (readingB == B00000100) dFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinD(){
cli(); //stop interrupts happening before we read pin values
readingB = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
if (readingB == B00001100 && dFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPosB ++; //increment the encoder's position count
dFlag = 0; //reset flags for the next turn
cFlag = 0; //reset flags for the next turn
} else if (readingB == B00001000) cFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void loop(){
analogWrite(pwmLeft, motorSpeedA);
analogWrite(pwmRight, motorSpeedA);
if(oldEncPos != encoderPos) {
newtime = millis();
tempPos = encoderPos - oldEncPos;
vel = tempPos / (newtime - oldtime);
Serial.println(tempPos);
oldEncPos = encoderPos;
oldtime = newtime;
delay(250);
}
if(oldEncPosB != encoderPosB) {
Serial.println(encoderPosB);
oldEncPosB = encoderPosB;
}
}
The two if statements are just made to check that the encoders are working properly. In the first if statement I'm trying to do the calculations of the velocity.
I would appreciate any feedback.
Thank you.
EDIT:
I found out theres an encoder library which makes everything a lot easier.
so now my code looks like this:
#include <Encoder.h>
#define pwmLeft 10
#define pwmRight 5
Encoder myEncA(3, 2);
Encoder myEncB(13, 12);
unsigned long oldtimeA = 0;
unsigned long oldtimeB = 0;
int speedA = 100;
int speedB = 130;
void setup() {
Serial.begin(9600);
digitalWrite (8, HIGH);
digitalWrite (9, LOW); //LOW
digitalWrite (7, LOW); //LOW
digitalWrite (6, HIGH);
pinMode (pwmLeft, OUTPUT);
pinMode (pwmRight, OUTPUT);
}
long oldPositionA = -999;
long oldPositionB = -999;
void loop() {
analogWrite(pwmLeft, speedA);
analogWrite(pwmRight, speedB);
long newPositionA = myEncA.read();
long newPositionB = myEncB.read();
if ((newPositionA != oldPositionA) || (newPositionB != oldPositionB)) {
unsigned long newtimeA = millis ();
long positionA = newPositionA - oldPositionA;
long positionB = newPositionB - oldPositionB;
long velB = (positionB) / (newtimeA - oldtimeA);
long velA = (positionA) / (newtimeA - oldtimeA);
oldtimeA = newtimeA;
oldPositionA = newPositionA;
oldPositionB = newPositionB;
Serial.println(velB);
}
}
I am still having problems with my "B" motor, the calculation is still way off for some reason.
Motor "A" works fine
A couple of issues, including a divide by zero error in loop(). This scan cause a reset of your controller. Always check the value of the divisor when doing a division!
Using only positive transitions unnecessarily reduces the resolution of your readings by 2.
The Arduino is an 8bit controller... Reading an int requires multiple instruction, which means you should disable interrupts before reading an int that's modified by an interrupt routine. Failure to do so will cause odd jumps in the vakue read. This is usually done like this:
//...
NoInterrupts();
int copyOfValue = value; // use the copy to work with.
interrupts();
//...
In your case, a single byte value is likely enough to store movement, with a reset every 30 ms, this should give you a top speed of 255 pulses/30ms = 8500 pulses/second or 1275000 rpm for a 24 ticks/turn encoder. :) in that case, no need to disable interrupts for a reading.
with one reading per 30ms, 1 tick /30ms = 33 tick/seconds, or 85 RPM. It's a bit high for motion. You may need to average readings, depending on your application.
Also, the algorithm you are using will definitely not work. The main reason is that the delay between reads and adjustments is too small. Most readings will be of zero. You will run into the problem when removing the println() calls. I suggest a pacing of at least 30 ms between readings. 100 ms may work a bit better, depending on your application. Using a float variable for speed average will definitely help.
void loop()
{
//...
if(oldEncPos != encoderPos) {
newtime = millis();
tempPos = encoderPos - oldEncPos;
vel = tempPos / (newtime - oldtime); // <-- if newtime == oltime => divide by zero.
//...
}
//...
}
The encoder reading code seems awfully complex...
#define PIN_A 2 // encoder bit 0
#define PIN_B 3 // encoder bit 1
volatile char encVal1;
volatile unsigned char encPos1; // using char
void OnEncoder1Change()
{
char c = (digitalRead(pinA) ? 0b01 : 0)
+ (digitalRead(pinB) ? 0b10 : 0); // read
char delta = (c - encVal1) & 0b11; // get difference, mask
if (delta == 1) // delta is either 1 or 3
++encPos1;
else
--encPos1;
encVal1 = c; // keep reading for next time.
encPos1 += delta; // get position.
// no need to call sei()
}
setup()
{
pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP);
// get an initial value
encValA = digitalRead(pinA) ? 0b01 : 0;
encValA += digitalRead(pinB) ? 0b10 : 0;
// use digitalPinToInterrupt() to map interrupts to a pin #
// ask for interrupt on change, this doubles .
attachInterrupt(digitalPinToInterrupt(PIN_A), OnEncoder1Change, CHANGE);
attachInterrupt(digitalPinToInterrupt(PIN_B), OnEncoder1Change, CHANGE);
//...
}
unsigned char oldTime;
unsigned char oldPos;
int speed;
void loop()
{
unsigned char time = millis();
if (time - oldTime > 30) // pace readings so you have a reasonable value.
{
unsigned char pos = encPos1;
signed char delta = pos - oldPos;
speed = 1000 * delta) / (time - oldTime); // signed ticks/s
encPos1 -= pos; // reset using subtraction, do you don't miss out
// on any encoder pulses.
oldTime = time;
}
}

Unable to toggle Arduino State from HIGH to LOW after delay

functionality:
Arduino make use of the following:
1. IR sensor
2. LED Light
3. MotorFan
4. Relay
hence, when user approaches the IR sensor, it will give a serialPrint of '1' since it has detected proximity, this will then toggle the state of LED and motorfan from LOW to HIGH. However, after 5s, the state of motorfan will toggle to LOW from HIGH, while the state of LED will still remain HIGH and will remain as HIGH as long as the serialPrint is '1'.
However, when user leaves the proximity of the IR sensor, the LED state will toggle to LOW from HIGH after 10s.
What I have done:
Code that I have done:
const int signalPin = 1; //wire pin to analog for IR Sensor
//Motor-Fan Relay
byte FanRelay = 4;
byte LightRelay = 6;
int IRSignal; //variable signal, will hold the analog value read by Arduino
long duration;
int distance;
unsigned long Timer;
unsigned long Interval = 10000; //teh repeat Interval
unsigned long MotorFanOff = 5000;
void setup()
{
//Execute only once at startup
//pinMode (FanPin , OUTPUT) ; // Set pinMode for FanPin as OUTPUT, display
pinMode (signalPin, INPUT); //infared sensor line will be an input to the Arduino
pinMode(FanRelay, OUTPUT);
pinMode(LightRelay, OUTPUT);
Serial.begin(9600); // Open serial port to communicate with the Ultrasaonic Sensor
}
void loop()
{
//execute multiple times in a loop
IRSignal = analogRead(signalPin); //arduino reads the value from the infared sensor
distance = 9462 / (IRSignal -16.92);
if(distance < 30 && distance > 0)
{
Timer = millis();
// Write a pin of HIGH
Serial.println("1");
//Set motor-fan to operate
digitalWrite (FanRelay, HIGH);
digitalWrite (LightRelay, HIGH);
//After a delay of 5s, MotorFan will toggle to LOW
//Toggle MotorFan to LOW after 5s
if ((millis()-Timer)>MotorFanOff){
digitalWrite (FanRelay, LOW);
}
}
else
{
Serial.println("0");
//Check if Timer is longer than 10s
if ((millis()-Timer)>Interval){
digitalWrite (LightRelay, LOW);
digitalWrite (FanRelay, LOW);
}
}
delay(1000);
}
Issue:
At this point, when tested, both the state of the LED and motorfan will toggle from LOW to HIGH when the serialPrint is '1' from the IR sensor. However, the main issue that i am facing is that the state of the MOTORFAN does not toggle to LOW from HIGH after 5seconds, but both state will only toggle to LOW when serialPrint is '0'.
Hence, what have I done wrong? pls help.thanks.
Be careful! The first two pins on the Arduino are used for serial communication and break if you use them for something else. Switch over to higher pin numbers and the problem should go away.

Read Mutiple Channels of 9 Channel Transmitter Receiver

I have 9 channel RF RX/TX,i want to connect 3 motors to it.I am able to connect channel 1 with motor1 but unable to connect channel2 with motor2 simultaneously with ardunio.Pls take a look at code. cant able to rotate motors with different channels
int motor1Left = 7;// defines pin 5 as connected to the motor
int motor1Right= 9;// defines pin 6 as connected to the motor
int motor2Left = 22;// defines pin 7 as connected to the motor
int motor2Right = 26;// defines pin 8 as connected to the motor
int enable = 5;
int enable2 = 10;
int channel1 = 2; // defines the channels that are connected
int channel2 = 3;// to pins 9 and 10 of arduino respectively
int Channel1 ; // Used later to
int Channel2 ; // store values
void setup ()
{
pinMode (motor1Left, OUTPUT);// initialises the motor pins
pinMode (motor1Right, OUTPUT);
pinMode (motor2Left, OUTPUT);
pinMode (motor2Right, OUTPUT);// as outputs
pinMode (channel1, INPUT);// initialises the channels
pinMode (channel2, INPUT);// as inputs
//pinMode (enable, OUTPUT);
Serial.begin (9600); // Sets the baud rate to 9600 bps
}
void loop ()
{
Channel1 = (pulseIn (channel1, HIGH)); // Checks the value of channel1
Serial.println (Channel1); //Prints the channels value on the serial monitor
delay(1000);
Channel2 = (pulseIn (channel2, HIGH)); // Checks the value of channel1
Serial.println (Channel2); //Prints the channels value value on the serial monitor
delay(1000);
if (Channel1 > 1470 && Channel1 < 1500) /*If these conditions are true, do the following. These are the values that I got from my transmitter, which you may customize according to your transmitter values */
{
digitalWrite (motor1Left, LOW); // Sets both the
digitalWrite (motor1Right, LOW);// motors to low
analogWrite(enable, 100);
}
if (Channel1 < 1460) // Checks if Channel1 is lesser than 1300
{
digitalWrite (motor1Left, HIGH);// Turns the left
digitalWrite (motor1Right, LOW); // motor forward
analogWrite(enable, 100);
//delay(500);
//delay(500);
//digitalWrite(motor1Left, LOW);
//delay(1);
}
if (Channel1 > 1510) // Checks if Channel1 is greater than 1500
{
digitalWrite (motor1Left, LOW);// Turns the right
digitalWrite (motor1Right, HIGH);// motor forward
analogWrite(enable, 70);
//delay(500);
//digitalWrite (motor1Right, LOW);
// delay(50);
//digitalWrite (motor1Right, HIGH);
}
if (Channel2 > 1480 && Channel1 < 1500 ) // If these conditions are true, do the following
{
digitalWrite (motor2Left, LOW);// Sets both the
digitalWrite (motor2Right, LOW);// motors to low
analogWrite (enable2, 100);
}
if (Channel2 < 1300) // Checks if Channel2 is lesser than 1300
{
digitalWrite (motor2Left, LOW);// Turns the left
digitalWrite (motor2Right, HIGH);// motor backward
analogWrite (enable2, 100);
}
if (Channel2 > 1500) // Checks if Channel2 is greater than 1500
{
digitalWrite (motor2Left, HIGH);// Turns the right
digitalWrite (motor2Right, LOW);// motor backward
analogWrite (enable2, 100);
}
}
I see a couple of problems:
You mention 3 motors, but there appear to be only 2.
You print out th value read, but don't have any help to identify which one. Consider adding "Serial.print("Ch1 = ");" just before the first println to differentiate.
About line 72, you are have mixed Channel2 and Channel1.
In both channels, you have some dead zones for your readings. For example, for Channel 1, what will happen if Channel1 is 1465? Or 1460 or 1470? In all those cases none of your "if" statements will fire. In Channel2 the lower dead zone is much bigger, and the upper dead zone is still there (what if Channel2 is exactly 1500?).
Lastly, it still seems like your motor should have done something. Maybe it was because Channel 1 was less than 1500. But if it still doesn't work, check your wiring.
Also, a style note: You use channel1 to be the input pin to read. You use Channel1 to be the value read from that. These are extremely similar looking. If you had mistyped one, it would be almost impossible to spot. You should consider naming these in a way that makes it easier to spot a mistake, and more obvious. For example, channel1 could become pinCh1, and Channel1 could become inpCh1. Others might suggest even more descriptive names, possibly with the first letter indicating data type. e.g.,
int iChannel1_PinNumber;
int iChannel1_InputValue;

Resources