Hi, I am playing little with DC brushed motor and encoders, so I glued together some code:
/*******Interrupt-based Rotary Encoder Sketch*******
by Simon Merrett, based on insight from Oleg Mazurov, Nick Gammon, rt, Steve Spence
*/
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 byte 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 byte 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 byte 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
#define pin 9
int readInt() { //dirty
if (Serial.available() > 0) { // something came across serial
int integerValue = 0; // throw away previous integerValue
while(1) { // force into a loop until 'n' is received
char incomingByte = Serial.read();
if (incomingByte == '\n') break; // exit the while(1), we're done receiving
if (incomingByte == -1) continue; // if no characters are in the buffer read() returns -1
integerValue *= 10; // shift left 1 decimal place
// convert ASCII to integer, add, and shift left 1 decimal place
integerValue = ((incomingByte - 48) + integerValue);
}
return integerValue;
}
return -1;
}
void setup() {
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)
pinMode(pin, OUTPUT);
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)
Serial.begin(74880); // start the serial monitor link
}
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 loop(){
if(oldEncPos != encoderPos) {
Serial.println(encoderPos);
oldEncPos = encoderPos;
}
int val = readInt();
if(val != -1) {
Serial.println(val);
analogWrite(pin, val);
}
}
The problem is however that when I plug in the Arduino, open serial and write any number, i got messages for like 3 seconds and then it freezes. When I write something, serial just kind of waits. But the chip is working and generating PWM. I tried initializing serial to 9600, 74880 and 115200 bauds, but it didn't helped. If you could help me, I would be very grateful. Thanks
in addition to receiving digits and '\n' could the Serial.read() be receiving other characters (e.g.'\r') which could be causing problems
try inserting the following test
if((incomminByte <'0') || (incommingByte > '9'))continue;
integerValue *= 10; // shift left 1 decimal place
Your system for receiving data is not robust. For one thing, you are assuming the data will be there when you expect it.
Have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.
I know that the integer parsing is quick-and-dirty code, but that does not matter. The problem is in both input / output. In whole serial. I send number 100, it is received and printed and the movement begin. As I am receiving the position from arduino, it just freezes and thats it.
I am using Arduino serial monitor. Here is video. The serial freezes (sometimes in one sec, sometimes in 5 sec; I tried it like 20 times and the longest 2 periods were ~40seconds and ~15seconds - video) but the arduino keeps outputing signal. I tried 3 different usb cables and different ports (and even second computer), but it just does not work. Serial freezes while receiving position data from arduino - YouTube
The motor is driven with 5V 2A (usb charger) through mosfet with push-pull transistors. Is there any chance that this is caused by bad grounding or any oscilations or whatever electrical?
I do send the \n.
EDIT: I little edited the receiving, but still the serial freezes.
//#include <PID_v1.h>
/*******Interrupt-based Rotary Encoder Sketch*******
by Simon Merrett, based on insight from Oleg Mazurov, Nick Gammon, rt, Steve Spence
*/
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 byte 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 byte 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 byte 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
#define pin 9
const byte numChars = 32;
char receivedChars[numChars]; // an array to store the received data
boolean newData = false;
void recvWithEndMarker() {
static byte ndx = 0;
char endMarker = '\n';
char rc;
if (Serial.available() > 0) {
rc = Serial.read();
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
ndx = 0;
newData = true;
}
}
}
int readInt() {
if (newData == true) {
return atoi(receivedChars); // new for this version
}
return -1;
}
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
}
//input cur temp, output last temp, setpoint settmp
double input, output, setpoint;
//PID pid(&input, &output, &setpoint, 1, 0.05, 0.25, DIRECT);
void setup() {
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)
pinMode(pin, OUTPUT);
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)
Serial.begin(115200); // start the serial monitor link
//pid.SetMode(AUTOMATIC);
}
void loop(){
/*input = output = encoderPos;
setpoint = 100;
if(input > setpoint) input = setpoint;
analogWrite(pin, (int) input);
pid.Compute(); */
recvWithEndMarker();
if(oldEncPos != encoderPos) {
Serial.println(encoderPos);
oldEncPos = encoderPos;
}
int val = readInt();
if(val != -1) {
//Serial.println(val);
analogWrite(pin, val);
}
}
if I run your code it works OK accepting numeric input from the PC without problems
I would suspect that the motor is causing problems - possibly a reduction in voltage as it starts
try a more powerful power supply?