I am a beginner and I need some help/guidance! So basically I am trying to make an attendance system using Raspberry Pi 4 and RFID reader and writer. So far, everything is working well but I am struggling with sending data for sign_out. For now, the same timestamp that is in sign_in get marked in signed_out in the database also, but here I would like to make that when the RFID card is read for the 2nd time the data get filled in sign_out column. If you have any other suggestion I will happy to hear/learn, thanks. EDIT: "the whole code was deleted from this question"
-
Currently I don't understand the database structure and how sign out should be recorded in the database. Is this your question? You should then show the SQL commands for table creation (as properly formatted text in the question). – Michael Butscher Apr 25 '20 at 16:18
-
@MichaelButscher the database name is attendancesystem and has a table named attendance which consists of id and rfid_id and sign_in as timestamp and sign_out as timestamp. SO far, when I put the rfid card on the reader it does register the time and it fills the sign_in and sign_out with the same time – ij817 Apr 25 '20 at 16:28
-
@MichaelButscher I am trying to find how I can implement a code that will recognize when the rfid card is read for the 1st time and 2nd time, or a code that shows that sign_in is filled and next time the rfid card is read the data will be filled in sign_out ! – ij817 Apr 25 '20 at 16:30
-
It seems the timestamp recording for "sign_in" isn't part of the shown code. Is it implemented in the database logic? – Michael Butscher Apr 25 '20 at 16:33
-
@MichaelButscher it is apparently, the answer of Dragos Dumitrache did help somehow but once it says sign out iut never goes to sign in and the data in the database is messy – ij817 Apr 25 '20 at 17:29
-
You should modify the table creation code (or alter the table) so that `sign_out` is not filled in automatically by the database. You can then check if the newest entry for a user id has already a `sign_out` timestamp and fill it in or (on a sign in) create a new entry. – Michael Butscher Apr 26 '20 at 07:55
1 Answers
Have you considered treating sign-in/sign-out as a switch? The default state is sign-in
set to 0 or false, sign-out
set to 1 or true. Then, when the card is read, it checks the value of sign-in. If it's 0/false it gets flipped to 1/true, and does the opposite flip to sign-out. When the card is read the second time, it reads sign-out and if 0/false, it sets it to 1/true, and does the opposite to sign-in.
For clarity:
current state:
sign-in = 0
sign-out = 1
---> card is read for the first time
sign-in = (sign-in + 1) % 2
sign-out = (sign-out + 1) %2
state now:
sign-in = 1
sign-out = 0
---> card is read for the second time, consider previous state
sign-in = (sign-i + 1) % 2
sign-out = (sign-out + 1) % 2
state:
sign-in = 0
sign-out = 1
This could all be simplified further by reducing the 2 states to a single one, signed-in
.
initial state:
signed-in = 0
---> card is read for entry
signed-in = (signed-in + 1) % 2
state:
signed-in = 1
---> card is read for exit
signed-out = (signed-in + 1) % 2
state:
signed-in = 0
Simply put, you want to consider this as a simple state machine with two transitions: from In
to Out
and from Out
to In
Does this help point you in the right direction?
Timestamp question
So, it seems you initialize the timestamp once, before entering the loop. Try moving
ts = time.time()
timestamp = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S'
after the following line:
id, text = reader.read()
Does that solve your problem?
General style comments
You could simplify the following:
if sign_in == 0:
sign_in = (sign_in + 1) % 2
sign_out = (sign_out + 1) % 2
#id, text = reader.read()
cursor.execute("INSERT INTO attendance (user_id, clock_in) VALUES (%s, %s)", (result[0], timestamp,) )
lcd.lcd_display_string("Sign in " + result[1])
elif sign_in == 1:
sign_out = (sign_out + 1) % 2
sign_in = (sign_in + 1) % 2
#id, text = reader.read()
cursor.execute("INSERT INTO attendance (user_id, clock_out) VALUES (%s, %s)", (result[0], timestamp,) )
lcd.lcd_display_string("Sign out " + result[1])
to something more like
sign_in = (sign_in + 1) % 2
sign_out = (sign_out + 1) % 2
#id, text = reader.read()
field_in_or_out = 'in' if sign_in == 1 else 'out'
cursor.execute(f"INSERT INTO attendance (user_id, clock_{field_in_or_out}) VALUES (%s, %s)", (result[0], timestamp,) )
lcd.lcd_display_string(f"Sign {field_in_or_out} " + result[1])
How I would simplify the logic further
#!/usr/bin/env python
import time
import datetime
import RPi.GPIO as GPIO
from mfrc522 import SimpleMFRC522
import mysql.connector
import I2C_LCD_driver
db = mysql.connector.connect(
host="localhost",
user="admin",
passwd="*******",
database="attendancesystem"
)
cursor = db.cursor()
reader = SimpleMFRC522()
lcd = I2C_LCD_driver.lcd()
#redLED = 4
#yellowLED = 17
#greenLED = 27
#GPIO.setmode(GPIO.BCM)
signed_in = 0
try:
while True:
lcd.lcd_clear()
lcd.lcd_display_string('Place Card to')
lcd.lcd_display_string('record attendance', 2)
id, text = reader.read()
ts = time.time()
timestamp = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
cursor.execute("Select id, name FROM users WHERE rfid_uid="+str(id))
result = cursor.fetchone()
lcd.lcd_clear()
if cursor.rowcount >= 1:
lcd.lcd_display_string("Welcome")
lcd.lcd_display_string(""+ result[1], 2)
#GPIO.output(greenLED,GPIO.HIGH)
#time.sleep(3)
#GPIO.output(greenLED,GPIO.LOW)
cursor.execute("INSERT INTO attendance (user_id) VALUES (%s)", (result[0],) )
signed_in = (signed_in + 1) % 2
#id, text = reader.read()
cursor.execute(f"INSERT INTO attendance (user_id, read_at) VALUES (%s, %s)", (result[0], timestamp,) )
lcd.lcd_display_string(f"Card read " + result[1])
db.commit()
else:
lcd.lcd_display_string("User does not")
lcd.lcd_display_string("exist !!", 2)
#GPIO.output(yellowLED,GPIO.HIGH)
#time.sleep(3)
#GPIO.output(yellowLED,GPIO.LOW)
time.sleep(2)
finally:
GPIO.cleanup()
And update the database schema to only have a user_id
and a read_at
time column for when the card was read. Change your sign_in
/sign_out
logic to only have a signed_in
field that expresses both states, 1 for when the user is signed in, 0 for when it's not.
The other change in the code is pulling the timestamp logic into the while
loop.
Then, your expected database will have a line for each time the card was touched to the reader, with the time of the event, and you won't have to update an already existing entry.

