├── CHANGES.txt ├── README.md ├── examples ├── basic_insert │ └── basic_insert.ino ├── basic_insert_esp8266 │ └── basic_insert_esp8266.ino ├── basic_select │ └── basic_select.ino ├── complex_insert │ └── complex_insert.ino ├── complex_select │ └── complex_select.ino ├── connect │ └── connect.ino ├── connect_by_hostname │ └── connect_by_hostname.ino ├── connect_default_database │ └── connect_default_database.ino ├── connect_disconnect │ └── connect_disconnect.ino ├── connect_wifi │ └── connect_wifi.ino ├── connect_wifi_101 │ └── connect_wifi_101.ino ├── query_progmem │ └── query_progmem.ino ├── query_results │ └── query_results.ino └── reboot │ └── reboot.ino ├── extras └── Reference_Manual.txt ├── keywords.txt ├── library.properties └── src ├── MySQL_Connection.cpp ├── MySQL_Connection.h ├── MySQL_Cursor.cpp ├── MySQL_Cursor.h ├── MySQL_Encrypt_Sha1.cpp ├── MySQL_Encrypt_Sha1.h ├── MySQL_Packet.cpp └── MySQL_Packet.h /CHANGES.txt: -------------------------------------------------------------------------------- 1 | CHANGES 2 | ======= 3 | This file contains a brief summary of changes made from previous versions of 4 | the connector. 5 | 6 | 1.2.0 - March 2020 7 | ------------------ 8 | * Added connect with default database. 9 | * Added rows effected, last insert id. 10 | * Stability and speed improvements. 11 | * Removed infinite wait in read_packet() if connection dropped. 12 | Merged wait_for_client() and wait_for_data() into one 13 | universal wait_for_bytes(int). 14 | * Improved connect() speed. 15 | * Improved sending queries speed. 16 | Sending the whole buffer at a time (not byte by byte). 17 | * Fixed bug with check_ok_packet() return value. 18 | check_ok_packet() simplified to get_packet_type(). 19 | * Added checks for buffer validity in places it is going to be used. 20 | Improved stability on "incorrect" methods calls. 21 | * Restored WITH_SELECT, but defined by default. 22 | * Added DEBUG define to avoid prints to Serial port. 23 | Useful when Serial is used to communicate with sensors. 24 | * Added example for ESP8266. 25 | 26 | 1.1.1a - January 2016 27 | --------------------- 28 | * Minor issue with deprecated #import fixed. No new functionality. 29 | 30 | 1.1.0a - January 2016 31 | --------------------- 32 | * Major redesign of classes. Now have separate connection and cursor classes 33 | * Removed WITH_SELECT, WITH_WIFI, etc. to avoid confusion and simplify 34 | * Renamed many methods to correspond with traditional MySQL connector 35 | nomenclature 36 | * Added more error handling for lossy networks 37 | * Memory deallocation methods consolidated (see examples) 38 | * More, smaller examples added 39 | * Documentation updated 40 | 41 | 1.0.4ga - July 2015 42 | -------------------- 43 | * Fixed a defect in the get_next_row() method. 44 | * Added the reference manual. Yippee! 45 | 46 | 1.0.3rc - March 2015 47 | -------------------- 48 | * Code has been changed slightly to help with long latency issues over 49 | wifi and slow connections. 50 | * A new cleanup method was added to cleanup a final OK packet after a 51 | stored procedure call with a result. 52 | * Code now compiles without errors for the latest Beta Arduino IDE 53 | (const error). 54 | 55 | 1.0.2b - April 2014 56 | ------------------- 57 | * The WITH_SELECT is turned *OFF* by default. If you want to use select 58 | queries, be sure to uncomment this in the mysql.h file. 59 | * Reduced memory use! The library has been trimmed to save memory. 60 | - All static strings moved to PROGMEM strings 61 | - Unused structures removed (e.g. ok_packet) 62 | - Moved more methods not needed to optional compilation 63 | 64 | 1.0.1b - February 2014 65 | ---------------------- 66 | * Added disconnect() method for disconnecting from server. 67 | * Improved error handling for dropped packets. 68 | * Better error handling for lost connections and out of memory detection. 69 | 70 | 1.0.0b - October 2013 71 | --------------------- 72 | * Improved support for result sets (select queries) 73 | * Added conditional compile for use with select queries. If you don't use 74 | select queries, comment out WITH_SELECT in mysql.h to save some memory. 75 | * Added support for WiFi shield using conditional compilation. 76 | * New version() method to check version of the connector. 77 | * Simplified, single-file download 78 | 79 | Initial Release - April 2013 80 | ---------------------------- 81 | * Basic query capability 82 | * Basic result set handling 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MySQL Connector/Arduino 2 | ======================= 3 | Have you ever wanted to connect your Arduino project to a database to either store the data you've collected or retrieve data saved to trigger events in your sketch? Well, now you can connect your Arduino project directly to a MySQL server without using an intermediate computer or a web- or cloud-based service. Having direct access to a database server means you can store data acquired from your project as well as check values stored in tables on the server. 4 | 5 | This also means you can setup your own, local MySQL server to store your data further removing the need for Internet connectivity. If that is not an issue, you can still connect to and store data on a MySQL server via your network, Internet, or even in the cloud! 6 | 7 | The MySQL Connector/Arduino is a library that permits you to do exactly that and more! 8 | 9 | See the all new [wiki documentation](https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki) for how to get starting using the library. 10 | -------------------------------------------------------------------------------- /examples/basic_insert/basic_insert.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Connector/Arduino Example : basic insert 3 | 4 | This example demonstrates how to issue an INSERT query to store data in a 5 | table. For this, we will create a special database and table for testing. 6 | The following are the SQL commands you will need to run in order to setup 7 | your database for running this sketch. 8 | 9 | CREATE DATABASE test_arduino; 10 | CREATE TABLE test_arduino.hello_arduino ( 11 | num integer primary key auto_increment, 12 | message char(40), 13 | recorded timestamp 14 | ); 15 | 16 | Here we see one database and a table with three fields; a primary key that 17 | is an auto_increment, a string, and a timestamp. This will demonstrate how 18 | to save a date and time of when the row was inserted, which can help you 19 | determine when data was recorded or updated. 20 | 21 | For more information and documentation, visit the wiki: 22 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 23 | 24 | INSTRUCTIONS FOR USE 25 | 26 | 1) Create the database and table as shown above. 27 | 2) Change the address of the server to the IP address of the MySQL server 28 | 3) Change the user and password to a valid MySQL user and password 29 | 4) Connect a USB cable to your Arduino 30 | 5) Select the correct board and port 31 | 6) Compile and upload the sketch to your Arduino 32 | 7) Once uploaded, open Serial Monitor (use 115200 speed) and observe 33 | 8) After the sketch has run for some time, open a mysql client and issue 34 | the command: "SELECT * FROM test_arduino.hello_arduino" to see the data 35 | recorded. Note the field values and how the database handles both the 36 | auto_increment and timestamp fields for us. You can clear the data with 37 | "DELETE FROM test_arduino.hello_arduino". 38 | 39 | Note: The MAC address can be anything so long as it is unique on your network. 40 | 41 | Created by: Dr. Charles A. Bell 42 | */ 43 | #include 44 | #include 45 | #include 46 | 47 | byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 48 | 49 | IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here 50 | char user[] = "root"; // MySQL user login username 51 | char password[] = "secret"; // MySQL user login password 52 | 53 | // Sample query 54 | char INSERT_SQL[] = "INSERT INTO test_arduino.hello_arduino (message) VALUES ('Hello, Arduino!')"; 55 | 56 | EthernetClient client; 57 | MySQL_Connection conn((Client *)&client); 58 | 59 | void setup() { 60 | Serial.begin(115200); 61 | while (!Serial); // wait for serial port to connect 62 | Ethernet.begin(mac_addr); 63 | Serial.println("Connecting..."); 64 | if (conn.connect(server_addr, 3306, user, password)) { 65 | delay(1000); 66 | } 67 | else 68 | Serial.println("Connection failed."); 69 | } 70 | 71 | 72 | void loop() { 73 | delay(2000); 74 | 75 | Serial.println("Recording data."); 76 | 77 | // Initiate the query class instance 78 | MySQL_Cursor *cur_mem = new MySQL_Cursor(&conn); 79 | // Execute the query 80 | cur_mem->execute(INSERT_SQL); 81 | // Note: since there are no results, we do not need to read any data 82 | // Deleting the cursor also frees up memory used 83 | delete cur_mem; 84 | } 85 | -------------------------------------------------------------------------------- /examples/basic_insert_esp8266/basic_insert_esp8266.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Connector/Arduino Example : connect by wifi 3 | 4 | This example demonstrates how to connect to a MySQL server from an 5 | Arduino using an Arduino-compatible Wifi shield. Note that "compatible" 6 | means it must conform to the Ethernet class library or be a derivative 7 | with the same classes and methods. 8 | 9 | For more information and documentation, visit the wiki: 10 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 11 | 12 | INSTRUCTIONS FOR USE 13 | 14 | 1) Change the address of the server to the IP address of the MySQL server 15 | 2) Change the user and password to a valid MySQL user and password 16 | 3) Change the SSID and pass to match your WiFi network 17 | 4) Connect a USB cable to your Arduino 18 | 5) Select the correct board and port 19 | 6) Compile and upload the sketch to your Arduino 20 | 7) Once uploaded, open Serial Monitor (use 115200 speed) and observe 21 | 22 | If you do not see messages indicating you have a connection, refer to the 23 | manual for troubleshooting tips. The most common issues are the server is 24 | not accessible from the network or the user name and password is incorrect. 25 | 26 | Created by: Dr. Charles A. Bell 27 | */ 28 | #include // Use this for WiFi instead of Ethernet.h 29 | #include 30 | #include 31 | 32 | IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here 33 | char user[] = "root"; // MySQL user login username 34 | char password[] = "secret"; // MySQL user login password 35 | 36 | // Sample query 37 | char INSERT_SQL[] = "INSERT INTO test_arduino.hello_arduino (message) VALUES ('Hello, Arduino!')"; 38 | 39 | // WiFi card example 40 | char ssid[] = "your-ssid"; // your SSID 41 | char pass[] = "ssid-password"; // your SSID Password 42 | 43 | WiFiClient client; // Use this for WiFi instead of EthernetClient 44 | MySQL_Connection conn(&client); 45 | MySQL_Cursor* cursor; 46 | 47 | void setup() 48 | { 49 | Serial.begin(115200); 50 | while (!Serial); // wait for serial port to connect. Needed for Leonardo only 51 | 52 | // Begin WiFi section 53 | Serial.printf("\nConnecting to %s", ssid); 54 | WiFi.begin(ssid, pass); 55 | while (WiFi.status() != WL_CONNECTED) { 56 | delay(500); 57 | Serial.print("."); 58 | } 59 | 60 | // print out info about the connection: 61 | Serial.println("\nConnected to network"); 62 | Serial.print("My IP address is: "); 63 | Serial.println(WiFi.localIP()); 64 | 65 | Serial.print("Connecting to SQL... "); 66 | if (conn.connect(server_addr, 3306, user, password)) 67 | Serial.println("OK."); 68 | else 69 | Serial.println("FAILED."); 70 | 71 | // create MySQL cursor object 72 | cursor = new MySQL_Cursor(&conn); 73 | } 74 | 75 | void loop() 76 | { 77 | if (conn.connected()) 78 | cursor->execute(INSERT_SQL); 79 | 80 | delay(5000); 81 | } 82 | -------------------------------------------------------------------------------- /examples/basic_select/basic_select.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Connector/Arduino Example : basic select 3 | 4 | This example demonstrates how to issue a SELECT query with no parameters 5 | and use the data returned. For this, we use the Cursor class to execute 6 | the query and get the results. 7 | 8 | It demonstrates who methods for running queries. The first allows you to 9 | allocate memory for the cursor and later reclaim it, the second shows how 10 | to use a single instance of the cursor use throughout a sketch. 11 | 12 | NOTICE: You must download and install the World sample database to run 13 | this sketch unaltered. See http://dev.mysql.com/doc/index-other.html. 14 | 15 | CAUTION: Don't mix and match the examples. Use one or the other in your 16 | own sketch -- you'll get compilation errors at the least. 17 | 18 | For more information and documentation, visit the wiki: 19 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 20 | 21 | INSTRUCTIONS FOR USE 22 | 23 | 1) Change the address of the server to the IP address of the MySQL server 24 | 2) Change the user and password to a valid MySQL user and password 25 | 3) Connect a USB cable to your Arduino 26 | 4) Select the correct board and port 27 | 5) Compile and upload the sketch to your Arduino 28 | 6) Once uploaded, open Serial Monitor (use 115200 speed) and observe 29 | 30 | Note: The MAC address can be anything so long as it is unique on your network. 31 | 32 | Created by: Dr. Charles A. Bell 33 | */ 34 | #include 35 | #include 36 | #include 37 | 38 | byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 39 | 40 | IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here 41 | char user[] = "root"; // MySQL user login username 42 | char password[] = "secret"; // MySQL user login password 43 | 44 | // Sample query 45 | char query[] = "SELECT population FROM world.city WHERE name = 'New York'"; 46 | 47 | EthernetClient client; 48 | MySQL_Connection conn((Client *)&client); 49 | // Create an instance of the cursor passing in the connection 50 | MySQL_Cursor cur = MySQL_Cursor(&conn); 51 | 52 | void setup() { 53 | Serial.begin(115200); 54 | while (!Serial); // wait for serial port to connect 55 | Ethernet.begin(mac_addr); 56 | Serial.println("Connecting..."); 57 | if (conn.connect(server_addr, 3306, user, password)) { 58 | delay(1000); 59 | } 60 | else 61 | Serial.println("Connection failed."); 62 | } 63 | 64 | 65 | void loop() { 66 | row_values *row = NULL; 67 | long head_count = 0; 68 | 69 | delay(1000); 70 | 71 | Serial.println("1) Demonstrating using a cursor dynamically allocated."); 72 | // Initiate the query class instance 73 | MySQL_Cursor *cur_mem = new MySQL_Cursor(&conn); 74 | // Execute the query 75 | cur_mem->execute(query); 76 | // Fetch the columns (required) but we don't use them. 77 | column_names *columns = cur_mem->get_columns(); 78 | 79 | // Read the row (we are only expecting the one) 80 | do { 81 | row = cur_mem->get_next_row(); 82 | if (row != NULL) { 83 | head_count = atol(row->values[0]); 84 | } 85 | } while (row != NULL); 86 | // Deleting the cursor also frees up memory used 87 | delete cur_mem; 88 | 89 | // Show the result 90 | Serial.print(" NYC pop = "); 91 | Serial.println(head_count); 92 | 93 | delay(500); 94 | 95 | Serial.println("2) Demonstrating using a local, global cursor."); 96 | // Execute the query 97 | cur.execute(query); 98 | // Fetch the columns (required) but we don't use them. 99 | cur.get_columns(); 100 | // Read the row (we are only expecting the one) 101 | do { 102 | row = cur.get_next_row(); 103 | if (row != NULL) { 104 | head_count = atol(row->values[0]); 105 | } 106 | } while (row != NULL); 107 | // Now we close the cursor to free any memory 108 | cur.close(); 109 | 110 | // Show the result but this time do some math on it 111 | Serial.print(" NYC pop = "); 112 | Serial.println(head_count); 113 | Serial.print(" NYC pop increased by 12 = "); 114 | Serial.println(head_count+12); 115 | 116 | } 117 | -------------------------------------------------------------------------------- /examples/complex_insert/complex_insert.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Connector/Arduino Example : complex insert 3 | 4 | This example demonstrates how to issue an INSERT query to store data in a 5 | table using data from variables in our sketch. In this case, we supply the 6 | values dynamically. 7 | 8 | This sketch simulates storing data from a sensor in a table. 9 | 10 | For this, we will create a special database and table for testing. 11 | The following are the SQL commands you will need to run in order to setup 12 | your database for running this sketch. 13 | 14 | CREATE DATABASE test_arduino; 15 | CREATE TABLE test_arduino.hello_sensor ( 16 | num integer primary key auto_increment, 17 | message char(40), 18 | sensor_num integer, 19 | value float, 20 | recorded timestamp 21 | ); 22 | 23 | Here we have a table that contains an auto_increment primary key, a text 24 | field, a field to identify the sensor, the value read, and timestamp of 25 | the recorded data. 26 | 27 | Note: Since this sketch uses test data, we place the INSERT in the setup() 28 | method so that it runs only once. Typically, you would have the 29 | INSERT in the loop() method after your code to read from the sensor. 30 | 31 | For more information and documentation, visit the wiki: 32 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 33 | 34 | INSTRUCTIONS FOR USE 35 | 36 | 1) Create the database and table as shown above. 37 | 2) Change the address of the server to the IP address of the MySQL server 38 | 3) Change the user and password to a valid MySQL user and password 39 | 4) Connect a USB cable to your Arduino 40 | 5) Select the correct board and port 41 | 6) Compile and upload the sketch to your Arduino 42 | 7) Once uploaded, open Serial Monitor (use 115200 speed) and observe 43 | 8) After the sketch has run for some time, open a mysql client and issue 44 | the command: "SELECT * FROM test_arduino.hello_sensor" to see the data 45 | recorded. Note the field values and how the database handles both the 46 | auto_increment and timestamp fields for us. You can clear the data with 47 | "DELETE FROM test_arduino.hello_sensor". 48 | 49 | Note: The MAC address can be anything so long as it is unique on your network. 50 | 51 | Created by: Dr. Charles A. Bell 52 | */ 53 | #include 54 | #include 55 | #include 56 | 57 | byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 58 | 59 | IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here 60 | char user[] = "root"; // MySQL user login username 61 | char password[] = "secret"; // MySQL user login password 62 | 63 | // Sample query 64 | char INSERT_DATA[] = "INSERT INTO test_arduino.hello_sensor (message, sensor_num, value) VALUES ('%s',%d,%s)"; 65 | char query[128]; 66 | char temperature[10]; 67 | 68 | EthernetClient client; 69 | MySQL_Connection conn((Client *)&client); 70 | 71 | void setup() { 72 | Serial.begin(115200); 73 | while (!Serial); // wait for serial port to connect 74 | Ethernet.begin(mac_addr); 75 | Serial.println("Connecting..."); 76 | if (conn.connect(server_addr, 3306, user, password)) { 77 | delay(1000); 78 | // Initiate the query class instance 79 | MySQL_Cursor *cur_mem = new MySQL_Cursor(&conn); 80 | // Save 81 | dtostrf(50.125, 1, 1, temperature); 82 | sprintf(query, INSERT_DATA, "test sensor", 24, temperature); 83 | // Execute the query 84 | cur_mem->execute(query); 85 | // Note: since there are no results, we do not need to read any data 86 | // Deleting the cursor also frees up memory used 87 | delete cur_mem; 88 | Serial.println("Data recorded."); 89 | } 90 | else 91 | Serial.println("Connection failed."); 92 | conn.close(); 93 | } 94 | 95 | 96 | void loop() { 97 | } 98 | -------------------------------------------------------------------------------- /examples/complex_select/complex_select.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Connector/Arduino Example : complex select 3 | 4 | This example demonstrates how to issue a SELECT query with parameters that 5 | we provide from code. Thus, it demonstrates how to build query parameters 6 | dynamically. 7 | 8 | Notice also the sketch demonstrates how to read columns and rows from 9 | the result set. Study this example until you are familiar with how to 10 | do this before writing your own sketch to read and consume query results. 11 | 12 | For more information and documentation, visit the wiki: 13 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 14 | 15 | NOTICE: You must download and install the World sample database to run 16 | this sketch unaltered. See http://dev.mysql.com/doc/index-other.html. 17 | 18 | INSTRUCTIONS FOR USE 19 | 20 | 1) Change the address of the server to the IP address of the MySQL server 21 | 2) Change the user and password to a valid MySQL user and password 22 | 3) Connect a USB cable to your Arduino 23 | 4) Select the correct board and port 24 | 5) Compile and upload the sketch to your Arduino 25 | 6) Once uploaded, open Serial Monitor (use 115200 speed) and observe 26 | 27 | Note: The MAC address can be anything so long as it is unique on your network. 28 | 29 | Created by: Dr. Charles A. Bell 30 | */ 31 | #include 32 | #include 33 | #include 34 | 35 | byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 36 | 37 | IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here 38 | char user[] = "root"; // MySQL user login username 39 | char password[] = "secret"; // MySQL user login password 40 | 41 | // Sample query 42 | // 43 | // Notice the "%lu" - that's a placeholder for the parameter we will 44 | // supply. See sprintf() documentation for more formatting specifier 45 | // options 46 | const char QUERY_POP[] = "SELECT name, population FROM world.city WHERE population > %lu ORDER BY population DESC;"; 47 | char query[128]; 48 | 49 | EthernetClient client; 50 | MySQL_Connection conn((Client *)&client); 51 | 52 | void setup() { 53 | Serial.begin(115200); 54 | while (!Serial); // wait for serial port to connect 55 | Ethernet.begin(mac_addr); 56 | Serial.println("Connecting..."); 57 | if (conn.connect(server_addr, 3306, user, password)) { 58 | delay(1000); 59 | } 60 | else 61 | Serial.println("Connection failed."); 62 | } 63 | 64 | 65 | void loop() { 66 | delay(1000); 67 | 68 | Serial.println("> Running SELECT with dynamically supplied parameter"); 69 | 70 | // Initiate the query class instance 71 | MySQL_Cursor *cur_mem = new MySQL_Cursor(&conn); 72 | // Supply the parameter for the query 73 | // Here we use the QUERY_POP as the format string and query as the 74 | // destination. This uses twice the memory so another option would be 75 | // to allocate one buffer for all formatted queries or allocate the 76 | // memory as needed (just make sure you allocate enough memory and 77 | // free it when you're done!). 78 | sprintf(query, QUERY_POP, 9000000); 79 | // Execute the query 80 | cur_mem->execute(query); 81 | // Fetch the columns and print them 82 | column_names *cols = cur_mem->get_columns(); 83 | for (int f = 0; f < cols->num_fields; f++) { 84 | Serial.print(cols->fields[f]->name); 85 | if (f < cols->num_fields-1) { 86 | Serial.print(','); 87 | } 88 | } 89 | Serial.println(); 90 | // Read the rows and print them 91 | row_values *row = NULL; 92 | do { 93 | row = cur_mem->get_next_row(); 94 | if (row != NULL) { 95 | for (int f = 0; f < cols->num_fields; f++) { 96 | Serial.print(row->values[f]); 97 | if (f < cols->num_fields-1) { 98 | Serial.print(','); 99 | } 100 | } 101 | Serial.println(); 102 | } 103 | } while (row != NULL); 104 | // Deleting the cursor also frees up memory used 105 | delete cur_mem; 106 | } 107 | -------------------------------------------------------------------------------- /examples/connect/connect.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Connector/Arduino Example : connect 3 | 4 | This example demonstrates how to connect to a MySQL server from an 5 | Arduino using an Arduino-compatible Ethernet shield. Note that "compatible" 6 | means it must conform to the Ethernet class library or be a derivative 7 | with the same classes and methods. 8 | 9 | For more information and documentation, visit the wiki: 10 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 11 | 12 | INSTRUCTIONS FOR USE 13 | 14 | 1) Change the address of the server to the IP address of the MySQL server 15 | 2) Change the user and password to a valid MySQL user and password 16 | 3) Connect a USB cable to your Arduino 17 | 4) Select the correct board and port 18 | 5) Compile and upload the sketch to your Arduino 19 | 6) Once uploaded, open Serial Monitor (use 115200 speed) and observe 20 | 21 | If you do not see messages indicating you have a connection, refer to the 22 | manual for troubleshooting tips. The most common issues are the server is 23 | not accessible from the network or the user name and password is incorrect. 24 | 25 | Note: The MAC address can be anything so long as it is unique on your network. 26 | 27 | Created by: Dr. Charles A. Bell 28 | */ 29 | #include 30 | #include 31 | 32 | byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 33 | 34 | IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here 35 | char user[] = "root"; // MySQL user login username 36 | char password[] = "secret"; // MySQL user login password 37 | 38 | EthernetClient client; 39 | MySQL_Connection conn((Client *)&client); 40 | 41 | void setup() { 42 | Serial.begin(115200); 43 | while (!Serial); // wait for serial port to connect 44 | Ethernet.begin(mac_addr); 45 | Serial.println("Connecting..."); 46 | if (conn.connect(server_addr, 3306, user, password)) { 47 | delay(1000); 48 | // You would add your code here to run a query once on startup. 49 | } 50 | else 51 | Serial.println("Connection failed."); 52 | conn.close(); 53 | } 54 | 55 | void loop() { 56 | } 57 | -------------------------------------------------------------------------------- /examples/connect_by_hostname/connect_by_hostname.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Connector/Arduino Example : connect by hostname 3 | 4 | This example demonstrates how to connect to a MySQL server resolving the 5 | hostname for cases when you do not know the IP address of the server or 6 | it changes because it is in the cloud. 7 | 8 | For more information and documentation, visit the wiki: 9 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 10 | 11 | INSTRUCTIONS FOR USE 12 | 13 | 1) Change the hostname variable to the hostname of the MySQL server 14 | 2) Change the user and password to a valid MySQL user and password 15 | 3) Connect a USB cable to your Arduino 16 | 4) Select the correct board and port 17 | 5) Compile and upload the sketch to your Arduino 18 | 6) Once uploaded, open Serial Monitor (use 115200 speed) and observe 19 | 20 | If you do not see messages indicating you have a connection, refer to the 21 | manual for troubleshooting tips. The most common issues are the server is 22 | not accessible from the network or the user name and password is incorrect. 23 | 24 | Note: The MAC address can be anything so long as it is unique on your network. 25 | 26 | Created by: Dr. Charles A. Bell 27 | */ 28 | #include 29 | #include 30 | #include 31 | 32 | byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 33 | 34 | char hostname[] = "www.google.com"; // change to your server's hostname/URL 35 | char user[] = "root"; // MySQL user login username 36 | char password[] = "secret"; // MySQL user login password 37 | 38 | IPAddress server_ip; 39 | EthernetClient client; 40 | MySQL_Connection conn((Client *)&client); 41 | DNSClient dns_client; // DNS instance 42 | 43 | void setup() { 44 | Serial.begin(115200); 45 | while (!Serial); // wait for serial port to connect 46 | Ethernet.begin(mac_addr); 47 | // Begin DNS lookup 48 | dns_client.begin(Ethernet.dnsServerIP()); 49 | dns_client.getHostByName(hostname, server_ip); 50 | Serial.println(server_ip); 51 | // End DNS lookup 52 | Serial.println("Connecting..."); 53 | if (conn.connect(server_ip, 3306, user, password)) { 54 | delay(1000); 55 | // You would add your code here to run a query once on startup. 56 | } 57 | else 58 | Serial.println("Connection failed."); 59 | conn.close(); 60 | } 61 | 62 | void loop() { 63 | } 64 | -------------------------------------------------------------------------------- /examples/connect_default_database/connect_default_database.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Connector/Arduino Example : connect with default database 3 | 4 | This example demonstrates how to connect to a MySQL server and specifying 5 | the default database when connecting. 6 | 7 | For more information and documentation, visit the wiki: 8 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 9 | 10 | INSTRUCTIONS FOR USE 11 | 12 | 1) Change the address of the server to the IP address of the MySQL server 13 | 2) Change the user and password to a valid MySQL user and password 14 | 3) Connect a USB cable to your Arduino 15 | 4) Select the correct board and port 16 | 5) Compile and upload the sketch to your Arduino 17 | 6) Once uploaded, open Serial Monitor (use 115200 speed) and observe 18 | 19 | If you do not see messages indicating you have a connection, refer to the 20 | manual for troubleshooting tips. The most common issues are the server is 21 | not accessible from the network or the user name and password is incorrect. 22 | 23 | Note: The MAC address can be anything so long as it is unique on your network. 24 | 25 | Created by: Dr. Charles A. Bell 26 | */ 27 | #include 28 | #include 29 | 30 | byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 31 | 32 | IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here 33 | char user[] = "root"; // MySQL user login username 34 | char password[] = "secret"; // MySQL user login password 35 | char default_db = "test_arduino; 36 | 37 | EthernetClient client; 38 | MySQL_Connection conn((Client *)&client); 39 | 40 | void setup() { 41 | Serial.begin(115200); 42 | while (!Serial); // wait for serial port to connect 43 | Ethernet.begin(mac_addr); 44 | Serial.println("Connecting..."); 45 | if (conn.connect(server_addr, 3306, user, password, default_db)) { 46 | delay(1000); 47 | // You would add your code here to run a query once on startup. 48 | } 49 | else 50 | Serial.println("Connection failed."); 51 | conn.close(); 52 | } 53 | 54 | void loop() { 55 | } 56 | -------------------------------------------------------------------------------- /examples/connect_disconnect/connect_disconnect.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Connector/Arduino Example : connect and disconnect (close) 3 | 4 | This example demonstrates how to use the connection to open at the start 5 | of a loop, perform some query, then close the connection. Use this technique 6 | for solutions that must sleep for a long period or otherwise require 7 | additional processing or delays. The connect/close pair allow you to 8 | control how long the connection is open and thus reduce the amount of 9 | time a connection is held open. It also helps for lossy connections. 10 | 11 | This example demonstrates how to connect to a MySQL server and specifying 12 | the default database when connecting. 13 | 14 | For more information and documentation, visit the wiki: 15 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 16 | 17 | INSTRUCTIONS FOR USE 18 | 19 | 1) Change the address of the server to the IP address of the MySQL server 20 | 2) Change the user and password to a valid MySQL user and password 21 | 3) Connect a USB cable to your Arduino 22 | 4) Select the correct board and port 23 | 5) Compile and upload the sketch to your Arduino 24 | 6) Once uploaded, open Serial Monitor (use 115200 speed) and observe 25 | 26 | Created by: Dr. Charles A. Bell 27 | */ 28 | #include 29 | #include 30 | #include 31 | 32 | byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 33 | 34 | IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here 35 | char user[] = "root"; // MySQL user login username 36 | char password[] = "secret"; // MySQL user login password 37 | 38 | EthernetClient client; 39 | MySQL_Connection conn((Client *)&client); 40 | MySQL_Cursor cur = MySQL_Cursor(&conn); 41 | 42 | void setup() { 43 | Serial.begin(115200); 44 | while (!Serial); // wait for serial port to connect 45 | Ethernet.begin(mac_addr); 46 | } 47 | 48 | void loop() { 49 | Serial.println("Sleeping..."); 50 | delay(2000); 51 | Serial.println("Connecting..."); 52 | if (conn.connect(server_addr, 3306, user, password)) { 53 | delay(500); 54 | Serial.println("Running a query"); 55 | cur.execute("SHOW DATABASES"); // execute a query 56 | cur.show_results(); // show the results 57 | cur.close(); // close the cursor 58 | conn.close(); // close the connection 59 | } else { 60 | Serial.println("Connect failed. Trying again on next iteration."); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/connect_wifi/connect_wifi.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Connector/Arduino Example : connect by wifi 3 | 4 | This example demonstrates how to connect to a MySQL server from an 5 | Arduino using an Arduino-compatible Wifi shield. Note that "compatible" 6 | means it must conform to the Ethernet class library or be a derivative 7 | with the same classes and methods. 8 | 9 | For more information and documentation, visit the wiki: 10 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 11 | 12 | INSTRUCTIONS FOR USE 13 | 14 | 1) Change the address of the server to the IP address of the MySQL server 15 | 2) Change the user and password to a valid MySQL user and password 16 | 3) Change the SSID and pass to match your WiFi network 17 | 4) Connect a USB cable to your Arduino 18 | 5) Select the correct board and port 19 | 6) Compile and upload the sketch to your Arduino 20 | 7) Once uploaded, open Serial Monitor (use 115200 speed) and observe 21 | 22 | If you do not see messages indicating you have a connection, refer to the 23 | manual for troubleshooting tips. The most common issues are the server is 24 | not accessible from the network or the user name and password is incorrect. 25 | 26 | Note: The MAC address can be anything so long as it is unique on your network. 27 | 28 | Created by: Dr. Charles A. Bell 29 | */ 30 | #include // Use this for WiFi instead of Ethernet.h 31 | #include 32 | #include 33 | 34 | byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 35 | 36 | IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here 37 | char user[] = "root"; // MySQL user login username 38 | char password[] = "secret"; // MySQL user login password 39 | 40 | // WiFi card example 41 | char ssid[] = "horse_pen"; // your SSID 42 | char pass[] = "noname"; // your SSID Password 43 | 44 | WiFiClient client; // Use this for WiFi instead of EthernetClient 45 | MySQL_Connection conn((Client *)&client); 46 | 47 | void setup() { 48 | Serial.begin(115200); 49 | while (!Serial); // wait for serial port to connect. Needed for Leonardo only 50 | 51 | // Begin WiFi section 52 | int status = WiFi.begin(ssid, pass); 53 | if ( status != WL_CONNECTED) { 54 | Serial.println("Couldn't get a wifi connection"); 55 | while(true); 56 | } 57 | // print out info about the connection: 58 | else { 59 | Serial.println("Connected to network"); 60 | IPAddress ip = WiFi.localIP(); 61 | Serial.print("My IP address is: "); 62 | Serial.println(ip); 63 | } 64 | // End WiFi section 65 | 66 | Serial.println("Connecting..."); 67 | if (conn.connect(server_addr, 3306, user, password)) { 68 | delay(1000); 69 | } 70 | else 71 | Serial.println("Connection failed."); 72 | conn.close(); 73 | } 74 | 75 | void loop() { 76 | } 77 | -------------------------------------------------------------------------------- /examples/connect_wifi_101/connect_wifi_101.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Connector/Arduino Example : connect by wifi using WiFi 101 shield 3 | 4 | This example demonstrates how to connect to a MySQL server from an 5 | Arduino using using the new WiFi Shield 101 from arduino.cc. 6 | 7 | For more information and documentation, visit the wiki: 8 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 9 | 10 | INSTRUCTIONS FOR USE 11 | 12 | 1) Change the address of the server to the IP address of the MySQL server 13 | 2) Change the user and password to a valid MySQL user and password 14 | 3) Change the SSID and pass to match your WiFi network 15 | 4) Connect a USB cable to your Arduino 16 | 5) Select the correct board and port 17 | 6) Compile and upload the sketch to your Arduino 18 | 7) Once uploaded, open Serial Monitor (use 115200 speed) and observe 19 | 20 | If you do not see messages indicating you have a connection, refer to the 21 | manual for troubleshooting tips. The most common issues are the server is 22 | not accessible from the network or the user name and password is incorrect. 23 | 24 | Note: The MAC address can be anything so long as it is unique on your network. 25 | 26 | Created by: Dr. Charles A. Bell 27 | */ 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 34 | 35 | IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here 36 | char user[] = "root"; // MySQL user login username 37 | char password[] = "secret"; // MySQL user login password 38 | 39 | // WiFi card example 40 | char ssid[] = "horse_pen"; // your SSID 41 | char pass[] = "noname"; // your SSID Password 42 | 43 | WiFiClient client; 44 | MySQL_Connection conn((Client *)&client); 45 | 46 | void setup() { 47 | Serial.begin(115200); 48 | while (!Serial); // wait for serial port to connect 49 | 50 | // Begin WiFi section 51 | int status = WiFi.begin(ssid, pass); 52 | if ( status != WL_CONNECTED) { 53 | Serial.println("Couldn't get a wifi connection"); 54 | while(true); 55 | } 56 | // print out info about the connection: 57 | else { 58 | Serial.println("Connected to network"); 59 | IPAddress ip = WiFi.localIP(); 60 | Serial.print("My IP address is: "); 61 | Serial.println(ip); 62 | } 63 | // End WiFi section 64 | 65 | Serial.println("Connecting..."); 66 | if (conn.connect(server_addr, 3306, user, password)) { 67 | delay(1000); 68 | } 69 | else 70 | Serial.println("Connection failed."); 71 | conn.close(); 72 | } 73 | 74 | void loop() { 75 | } 76 | -------------------------------------------------------------------------------- /examples/query_progmem/query_progmem.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Connector/Arduino Example : query with PROGMEM strings 3 | 4 | This example demonstrates how to issue queries using strings stored in 5 | PROGMEM. As you will see, you need only add a parameter to the execute() 6 | method in the cursor class, const and PROGMEM to the string declaration 7 | and add the #include directive. 8 | 9 | For more information and documentation, visit the wiki: 10 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 11 | 12 | NOTICE: You must download and install the World sample database to run 13 | this sketch unaltered. See http://dev.mysql.com/doc/index-other.html. 14 | 15 | INSTRUCTIONS FOR USE 16 | 17 | 1) Change the address of the server to the IP address of the MySQL server 18 | 2) Change the user and password to a valid MySQL user and password 19 | 3) Connect a USB cable to your Arduino 20 | 4) Select the correct board and port 21 | 5) Compile and upload the sketch to your Arduino 22 | 6) Once uploaded, open Serial Monitor (use 115200 speed) and observe 23 | 24 | Note: The MAC address can be anything so long as it is unique on your network. 25 | 26 | Created by: Dr. Charles A. Bell 27 | */ 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 34 | 35 | IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here 36 | char user[] = "root"; // MySQL user login username 37 | char password[] = "secret"; // MySQL user login password 38 | 39 | // Sample query 40 | const char PROGMEM query[] = "SELECT * FROM world.city LIMIT 12"; 41 | 42 | EthernetClient client; 43 | MySQL_Connection conn((Client *)&client); 44 | 45 | void setup() { 46 | Serial.begin(115200); 47 | while (!Serial); // wait for serial port to connect 48 | Ethernet.begin(mac_addr); 49 | Serial.println("Connecting..."); 50 | if (conn.connect(server_addr, 3306, user, password)) { 51 | delay(1000); 52 | } 53 | else 54 | Serial.println("Connection failed."); 55 | } 56 | 57 | 58 | void loop() { 59 | delay(2000); 60 | 61 | Serial.println("\nRunning SELECT from PROGMEM and printing results\n"); 62 | 63 | // Initiate the query class instance 64 | MySQL_Cursor *cur_mem = new MySQL_Cursor(&conn); 65 | // Execute the query with the PROGMEM option 66 | cur_mem->execute(query, true); 67 | // Show the results 68 | cur_mem->show_results(); 69 | // Deleting the cursor also frees up memory used 70 | delete cur_mem; 71 | } 72 | -------------------------------------------------------------------------------- /examples/query_results/query_results.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Connector/Arduino Example : query results 3 | 4 | This example demonstrates how to issue a SELECT query and how to read columns 5 | and rows from the result set. Study this example until you are familiar with how to 6 | do this before writing your own sketch to read and consume query results. 7 | 8 | For more information and documentation, visit the wiki: 9 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 10 | 11 | NOTICE: You must download and install the World sample database to run 12 | this sketch unaltered. See http://dev.mysql.com/doc/index-other.html. 13 | 14 | INSTRUCTIONS FOR USE 15 | 16 | 1) Change the address of the server to the IP address of the MySQL server 17 | 2) Change the user and password to a valid MySQL user and password 18 | 3) Connect a USB cable to your Arduino 19 | 4) Select the correct board and port 20 | 5) Compile and upload the sketch to your Arduino 21 | 6) Once uploaded, open Serial Monitor (use 115200 speed) and observe 22 | 23 | Note: The MAC address can be anything so long as it is unique on your network. 24 | 25 | Created by: Dr. Charles A. Bell 26 | */ 27 | #include 28 | #include 29 | #include 30 | 31 | byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 32 | 33 | IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here 34 | char user[] = "root"; // MySQL user login username 35 | char password[] = "secret"; // MySQL user login password 36 | 37 | // Sample query 38 | char query[] = "SELECT * FROM world.city LIMIT 12"; 39 | 40 | EthernetClient client; 41 | MySQL_Connection conn((Client *)&client); 42 | 43 | void setup() { 44 | Serial.begin(115200); 45 | while (!Serial); // wait for serial port to connect 46 | Ethernet.begin(mac_addr); 47 | Serial.println("Connecting..."); 48 | if (conn.connect(server_addr, 3306, user, password)) { 49 | delay(1000); 50 | } 51 | else 52 | Serial.println("Connection failed."); 53 | } 54 | 55 | 56 | void loop() { 57 | delay(2000); 58 | 59 | Serial.println("\nRunning SELECT and printing results\n"); 60 | 61 | // Initiate the query class instance 62 | MySQL_Cursor *cur_mem = new MySQL_Cursor(&conn); 63 | // Execute the query 64 | cur_mem->execute(query); 65 | // Fetch the columns and print them 66 | column_names *cols = cur_mem->get_columns(); 67 | for (int f = 0; f < cols->num_fields; f++) { 68 | Serial.print(cols->fields[f]->name); 69 | if (f < cols->num_fields-1) { 70 | Serial.print(", "); 71 | } 72 | } 73 | Serial.println(); 74 | // Read the rows and print them 75 | row_values *row = NULL; 76 | do { 77 | row = cur_mem->get_next_row(); 78 | if (row != NULL) { 79 | for (int f = 0; f < cols->num_fields; f++) { 80 | Serial.print(row->values[f]); 81 | if (f < cols->num_fields-1) { 82 | Serial.print(", "); 83 | } 84 | } 85 | Serial.println(); 86 | } 87 | } while (row != NULL); 88 | // Deleting the cursor also frees up memory used 89 | delete cur_mem; 90 | } 91 | -------------------------------------------------------------------------------- /examples/reboot/reboot.ino: -------------------------------------------------------------------------------- 1 | /* 2 | MySQL Connector/Arduino Example : reboot if connection lost 3 | 4 | This example demonstrates how to reboot an Arduino if connection to the 5 | server is lost for a period of time. 6 | 7 | For more information and documentation, visit the wiki: 8 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 9 | 10 | INSTRUCTIONS FOR USE 11 | 12 | 1) Change the address of the server to the IP address of the MySQL server 13 | 2) Change the user and password to a valid MySQL user and password 14 | 3) Connect a USB cable to your Arduino 15 | 4) Select the correct board and port 16 | 5) Compile and upload the sketch to your Arduino 17 | 6) Once uploaded, open Serial Monitor (use 115200 speed) and observe 18 | 19 | To test the reboot, unplug your Ethernet cable once you see "disconnected" 20 | then wait for the timeout. Once the Arduino reboots, plug the cable in again 21 | and you should see the query processing resume. 22 | 23 | Created by: Dr. Charles A. Bell 24 | */ 25 | #include 26 | #include 27 | #include 28 | 29 | byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 30 | 31 | IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here 32 | char user[] = "root"; // MySQL user login username 33 | char password[] = "secret"; // MySQL user login password 34 | 35 | EthernetClient client; 36 | MySQL_Connection conn((Client *)&client); 37 | MySQL_Cursor cur = MySQL_Cursor(&conn); 38 | 39 | void setup() { 40 | Serial.begin(115200); 41 | while (!Serial); // wait for serial port to connect 42 | Ethernet.begin(mac_addr); 43 | } 44 | 45 | // Begin reboot code 46 | int num_fails; // variable for number of failure attempts 47 | #define MAX_FAILED_CONNECTS 5 // maximum number of failed connects to MySQL 48 | 49 | void soft_reset() { 50 | asm volatile("jmp 0"); 51 | } 52 | // End reboot code 53 | 54 | void loop() { 55 | Serial.println("Sleeping..."); 56 | delay(1000); 57 | if (conn.connected()) { 58 | Serial.println("Running a query"); 59 | cur.execute("SHOW DATABASES"); // execute a query 60 | cur.show_results(); // show the results 61 | cur.close(); // close the cursor 62 | conn.close(); // close the connection 63 | num_fails = 0; // reset failures 64 | delay(3000); 65 | } else { 66 | Serial.println("Connecting..."); 67 | if (conn.connect(server_addr, 3306, user, password)) { 68 | delay(500); 69 | } else { 70 | num_fails++; 71 | Serial.println("Connect failed!"); 72 | if (num_fails == MAX_FAILED_CONNECTS) { 73 | Serial.println("Ok, that's it. I'm outta here. Rebooting..."); 74 | delay(2000); 75 | // Here we tell the Arduino to reboot by redirecting the instruction 76 | // pointer to the "top" or position 0. This is a soft reset and may 77 | // not solve all hardware-related lockups. 78 | soft_reset(); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /extras/Reference_Manual.txt: -------------------------------------------------------------------------------- 1 | The documentation for the connector has been moved to the wiki page on Github. For more information and documentation, visit the wiki: 2 | 3 | https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki. 4 | 5 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | connect KEYWORD2 2 | execute KEYWORD2 3 | show_results KEYWORD2 4 | connected KEYWORD2 5 | field_struct KEYWORD3 6 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=MySQL Connector Arduino 2 | version=1.2.0 3 | author=Dr. Charles Bell 4 | maintainer=Dr. Charles Bell 5 | sentence=Connects Arduino using Arduino Ethernet-compatible shields including the Ethernet Shield and WiFi Shield. 6 | paragraph=You can use this library to connect your Arduino project directly to a MySQL server without using an intermediate computer or a web- or cloud-based service. Having direct access to a database server means you can store data acquired from your project as well as check values stored in tables on the server. This also means you can setup your own, local MySQL server to store your data further removing the need for Internet connectivity. If that is not an issue, you can still connect to and store data on a MySQL server via your network, Internet, or even in the cloud! 7 | category=Communication 8 | url=https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/MySQL_Connection.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, 2016 Oracle and/or its affiliates. All rights reserved. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; version 2 of the License. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, write to the Free Software 15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 16 | 17 | MySQL_Connection.cpp - Library for communicating with a MySQL Server over 18 | Ethernet. (formerly mysql.cpp) 19 | 20 | Change History: 21 | 22 | Version 1.0.0a Created by Dr. Charles A. Bell, April 2012. 23 | Version 1.0.0b Updated by Dr. Charles A. Bell, October 2013. 24 | Version 1.0.1b Updated by Dr. Charles A. Bell, February 2014. 25 | Version 1.0.2b Updated by Dr. Charles A. Bell, April 2014. 26 | Version 1.0.3rc Updated by Dr. Charles A. Bell, March 2015. 27 | Version 1.0.4ga Updated by Dr. Charles A. Bell, July 2015. 28 | Version 1.1.0a Created by Dr. Charles A. Bell, January 2016. 29 | Version 1.1.1a Created by Dr. Charles A. Bell, January 2016. 30 | Version 1.1.2b Created by Dr. Charles A. Bell, November 2016. 31 | Version 1.2.0 Created by Dr. Charles A. Bell, March 2020. 32 | */ 33 | #include 34 | #include 35 | #include 36 | 37 | #define MAX_CONNECT_ATTEMPTS 3 38 | #define CONNECT_DELAY_MS 500 39 | #define SUCCESS 1 40 | 41 | const char CONNECTED[] PROGMEM = "Connected to server version "; 42 | const char DISCONNECTED[] PROGMEM = "Disconnected."; 43 | 44 | /* 45 | connect - Connect to a MySQL server. 46 | 47 | This method is used to connect to a MySQL server. It will attempt to 48 | connect to the server as a client retrying up to MAX_CONNECT_ATTEMPTS. 49 | This permits the possibility of longer than normal network lag times 50 | for wireless networks. You can adjust MAX_CONNECT_ATTEMPTS to suit 51 | your environment. 52 | 53 | server[in] IP address of the server as IPAddress type 54 | port[in] port number of the server 55 | user[in] user name 56 | password[in] (optional) user password 57 | db[in] (optional) default database 58 | 59 | Returns boolean - True = connection succeeded 60 | */ 61 | boolean MySQL_Connection::connect(IPAddress server, int port, char *user, 62 | char *password, char *db) 63 | { 64 | int connected = 0; 65 | int retries = MAX_CONNECT_ATTEMPTS; 66 | 67 | // Retry up to MAX_CONNECT_ATTEMPTS times. 68 | while (retries--) 69 | { 70 | Serial.println("...trying..."); 71 | connected = client->connect(server, port); 72 | if (connected != SUCCESS) { 73 | Serial.print("...got: "); 74 | Serial.print(connected); 75 | Serial.println(" retrying..."); 76 | delay(CONNECT_DELAY_MS); 77 | } else { 78 | break; 79 | } 80 | } 81 | 82 | if (connected != SUCCESS) 83 | return false; 84 | 85 | read_packet(); 86 | parse_handshake_packet(); 87 | send_authentication_packet(user, password, db); 88 | read_packet(); 89 | if (get_packet_type() != MYSQL_OK_PACKET) { 90 | parse_error_packet(); 91 | return false; 92 | } 93 | 94 | show_error(CONNECTED); 95 | 96 | Serial.println(server_version); 97 | 98 | free(server_version); // don't need it anymore 99 | return true; 100 | } 101 | 102 | /* 103 | close - cancel the connection 104 | 105 | This method closes the connection to the server and frees up any memory 106 | used in the buffer. 107 | */ 108 | void MySQL_Connection::close() 109 | { 110 | if (connected()) 111 | { 112 | client->flush(); 113 | client->stop(); 114 | show_error(DISCONNECTED, true); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/MySQL_Connection.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, 2016 Oracle and/or its affiliates. All rights reserved. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; version 2 of the License. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, write to the Free Software 15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 16 | 17 | MySQL_Connection.h - Library for communicating with a MySQL Server over 18 | Ethernet. (formerly mysql.h) 19 | 20 | This header file defines a connection class for connecting to a MySQL 21 | server. 22 | 23 | Change History: 24 | 25 | Version 1.0.0a Created by Dr. Charles A. Bell, April 2012. 26 | Version 1.0.0b Updated by Dr. Charles A. Bell, October 2013. 27 | Version 1.0.1b Updated by Dr. Charles A. Bell, February 2014. 28 | Version 1.0.2b Updated by Dr. Charles A. Bell, April 2014. 29 | Version 1.0.3rc Updated by Dr. Charles A. Bell, March 2015. 30 | Version 1.0.4ga Updated by Dr. Charles A. Bell, July 2015. 31 | Version 1.1.0a Created by Dr. Charles A. Bell, January 2016. 32 | Version 1.1.1a Created by Dr. Charles A. Bell, January 2016. 33 | Version 1.1.2b Created by Dr. Charles A. Bell, November 2016. 34 | Version 1.2.0 Created by Dr. Charles A. Bell, March 2020. 35 | */ 36 | #ifndef MYSQL_CONNECTION_H 37 | #define MYSQL_CONNECTION_H 38 | 39 | #include 40 | 41 | class MySQL_Connection : public MySQL_Packet { 42 | public: 43 | MySQL_Connection(Client *client_instance) : 44 | MySQL_Packet(client_instance) {} 45 | boolean connect(IPAddress server, int port, char *user, char *password, 46 | char *db=NULL); 47 | int connected() { return client->connected(); } 48 | const char *version() { return MYSQL_VERSION_STR; } 49 | void close(); 50 | }; 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/MySQL_Cursor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, 2016 Oracle and/or its affiliates. All rights reserved. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; version 2 of the License. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, write to the Free Software 15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 16 | 17 | MySQL_Cursor.cpp - Run queries on a MySQL Server 18 | 19 | Change History: 20 | 21 | Version 1.0.0a Created by Dr. Charles A. Bell, April 2012. 22 | Version 1.0.0b Updated by Dr. Charles A. Bell, October 2013. 23 | Version 1.0.1b Updated by Dr. Charles A. Bell, February 2014. 24 | Version 1.0.2b Updated by Dr. Charles A. Bell, April 2014. 25 | Version 1.0.3rc Updated by Dr. Charles A. Bell, March 2015. 26 | Version 1.0.4ga Updated by Dr. Charles A. Bell, July 2015. 27 | Version 1.1.0a Created by Dr. Charles A. Bell, January 2016. 28 | Version 1.1.1a Created by Dr. Charles A. Bell, January 2016. 29 | Version 1.1.2b Created by Dr. Charles A. Bell, November 2016. 30 | Version 1.2.0 Created by Dr. Charles A. Bell, March 2020. 31 | */ 32 | #include 33 | 34 | const char BAD_MOJO[] PROGMEM = "Bad mojo. EOF found reading column header."; 35 | const char ROWS[] PROGMEM = " rows in result."; 36 | const char READ_COLS[] PROGMEM = "ERROR: You must read the columns first!"; 37 | const char NOT_CONNECTED[] PROGMEM = "ERROR: Class requires connected server."; 38 | 39 | /* 40 | Constructor 41 | 42 | Requires an instance of the MySQL_Connection class to communicate with a 43 | MySQL server. 44 | 45 | connection[in] Connection to a MySQL server - must be connected. 46 | */ 47 | MySQL_Cursor::MySQL_Cursor(MySQL_Connection *connection) { 48 | conn = connection; 49 | #ifdef WITH_SELECT 50 | columns.num_fields = 0; 51 | for (int f = 0; f < MAX_FIELDS; f++) { 52 | columns.fields[f] = NULL; 53 | row.values[f] = NULL; 54 | } 55 | columns_read = false; 56 | rows_affected = -1; 57 | last_insert_id = -1; 58 | #endif 59 | } 60 | 61 | 62 | /* 63 | Destructor 64 | */ 65 | MySQL_Cursor::~MySQL_Cursor() { 66 | #ifdef WITH_SELECT 67 | close(); 68 | #endif 69 | } 70 | 71 | 72 | /* 73 | execute - Execute a SQL statement 74 | 75 | This method executes the query specified as a character array. It copies 76 | the query to the local buffer then calls the execute_query() method to 77 | execute the query. 78 | 79 | If a result set is available after the query executes, the field 80 | packets and rows can be read separately using the get_field() and 81 | get_row() methods. 82 | 83 | query[in] SQL statement (using normal memory access) 84 | progmem[in] True if string is in program memory 85 | 86 | Returns boolean - True = a result set is available for reading 87 | */ 88 | boolean MySQL_Cursor::execute(const char *query, boolean progmem) 89 | { 90 | int query_len; // length of query 91 | 92 | if (!conn->connected()) { 93 | conn->show_error(NOT_CONNECTED, true); 94 | return false; 95 | } 96 | 97 | if (progmem) { 98 | query_len = (int)strlen_P(query); 99 | } else { 100 | query_len = (int)strlen(query); 101 | } 102 | if (conn->buffer != NULL) 103 | free(conn->buffer); 104 | 105 | conn->buffer = (byte *)malloc(query_len+5); 106 | 107 | // Write query to packet 108 | if (progmem) { 109 | for (int c = 0; c < query_len; c++) 110 | conn->buffer[c+5] = pgm_read_byte_near(query+c); 111 | } else { 112 | memcpy(&conn->buffer[5], query, query_len); 113 | } 114 | 115 | // Send the query 116 | return execute_query(query_len); 117 | } 118 | 119 | 120 | /* 121 | execute_query - execute a query 122 | 123 | This method sends the query string to the server and waits for a 124 | response. If the result is a result set, it returns true, if it is 125 | an error, it processes the error packet and prints the error via 126 | Serial.print(). If it is an Ok packet, it parses the packet and 127 | returns false. 128 | 129 | query_len[in] Number of bytes in the query string 130 | 131 | Returns boolean - true = result set available, 132 | false = no result set returned. 133 | */ 134 | boolean MySQL_Cursor::execute_query(int query_len) 135 | { 136 | if (!conn->buffer) 137 | return false; 138 | 139 | // Reset the rows affected and last insert id before query. 140 | rows_affected = -1; 141 | last_insert_id = -1; 142 | 143 | conn->store_int(&conn->buffer[0], query_len+1, 3); 144 | conn->buffer[3] = byte(0x00); 145 | conn->buffer[4] = byte(0x03); // command packet 146 | 147 | // Send the query 148 | conn->client->write((uint8_t*)conn->buffer, query_len + 5); 149 | conn->client->flush(); 150 | 151 | // Read a response packet and check it for Ok or Error. 152 | conn->read_packet(); 153 | int res = conn->get_packet_type(); 154 | if (res == MYSQL_ERROR_PACKET) { 155 | conn->parse_error_packet(); 156 | return false; 157 | } else if (res == MYSQL_OK_PACKET || res == MYSQL_EOF_PACKET) { 158 | // Read the rows affected and last insert id. 159 | int loc1 = conn->buffer[5]; // Location of rows affected 160 | int loc2 = 5; 161 | if (loc1 < 252) { 162 | loc2++; 163 | } else if (loc1 == 252) { 164 | loc2 += 2; 165 | } else if (loc1 == 253) { 166 | loc2 += 3; 167 | } else { 168 | loc2 += 8; 169 | } 170 | rows_affected = conn->read_lcb_int(5); 171 | if (rows_affected > 0) { 172 | last_insert_id = conn->read_lcb_int(loc2); 173 | } 174 | return true; 175 | } 176 | 177 | // Not an Ok packet, so we now have the result set to process. 178 | #ifdef WITH_SELECT 179 | columns_read = false; 180 | #endif 181 | return true; 182 | } 183 | 184 | 185 | #ifdef WITH_SELECT 186 | /* 187 | Close 188 | 189 | Takes care of removing allocated memory. 190 | */ 191 | void MySQL_Cursor::close() { 192 | free_columns_buffer(); 193 | free_row_buffer(); 194 | } 195 | 196 | 197 | /* 198 | get_columns - Get a list of the columns (fields) 199 | 200 | This method returns an instance of the column_names structure 201 | that contains an array of fields. 202 | 203 | Note: you should call free_columns_buffer() after consuming 204 | the field data to free memory. 205 | */ 206 | column_names *MySQL_Cursor::get_columns() { 207 | free_columns_buffer(); 208 | free_row_buffer(); 209 | num_cols = 0; 210 | if (get_fields()) { 211 | columns_read = true; 212 | return &columns; 213 | } 214 | else { 215 | return NULL; 216 | } 217 | } 218 | 219 | 220 | /* 221 | get_next_row - Iterator for reading rows from a result set 222 | 223 | This method returns an instance of a structure (row_values) 224 | that contains an array of strings representing the row 225 | values returned from the server. 226 | 227 | The caller can use the values however needed - by first 228 | converting them to a specific type or as a string. 229 | */ 230 | row_values *MySQL_Cursor::get_next_row() { 231 | int res = 0; 232 | 233 | free_row_buffer(); 234 | 235 | // Read the rows 236 | res = get_row_values(); 237 | if (res != MYSQL_EOF_PACKET) { 238 | return &row; 239 | } 240 | return NULL; 241 | } 242 | 243 | 244 | /* 245 | show_results - Show a result set from the server via Serial.print 246 | 247 | This method reads a result from the server and displays it via the 248 | via the Serial.print methods. It can be used in cases where 249 | you may want to issue a SELECT or SHOW and see the results on your 250 | computer from the Arduino. 251 | 252 | It is also a good example of how to read a result set from the 253 | because it uses the public methods designed to return result 254 | sets from the server. 255 | */ 256 | void MySQL_Cursor::show_results() { 257 | column_names *cols; 258 | int rows = 0; 259 | 260 | // Get the columns 261 | cols = get_columns(); 262 | if (cols == NULL) { 263 | return; 264 | } 265 | 266 | for (int f = 0; f < columns.num_fields; f++) { 267 | Serial.print(columns.fields[f]->name); 268 | if (f < columns.num_fields-1) 269 | Serial.print(','); 270 | } 271 | Serial.println(); 272 | 273 | // Read the rows 274 | while (get_next_row()) { 275 | rows++; 276 | for (int f = 0; f < columns.num_fields; f++) { 277 | Serial.print(row.values[f]); 278 | if (f < columns.num_fields-1) 279 | Serial.print(','); 280 | } 281 | free_row_buffer(); 282 | Serial.println(); 283 | } 284 | 285 | // Report how many rows were read 286 | Serial.print(rows); 287 | conn->show_error(ROWS, true); 288 | free_columns_buffer(); 289 | 290 | // Free any post-query messages in queue for stored procedures 291 | clear_ok_packet(); 292 | } 293 | 294 | 295 | /* 296 | clear_ok_packet - clear last Ok packet (if present) 297 | 298 | This method reads the header and status to see if this is an Ok packet. 299 | If it is, it reads the packet and discards it. This is useful for 300 | processing result sets from stored procedures. 301 | 302 | Returns False if the packet was not an Ok packet. 303 | */ 304 | bool MySQL_Cursor::clear_ok_packet() { 305 | int num = 0; 306 | 307 | do { 308 | num = conn->client->available(); 309 | if (num > 0) { 310 | conn->read_packet(); 311 | if (conn->get_packet_type() != MYSQL_OK_PACKET) { 312 | conn->parse_error_packet(); 313 | return false; 314 | } 315 | } 316 | } while (num > 0); 317 | rows_affected = -1; 318 | last_insert_id = -1; 319 | return true; 320 | } 321 | 322 | 323 | /* 324 | free_columns_buffer - Free memory allocated for column names 325 | 326 | This method frees the memory allocated during the get_columns() 327 | method. 328 | 329 | NOTICE: Failing to call this method after calling get_columns() 330 | and consuming the column names, types, etc. will result 331 | in a memory leak. The size of the leak will depend on 332 | the size of the combined column names (bytes). 333 | */ 334 | void MySQL_Cursor::free_columns_buffer() { 335 | // clear the columns 336 | for (int f = 0; f < MAX_FIELDS; f++) { 337 | if (columns.fields[f] != NULL) { 338 | free(columns.fields[f]->db); 339 | free(columns.fields[f]->table); 340 | free(columns.fields[f]->name); 341 | free(columns.fields[f]); 342 | } 343 | columns.fields[f] = NULL; 344 | } 345 | num_cols = 0; 346 | columns_read = false; 347 | } 348 | 349 | 350 | /* 351 | free_row_buffer - Free memory allocated for row values 352 | 353 | This method frees the memory allocated during the get_next_row() 354 | method. 355 | 356 | NOTICE: You must call this method at least once after you 357 | have consumed the values you wish to process. Failing 358 | to do will result in a memory leak equal to the sum 359 | of the length of values and one byte for each max cols. 360 | */ 361 | void MySQL_Cursor::free_row_buffer() { 362 | // clear the row 363 | for (int f = 0; f < MAX_FIELDS; f++) { 364 | if (row.values[f] != NULL) { 365 | free(row.values[f]); 366 | } 367 | row.values[f] = NULL; 368 | } 369 | } 370 | 371 | 372 | /* 373 | read_string - Retrieve a string from the buffer 374 | 375 | This reads a string from the buffer. It reads the length of the string 376 | as the first byte. 377 | 378 | offset[in] offset from start of buffer 379 | 380 | Returns string - String from the buffer 381 | */ 382 | char *MySQL_Cursor::read_string(int *offset) { 383 | char *str; 384 | int len_bytes = conn->get_lcb_len(conn->buffer[*offset]); 385 | int len = conn->read_int(*offset, len_bytes); 386 | if (len == 251) { 387 | // This is a null field. 388 | str = (char *)malloc(5); 389 | strncpy(str, "NULL", 4); 390 | str[4] = 0x00; 391 | *offset += len_bytes; 392 | } else { 393 | str = (char *)malloc(len+1); 394 | strncpy(str, (char *)&conn->buffer[*offset+len_bytes], len); 395 | str[len] = 0x00; 396 | *offset += len_bytes+len; 397 | } 398 | return str; 399 | } 400 | 401 | 402 | /* 403 | get_field - Read a field from the server 404 | 405 | This method reads a field packet from the server. Field packets are 406 | defined as: 407 | 408 | Bytes Name 409 | ----- ---- 410 | n (Length Coded String) catalog 411 | n (Length Coded String) db 412 | n (Length Coded String) table 413 | n (Length Coded String) org_table 414 | n (Length Coded String) name 415 | n (Length Coded String) org_name 416 | 1 (filler) 417 | 2 charsetnr 418 | 4 length 419 | 1 type 420 | 2 flags 421 | 1 decimals 422 | 2 (filler), always 0x00 423 | n (Length Coded Binary) default 424 | 425 | Note: the sum of all db, column, and field names must be < 255 in length 426 | */ 427 | int MySQL_Cursor::get_field(field_struct *fs) { 428 | int len_bytes; 429 | int len; 430 | int offset; 431 | 432 | // Read field packets until EOF 433 | conn->read_packet(); 434 | if (conn->buffer && conn->buffer[4] != MYSQL_EOF_PACKET) { 435 | // calculate location of db 436 | len_bytes = conn->get_lcb_len(4); 437 | len = conn->read_int(4, len_bytes); 438 | offset = 4+len_bytes+len; 439 | fs->db = read_string(&offset); 440 | // get table 441 | fs->table = read_string(&offset); 442 | // calculate location of name 443 | len_bytes = conn->get_lcb_len(offset); 444 | len = conn->read_int(offset, len_bytes); 445 | offset += len_bytes+len; 446 | fs->name = read_string(&offset); 447 | return 0; 448 | } 449 | return MYSQL_EOF_PACKET; 450 | } 451 | 452 | 453 | /* 454 | get_row - Read a row from the server and store it in the buffer 455 | 456 | This reads a single row and stores it in the buffer. If there are 457 | no more rows, it returns MYSQL_EOF_PACKET. A row packet is defined as 458 | follows. 459 | 460 | Bytes Name 461 | ----- ---- 462 | n (Length Coded String) (column value) 463 | ... 464 | 465 | Note: each column is store as a length coded string concatenated 466 | as a single stream 467 | 468 | Returns integer - MYSQL_EOF_PACKET if no more rows, 0 if more rows available 469 | */ 470 | int MySQL_Cursor::get_row() { 471 | // Read row packets 472 | conn->read_packet(); 473 | if (conn->buffer && conn->buffer[4] != MYSQL_EOF_PACKET) 474 | return 0; 475 | return MYSQL_EOF_PACKET; 476 | } 477 | 478 | 479 | /* 480 | get_fields - reads the fields from the read buffer 481 | 482 | This method is used to read the field names, types, etc. 483 | from the read buffer and store them in the columns structure 484 | in the class. 485 | */ 486 | boolean MySQL_Cursor::get_fields() 487 | { 488 | int num_fields = 0; 489 | int res = 0; 490 | 491 | if (conn->buffer == NULL) { 492 | return false; 493 | } 494 | num_fields = conn->buffer[4]; // From result header packet 495 | columns.num_fields = num_fields; 496 | num_cols = num_fields; // Save this for later use 497 | for (int f = 0; f < num_fields; f++) { 498 | field_struct *field = (field_struct *)malloc(sizeof(field_struct)); 499 | res = get_field(field); 500 | if (res == MYSQL_EOF_PACKET) { 501 | conn->show_error(BAD_MOJO, true); 502 | return false; 503 | } 504 | columns.fields[f] = field; 505 | } 506 | conn->read_packet(); // EOF packet 507 | return true; 508 | } 509 | 510 | 511 | /* 512 | get_row_values - reads the row values from the read buffer 513 | 514 | This method is used to read the row column values 515 | from the read buffer and store them in the row structure 516 | in the class. 517 | */ 518 | int MySQL_Cursor::get_row_values() { 519 | int res = 0; 520 | int offset = 0; 521 | 522 | // It is an error to try to read rows before columns 523 | // are read. 524 | if (!columns_read) { 525 | conn->show_error(READ_COLS, true); 526 | return MYSQL_EOF_PACKET; 527 | } 528 | // Drop any row data already read 529 | free_row_buffer(); 530 | 531 | // Read a row 532 | res = get_row(); 533 | if (res != MYSQL_EOF_PACKET) { 534 | offset = 4; 535 | for (int f = 0; f < num_cols; f++) { 536 | row.values[f] = read_string(&offset); 537 | } 538 | } 539 | return res; 540 | } 541 | 542 | #endif // WITH_SELECT 543 | -------------------------------------------------------------------------------- /src/MySQL_Cursor.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, 2016 Oracle and/or its affiliates. All rights reserved. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; version 2 of the License. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, write to the Free Software 15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 16 | 17 | MySQL_Cursor.h - Run queries on a MySQL Server 18 | 19 | This header file defines a cursor class for running queries on a MySQL 20 | server. You can issue any command using SQL statements for inserting or 21 | retrieving data. 22 | 23 | Change History: 24 | 25 | Version 1.0.0a Created by Dr. Charles A. Bell, April 2012. 26 | Version 1.0.0b Updated by Dr. Charles A. Bell, October 2013. 27 | Version 1.0.1b Updated by Dr. Charles A. Bell, February 2014. 28 | Version 1.0.2b Updated by Dr. Charles A. Bell, April 2014. 29 | Version 1.0.3rc Updated by Dr. Charles A. Bell, March 2015. 30 | Version 1.0.4ga Updated by Dr. Charles A. Bell, July 2015. 31 | Version 1.1.0a Created by Dr. Charles A. Bell, January 2016. 32 | Version 1.1.1a Created by Dr. Charles A. Bell, January 2016. 33 | Version 1.1.2b Created by Dr. Charles A. Bell, November 2016. 34 | Version 1.2.0 Created by Dr. Charles A. Bell, March 2020. 35 | */ 36 | #ifndef MYSQL_QUERY_H 37 | #define MYSQL_QUERY_H 38 | 39 | #include 40 | 41 | #define WITH_SELECT // Comment this if you don't need SELECT queries. 42 | // Reduces memory footprint of the library. 43 | #define MAX_FIELDS 0x20 // Maximum number of fields. Reduce to save memory. Default=32 44 | 45 | #ifdef WITH_SELECT 46 | // Structure for retrieving a field (minimal implementation). 47 | typedef struct { 48 | char *db; 49 | char *table; 50 | char *name; 51 | } field_struct; 52 | 53 | // Structure for storing result set metadata. 54 | typedef struct { 55 | int num_fields; // actual number of fields 56 | field_struct *fields[MAX_FIELDS]; 57 | } column_names; 58 | 59 | // Structure for storing row data. 60 | typedef struct { 61 | char *values[MAX_FIELDS]; 62 | } row_values; 63 | #endif // WITH_SELECT 64 | 65 | class MySQL_Cursor { 66 | public: 67 | MySQL_Cursor(MySQL_Connection *connection); 68 | ~MySQL_Cursor(); 69 | boolean execute(const char *query, boolean progmem=false); 70 | 71 | private: 72 | boolean execute_query(int query_len); 73 | 74 | #ifdef WITH_SELECT 75 | public: 76 | void close(); 77 | column_names *get_columns(); 78 | row_values *get_next_row(); 79 | void show_results(); 80 | int get_rows_affected() { return rows_affected; } 81 | int get_last_insert_id() { return last_insert_id; } 82 | 83 | private: 84 | void free_columns_buffer(); 85 | void free_row_buffer(); 86 | bool clear_ok_packet(); 87 | 88 | char *read_string(int *offset); 89 | int get_field(field_struct *fs); 90 | int get_row(); 91 | boolean get_fields(); 92 | int get_row_values(); 93 | column_names *query_result(); 94 | 95 | boolean columns_read; 96 | int num_cols; 97 | column_names columns; 98 | row_values row; 99 | int rows_affected; 100 | int last_insert_id; 101 | #endif 102 | 103 | MySQL_Connection *conn; 104 | }; 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /src/MySQL_Encrypt_Sha1.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * GNU GPL v3 3 | * 4 | * This file is part of the code entitled, "cryptosuite" available at 5 | * https://code.google.com/p/cryptosuite/. The file was copied from that 6 | * repository and renamed for use in Connector/Arduino to preserve 7 | * compatibility and protect against namespace collisions for users who 8 | * want to use the full cryptosuite functionality. For Connector/Arduino 9 | * all that is needed is this one sha1 class. 10 | * 11 | * Note: #defines renamed to prevent collisions 12 | */ 13 | #include 14 | #include "MySQL_Encrypt_Sha1.h" 15 | 16 | #define MYSQL_SHA1_K0 0x5a827999 17 | #define MYSQL_SHA1_K20 0x6ed9eba1 18 | #define MYSQL_SHA1_K40 0x8f1bbcdc 19 | #define MYSQL_SHA1_K60 0xca62c1d6 20 | 21 | const uint8_t sha1InitState[] PROGMEM = { 22 | 0x01,0x23,0x45,0x67, // H0 23 | 0x89,0xab,0xcd,0xef, // H1 24 | 0xfe,0xdc,0xba,0x98, // H2 25 | 0x76,0x54,0x32,0x10, // H3 26 | 0xf0,0xe1,0xd2,0xc3 // H4 27 | }; 28 | 29 | void Encrypt_SHA1::init(void) { 30 | memcpy_P(state.b,sha1InitState,HASH_LENGTH); 31 | byteCount = 0; 32 | bufferOffset = 0; 33 | } 34 | 35 | uint32_t Encrypt_SHA1::rol32(uint32_t number, uint8_t bits) { 36 | return ((number << bits) | (number >> (32-bits))); 37 | } 38 | 39 | void Encrypt_SHA1::hashBlock() { 40 | // SHA1 only for now 41 | uint8_t i; 42 | uint32_t a,b,c,d,e,t; 43 | 44 | a=state.w[0]; 45 | b=state.w[1]; 46 | c=state.w[2]; 47 | d=state.w[3]; 48 | e=state.w[4]; 49 | for (i=0; i<80; i++) { 50 | if (i>=16) { 51 | t = buffer.w[(i+13)&15] ^ buffer.w[(i+8)&15] ^ buffer.w[(i+2)&15] ^ buffer.w[i&15]; 52 | buffer.w[i&15] = rol32(t,1); 53 | } 54 | if (i<20) { 55 | t = (d ^ (b & (c ^ d))) + MYSQL_SHA1_K0; 56 | } else if (i<40) { 57 | t = (b ^ c ^ d) + MYSQL_SHA1_K20; 58 | } else if (i<60) { 59 | t = ((b & c) | (d & (b | c))) + MYSQL_SHA1_K40; 60 | } else { 61 | t = (b ^ c ^ d) + MYSQL_SHA1_K60; 62 | } 63 | t+=rol32(a,5) + e + buffer.w[i&15]; 64 | e=d; 65 | d=c; 66 | c=rol32(b,30); 67 | b=a; 68 | a=t; 69 | } 70 | state.w[0] += a; 71 | state.w[1] += b; 72 | state.w[2] += c; 73 | state.w[3] += d; 74 | state.w[4] += e; 75 | } 76 | 77 | void Encrypt_SHA1::addUncounted(uint8_t data) { 78 | buffer.b[bufferOffset ^ 3] = data; 79 | bufferOffset++; 80 | if (bufferOffset == BLOCK_LENGTH) { 81 | hashBlock(); 82 | bufferOffset = 0; 83 | } 84 | } 85 | 86 | size_t Encrypt_SHA1::write(uint8_t data) { 87 | ++byteCount; 88 | addUncounted(data); 89 | return 1; 90 | } 91 | 92 | size_t Encrypt_SHA1::write(uint8_t* data, int length) { 93 | for (int i=0; i> 29); // Shifting to multiply by 8 111 | addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as 112 | addUncounted(byteCount >> 13); // byte. 113 | addUncounted(byteCount >> 5); 114 | addUncounted(byteCount << 3); 115 | } 116 | 117 | 118 | uint8_t* Encrypt_SHA1::result(void) { 119 | // Pad to complete the last block 120 | pad(); 121 | 122 | // Swap byte order back 123 | for (int i=0; i<5; i++) { 124 | uint32_t a,b; 125 | a=state.w[i]; 126 | b=a<<24; 127 | b|=(a<<8) & 0x00ff0000; 128 | b|=(a>>8) & 0x0000ff00; 129 | b|=a>>24; 130 | state.w[i]=b; 131 | } 132 | 133 | // Return pointer to hash (20 characters) 134 | return state.b; 135 | } 136 | 137 | 138 | #define HMAC_IPAD 0x36 139 | #define HMAC_OPAD 0x5c 140 | 141 | Encrypt_SHA1 Sha1; 142 | -------------------------------------------------------------------------------- /src/MySQL_Encrypt_Sha1.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GNU GPL v3 3 | * 4 | * This file is part of the code entitled, "cryptosuite" available at 5 | * https://code.google.com/p/cryptosuite/. The file was copied from that 6 | * repository and renamed for use in Connector/Arduino to preserve 7 | * compatibility and protect against namespace collisions for users who 8 | * want to use the full cryptosuite functionality. For Connector/Arduino 9 | * all that is needed is this one sha1 class. 10 | */ 11 | #ifndef ENCRYPT_SHA1_H 12 | #define ENCRYPT_SHA1_H 13 | 14 | #include 15 | #include "Print.h" 16 | 17 | #define HASH_LENGTH 20 18 | #define BLOCK_LENGTH 64 19 | 20 | union _buffer { 21 | uint8_t b[BLOCK_LENGTH]; 22 | uint32_t w[BLOCK_LENGTH/4]; 23 | }; 24 | union _state { 25 | uint8_t b[HASH_LENGTH]; 26 | uint32_t w[HASH_LENGTH/4]; 27 | }; 28 | 29 | class Encrypt_SHA1 : public Print 30 | { 31 | public: 32 | void init(void); 33 | void initHmac(const uint8_t* secret, int secretLength); 34 | uint8_t* result(void); 35 | virtual size_t write(uint8_t); 36 | virtual size_t write(uint8_t* data, int length); 37 | using Print::write; 38 | private: 39 | void pad(); 40 | void addUncounted(uint8_t data); 41 | void hashBlock(); 42 | uint32_t rol32(uint32_t number, uint8_t bits); 43 | _buffer buffer; 44 | uint8_t bufferOffset; 45 | _state state; 46 | uint32_t byteCount; 47 | uint8_t keyBuffer[BLOCK_LENGTH]; 48 | uint8_t innerHash[HASH_LENGTH]; 49 | 50 | }; 51 | 52 | extern Encrypt_SHA1 Sha1; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/MySQL_Packet.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, 2016 Oracle and/or its affiliates. All rights reserved. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; version 2 of the License. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, write to the Free Software 15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 16 | 17 | MySQL_Packet.cpp - Packet library for communicating with a MySQL Server 18 | 19 | Change History: 20 | 21 | Version 1.0.0a Created by Dr. Charles A. Bell, April 2012. 22 | Version 1.0.0b Updated by Dr. Charles A. Bell, October 2013. 23 | Version 1.0.1b Updated by Dr. Charles A. Bell, February 2014. 24 | Version 1.0.2b Updated by Dr. Charles A. Bell, April 2014. 25 | Version 1.0.3rc Updated by Dr. Charles A. Bell, March 2015. 26 | Version 1.0.4ga Updated by Dr. Charles A. Bell, July 2015. 27 | Version 1.1.0a Created by Dr. Charles A. Bell, January 2016. 28 | Version 1.1.1a Created by Dr. Charles A. Bell, January 2016. 29 | Version 1.1.2b Created by Dr. Charles A. Bell, November 2016. 30 | Version 1.2.0 Created by Dr. Charles A. Bell, March 2020. 31 | */ 32 | #include 33 | #include 34 | #include 35 | 36 | #define MYSQL_DATA_TIMEOUT 3000 // Wifi client wait in milliseconds 37 | #define MYSQL_WAIT_INTERVAL 300 // WiFi client wait interval 38 | 39 | /* 40 | Constructor 41 | 42 | Initialize the buffer and store client instance. 43 | */ 44 | MySQL_Packet::MySQL_Packet(Client *client_instance) { 45 | buffer = NULL; 46 | client = client_instance; 47 | } 48 | 49 | /* 50 | show_error 51 | 52 | Print the curent error message. 53 | 54 | msg[in] Message to print 55 | EOL[in] True if we print EOLN character 56 | */ 57 | void MySQL_Packet::show_error(const char *msg, bool EOL) { 58 | #ifdef DEBUG 59 | char pos; 60 | while ((pos = pgm_read_byte(msg))) { 61 | Serial.print(pos); 62 | msg++; 63 | } 64 | if (EOL) 65 | Serial.println(); 66 | #endif 67 | } 68 | 69 | /* 70 | send_authentication_packet 71 | 72 | This method builds a response packet used to respond to the server's 73 | challenge packet (called the handshake packet). It includes the user 74 | name and password scrambled using the SHA1 seed from the handshake 75 | packet. It also sets the character set (default is 8 which you can 76 | change to meet your needs). 77 | 78 | Note: you can also set the default database in this packet. See 79 | the code before for a comment on where this happens. 80 | 81 | The authentication packet is defined as follows. 82 | 83 | Bytes Name 84 | ----- ---- 85 | 4 client_flags 86 | 4 max_packet_size 87 | 1 charset_number 88 | 23 (filler) always 0x00... 89 | n (Null-Terminated String) user 90 | n (Length Coded Binary) scramble_buff (1 + x bytes) 91 | n (Null-Terminated String) databasename (optional) 92 | 93 | user[in] User name 94 | password[in] password 95 | db[in] default database 96 | */ 97 | void MySQL_Packet::send_authentication_packet(char *user, char *password, 98 | char *db) 99 | { 100 | if (buffer != NULL) 101 | free(buffer); 102 | 103 | buffer = (byte *)malloc(256); 104 | 105 | int size_send = 4; 106 | 107 | // client flags 108 | buffer[size_send] = byte(0x0D); 109 | buffer[size_send+1] = byte(0xa6); 110 | buffer[size_send+2] = byte(0x03); 111 | buffer[size_send+3] = byte(0x00); 112 | size_send += 4; 113 | 114 | // max_allowed_packet 115 | buffer[size_send] = 0; 116 | buffer[size_send+1] = 0; 117 | buffer[size_send+2] = 0; 118 | buffer[size_send+3] = 1; 119 | size_send += 4; 120 | 121 | // charset - default is 8 122 | buffer[size_send] = byte(0x08); 123 | size_send += 1; 124 | for(int i = 0; i < 24; i++) 125 | buffer[size_send+i] = 0x00; 126 | size_send += 23; 127 | 128 | // user name 129 | memcpy((char *)&buffer[size_send], user, strlen(user)); 130 | size_send += strlen(user) + 1; 131 | buffer[size_send-1] = 0x00; 132 | 133 | // password - see scramble password 134 | byte *scramble = (uint8_t *)malloc(20); 135 | if (scramble_password(password, scramble)) { 136 | buffer[size_send] = 0x14; 137 | size_send += 1; 138 | for (int i = 0; i < 20; i++) 139 | buffer[i+size_send] = scramble[i]; 140 | size_send += 20; 141 | buffer[size_send] = 0x00; 142 | } 143 | free(scramble); 144 | 145 | if (db) { 146 | memcpy((char *)&buffer[size_send], db, strlen(db)); 147 | size_send += strlen(db) + 1; 148 | buffer[size_send-1] = 0x00; 149 | } else { 150 | buffer[size_send+1] = 0x00; 151 | size_send += 1; 152 | } 153 | 154 | // Write packet size 155 | int p_size = size_send - 4; 156 | store_int(&buffer[0], p_size, 3); 157 | buffer[3] = byte(0x01); 158 | 159 | // Write the packet 160 | client->write((uint8_t*)buffer, size_send); 161 | client->flush(); 162 | } 163 | 164 | 165 | /* 166 | scramble_password - Build a SHA1 scramble of the user password 167 | 168 | This method uses the password hash seed sent from the server to 169 | form a SHA1 hash of the password. This is used to send back to 170 | the server to complete the challenge and response step in the 171 | authentication handshake. 172 | 173 | password[in] User's password in clear text 174 | pwd_hash[in] Seed from the server 175 | 176 | Returns boolean - True = scramble succeeded 177 | */ 178 | boolean MySQL_Packet::scramble_password(char *password, byte *pwd_hash) { 179 | byte *digest; 180 | byte hash1[20]; 181 | byte hash2[20]; 182 | byte hash3[20]; 183 | byte pwd_buffer[40]; 184 | 185 | if (strlen(password) == 0) 186 | return false; 187 | 188 | // hash1 189 | Sha1.init(); 190 | Sha1.print(password); 191 | digest = Sha1.result(); 192 | memcpy(hash1, digest, 20); 193 | 194 | // hash2 195 | Sha1.init(); 196 | Sha1.write(hash1, 20); 197 | digest = Sha1.result(); 198 | memcpy(hash2, digest, 20); 199 | 200 | // hash3 of seed + hash2 201 | Sha1.init(); 202 | memcpy(pwd_buffer, &seed, 20); 203 | memcpy(pwd_buffer+20, hash2, 20); 204 | Sha1.write(pwd_buffer, 40); 205 | digest = Sha1.result(); 206 | memcpy(hash3, digest, 20); 207 | 208 | // XOR for hash4 209 | for (int i = 0; i < 20; i++) 210 | pwd_hash[i] = hash1[i] ^ hash3[i]; 211 | 212 | return true; 213 | } 214 | 215 | /* 216 | wait_for_bytes - Wait until data is available for reading 217 | 218 | This method is used to permit the connector to respond to servers 219 | that have high latency or execute long queries. The timeout is 220 | set by MYSQL_DATA_TIMEOUT. Adjust this value to match the performance of 221 | your server and network. 222 | 223 | It is also used to read how many bytes in total are available from the 224 | server. Thus, it can be used to know how large a data burst is from 225 | the server. 226 | 227 | bytes_need[in] Bytes count to wait for 228 | 229 | Returns integer - Number of bytes available to read. 230 | */ 231 | int MySQL_Packet::wait_for_bytes(int bytes_need) 232 | { 233 | const long wait_till = millis() + MYSQL_DATA_TIMEOUT; 234 | int num = 0; 235 | long now = 0; 236 | 237 | do 238 | { 239 | now = millis(); 240 | num = client->available(); 241 | if (num < bytes_need) 242 | delay(MYSQL_WAIT_INTERVAL); 243 | else 244 | break; 245 | } while (now < wait_till); 246 | 247 | if (num == 0 && now >= wait_till) 248 | client->stop(); 249 | 250 | return num; 251 | } 252 | 253 | /* 254 | read_packet - Read a packet from the server and store it in the buffer 255 | 256 | This method reads the bytes sent by the server as a packet. All packets 257 | have a packet header defined as follows. 258 | 259 | Bytes Name 260 | ----- ---- 261 | 3 Packet Length 262 | 1 Packet Number 263 | 264 | Thus, the length of the packet (not including the packet header) can 265 | be found by reading the first 4 bytes from the server then reading 266 | N bytes for the packet payload. 267 | */ 268 | void MySQL_Packet::read_packet() { 269 | byte local[4]; 270 | 271 | if (buffer) { 272 | free(buffer); 273 | buffer = NULL; 274 | } 275 | 276 | // Read packet header 277 | if (wait_for_bytes(4) < 4) { 278 | show_error(READ_TIMEOUT, true); 279 | return; 280 | } 281 | for (int i = 0; i < 4; i++) 282 | local[i] = client->read(); 283 | 284 | // Get packet length 285 | packet_len = local[0]; 286 | packet_len += (local[1] << 8); 287 | packet_len += ((uint32_t)local[2] << 16); 288 | 289 | // We must wait for slow arriving packets for Ethernet shields only. 290 | /* 291 | if (wait_for_bytes(packet_len) < packet_len) { 292 | show_error(READ_TIMEOUT, true); 293 | return; 294 | } 295 | */ 296 | // Check for valid packet. 297 | if (packet_len < 0) { 298 | show_error(PACKET_ERROR, true); 299 | packet_len = 0; 300 | } 301 | buffer = (byte *)malloc(packet_len+4); 302 | if (buffer == NULL) { 303 | show_error(MEMORY_ERROR, true); 304 | return; 305 | } 306 | for (int i = 0; i < 4; i++) 307 | buffer[i] = local[i]; 308 | 309 | for (int i = 4; i < packet_len+4; i++) 310 | buffer[i] = client->read(); 311 | } 312 | 313 | 314 | /* 315 | parse_handshake_packet - Decipher the server's challenge data 316 | 317 | This method reads the server version string and the seed from the 318 | server. The handshake packet is defined as follows. 319 | 320 | Bytes Name 321 | ----- ---- 322 | 1 protocol_version 323 | n (Null-Terminated String) server_version 324 | 4 thread_id 325 | 8 scramble_buff 326 | 1 (filler) always 0x00 327 | 2 server_capabilities 328 | 1 server_language 329 | 2 server_status 330 | 2 server capabilities (two upper bytes) 331 | 1 length of the scramble seed 332 | 10 (filler) always 0 333 | n rest of the plugin provided data 334 | (at least 12 bytes) 335 | 1 \0 byte, terminating the second part of 336 | a scramble seed 337 | */ 338 | void MySQL_Packet::parse_handshake_packet() { 339 | if (!buffer) 340 | return; 341 | 342 | int i = 5; 343 | do { 344 | i++; 345 | } while (buffer[i-1] != 0x00); 346 | 347 | server_version = (char *)malloc(i-5); 348 | strncpy(server_version, (char *)&buffer[5], i-5); 349 | 350 | // Capture the first 8 characters of seed 351 | i += 4; // Skip thread id 352 | for (int j = 0; j < 8; j++) { 353 | seed[j] = buffer[i+j]; 354 | } 355 | 356 | // Capture rest of seed 357 | i += 27; // skip ahead 358 | for (int j = 0; j < 12; j++) { 359 | seed[j+8] = buffer[i+j]; 360 | } 361 | } 362 | 363 | /* 364 | parse_error_packet - Display the error returned from the server 365 | 366 | This method parses an error packet from the server and displays the 367 | error code and text via Serial.print. The error packet is defined 368 | as follows. 369 | 370 | Note: the error packet is already stored in the buffer since this 371 | packet is not an expected response. 372 | 373 | Bytes Name 374 | ----- ---- 375 | 1 field_count, always = 0xff 376 | 2 errno 377 | 1 (sqlstate marker), always '#' 378 | 5 sqlstate (5 characters) 379 | n message 380 | */ 381 | void MySQL_Packet::parse_error_packet() { 382 | #ifdef DEBUG 383 | Serial.print("Error: "); 384 | Serial.print(read_int(5, 2)); 385 | Serial.print(" = "); 386 | 387 | if (!buffer) 388 | return; 389 | 390 | for (int i = 0; i < packet_len-9; i++) 391 | Serial.print((char)buffer[i+13]); 392 | Serial.println("."); 393 | #endif 394 | } 395 | 396 | 397 | /* 398 | get_packet_type - Returns the packet type received from the server. 399 | 400 | Bytes Name 401 | ----- ---- 402 | 1 (Length Coded Binary) field_count, always = 0 403 | 1-9 (Length Coded Binary) affected_rows 404 | 1-9 (Length Coded Binary) insert_id 405 | 2 server_status 406 | 2 warning_count 407 | n (until end of packet) message 408 | 409 | Returns integer - 0 = successful parse, packet type if not an Ok packet 410 | */ 411 | int MySQL_Packet::get_packet_type() { 412 | if (!buffer) 413 | return -1; 414 | 415 | int type = buffer[4]; 416 | return type; 417 | } 418 | 419 | 420 | /* 421 | get_lcb_len - Retrieves the length of a length coded binary value 422 | 423 | This reads the first byte from the offset into the buffer and returns 424 | the number of bytes (size) that the integer consumes. It is used in 425 | conjunction with read_int() to read length coded binary integers 426 | from the buffer. 427 | 428 | Returns integer - number of bytes integer consumes 429 | */ 430 | int MySQL_Packet::get_lcb_len(int offset) { 431 | if (!buffer) 432 | return 0; 433 | 434 | int read_len = buffer[offset]; 435 | if (read_len > 250) { 436 | // read type: 437 | byte type = buffer[offset+1]; 438 | if (type == 0xfc) 439 | read_len = 2; 440 | else if (type == 0xfd) 441 | read_len = 3; 442 | else if (type == 0xfe) 443 | read_len = 8; 444 | } else { 445 | read_len = 1; 446 | } 447 | return read_len; 448 | } 449 | 450 | /* 451 | read_int - Retrieve an integer from the buffer in size bytes. 452 | 453 | This reads an integer from the buffer at offset position indicated for 454 | the number of bytes specified (size). 455 | 456 | offset[in] offset from start of buffer 457 | size[in] number of bytes to use to store the integer 458 | 459 | Returns integer - integer from the buffer 460 | */ 461 | int MySQL_Packet::read_int(int offset, int size) { 462 | int value = 0; 463 | int new_size = 0; 464 | if (!buffer) 465 | return -1; 466 | if (size == 0) 467 | new_size = get_lcb_len(offset); 468 | if (size == 1) 469 | return buffer[offset]; 470 | new_size = size; 471 | int shifter = (new_size - 1) * 8; 472 | for (int i = new_size; i > 0; i--) { 473 | value += (buffer[i-1] << shifter); 474 | shifter -= 8; 475 | } 476 | return value; 477 | } 478 | 479 | 480 | /* 481 | store_int - Store an integer value into a byte array of size bytes. 482 | 483 | This writes an integer into the buffer at the current position of the 484 | buffer. It will transform an integer of size to a length coded binary 485 | form where 1-3 bytes are used to store the value (set by size). 486 | 487 | buff[in] pointer to location in internal buffer where the 488 | integer will be stored 489 | value[in] integer value to be stored 490 | size[in] number of bytes to use to store the integer 491 | */ 492 | void MySQL_Packet::store_int(byte *buff, long value, int size) { 493 | memset(buff, 0, size); 494 | if (value < 0xff) 495 | buff[0] = (byte)value; 496 | else if (value < 0xffff) { 497 | buff[0] = (byte)value; 498 | buff[1] = (byte)(value >> 8); 499 | } else if (value < 0xffffff) { 500 | buff[0] = (byte)value; 501 | buff[1] = (byte)(value >> 8); 502 | buff[2] = (byte)(value >> 16); 503 | } else if (value < 0xffffff) { 504 | buff[0] = (byte)value; 505 | buff[1] = (byte)(value >> 8); 506 | buff[2] = (byte)(value >> 16); 507 | buff[3] = (byte)(value >> 24); 508 | } 509 | } 510 | 511 | /* 512 | read_lcb_int - Read an integer with len encoded byte 513 | 514 | This reads an integer from the buffer looking at the first byte in the offset 515 | as the encoded length of the integer. 516 | 517 | offset[in] offset from start of buffer 518 | 519 | Returns integer - integer from the buffer 520 | */ 521 | int MySQL_Packet::read_lcb_int(int offset) { 522 | int len_size = 0; 523 | int size = 0; 524 | int value = 0; 525 | if (!buffer) 526 | return -1; 527 | len_size = buffer[offset]; 528 | if (len_size < 252) { 529 | return buffer[offset]; 530 | } else if (len_size == 252) { 531 | len_size = 2; 532 | } else if (len_size == 253) { 533 | len_size = 3; 534 | } else { 535 | len_size = 8; 536 | } 537 | int shifter = (len_size-1) * 8; 538 | for (int i = len_size; i > 0; i--) { 539 | value += (buffer[offset+i] << shifter); 540 | shifter -= 8; 541 | } 542 | return value; 543 | } 544 | 545 | /* 546 | print_packet - Print the contents of a packet via Serial.print 547 | 548 | This method is a diagnostic method. It is best used to decipher a 549 | packet from the server (or before being sent to the server). If you 550 | are looking for additional program memory space, you can safely 551 | delete this method. 552 | */ 553 | void MySQL_Packet::print_packet() { 554 | if (!buffer) 555 | return; 556 | 557 | Serial.print("Packet: "); 558 | Serial.print(buffer[3]); 559 | Serial.print(" contains "); 560 | Serial.print(packet_len+3); 561 | Serial.println(" bytes."); 562 | 563 | Serial.print(" HEX: "); 564 | for (int i = 0; i < packet_len+3; i++) { 565 | Serial.print(buffer[i], HEX); 566 | Serial.print(' '); 567 | } 568 | Serial.println(); 569 | Serial.print("ASCII: "); 570 | for (int i = 0; i < packet_len+3; i++) 571 | Serial.print((char)buffer[i]); 572 | Serial.println(); 573 | } 574 | -------------------------------------------------------------------------------- /src/MySQL_Packet.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, 2016 Oracle and/or its affiliates. All rights reserved. 3 | 4 | This program is free software; you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation; version 2 of the License. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, write to the Free Software 15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 16 | 17 | MySQL_Packet.h - Packet library for communicating with a MySQL Server 18 | 19 | This header file defines the base packet handling code for connecting 20 | to a MySQL server and executing queries. 21 | 22 | Change History: 23 | 24 | Version 1.0.0a Created by Dr. Charles A. Bell, April 2012. 25 | Version 1.0.0b Updated by Dr. Charles A. Bell, October 2013. 26 | Version 1.0.1b Updated by Dr. Charles A. Bell, February 2014. 27 | Version 1.0.2b Updated by Dr. Charles A. Bell, April 2014. 28 | Version 1.0.3rc Updated by Dr. Charles A. Bell, March 2015. 29 | Version 1.0.4ga Updated by Dr. Charles A. Bell, July 2015. 30 | Version 1.1.0a Created by Dr. Charles A. Bell, January 2016. 31 | Version 1.1.1a Created by Dr. Charles A. Bell, January 2016. 32 | Version 1.1.2b Created by Dr. Charles A. Bell, November 2016. 33 | Version 1.2.0 Created by Dr. Charles A. Bell, March 2020. 34 | */ 35 | #ifndef MYSQL_PACKET_H 36 | #define MYSQL_PACKET_H 37 | 38 | #ifdef ARDUINO_ARCH_ESP32 39 | #include 40 | #elif ARDUINO_ARCH_ESP8266 41 | #include 42 | #else 43 | // #include 44 | #include 45 | #endif 46 | 47 | #define MYSQL_OK_PACKET 0x00 48 | #define MYSQL_EOF_PACKET 0xfe 49 | #define MYSQL_ERROR_PACKET 0xff 50 | #define MYSQL_VERSION_STR "1.2.0" 51 | #define DEBUG 52 | 53 | const char MEMORY_ERROR[] PROGMEM = "Memory error."; 54 | const char PACKET_ERROR[] PROGMEM = "Packet error."; 55 | const char READ_TIMEOUT[] PROGMEM = "ERROR: Timeout waiting for client."; 56 | 57 | class MySQL_Packet { 58 | public: 59 | byte *buffer; // buffer for reading packets 60 | int packet_len; // length of current packet 61 | Client *client; // instance of client class (e.g. EthernetClient) 62 | char *server_version; // save server version from handshake 63 | 64 | MySQL_Packet(Client *client_instance); 65 | boolean complete_handshake(char *user, char *password); 66 | void send_authentication_packet(char *user, char *password, 67 | char *db=NULL); 68 | void parse_handshake_packet(); 69 | boolean scramble_password(char *password, byte *pwd_hash); 70 | void read_packet(); 71 | int get_packet_type(); 72 | void parse_error_packet(); 73 | int get_lcb_len(int offset); 74 | int read_int(int offset, int size=0); 75 | void store_int(byte *buff, long value, int size); 76 | int read_lcb_int(int offset); 77 | int wait_for_bytes(int bytes_count); 78 | void show_error(const char *msg, bool EOL = false); 79 | void print_packet(); 80 | 81 | private: 82 | byte seed[20]; 83 | }; 84 | 85 | #endif 86 | --------------------------------------------------------------------------------