diff --git a/.vscode/arduino.json b/.vscode/arduino.json index f8d5ce0..1b660b5 100644 --- a/.vscode/arduino.json +++ b/.vscode/arduino.json @@ -1,6 +1,6 @@ { "board": "arduino:avr:uno", "programmer": "avrisp", - "port": "COM9", + "port": "COM10", "sketch": "transmitter\\transmitter.ino" } \ No newline at end of file diff --git a/README.md b/README.md index 00b4c53..184953d 100644 --- a/README.md +++ b/README.md @@ -46,12 +46,12 @@ To set up and run this project locally, follow these steps: - It must be able to go staright and reverse, has RFID reader on the bottom, has some kind of arm and storage to pick and carry items and has to facilitate enough space for Raspberry Pi, Arduino and batteries. 1. **Clone the repository**: `git clone https://github.com/your-username/order-fulfillment.git` 2. **Front end side**: - - The front-end for this project has been developed and deployed. You can find detailed instructions and information about the front-end deployment in this repository: [Front-end Repository](https://github.com/muradtaghiyev05/pam-project). Please follow the instructions provided in the front-end repository for setting up, deploying, and using the front-end interface of this system. After deployment you can set custom URL to your site. + - The front-end for this project has been developed and deployed. You can find detailed instructions and information about the front-end deployment in this repository: [Front-end Repository](https://github.com/elshadsabziyev/ShopBot-Webpage). (Full credit to team member [Murad Taghiyev](https://github.com/muradtaghiyev05)) Please follow the instructions provided in the front-end repository for setting up, deploying, and using the front-end interface of this system. After deployment you can set custom URL to your site. - (OPTIONAL) You can develop your own front-end, connect it to any database, make required changes to `main.py` depending on this database and deploy the site. 3. **Configure database credentials**: - Update the `CREDENTIALS` dictionary in the Python script with your database details. 4. **Install necessary dependencies on Raspberry Pi and your Machine**: - - Python packages - Raspberryy Pi: `pip install -r requirements.txt` + - Python packages - Raspberry Pi: `pip install -r requirements.txt` - Arduino libraries - Your Machine/Raspberry Pi: Refer to the list of libraries used in the Arduino sketch and install them using the Arduino IDE's Library Manager. 5. **Connect the Arduino**: - Connect it to the specified port (change `PORT` variable if necessary) and upload the Arduino sketch to the board. @@ -60,7 +60,7 @@ To set up and run this project locally, follow these steps: ## 🔧 Usage -1. Users place orders via the website [https://pam-project.vercel.app/](https://pam-project.vercel.app) associating each item with a unique RFID code. +1. Users place orders via the website [https://pam-project.vercel.app/](https://shopbot-site.vercel.app) associating each item with a unique RFID code. (URL could be anything you set, for more info see : [⚙️ Installation](#installation) - 3) 3. Run the Python script `main.py` to start monitoring the database for new orders. 4. Upon detecting new orders, the Python script triggers the Arduino bot, which navigates shelves and picks items based on RFID matches. @@ -84,10 +84,10 @@ To set up and run this project locally, follow these steps: - **Supervisor (BHOS Lecturer - Robotics)** : [Aydin Nasirzade](https://github.com/supervisor) - **Hardware (Arduino - Sensors)**: [Arif Najafov](https://github.com/hardware-lead) -- **Hardware Assistant (Construction - Mechanics)**: [Nijat Aliyev](https://github.com/hardware-assistant) -- **Sofware Project Lead (Arduino - Python)**: [Elshad Sabziyev](https://github.com/sabziyevelshad) +- **Hardware Assistant (Construction - Mechanics)**: [Nijat Aliyev](https://github.com/nicataie) +- **Software (Arduino - Python)**: [Elshad Sabziyev](https://github.com/sabziyevelshad) - **Front-end Developer**: [Murad Taghiyev](https://github.com/muradtaghiyev05) -- **AI/ML Expert (Planned for future development)**: [Gurban Guliyev](https://github.com/ai-ml-expert) +- **AI/ML Expert (Planned for future development)**: [Gurban Guliyev](https://github.com/QuriMAY) ## 🛣️ Roadmap @@ -104,8 +104,8 @@ To set up and run this project locally, follow these steps: - Add visual indicators for order statuses and item picking progress. - **🔐 QR Code Token Generation for Pickup Verification**: - - Generate unique QR code tokens for each order placed, containing the site URL and a special token. - - Allow customers to access their unique QR codes through the order confirmation page for in-store pickup verification. + - Verification of User Presence in Store: The QR code, when scanned with the token, verifies that the user is physically present in the store. + - Identification of Location: It also identifies the specific location within the store where the scanning occurred. ### 🚀 Version 1.2.0 diff --git a/itemlist_retriever.py b/itemlist_retriever.py index 3d9a22c..0e393f9 100644 --- a/itemlist_retriever.py +++ b/itemlist_retriever.py @@ -9,6 +9,7 @@ 9600 # baudrate of the serial connection to the arduino, change this if needed ) + # used to store the previous items that were retrieved from the database # this is used to check if the items have changed during the next iteration of the loop prev_items = None @@ -51,27 +52,28 @@ def find_ARDUINO_serial_port(): try: port = "COM" + str(i) ser = Serial(port) - ser.close() return port except: pass elif os_name == "posix": - for port in range(0, 256): - try: - port = "/dev/ttyUSB" + str(port) - ser = Serial(port) - ser.close() - return port - except: - pass + for port_num in range(0, 256): + for port_prefix in ["/dev/ttyUSB", "/dev/ttyACM"]: + try: + port = port_prefix + str(port_num) + ser = Serial(port) + ser.close() + return port + except: + print("OS: " + os_name + " Port: " + port + " not found\nTrying next port...") + else: raise Exception("Unknown OS") def read(self): try: return self.ser.readline().decode().strip() - except: - return ">>>>> ERROR - Serial read failed - possible cause: serial port not connected" + except Exception as e: + print(e) def close(self): self.ser.close() @@ -98,11 +100,15 @@ def main(): ser.write(to_write) else: print("Items not changed") - print("Reading from serial... :" + ser.read()) + try: + print("Reading from serial... :" + ser.read()) + except: + print("Error reading from serial") + iter_cnt += 1 time.sleep(0.1) if __name__ == "__main__": main() - # test() \ No newline at end of file + # test() diff --git a/test_itemlist_retriever.py b/test_itemlist_retriever.py deleted file mode 100644 index 469cf0d..0000000 --- a/test_itemlist_retriever.py +++ /dev/null @@ -1,108 +0,0 @@ -import unittest -from unittest.mock import patch -from itemlist_retriever import DatabaseConnection, SerialConnection - - -class TestDatabaseConnection(unittest.TestCase): - def setUp(self): - self.credentials = { - "host": "localhost", - "user": "root", - "password": "password", - "database": "test_db", - } - self.db = DatabaseConnection(self.credentials) - - def test_fetch_all_items(self): - expected_items = {1: ["item1", "item2"], 2: ["item3", "item4"]} - with patch.object(self.db.conn, "cursor") as mock_cursor: - mock_cursor.return_value.fetchall.return_value = [ - (1, '["item1", "item2"]'), - (2, '["item3", "item4"]'), - ] - items_dict = self.db.fetch_all_items() - self.assertEqual(items_dict, expected_items) - - -class TestSerialConnection(unittest.TestCase): - def setUp(self): - self.baudrate = 9600 - self.ser = SerialConnection(self.baudrate) - - def test_write(self): - with patch.object(self.ser.ser, "write") as mock_write: - data = "test data" - self.ser.write(data) - mock_write.assert_called_once_with(data.encode()) - - def test_read(self): - with patch.object(self.ser.ser, "readline") as mock_readline: - mock_readline.return_value.decode.return_value = "test" - result = self.ser.read() - self.assertEqual(result, "test") - - def test_close(self): - with patch.object(self.ser.ser, "close") as mock_close: - self.ser.close() - mock_close.assert_called_once() - - -if __name__ == "__main__": - unittest.main() -import unittest -from unittest.mock import patch -from itemlist_retriever import DatabaseConnection, SerialConnection, main - - -class TestDatabaseConnection(unittest.TestCase): - def setUp(self): - self.credentials = { - "host": "localhost", - "user": "root", - "password": "password", - "database": "test_db", - } - self.db = DatabaseConnection(self.credentials) - - def test_fetch_all_items(self): - expected_items = {1: ["item1", "item2"], 2: ["item3", "item4"]} - with patch.object(self.db.conn, "cursor") as mock_cursor: - mock_cursor.return_value.fetchall.return_value = [ - (1, '["item1", "item2"]'), - (2, '["item3", "item4"]'), - ] - items_dict = self.db.fetch_all_items() - self.assertEqual(items_dict, expected_items) - - -class TestSerialConnection(unittest.TestCase): - def setUp(self): - self.baudrate = 9600 - self.ser = SerialConnection(self.baudrate) - - def test_write(self): - with patch.object(self.ser.ser, "write") as mock_write: - data = "test data" - self.ser.write(data) - mock_write.assert_called_once_with(data.encode()) - - def test_read(self): - with patch.object(self.ser.ser, "readline") as mock_readline: - mock_readline.return_value.decode.return_value = "test" - result = self.ser.read() - self.assertEqual(result, "test") - - def test_close(self): - with patch.object(self.ser.ser, "close") as mock_close: - self.ser.close() - mock_close.assert_called_once() - - -class TestMain(unittest.TestCase): - def test_main(self): - # TODO: Add your test case for the main function here - pass - - -if __name__ == "__main__": - unittest.main() diff --git a/transmitter/transmitter.ino b/transmitter/transmitter.ino index 6cbcbb3..47a3c4d 100644 --- a/transmitter/transmitter.ino +++ b/transmitter/transmitter.ino @@ -19,21 +19,22 @@ String previousUID = "0"; // Do not change // String previousRFIDUID = "0"; // Do not change // String fromSerialUID = "0"; // Do not change // -String fromRFIDUID = "0"; // Do not change // -String readSerialFlag = "0"; // Do not change // -int printCounter = 0; // Do not change // -int pickUpTimeout = 1500; // Set timeout for picking up item -int serialBaudRate = 9600; // Set serial baud rate -int optimizationDelay = 50; // Fine tune this value to optimize performance -int firstMainLoop = 1; // Do not change // -Servo myServo; // Do not change // -MFRC522 card(10, 9); // SDA, RST set up for MEGA -int motor1pin1 = 2; // pin 2 on L293D -int motor1pin2 = 4; // pin 7 on L293D -int motor2pin1 = 7; // pin 10 on L293D -int motor2pin2 = 8; // pin 15 on L293D -int enable1pin = 5; // pin 1 on L293D -int enable2pin = 6; // pin 9 on L293D +String stopRFID = "4475a389"; +String fromRFIDUID = "0"; // Do not change // +String readSerialFlag = "0"; // Do not change // +int printCounter = 0; // Do not change // +int pickUpTimeout = 1500; // Set timeout for picking up item +int serialBaudRate = 9600; // Set serial baud rate +int optimizationDelay = 50; // Fine tune this value to optimize performance +int firstMainLoop = 1; // Do not change // +Servo myServo; // Do not change // +MFRC522 card(10, 9); // SDA, RST set up for MEGA +int motor1pin1 = 2; // pin 2 on L293D +int motor1pin2 = 4; // pin 7 on L293D +int motor2pin1 = 7; // pin 10 on L293D +int motor2pin2 = 8; // pin 15 on L293D +int enable1pin = 5; // pin 1 on L293D +int enable2pin = 6; // pin 9 on L293D void setup() { @@ -48,12 +49,13 @@ void setup() pinMode(motor2pin2, OUTPUT); pinMode(enable1pin, OUTPUT); pinMode(enable2pin, OUTPUT); - analogWrite(enable1pin, 150); - analogWrite(enable2pin, 150); + analogWrite(enable1pin, 200); + analogWrite(enable2pin, 200); } void loop() { + // stop if no RFID is present from DB if (firstMainLoop == 1) { makeStartupNoiseUsingServo(); @@ -69,6 +71,19 @@ void loop() moveForward(); } } + if (readSerialFlag == "2") + { + while (readRFIDUID() != stopRFID) + { + moveBackward(); + } + digitalWrite(motor1pin1, LOW); + digitalWrite(motor1pin2, LOW); + digitalWrite(motor2pin1, LOW); + digitalWrite(motor2pin2, LOW); + delay(1000); + readSerialFlag = "0"; + } delay(optimizationDelay); fromRFIDUID = readRFIDUID(); delay(optimizationDelay); @@ -79,10 +94,9 @@ void loop() pickItem(); delay(pickUpTimeout); // set flag to 0 - readSerialFlag = "0"; + readSerialFlag = "2"; previousUID = "0"; previousRFIDUID = "0"; - Serial.println("Item picked and ready to get another RFID"); } // if there is a new RFID from DB and RFID from RFID reader are not the same else if (fromSerialUID == "0") @@ -173,22 +187,31 @@ void moveForward() digitalWrite(motor2pin2, LOW); } +void moveBackward() +{ + digitalWrite(motor1pin1, LOW); + digitalWrite(motor1pin2, HIGH); + digitalWrite(motor2pin1, LOW); + digitalWrite(motor2pin2, HIGH); +} + void pickItem() { digitalWrite(motor1pin1, LOW); digitalWrite(motor1pin2, LOW); digitalWrite(motor2pin1, LOW); digitalWrite(motor2pin2, LOW); - delay(4000); - for (int i = 0; i <= 150; i += 45) + delay(2000); + for (int i = 0; i <= 120; i += 30) { myServo.write(i); - delay(50); + delay(100); } - for (int i = 150; i >= 0; i -= 45) + delay(2000); + for (int i = 120; i >= 0; i -= 30) { myServo.write(i); - delay(50); + delay(100); } digitalWrite(motor1pin1, LOW); digitalWrite(motor1pin2, HIGH); @@ -207,3 +230,11 @@ void makeStartupNoiseUsingServo() myServo.write(0); delay(100); } + +void stop() +{ + digitalWrite(motor1pin1, LOW); + digitalWrite(motor1pin2, LOW); + digitalWrite(motor2pin1, LOW); + digitalWrite(motor2pin2, LOW); +} \ No newline at end of file