- 2,552
- 13
- 21
-
It works but once it gives sign out it doesn't go anymore to sign in ?? is it because it needs to be in a loop? – ij817 Apr 25 '20 at 17:26
-
It should be abstracted away to a `read_card_access()` method that you invoke whenever the card is read. If you're modelling that inside a loop, then the call to `read_card_access` would need to occur in the loop as well – afterburner Apr 25 '20 at 17:48
-
1Your first method seemed to be working for sign in and out, now I only have a problem with the insert the sign_out time into the database it does it in a weird way, check the question again I'll modify it ! Thanks for your help, I appreciate it ! – ij817 Apr 26 '20 at 11:59
-
Okay, so, I can't really figure out where the timestamp comes from. Did you update the code as well? And to clarify, the issue is that when the sign-in is read, that timestamp gets filled in to both the sign-in timestamp, and the sign-out timestamp? – afterburner Apr 26 '20 at 12:04
-
I updated the part where I wrote the code now and uploaded a photo of the database, check it out . I will be waiting for your reply, thanks ! – ij817 Apr 26 '20 at 12:09
-
1Here is where from timestamp in the code came from : ts = time.time() / timestamp = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') – ij817 Apr 26 '20 at 12:10
-
You might want to put the timestamp code in the original question code snippet for when other people look at your question, makes it easier to understand. – afterburner Apr 26 '20 at 12:12
-
-
That's a lot easier, thanks. Do my changes make sense, and do they solve the issue? – afterburner Apr 26 '20 at 12:19
-
I copied the code that you suggested to simplify.. the sign in and out in the LCD seemed to be working but when it comes to sending the data of in and out to the database it somehow get mixed like before, do you have any idea what can be the problem causing that ? – ij817 Apr 26 '20 at 12:42
-
Check my question again, I'll upload a photo of the database how it looks now – ij817 Apr 26 '20 at 12:42
-
Can you print the timestamp before sending it to the database? And let me know what timestamps you're getting. – afterburner Apr 26 '20 at 12:47
-
-
Okay, one more question, since I'm not familiar with MariaDB. Each line represents a single card read event, right? So each 2 lines represent a sign-in/sign-out? Or do you want to update the last read entry for a user to add a sign-out timestamp if not present? – afterburner Apr 26 '20 at 12:57
-
SO basically it never fills the sign_out time with the real time. Example: I put the card for sign in it does records the sign in time in two line but then when I put it for sign out it records the sign out time in a new line of sign in then another line in both sign in and sign out. WHat I would like to have is to record the sign in and out in the same line then next time sign in it records it in a new line?.. – ij817 Apr 26 '20 at 13:06
-
I will put a 3rd picture in my question of database of only one time of sign in and out how it looks like.. "Again thanks a lot of your help" – ij817 Apr 26 '20 at 13:09
-
To have it all in one line, you might want to look into updating an entry in MariaDB. – afterburner Apr 26 '20 at 13:35
-
I've amended my answer with some more of my thoughts on the matter, hope it helps. Not really sure why the fields are getting mixed in your current implementation. – afterburner Apr 26 '20 at 13:42
-
I tried your code with a new table but it always have the same problem, it duplicates the lines. For my case I need both sign in and out because in the end I need to calculate total hours for the user – ij817 Apr 26 '20 at 14:05
-
I'm afraid I'm out of ideas then. There would be ways of storing a login/logout timestamp in Python and doing the maths there. Furthermore, if you want this to work for more users, you will probably need to enhance the sign-in/sign-out logic to take that into account. Sorry I couldn't help more, but if something comes to me, I'll get back to you – afterburner Apr 26 '20 at 14:08
-
1ALTER IGNORE TABLE attendance ADD UNIQUE INDEX u(clock_in); --> This deleted for me the duplicated lines. Anyway, I thank you so much for all the help you offered in case you came up with something I will be here to read it! Have a nice spring – ij817 Apr 26 '20 at 14:12