I am working on a project on Arduino in which Arduino have to count 1 sec and on every sec Arduino has to run a certain function (every sec I mean a complete sec not an instance of a sec).
if (((millis()) % 1000) == 0)
{
tym_countdown();
Serial.println("Refresh");
What is happening as you can see below my Arduino is counting instances rather only complete secs.
20:54:04.145 -> Refresh
20:54:04.145 -> Refresh
20:54:05.119 -> Refresh
20:54:05.119 -> Refresh
20:54:05.119 -> Refresh
20:54:05.119 -> Refresh
20:54:05.119 -> Refresh
20:54:05.165 -> Refresh
20:54:05.165 -> Refresh
20:54:05.165 -> Refresh
20:54:06.103 -> Refresh
20:54:06.103 -> Refresh
20:54:06.103 -> Refresh
20:54:06.150 -> Refresh
Code:
#define X 13
#define Y 12
#define Z 11
char txt[15];
char tym[6];
int index,tym_index;
int pin_status_array[2][3]={0,0,0,0,0,0};
void setup() {
// put your setup code here, to run once:
pinMode(X,OUTPUT);// setting the pin flow of control as output
pinMode(Y,OUTPUT);
pinMode(Z,OUTPUT);
Serial.begin(9600);
while(!Serial)
{
; //to wait for pc to connect
}
Serial.println("\nHome Automation");
initial_dash_print();
}
void loop() {
// put your main code here, to run repeatedly:
read_incomming_serial_data();
if(((millis())%1000)==0)
{
tym_countdown();
Serial.println("Refresh");
}
turn_on_n_off();
}
void initial_dash_print() //to print dashes
{ Serial.println("-----------------------------------------------");
Serial.println("give me some command"); //ask for command
}
void read_incomming_serial_data()// read incoming data from serial
{
if(Serial.available()>0)
{ delay(17); // delay for arduino to wait and read commands,1ms per char.
index=0;
tym_index=0;
while(Serial.available()>0) //if serial available
{ char inchar=Serial.read();
txt[index]=inchar; // add char to txt string
index++;// increment to where to write next
if(index>4)
{ tym[tym_index]=inchar;
tym_index++;
}
}
Serial.print(txt);
check_data_from_serial();
}
}
void check_data_from_serial() // to check data from serial
{
if(strncmp(txt,"ON X",4)==0)
{
pin_status_array[0][0]=1;
if(tym_index!=0)
{
tym_update(X);
}
}
else if(strncmp(txt,"OFF X",5)==0)
{
pin_status_array[0][0]=0;
}
if(strncmp(txt,"ON Y",4)==0)
{
pin_status_array[0][1]=1;
if(tym_index!=0)
{
tym_update(Y);
}
}
else if(strncmp(txt,"OFF Y",5)==0)
{
pin_status_array[0][1]=0;
}
if(strncmp(txt,"ON Z",4)==0)
{
pin_status_array[0][2]=1;
if(tym_index!=0)
{
tym_update(Z);
}
}
else if(strncmp(txt,"OFF Z",5)==0)
{
pin_status_array[0][2]=0;
}
else if(txt=="STATUS")
{
}
}
void tym_update(int pin) // update time row
{
int temp=pin-13; // pin to array column equivalent
pin_status_array[1][temp]=atoi(tym);
}
void tym_countdown() //to perform time countdown
{ for(int temp_index=0; temp_index<4; temp_index++)
{
if(pin_status_array[0][temp_index]==1 && pin_status_array[1][temp_index]>0) // '0' => txt '1' => tym
{
int temp=pin_status_array[1][temp_index];
temp--;
pin_status_array[1][temp_index]=temp;
if(temp==0)
{
pin_status_array[0][temp_index]=0; //turn off
}
}
}
}
void turn_on_n_off()
{
if(pin_status_array[0][0]==1)
{
digitalWrite(X,HIGH);
}
if(pin_status_array[0][0]==0)
{
digitalWrite(X,LOW);
}
if(pin_status_array[0][1]==1)
{
digitalWrite(Y,HIGH);
}
if(pin_status_array[0][1]==0)
{
digitalWrite(Y,LOW);
}
if(pin_status_array[0][2]==1)
{
digitalWrite(Z,HIGH);
}
if(pin_status_array[0][2]==0)
{
digitalWrite(Z,LOW);
}
}
If you need to trigger an action exactly every 1 second then you should try using an Interrupt Service Routine (ISR). Depending on what board you are using then this can be done using the Timer library. You'll need to keep the ISR short or you could defer the processing to the loop() function.
If you don't have such hard realtime requirements and only need to perform an action once per second then you can record the last time the action ran in loop(). Then check if at least 1000 ms have elapsed since the last iteration of loop().
e.g.
unsigned long lastTriggerTime = 0;
void loop() {
read_incomming_serial_data();
auto currentLoopTime = millis();
if(currentLoopTime - lastTriggerTime >= 1000)
{
lastTriggerTime = currentLoopTime;
tym_countdown();
You can "synchronize" blt's suggestion to exact 1000 ms intervals easily:
unsigned long lastTriggerTime = 0;
void loop() {
read_incomming_serial_data();
unsigned long currentLoopTime = millis();
if(currentLoopTime - lastTriggerTime >= 1000)
{
lastTriggerTime += 1000; // will increment in 1000's steps
tym_countdown();
...
Of course, this has nothing to do with real world seconds and has a limited accuracy of about 0.1%
Related
Problem: As soon as I write to the USB serial port after accessing my I2C device asynchronously (polling it in main loop rather than a blocking loop), I get a CPU exception
Background: I have an Arduino project running under Microchip studio (for debugging via the J-Link interface). I am communicating with a VCNL4010 proximity sensor via I2C. I have a working synchronous function that reads proximity as such:
Zult<uint16_t> ProximitySensor::readProximity(void) {
auto res = setIntMask(0x80);
if (res.isError())
return Zult<uint16_t>(res);
res = write8(VCNL4010_COMMAND, VCNL4010_MEASUREPROXIMITY);
if (res.isError())
return Zult<uint16_t>(res);
auto start = millis();
do {
auto cmd = read8(VCNL4010_COMMAND);
if (cmd.isError())
return Zult<uint16_t>(cmd);
if (cmd.getResult() & VCNL4010_PROXIMITYREADY) {
return read16(VCNL4010_PROXIMITYDATA);
}
delay(1);
} while (millis() - start < 1000);
return Zult<uint16_t>(0, "timeout in readProximity");
}
This code works fine, but blocks my main loop until the sensor reading is complete. I need to avoid blocking because I'm also controlling a motor. I wrote an async version that's broken into these functions:
Zult<bool> ProximitySensor::readProximityBegin() {
auto res = setIntMask(0x80);
if (res.isError())
return Zult<uint16_t>(res);
res = write8(VCNL4010_COMMAND, VCNL4010_MEASUREPROXIMITY);
if (res.isError())
return Zult<uint16_t>(res);
asyncStart = millis();
asyncReady = false;
asyncResult = NotReadyZult;
}
bool ProximitySensor::readProximityReady() {
return asyncReady;
}
void ProximitySensor::stopAsync() {
asyncReady = true;
asyncStart = 0;
}
void ProximitySensor::update() {
if (asyncStart != 0) {
auto cmd = read8(VCNL4010_COMMAND);
if (cmd.isError()) {
asyncResult = Zult<uint16_t>(cmd);
stopAsync();
return;
}
if (cmd.getResult() & VCNL4010_PROXIMITYREADY) {
asyncResult = read16(VCNL4010_PROXIMITYDATA);
stopAsync();
return;
}
if (millis() - asyncStart > 1000) {
asyncResult = Zult<uint16_t>(0, "timeout in readProximity");
stopAsync();
}
}
}
Zult<uint16_t> ProximitySensor::readProximityResult() {
return asyncResult;
}
In response to a serial command I invoke readProximityBegin and call update in my main loop until readProximityReady becomes true, at which time I read the value using readProximityResult. As mentioned, as soon as I write to the USB serial port after all this, I get a CPU exception (the error originates from the serial write in the RTL). I'm not sure which exception exactly because in cortex_handlers.c it looks like the arduino code for the SAMD21 routes all sorts of exceptions to this same function:
void Dummy_Handler(void)
{
#if defined DEBUG
__BKPT(3); // this is where my debugger stops
#endif
for (;;) { }
}
Here's an example of one of my I2C functions referenced by the above:
Zult<uint8_t> ProximitySensor::read8(uint8_t command) {
Wire.beginTransmission(_i2caddr);
Wire.write(command);
auto result = Wire.endTransmission();
if (result != 0) {
return Zult<uint8_t>((int)result, "Writing request to read8");
}
delayMicroseconds(170); // delay required
Wire.requestFrom(_i2caddr, (uint8_t)1);
auto start = millis();
while (!Wire.available()) {
if (millis() - start > 1000)
return Zult<uint16_t>(0, "Timeout waiting for read");
}
return Zult<uint8_t>(Wire.read());
}
I am creating this temp monitoring system...Therefore, i want to get messages/alerts when the temperature are below 6 and again when they come back to above 6. Note: I don't want the alert (sendMailNormalValue():wink: to come when the system boots up....How do I deactivate the sendMailNormalValue(); and activate it only when the temp are below 6 (only to alert me when comes back to above 6)..
#include <OneWire.h>
#include <DallasTemperature.h>
// GPIO where the DS18B20 is connected to
const int oneWireBus = 5;
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);
//========================================
float t;
int period = 30000;
unsigned long time_now = 0;
bool DisplayFirstTime = true;
int period1 = 30000;
unsigned long time_now1 = 0;
void sendMailBelowValue(){
}
void sendMailNormalValue(){
}
void setup() {
// put your setup code here, to run once:
Serial.begin (9600);
}
void loop() {
// put your main code here, to run repeatedly:
sensors.requestTemperatures();
t = sensors.getTempCByIndex(0);
float p=t-2;
// Check if any reads failed and exit early (to try again).
if (isnan(t)) {
Serial.println("Failed to read from sensor !");
delay(500);
return;
}
if (t>6){
if(millis() > time_now + period){
time_now = millis();
sendMailNormalValue();
Serial.print ("You got messagae");
}
}
if (t<6){
if(millis() > time_now1 + period1 || DisplayFirstTime){
DisplayFirstTime = false;
time_now = millis();
sendMailBelowValue();
}
}
}
I think I understand what you mean. On error (temp < 6), you want to send an email every 30 seconds; when the teperature reaches 6 or mode, send a single email to say the error condition has been fixed. On boot, only send an email if in error.
If that's it, you'll need to keep track of the error condition using a global flag.
// ...
bool tempError; // initialized in setup()
void setup()
{
// ...
float t;
for(;;) // loop until we get a valid reading.
{
t = sensors.getTempCByIndex(0);
if (!isnan(t))
break;
Serial.println("Failed to read from sensor !");
delay(500);
}
tempError = (t < 6);
}
void loop()
{
// read temp and check sensor...
// ...
if (t < 6)
{
tempError = true;
// send error email, set timer, etc...
}
else if (tempError)
{
// temp is now fine, after being too low.
tempError = false; // Clear error flag.
// send OK email, only once.
// Don't forget to clear the 30 second email timer !!!
}
}
Usually, you'd want some hysteresis in an alert system. You should consider waiting some seconds after the temperature has been 'fixed' before sending the 'OK' email. Otherwise you may end up getting lots of fail/fixed emails when the temperature is around 6 degrees. This is usually done with a simple state machine engine, but that is a bit beyond the scope of this questkon.
#include <DS3231.h>
DS3231 clock;
RTCDateTime dt;
const int INTERRUPT_PIN = 2;
int prevTime, pressedTime, releasedTime, heldTime;
bool settingTimes = false;
int startHour1, startMin1, startHour2, startMin2, endHour1, endMin1, endHour2 = 0;
void setup() {
Serial.begin(9600);
// Initialize DS3231
clock.begin();
clock.setDateTime(__DATE__, __TIME__);
pinMode(INTERRUPT_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), actButtonPress, CHANGE);
}
void loop() {
if (!settingTimes) {
dt = clock.getDateTime();
// For leading zero look to DS3231_dateformat example
Serial.print(dt.hour); Serial.print(":");
Serial.println(dt.minute);
delay(1000);
} else {
//Serial.println("Setting Times, Check the buttons");
}
}
void actButtonPress() {
if (prevTime + 100 < millis()) {
Serial.println("This is being called");
if (digitalRead(INTERRUPT_PIN) == LOW) { //Button has been pressed
pressedTime = millis();
} else {
releasedTime = millis();
heldTime = releasedTime - pressedTime;
if (heldTime > 3000) {
settingTimes = !settingTimes;
} else {
Serial.println("Change Page");
}
}
prevTime = millis();
}
}
I want to be able to press the button for 3 seconds to set times, and anything less times, I want to change pages. I need to use interrupts because the ds3231 get date time function doesn't seem to work without the delay statement (I've already tried the millis() function)
Using interrupts is generally not a good idea for checking button presses. Switch inputs should always be considered as rather dirty signals... The initial contact is never clean-cut, there is always some noise before the signal is clean. That's called "bounce".
To debounce your switch input you should either:
wait a little while using a counter, or a time stamp, (using millis) before checking if the button is released.
or only poll the switches at regular intervals.
The duration of the bounce depends on the switch, and will degrade over time as the switch gets older. In practice, 30 ms is a good value for the delay.
I suggest you try this:
#include <DS3231.h>
DS3231 clock;
RTCDateTime dt;
// putting all switch states in this struct to take advantage of bit-sized
// storage, and save RAM space for the rest of the program.
struct SwitchStates
{
unsigned char action : 1; // state of action switch.
unsigned char sw1 : 1; // add one bit for each switch you have.
};
// you need to keep track of previous switch states to detect changes.
SwitchStates previousSwitchState;
#define ACTION_SWITCH_PIN (2)
#define SWITCH_TIME_SET_DELAY (3000) // time to hold switch for setting time in ms.
#define SWITCH_POLLING_DELAY (30) // switch polling delay, in ms.
#define RTC_READ_DELAY (1000) // RTC read delay in ms.
void setup()
{
Serial.begin(9600);
// Initialize DS3231
clock.begin();
clock.setDateTime(__DATE__, __TIME__); // imho, that's not a very good idea...
pinMode(ACTION_SWITCH_PIN, INPUT_PULLUP);
digitalWrite(ACTION_SWITCH_PIN, HIGH); // you must set the pin high
// for pull-up to work.
}
// only used witin loop()
static unsigned char switchPollingTimeStamp = 0; // char because 30 is less than 256.
static bool settingTimes = false;
static unsigned int rtcReadTimeStamp = 0;
static unsigned int modeSwitchTimeStamp = 0;
void loop()
{
// get current timestamp.
unsigned long now = millis();
if ((unsigned char)now - switchPollingTimeStamp >= SWITCH_POLLING_DELAY)
{
switchPollingTimeStamp = (unsigned char)now;
// all switch inputs will be handled within this block.
SwitchStates curSwitchState;
// polling at regular intervals, first read your inputs.
curSwitchState.action = digitalRead(ACTION_SWITCH_PIN);
// curSwitchState.sw1 = digitalRead(SW1_PIN);
// etc...
// check activity on action (mode) switch and do timing, etc...
if (curSwitchState.action && !previousSwitchState.action)
{
// keep track of how long the switch has been held
modeSwitchTimeStamp = (unsigned int)now;
if (settingTimes)
settingTimes = false; // exit time set mode, for example.
else
Serial.pritln("Next Page"); //
}
else if (curSwitchState.action)
{
// after 3000 ms, switch to timeset mode.
if ((unsigned int)now - modeSwitchTimeStamp >= SWITCH_TIME_SET_DELAY)
{
if (!settingTimes)
{
settingTimes = true:
Serial.println("Entering time set mode");
}
}
}
// That was the most complex one, others should be easier
if (settingTimes)
{
if (curState.sw1 && !previousSwitchState.sw1)
{
// whatever this switch would do in set time state.
}
// etc...
}
else
{
// the same when not in set time state.
}
// to keep track of switch activity.
// by first storing current switch states, then saving them
// like this, you cannot forget one, this avoids any 'I missed one' bugs.
previousSwitchState = curSwitchState;
}
// try to keep your code non blocking, so your device stays responsive...
// in other words, avoid calling delay().
if (!settingTimes && (unsigned int)now - rtcReadTimeStamp >= RTC_READ_DELAY)
{
rtcReadTimeStamp - (unsigned int)millis();
dt = clock.getDateTime();
// For leading zero look to DS3231_dateformat example
Serial.print(dt.hour); Serial.print(":");
Serial.println(dt.minute);
}
}
Note: I don't have an arduino with me, and the laptop I have is not set up for it, so I coud not compile and debug it. It should be very close, though.
I'm facing problem with user input in Arduino ide. I would like arduino to periodically check if user generate some input string (arduino checks it every 3 seconds), but even if there is no input after 3 seconds (string order="") arduino waits for user input and when I type something then it exit "checkIncomingOrder" function and wait another 3 seconds. I would be gratefull for any advice. My simple code is below:
String order = "";
int timer;
void setup() {
Serial.begin(9600);
timer = 0;
}
void loop() {
if (timer == 3)
{
checkIncomingOrder();
}
else
{
delay(1000);
Serial.println("waiting");
timer++;
}
}
void checkIncomingOrder() {
if (Serial.available() > 0) {
order = Serial.readStringUntil('\n');
}
if (order == "") {
return;
} else {
Serial.println("Order is: " + order);
order = "";
timer = 0;
}
}
Serial::Available() does not block. If there are characters available, readStringUntil() will be called, but that will not return until a newline is available - so that is where your blocking on input is likely to be occurring.
The following is perhaps a safer non-blocking solution:
void checkIncomingOrder()
{
char ch = 0 ;
while( ch != '\n' && Serial.available() > 0 )
{
Serial.readBytes( &ch, 1 ) ;
order += ch ;
}
if( ch == '\n' )
{
Serial.println("Order is: " + order) ;
order = "" ;
timer = 0 ;
}
}
It allows you to check for a line of input continuously rather then every three seconds - making the solution more responsive:
void loop()
{
checkIncomingOrder();
}
allowing in turn the timer variable to be removed.
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.