I'm trying to accomplish serial communication between two arduinos, one is transmitter and one reciever ofc. It is a guess game, when you generate a random number and print it on a lcd 16x2 and you have to guess a correct number. I'm using registers and interrupts. Im testing it in ISIS Proteus. The reciever behaves weirdly because whenever I press the button for a number 1, it shows this number only till Im holding this button otherwise it will change. It's weird because when I start to bruteforcing this buttons, after a while it gets fixed and after a restart it's broken again. Im trying to display also correct points, wrong points and round number but it also doesn't work. I will get only 1 good point or 1 wrong point.
RECIEVER:
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 10, 11, 12, 13);
short generatedNumber;
short userGuess;
short correctPoints = 0;
short wrongPoints = 0;
short numberOfRounds = 0;
void setup()
{
Serial.begin(9600);
randomSeed((PIND&(1<<0)));
lcd.begin(16, 2);
lcd.setCursor(0, 0);
}
void loop()
{
if(Serial.available() > 0 && numberOfRounds != 10)
{
userGuess = Serial.read();
generatedNumber = random(1, 6);
lcd.print(userGuess, DEC);
lcd.print(generatedNumber);
if(userGuess == generatedNumber)
correctPoints += 1;
else
wrongPoints += 1;
lcd.setCursor(0, 1);
lcd.print(correctPoints);
lcd.print(":");
lcd.print(wrongPoints);
lcd.print(" ");
lcd.print(numberOfRounds);
lcd.print("/10");
++numberOfRounds;
lcd.setCursor(0, 0);
}
else
{
userGuess = 0;
correctPoints = 0;
wrongPoints = 0;
numberOfRounds = 0;
}
}
TRANSMITTER:
ISR( PCINT0_vect)
{
if( (PINB&(1<<0)) == 0)
{
Serial.write(1);
}
else if( (PINB&(1<<1)) == 0)
{
Serial.write(2);
}
else if( (PINB&(1<<2)) == 0)
{
Serial.write(3);
}
else if( (PINB&(1<<3)) == 0)
{
Serial.write(4);
}
else if( (PINB&(1<<4)) == 0)
{
Serial.write(5);
}
}
void setup()
{
Serial.begin(9600);
DDRB &= ~(1 << 0);
DDRB &= ~(1 << 1);
DDRB &= ~(1 << 2);
DDRB &= ~(1 << 3);
DDRB &= ~(1 << 4);
SREG |= 1<<7;
PCICR |= 1<<PCIE0;
PCMSK0 |= 1<<PCINT0;
PCMSK0 |= 1<<PCINT1;
PCMSK0 |= 1<<PCINT2;
PCMSK0 |= 1<<PCINT3;
PCMSK0 |= 1<<PCINT4;
}
void loop()
{}
5 minutes after post I just realized, if there is no serial communication available, like when im not holding button, condition else will execute and set my variables to 0.
You have a problem in your logic. As written right now, if there is no serial available, which will be most of the time because the loop function can repeat thousands of times in the time that one character takes to come in, then you reset everything back to zero. So the user makes a guess and then a few microseconds later when nothing is available for serial to read it goes to that else and resets all your variables. I don't think that's what you want.
The transmitter is a mess. Why are you using interrupts to read these buttons? An interrupt is for really fast things that might get missed. A human pressing a button is really slow. Unless superman or The Flash are the user, you don't need an interrupt to read a button press. Just read the button from loop and react accordingly.
Related
I am trying to follow a tutorial from youtube on using ROS with Arduino to control motors, and I have connected my L298N with the battery precisely as the video describes and have uploaded sketch 1 with the supporting folder and it loads properly. The Arduino is powered properly via USB, but that connection is not shown in the diagram. When I type the "e" command, I get the proper response of "0 0" and when I do the "o 255 255" it says "OK" and drives properly but upon using "e" to recheck the encoders I am getting the same "0 0". If anyone can spot something wrong with this, I would really appreciate the help in fixing it. Diagram and Code Below
Code:
#define USE_BASE // Enable the base controller code
//#undef USE_BASE // Disable the base controller code
/* Define the motor controller and encoder library you are using */
#ifdef USE_BASE
/* The Pololu VNH5019 dual motor driver shield */
//#define POLOLU_VNH5019
/* The Pololu MC33926 dual motor driver shield */
//#define POLOLU_MC33926
/* The RoboGaia encoder shield */
//#define ROBOGAIA
/* Encoders directly attached to Arduino board */
#define ARDUINO_ENC_COUNTER
/* L298 Motor driver*/
#define L298_MOTOR_DRIVER
#endif
//#define USE_SERVOS // Enable use of PWM servos as defined in servos.h
#undef USE_SERVOS // Disable use of PWM servos
/* Serial port baud rate */
#define BAUDRATE 57600
/* Maximum PWM signal */
#define MAX_PWM 255
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
/* Include definition of serial commands */
#include "commands.h"
/* Sensor functions */
#include "sensors.h"
/* Include servo support if required */
#ifdef USE_SERVOS
#include <Servo.h>
#include "servos.h"
#endif
#ifdef USE_BASE
/* Motor driver function definitions */
#include "motor_driver.h"
/* Encoder driver function definitions */
#include "encoder_driver.h"
/* PID parameters and functions */
#include "diff_controller.h"
/* Run the PID loop at 30 times per second */
#define PID_RATE 30 // Hz
/* Convert the rate into an interval */
const int PID_INTERVAL = 1000 / PID_RATE;
/* Track the next time we make a PID calculation */
unsigned long nextPID = PID_INTERVAL;
/* Stop the robot if it hasn't received a movement command
in this number of milliseconds */
#define AUTO_STOP_INTERVAL 2000
long lastMotorCommand = AUTO_STOP_INTERVAL;
#endif
/* Variable initialization */
// A pair of varibles to help parse serial commands (thanks Fergs)
int arg = 0;
int index = 0;
// Variable to hold an input character
char chr;
// Variable to hold the current single-character command
char cmd;
// Character arrays to hold the first and second arguments
char argv1[16];
char argv2[16];
// The arguments converted to integers
long arg1;
long arg2;
/* Clear the current command parameters */
void resetCommand() {
cmd = NULL;
memset(argv1, 0, sizeof(argv1));
memset(argv2, 0, sizeof(argv2));
arg1 = 0;
arg2 = 0;
arg = 0;
index = 0;
}
/* Run a command. Commands are defined in commands.h */
int runCommand() {
int i = 0;
char *p = argv1;
char *str;
int pid_args[4];
arg1 = atoi(argv1);
arg2 = atoi(argv2);
switch(cmd) {
case GET_BAUDRATE:
Serial.println(BAUDRATE);
break;
case ANALOG_READ:
Serial.println(analogRead(arg1));
break;
case DIGITAL_READ:
Serial.println(digitalRead(arg1));
break;
case ANALOG_WRITE:
analogWrite(arg1, arg2);
Serial.println("OK");
break;
case DIGITAL_WRITE:
if (arg2 == 0) digitalWrite(arg1, LOW);
else if (arg2 == 1) digitalWrite(arg1, HIGH);
Serial.println("OK");
break;
case PIN_MODE:
if (arg2 == 0) pinMode(arg1, INPUT);
else if (arg2 == 1) pinMode(arg1, OUTPUT);
Serial.println("OK");
break;
case PING:
Serial.println(Ping(arg1));
break;
#ifdef USE_SERVOS
case SERVO_WRITE:
servos[arg1].setTargetPosition(arg2);
Serial.println("OK");
break;
case SERVO_READ:
Serial.println(servos[arg1].getServo().read());
break;
#endif
#ifdef USE_BASE
case READ_ENCODERS:
Serial.print(readEncoder(LEFT));
Serial.print(" ");
Serial.println(readEncoder(RIGHT));
break;
case RESET_ENCODERS:
resetEncoders();
resetPID();
Serial.println("OK");
break;
case MOTOR_SPEEDS:
/* Reset the auto stop timer */
lastMotorCommand = millis();
if (arg1 == 0 && arg2 == 0) {
setMotorSpeeds(0, 0);
resetPID();
moving = 0;
}
else moving = 1;
leftPID.TargetTicksPerFrame = arg1;
rightPID.TargetTicksPerFrame = arg2;
Serial.println("OK");
break;
case MOTOR_RAW_PWM:
/* Reset the auto stop timer */
lastMotorCommand = millis();
resetPID();
moving = 0; // Sneaky way to temporarily disable the PID
setMotorSpeeds(arg1, arg2);
Serial.println("OK");
break;
case UPDATE_PID:
while ((str = strtok_r(p, ":", &p)) != '\0') {
pid_args[i] = atoi(str);
i++;
}
Kp = pid_args[0];
Kd = pid_args[1];
Ki = pid_args[2];
Ko = pid_args[3];
Serial.println("OK");
break;
#endif
default:
Serial.println("Invalid Command");
break;
}
}
/* Setup function--runs once at startup. */
void setup() {
Serial.begin(BAUDRATE);
// Initialize the motor controller if used */
#ifdef USE_BASE
#ifdef ARDUINO_ENC_COUNTER
//set as inputs
DDRD &= ~(1<<LEFT_ENC_PIN_A);
DDRD &= ~(1<<LEFT_ENC_PIN_B);
DDRC &= ~(1<<RIGHT_ENC_PIN_A);
DDRC &= ~(1<<RIGHT_ENC_PIN_B);
//enable pull up resistors
PORTD |= (1<<LEFT_ENC_PIN_A);
PORTD |= (1<<LEFT_ENC_PIN_B);
PORTC |= (1<<RIGHT_ENC_PIN_A);
PORTC |= (1<<RIGHT_ENC_PIN_B);
// tell pin change mask to listen to left encoder pins
PCMSK2 |= (1 << LEFT_ENC_PIN_A)|(1 << LEFT_ENC_PIN_B);
// tell pin change mask to listen to right encoder pins
PCMSK1 |= (1 << RIGHT_ENC_PIN_A)|(1 << RIGHT_ENC_PIN_B);
// enable PCINT1 and PCINT2 interrupt in the general interrupt mask
PCICR |= (1 << PCIE1) | (1 << PCIE2);
#endif
initMotorController();
resetPID();
#endif
/* Attach servos if used */
#ifdef USE_SERVOS
int i;
for (i = 0; i < N_SERVOS; i++) {
servos[i].initServo(
servoPins[i],
stepDelay[i],
servoInitPosition[i]);
}
#endif
}
/* Enter the main loop. Read and parse input from the serial port
and run any valid commands. Run a PID calculation at the target
interval and check for auto-stop conditions.
*/
void loop() {
while (Serial.available() > 0) {
// Read the next character
chr = Serial.read();
// Terminate a command with a CR
if (chr == 13) {
if (arg == 1) argv1[index] = NULL;
else if (arg == 2) argv2[index] = NULL;
runCommand();
resetCommand();
}
// Use spaces to delimit parts of the command
else if (chr == ' ') {
// Step through the arguments
if (arg == 0) arg = 1;
else if (arg == 1) {
argv1[index] = NULL;
arg = 2;
index = 0;
}
continue;
}
else {
if (arg == 0) {
// The first arg is the single-letter command
cmd = chr;
}
else if (arg == 1) {
// Subsequent arguments can be more than one character
argv1[index] = chr;
index++;
}
else if (arg == 2) {
argv2[index] = chr;
index++;
}
}
}
// If we are using base control, run a PID calculation at the appropriate intervals
#ifdef USE_BASE
if (millis() > nextPID) {
updatePID();
nextPID += PID_INTERVAL;
}
// Check to see if we have exceeded the auto-stop interval
if ((millis() - lastMotorCommand) > AUTO_STOP_INTERVAL) {;
setMotorSpeeds(0, 0);
moving = 0;
}
#endif
// Sweep servos
#ifdef USE_SERVOS
int i;
for (i = 0; i < N_SERVOS; i++) {
servos[i].doSweep();
}
#endif
}
Encoder Pin Designations:
/* *************************************************************
Encoder driver function definitions - by James Nugen
************************************************************ */
#ifdef ARDUINO_ENC_COUNTER
//below can be changed, but should be PORTD pins;
//otherwise additional changes in the code are required
#define LEFT_ENC_PIN_A PD2 //pin 2
#define LEFT_ENC_PIN_B PD3 //pin 3
//below can be changed, but should be PORTC pins
#define RIGHT_ENC_PIN_A PC4 //pin A4
#define RIGHT_ENC_PIN_B PC5 //pin A5
#endif
long readEncoder(int i);
void resetEncoder(int i);
void resetEncoders();
Encoder Driver:
/* *************************************************************
Encoder definitions
Add an "#ifdef" block to this file to include support for
a particular encoder board or library. Then add the appropriate
#define near the top of the main ROSArduinoBridge.ino file.
************************************************************ */
#ifdef USE_BASE
#ifdef ROBOGAIA
/* The Robogaia Mega Encoder shield */
#include "MegaEncoderCounter.h"
/* Create the encoder shield object */
MegaEncoderCounter encoders = MegaEncoderCounter(4); // Initializes the Mega Encoder Counter in the 4X Count mode
/* Wrap the encoder reading function */
long readEncoder(int i) {
if (i == LEFT) return encoders.YAxisGetCount();
else return encoders.XAxisGetCount();
}
/* Wrap the encoder reset function */
void resetEncoder(int i) {
if (i == LEFT) return encoders.YAxisReset();
else return encoders.XAxisReset();
}
#elif defined(ARDUINO_ENC_COUNTER)
volatile long left_enc_pos = 0L;
volatile long right_enc_pos = 0L;
static const int8_t ENC_STATES [] = {0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0}; //encoder lookup table
/* Interrupt routine for LEFT encoder, taking care of actual counting */
ISR (PCINT2_vect){
static uint8_t enc_last=0;
enc_last <<=2; //shift previous state two places
enc_last |= (PIND & (3 << 2)) >> 2; //read the current state into lowest 2 bits
left_enc_pos += ENC_STATES[(enc_last & 0x0f)];
}
/* Interrupt routine for RIGHT encoder, taking care of actual counting */
ISR (PCINT1_vect){
static uint8_t enc_last=0;
enc_last <<=2; //shift previous state two places
enc_last |= (PINC & (3 << 4)) >> 4; //read the current state into lowest 2 bits
right_enc_pos += ENC_STATES[(enc_last & 0x0f)];
}
/* Wrap the encoder reading function */
long readEncoder(int i) {
if (i == LEFT) return left_enc_pos;
else return right_enc_pos;
}
/* Wrap the encoder reset function */
void resetEncoder(int i) {
if (i == LEFT){
left_enc_pos=0L;
return;
} else {
right_enc_pos=0L;
return;
}
}
#else
#error A encoder driver must be selected!
#endif
/* Wrap the encoder reset function */
void resetEncoders() {
resetEncoder(LEFT);
resetEncoder(RIGHT);
}
#endif
I think if you use a Mega instead of an Uno, the pin ports are different.
So change the port from PD4 to PE4 and PD3 to PE5. Also, change PC4 to PF4 and PC5 to PF5.
In the Encoder.ino, you also have to change the ports accordingly.
Encoder.h:
#define LEFT_ENC_PIN_A PE4 //pin 2
#define LEFT_ENC_PIN_B PE5 //pin 3
//below can be changed, but should be PORTC pins
#define RIGHT_ENC_PIN_A PF5 //pin A4
#define RIGHT_ENC_PIN_B PF5 //pin A5
Encoder.ino:
/* Interrupt routine for LEFT encoder, taking care of actual counting */
ISR (PCINT2_vect){
static uint8_t enc_last=0;
enc_last <<=2; //shift previous state two places
enc_last |= (PINE & (3 << 2)) >> 2; //read the current state into lowest 2 bits
left_enc_pos += ENC_STATES[(enc_last & 0x0f)];
}
/* Interrupt routine for RIGHT encoder, taking care of actual counting */
ISR (PCINT1_vect){
static uint8_t enc_last=0;
enc_last <<=2; //shift previous state two places
enc_last |= (PINF & (3 << 4)) >> 4; //read the current state into lowest 2 bits
right_enc_pos += ENC_STATES[(enc_last & 0x0f)];
}
#include <msp430.h>
int forward_cnt = 0;
int reverse_cnt = 0;
void main(void)
{
WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
P1DIR |= BIT0;
P1OUT &= ~BIT0; //LED 1.0
P1REN |= BIT1;
P1OUT |= BIT1;
P2REN |= BIT1;
P2OUT |= BIT1; //switches
P1IE |= BIT1;
P1IES |= BIT1; //falling edge(when button is pushed)
P1IFG &= ~BIT1;
P2IE |= BIT1;
P2IES |= BIT1;
P2IFG &= ~BIT1;
//PWM Settings
P2DIR |= (BIT5 | BIT4);
P2SEL |= (BIT5 | BIT4); //SET TO 1
TA2CTL = TASSEL_2 + MC_1 + TACLR;
TA2CCR0 = 1000;
TA2CCTL2 = OUTMOD_6;
TA2CCR2 = 0;
TA2CCTL1 = OUTMOD_6;
TA2CCR1 = 0;
__bis_SR_register(GIE);
while(1)
{
}
}
#pragma vector = PORT2_VECTOR
__interrupt void Port_2_1(void)
{
forward_cnt++;
switch(forward_cnt % 3){
case 0 :
TA2CCR2 = 400;
TA2CCR1 = 0;
P1OUT |= BIT0;
break;
case 1 :
TA2CCR2 = 800;
TA2CCR1 = 0;
P1OUT |= BIT0;
break;
case 2 :
TA2CCR2 = 0;
TA2CCR1 = 0;
P1OUT &= ~BIT0;
break;
}
P2IFG &= ~BIT1; //interrupt end, so go back with IFG clear
}
#pragma vector = PORT1_VECTOR
__interrupt void Port_1_1(void)
{
TA2CCR2 = 0;
TA2CCR1 = 0;
forward_cnt = 0;
P1IFG &= ~BIT1;
}
I want to control motor speed by slow, fast, stop with clicking switch P2.1(external interrupt) and keep turned on LED when motor is working.
But some problem is coming out. It seems like forward_cnt value is changing while I release button sometimes. It works different while I keep hold on button sometimes.
For example, It should change its speed once when I click button once with my expectation but sometimes it changes its speed when I push, and also changes when I release button. I think interrupt is not working well. How can I fix this clearly?
The interrupt may be working as intended the issue is more likely with the button itself. It seems you are running into the classic switch bouncing problem. When a button is clicked it is not a clean transition from one logic state to the next. Referencing the picture posted below a single press of the button can cause multiple rising and falling edges. Which in the case of your code will cause multiple interrupts to occur changing your forward_cnt variable almost unpredictably.
There are many different solutions to this problem but the most straightforward is to add in a delay after the button is pressed to allow for the bouncing of the switch to settle. After the bouncing has settled you can then increment you forward_cnt variable.
scope capture of button when pressed
Push buttons are noisy by nature, so you need to deal with that. For example, if you were to sample the input pin from the push button, you would find it will toggle many times before settling down on the pressed value. To deal with the noise, you should add a debouce function to the button press event.
The easiest way to do this is as mmeadwell mentioned, which is to add some delay. Normally, a good design would use hardware timer for this, but in a pinch, you can just implement a software delay. For example, in your Port_2_1(void) interrupt routine, add this code:
#pragma vector = PORT2_VECTOR
__interrupt void Port_2_1(void)
{
// Variable for button state
int button_now = 0;
int button_last = 0;
// Implement a debounce delay for 150 ms
for (int delay = 0; delay < 150; delay++)
{
// Sample the current button state
button_now = (P2IN & BIT1) ? 1 : 0;
// Reset delay when state of button changes
if (button_now != button_last) delay = 0;
// delay 1 ms (must know your CPU clock rate)
// using MSP430 compiler intrinsic function
__delay_cycles(CPU_CLOCKS_PER_US * 1000);
button_last = button_now;
}
// Only continue if button is currently pressed
if (button_now == 0) return;
// now process the button press event
// remaining code is unchanged
forward_cnt++;
switch(forward_cnt % 3){
Since the button P1.1 is only used to stop the PWM, then no debounce is necessary. This noise will not change the outcome once stopped.
I only started programming a few days ago and ran into a few problems.
I'm trying to make a servo turn 180 degrees when I type 1 and 180 degrees the other way when I type 0, I'm using an HC-05 Bluetooth module connected to my phone, so I tried to "merge" the servo sweep code from Arduino IDE library and another code that turns a light on by Bluetooth (which works), so I've been trying to fix this without any results.
Here's what I've done so far:
#include <Servo.h>
Servo myservo;
int pos = 0;
char data = 0;
void setup()
{
Serial.begin(9600);
pinMode(13, OUTPUT);
myservo.attach(13);
}
void loop()
{
if (Serial.available() > 0)
{
data = Serial.read();
Serial.print(data);
Serial.print("\n");
if (data == '1') for (pos = 0; pos <= 180; pos += 1)
else if (Serial.available() > 1)
digitalWrite(13, myservo(pos = 180; pos >= 0; pos -= 1));
}
}
Arduino create keeps telling me I'm missing a primary expression before else.
You've added the start of a for loop, but not told the compiler what operations to repeat.
When you're beginning, it's useful to add braces whenever you use a control statement (if, while, for, do, switch, case) irrespective of whether you have to, and indent consistently, then you can see where things should go and where the body of the control statement starts and ends.
void loop()
{
if (Serial.available() > 0)
{
data = Serial.read();
Serial.print(data);
Serial.print("\n");
if (data == '1')
{
// execute the code from the 'sweep' example if the user sends '1'
for (pos = 0; pos <= 180; pos += 1)
{
// goes from 0 degrees to 180 degrees
// in steps of 1 degree
myservo.write(pos);
delay(15);
}
}
else if (Serial.available() > 1)
{
// removed as code here made no real sense
}
}
}
I'm using an 125Khz RFID module RDM6300 with arduino nano.
While the card is near the RFID reader the loop will read the card multiple times. I want it to read only once while the card is near the reader then read it again if a new connection is being made.
*This code is not writen by me, this is the source:
https://github.com/Wookai/arduino-rfid
// define constants for pins
//int SUCCESS = 10;
//int ERROR = 13;
// variables to keep state
int readVal = 0; // individual character read from serial
unsigned int readData[10]; // data read from serial
int counter = -1; // counter to keep position in the buffer
char tagId[11]; // final tag ID converted to a string
char* authorizedTags[4]; // array to hold the list of authorized tags
// fills the list of authorzied tags
void initAuthorizedTags() {
// add your own tag IDs here
authorizedTags[0] = "0400680B85";
authorizedTags[1] = "0400063EB9";
authorizedTags[2] = "040004F3F5";
authorizedTags[3] = "04006813AB";
}
void setup() {
Serial.begin(9600);
// pinMode(SUCCESS, OUTPUT);
//pinMode(ERROR, OUTPUT);
initAuthorizedTags();
}
// check if the tag ID we just read is any of the authorized tags
int checkTag() {
int i;
for (i = 0; i < 4; ++i) {
if (strcmp(authorizedTags[i], tagId) == 0) {
return 1;
}
}
return 0;
}
// convert the int values read from serial to ASCII chars
void parseTag() {
int i;
for (i = 0; i < 10; ++i) {
tagId[i] = readData[i];
}
tagId[10] = 0;
}
// once a whole tag is read, process it
void processTag() {
// convert id to a string
parseTag();
// print it
printTag();
// check if the tag is authorized
if (checkTag() == 1) {
tagSuccess(); // if so, perform an action (blink a led, open a door, etc...)
} else {
tagFailed(); // otherwise, inform user of failure
}
}
void printTag() {
Serial.print("Tag value: ");
Serial.println(tagId);
}
// perform an action when an authorized tag was read
void tagSuccess() {
Serial.println("Tag authorized.");
// here, we simply turn on the success LED for 2s
// digitalWrite(SUCCESS, HIGH);
//digitalWrite(ERROR, LOW);
// delay(2000);
}
// inform the user that the tag is not authorized
void tagFailed() {
Serial.println("Unauthorized access!");
//digitalWrite(SUCCESS, LOW);
// digitalWrite(ERROR, HIGH);
// delay(2000);
}
// this function clears the rest of data on the serial, to prevent multiple scans
void clearSerial() {
while (Serial.read() >= 0) {
; // do nothing
}
}
void loop() {
// turn LEDs off
// digitalWrite(SUCCESS, LOW);
// digitalWrite(ERROR, LOW);
if (Serial.available() > 0) {
// read the incoming byte:
readVal = Serial.read();
// a "2" signals the beginning of a tag
if (readVal == 2) {
counter = 0; // start reading
}
// a "3" signals the end of a tag
else if (readVal == 3) {
// process the tag we just read
processTag();
// clear serial to prevent multiple reads
clearSerial();
// reset reading state
counter = -1;
}
// if we are in the middle of reading a tag
else if (counter >= 0) {
// save valuee
readData[counter] = readVal;
// increment counter
++counter;
}
}
}
Thank you.
Thank you for your answers. I tried to accept the multiple reads and print only one but it keeps printing "already read" instead of reading the card first time, this is the code:
void printTag() {
if(strcmp(tagId,previous)==1){
strcpy(previous, tagId);
Serial.print("Tag value: ");
Serial.println(tagId);
}
else
{
Serial.print("already read");
}
}
I also tried to put the delay after end of tag but it still reads the card multiple times.
I tried another code, it still reads the tag multiple times.
#include <SoftwareSerial.h>
// RFID | Nano
// Pin 1 | D2
// Pin 2 | D3
SoftwareSerial Rfid = SoftwareSerial(2,3);
int timer=0;
int reference = 1000;
int card_status = 0;
void setup() {
// Serial Monitor to see results on the computer
Serial.begin(9600);
// Communication to the RFID reader
Rfid.begin(9600);
}
void read() {
// check, if any data is available
// as long as there is data available...
while(Rfid.available() > 0 ){
// read a byte
int r = Rfid.read();
// print it to the serial monitor
Serial.print(r, DEC);
Serial.print(" ");
}
// linebreak
Serial.println();
timer=0;
}
void loop()
{
if((Rfid.available() > 0 ) && (card_status == 0) )
{
read();
}
if((!Rfid.available() > 0 ) && (card_status == 1) )
{
card_status=0;
}
}
I'm sorry for the late response. I forgot about this topic.
I solved the problem by making the arduino wait for a response after writing the RFID code for the frist time.
I was able to do that because my arduino was sending the code to a C# application via serial port.
Here is how it works: the arduino prints the RFID code on the serial, from there it is picked up by the C# application which searches a database to see if the code is stored there. Depending on the result, the application prints a character('y' or 'n') which is picked up by the arduino. Depending on the character recieved, the arduino lights up a led ( green or red) and makes a noise. Now a new RFID reading can be made.
Here is the code:
#include <SoftwareSerial.h>
#include "RDM6300.h"
SoftwareSerial rdm_serial(8, 9);
RDM6300<SoftwareSerial> rdm(&rdm_serial);
String comanda;
char c="";
int led_verde = 2;
int led_rosu = 7;
int buzzer = 12;
int i;
void buzz(int n = 1)
{
for (int i = 0; i < n; i++) {
digitalWrite(buzzer, LOW);
delay(200);
digitalWrite(buzzer, HIGH);
delay(200);
}
}
void ledVerde()
{
digitalWrite(led_verde, HIGH);
buzz(1);
delay(1000);
digitalWrite(led_verde, LOW);
}
void ledRosu()
{
digitalWrite(led_rosu, HIGH);
buzz(3);
delay(1000);
digitalWrite(led_rosu, LOW);
}
void setup()
{
pinMode(led_verde, OUTPUT);
pinMode(led_rosu, OUTPUT);
pinMode(buzzer, OUTPUT);
digitalWrite(led_verde, LOW);
digitalWrite(led_rosu, LOW);
digitalWrite(buzzer, HIGH);
Serial.begin(9600);
}
void loop()
{
static unsigned long long last_id = 0;
last_id = rdm.read();
rdm.print_int64(last_id);
Serial.println();
rdm_serial.end();
Serial.flush();
while(!Serial.available());
c=Serial.read();
if(c=='y')
{
ledVerde();
c="";
}
if(c=='n')
{
ledRosu();
}
Serial.flush();
last_id="";
c="";
rdm_serial.begin(9600);
}
You can find the RDM6300 library here: https://github.com/arliones/RDM6300-Arduino
Long time passed the original question, but maybe my answer would be useful for future visitors.
The RDM6300 works by:
automatically reading the data, and then
your code transfers the read data to the buffer for further processing.
Imagine it as a Baggage carousel in the airport. There are multiple luggage (data) on the carousel (reader), and you pick them one by one (transferring to buffer).
So, the problem of multiple reads, is that you have got read data in the reader (luggage on the carousel), that your code is gradually transferring them to the buffer (picking the luggage up).
In our example, if you don't like to collect all luggage, you can ask someone to take some of them, before they reach to you.
The below code does this. While you have data in the reader (the card is near to the reader), it transfers data from reader to buffer and then zeros all of them in the buffer:
First, place this code before "void setup()":
boolean multipleRead = false;
This defines a false/true variable to tell if this is the first time you are reading the tag (false), or it's being read multiple times (true).
Then, put this one at the end of the code block that shows the tag is fully read. If you are using Michael Schoeffler's library for your RDM6300/630 RFID, put it after "else if (ssvalue == 3) {":
multipleRead = true;
It change the variable to true, when your tag is read. That tells the program that your first read is done and the next upcoming read(s) would be "multiple read" and you don't want them.
Then put this at the end of the RFID reader code (if your RFID code is under void loop (), put the below code just after "void loop (){":
if (multipleRead) {
while (ssrfid.available() > 0) {
int ssvalue = ssrfid.read(); // read
if (ssvalue == -1) { // no data was read
break;
}
}
for (int x = 0; x < 14; x++)
{
buffer[x] = 0;
}
multipleRead = false;
}
It empties the reader and then zeros the buffer, while there is a card nearby. When you move the card away, multipleRead value would turn to false, which let another RFID reading loop to initiate.
Hope that helped :)
I guess what you want is a edge trigger instead of level trigger with time limit.
For example, you may prefer to read a RFID card when it firstly comes near to the antenna, once only; when it keeps to contact the antenna, still take no more actions. When you remove the card and place it again near to the antenna, the MCU starts to read the card again.
If so, the following code could be for your reference. What I have done is just to add 1 more flag to keep checking the card_status before checking if a RFID card comes near to the antenna.
int card_status = 0; //0:readable; 1:not-readable
if ( (Serial.available() > 0) && (card_status == 0) ) {
card_status = 1; //disable to read card after exit this loop
//your code, a card is near to the antenna, try to read it.
readVal = Serial.read();
if (readVal == 2) {
counter = 0; // start reading
} else if (readVal == 3) {
processTag();
clearSerial();
counter = -1;
} else if (counter >= 0) {
readData[counter] = readVal;
++counter;
}
}
reset the card status when no RFID signal comes in && card status is true,
if ( (!(Serial.available() > 0)) && (card_status == 1) ) {
card_status = 0; //enable to read card again
//no signal, no card is near to the antenna.
}
You can set a delay (e.g. delay(2000)) after reading the (end of tag). A delay of (say) 2 seconds will allow the user to move the card far enough away from the reader. Note that delay is a blocking command which might not suit your purposes, in which case you could be looking at using the millis count to activate/deactivate the Serial.read.
Another option is to accept the multiple reads, but keep a state of which card has been read. If the new card number is the same as the old card number (within a reasonable timeframe) then just ignore.
I am trying to wait for user input to start a program operation and then when the user sends a stop command, the loop stops running. I have been unable to get the Serial port to keep reading an input while the loop is running.
So I want the user to Press 1 and then it'll go into the loop and will display the data from the interrupt. But I want it to keep monitoring the Serial Input so when I type in 2, I will get out of the loop and stop printing to the Serial Port.
The serial port isn't registering my second input.
I left out some of the code, but the important stuff should be there.
int userStart = 0; // Holder for user input to start program
int userStop = 0; // Holder for user input to stop program
void setup() {
Serial.begin(115200);
pinMode(motorEncoderA, INPUT);
digitalWrite(motorEncoderA, HIGH); // Pull up resistor
pinMode(motorEncoderB, INPUT);
digitalWrite(motorEncoderB,HIGH); // Pull up resistor
// Interrupt on change of Pin A
attachInterrupt(digitalPinToInterrupt(2), encoderFunc, CHANGE);
Serial.print("Press 1 to start the Process & 2 to Stop");
}
void loop() {
if (Serial.available() > 0)
{
userStart = Serial.read();
if (userStart = 1) {
Serial.print('\n');
while(userStop != 2) {
unsigned long timee = millis();
// Only update if the shaft has moved
if (encoderPositionLast != rotationCounter) {
Serial.print("Time: ");
Serial.print(timee);
Serial.print(" Count: ");
Serial.print (rotationCounter);
Serial.print('\n');
encoderPositionLast = rotationCounter;
Serial.print(userStart);
}
if (Serial.available() > 0) {
userStop = Serial.read();
Serial.print(userStop);
}
}
}
}
Well, I think your problem is that userStart and userStop should not be 1 and 2, but '1' and '2'.
That said, there are some things in your code I dislike.
First of all why is everybody using int as the base type for all numeric variables? If one single byte is enough, use it. On 32bit machines int and byte are almost the same, but on 8bit ones working with ints wastes space and time.
Secondly, I highly discourage you to block the loop function, otherwise you won-t be able to do anything else. Instead, use a variable to track wheter you are running or not, update it with the serial interface, and then execute the code if you are running.
This code should do it. And IMHO it is much better than blocking the loop:
bool running = false;
void setup()
{
...
running = false;
}
void loop()
{
if (Serial.available() > 0)
{
switch(Serial.read())
{
case '1':
running = true;
Serial.print('\n');
break;
case '2':
running = false;
Serial.print("stopped");
break;
}
}
if (running)
{
unsigned long timee = millis();
// Only update if the shaft has moved
if (encoderPositionLast != rotationCounter) {
Serial.print("Time: ");
Serial.print(timee);
Serial.print(" Count: ");
Serial.print (rotationCounter);
Serial.print('\n');
encoderPositionLast = rotationCounter;
Serial.print("running");
}
}
}