First Commit

This commit is contained in:
MindCreeper03
2025-02-27 19:31:50 +01:00
parent bcbb6aff9a
commit e490df1715
2470 changed files with 1479965 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
CHANGES
=======
This file contains a brief summary of changes made from previous versions of
the connector.
1.2.0 - March 2020
------------------
* Added connect with default database.
* Added rows effected, last insert id.
* Stability and speed improvements.
* Removed infinite wait in read_packet() if connection dropped.
Merged wait_for_client() and wait_for_data() into one
universal wait_for_bytes(int).
* Improved connect() speed.
* Improved sending queries speed.
Sending the whole buffer at a time (not byte by byte).
* Fixed bug with check_ok_packet() return value.
check_ok_packet() simplified to get_packet_type().
* Added checks for buffer validity in places it is going to be used.
Improved stability on "incorrect" methods calls.
* Restored WITH_SELECT, but defined by default.
* Added DEBUG define to avoid prints to Serial port.
Useful when Serial is used to communicate with sensors.
* Added example for ESP8266.
1.1.1a - January 2016
---------------------
* Minor issue with deprecated #import fixed. No new functionality.
1.1.0a - January 2016
---------------------
* Major redesign of classes. Now have separate connection and cursor classes
* Removed WITH_SELECT, WITH_WIFI, etc. to avoid confusion and simplify
* Renamed many methods to correspond with traditional MySQL connector
nomenclature
* Added more error handling for lossy networks
* Memory deallocation methods consolidated (see examples)
* More, smaller examples added
* Documentation updated
1.0.4ga - July 2015
--------------------
* Fixed a defect in the get_next_row() method.
* Added the reference manual. Yippee!
1.0.3rc - March 2015
--------------------
* Code has been changed slightly to help with long latency issues over
wifi and slow connections.
* A new cleanup method was added to cleanup a final OK packet after a
stored procedure call with a result.
* Code now compiles without errors for the latest Beta Arduino IDE
(const error).
1.0.2b - April 2014
-------------------
* The WITH_SELECT is turned *OFF* by default. If you want to use select
queries, be sure to uncomment this in the mysql.h file.
* Reduced memory use! The library has been trimmed to save memory.
- All static strings moved to PROGMEM strings
- Unused structures removed (e.g. ok_packet)
- Moved more methods not needed to optional compilation
1.0.1b - February 2014
----------------------
* Added disconnect() method for disconnecting from server.
* Improved error handling for dropped packets.
* Better error handling for lost connections and out of memory detection.
1.0.0b - October 2013
---------------------
* Improved support for result sets (select queries)
* Added conditional compile for use with select queries. If you don't use
select queries, comment out WITH_SELECT in mysql.h to save some memory.
* Added support for WiFi shield using conditional compilation.
* New version() method to check version of the connector.
* Simplified, single-file download
Initial Release - April 2013
----------------------------
* Basic query capability
* Basic result set handling

View File

@@ -0,0 +1,9 @@
MySQL Connector/Arduino
=======================
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.
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!
The MySQL Connector/Arduino is a library that permits you to do exactly that and more!
See the all new [wiki documentation](https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki) for how to get starting using the library.

View File

@@ -0,0 +1,84 @@
/*
MySQL Connector/Arduino Example : basic insert
This example demonstrates how to issue an INSERT query to store data in a
table. For this, we will create a special database and table for testing.
The following are the SQL commands you will need to run in order to setup
your database for running this sketch.
CREATE DATABASE test_arduino;
CREATE TABLE test_arduino.hello_arduino (
num integer primary key auto_increment,
message char(40),
recorded timestamp
);
Here we see one database and a table with three fields; a primary key that
is an auto_increment, a string, and a timestamp. This will demonstrate how
to save a date and time of when the row was inserted, which can help you
determine when data was recorded or updated.
For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.
INSTRUCTIONS FOR USE
1) Create the database and table as shown above.
2) Change the address of the server to the IP address of the MySQL server
3) Change the user and password to a valid MySQL user and password
4) Connect a USB cable to your Arduino
5) Select the correct board and port
6) Compile and upload the sketch to your Arduino
7) Once uploaded, open Serial Monitor (use 115200 speed) and observe
8) After the sketch has run for some time, open a mysql client and issue
the command: "SELECT * FROM test_arduino.hello_arduino" to see the data
recorded. Note the field values and how the database handles both the
auto_increment and timestamp fields for us. You can clear the data with
"DELETE FROM test_arduino.hello_arduino".
Note: The MAC address can be anything so long as it is unique on your network.
Created by: Dr. Charles A. Bell
*/
#include <Ethernet.h>
#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>
byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here
char user[] = "root"; // MySQL user login username
char password[] = "secret"; // MySQL user login password
// Sample query
char INSERT_SQL[] = "INSERT INTO test_arduino.hello_arduino (message) VALUES ('Hello, Arduino!')";
EthernetClient client;
MySQL_Connection conn((Client *)&client);
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial port to connect
Ethernet.begin(mac_addr);
Serial.println("Connecting...");
if (conn.connect(server_addr, 3306, user, password)) {
delay(1000);
}
else
Serial.println("Connection failed.");
}
void loop() {
delay(2000);
Serial.println("Recording data.");
// Initiate the query class instance
MySQL_Cursor *cur_mem = new MySQL_Cursor(&conn);
// Execute the query
cur_mem->execute(INSERT_SQL);
// Note: since there are no results, we do not need to read any data
// Deleting the cursor also frees up memory used
delete cur_mem;
}

View File

@@ -0,0 +1,81 @@
/*
MySQL Connector/Arduino Example : connect by wifi
This example demonstrates how to connect to a MySQL server from an
Arduino using an Arduino-compatible Wifi shield. Note that "compatible"
means it must conform to the Ethernet class library or be a derivative
with the same classes and methods.
For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.
INSTRUCTIONS FOR USE
1) Change the address of the server to the IP address of the MySQL server
2) Change the user and password to a valid MySQL user and password
3) Change the SSID and pass to match your WiFi network
4) Connect a USB cable to your Arduino
5) Select the correct board and port
6) Compile and upload the sketch to your Arduino
7) Once uploaded, open Serial Monitor (use 115200 speed) and observe
If you do not see messages indicating you have a connection, refer to the
manual for troubleshooting tips. The most common issues are the server is
not accessible from the network or the user name and password is incorrect.
Created by: Dr. Charles A. Bell
*/
#include <ESP8266WiFi.h> // Use this for WiFi instead of Ethernet.h
#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>
IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here
char user[] = "root"; // MySQL user login username
char password[] = "secret"; // MySQL user login password
// Sample query
char INSERT_SQL[] = "INSERT INTO test_arduino.hello_arduino (message) VALUES ('Hello, Arduino!')";
// WiFi card example
char ssid[] = "your-ssid"; // your SSID
char pass[] = "ssid-password"; // your SSID Password
WiFiClient client; // Use this for WiFi instead of EthernetClient
MySQL_Connection conn(&client);
MySQL_Cursor* cursor;
void setup()
{
Serial.begin(115200);
while (!Serial); // wait for serial port to connect. Needed for Leonardo only
// Begin WiFi section
Serial.printf("\nConnecting to %s", ssid);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// print out info about the connection:
Serial.println("\nConnected to network");
Serial.print("My IP address is: ");
Serial.println(WiFi.localIP());
Serial.print("Connecting to SQL... ");
if (conn.connect(server_addr, 3306, user, password))
Serial.println("OK.");
else
Serial.println("FAILED.");
// create MySQL cursor object
cursor = new MySQL_Cursor(&conn);
}
void loop()
{
if (conn.connected())
cursor->execute(INSERT_SQL);
delay(5000);
}

View File

@@ -0,0 +1,116 @@
/*
MySQL Connector/Arduino Example : basic select
This example demonstrates how to issue a SELECT query with no parameters
and use the data returned. For this, we use the Cursor class to execute
the query and get the results.
It demonstrates who methods for running queries. The first allows you to
allocate memory for the cursor and later reclaim it, the second shows how
to use a single instance of the cursor use throughout a sketch.
NOTICE: You must download and install the World sample database to run
this sketch unaltered. See http://dev.mysql.com/doc/index-other.html.
CAUTION: Don't mix and match the examples. Use one or the other in your
own sketch -- you'll get compilation errors at the least.
For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.
INSTRUCTIONS FOR USE
1) Change the address of the server to the IP address of the MySQL server
2) Change the user and password to a valid MySQL user and password
3) Connect a USB cable to your Arduino
4) Select the correct board and port
5) Compile and upload the sketch to your Arduino
6) Once uploaded, open Serial Monitor (use 115200 speed) and observe
Note: The MAC address can be anything so long as it is unique on your network.
Created by: Dr. Charles A. Bell
*/
#include <Ethernet.h>
#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>
byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here
char user[] = "root"; // MySQL user login username
char password[] = "secret"; // MySQL user login password
// Sample query
char query[] = "SELECT population FROM world.city WHERE name = 'New York'";
EthernetClient client;
MySQL_Connection conn((Client *)&client);
// Create an instance of the cursor passing in the connection
MySQL_Cursor cur = MySQL_Cursor(&conn);
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial port to connect
Ethernet.begin(mac_addr);
Serial.println("Connecting...");
if (conn.connect(server_addr, 3306, user, password)) {
delay(1000);
}
else
Serial.println("Connection failed.");
}
void loop() {
row_values *row = NULL;
long head_count = 0;
delay(1000);
Serial.println("1) Demonstrating using a cursor dynamically allocated.");
// Initiate the query class instance
MySQL_Cursor *cur_mem = new MySQL_Cursor(&conn);
// Execute the query
cur_mem->execute(query);
// Fetch the columns (required) but we don't use them.
column_names *columns = cur_mem->get_columns();
// Read the row (we are only expecting the one)
do {
row = cur_mem->get_next_row();
if (row != NULL) {
head_count = atol(row->values[0]);
}
} while (row != NULL);
// Deleting the cursor also frees up memory used
delete cur_mem;
// Show the result
Serial.print(" NYC pop = ");
Serial.println(head_count);
delay(500);
Serial.println("2) Demonstrating using a local, global cursor.");
// Execute the query
cur.execute(query);
// Fetch the columns (required) but we don't use them.
cur.get_columns();
// Read the row (we are only expecting the one)
do {
row = cur.get_next_row();
if (row != NULL) {
head_count = atol(row->values[0]);
}
} while (row != NULL);
// Now we close the cursor to free any memory
cur.close();
// Show the result but this time do some math on it
Serial.print(" NYC pop = ");
Serial.println(head_count);
Serial.print(" NYC pop increased by 12 = ");
Serial.println(head_count+12);
}

View File

@@ -0,0 +1,97 @@
/*
MySQL Connector/Arduino Example : complex insert
This example demonstrates how to issue an INSERT query to store data in a
table using data from variables in our sketch. In this case, we supply the
values dynamically.
This sketch simulates storing data from a sensor in a table.
For this, we will create a special database and table for testing.
The following are the SQL commands you will need to run in order to setup
your database for running this sketch.
CREATE DATABASE test_arduino;
CREATE TABLE test_arduino.hello_sensor (
num integer primary key auto_increment,
message char(40),
sensor_num integer,
value float,
recorded timestamp
);
Here we have a table that contains an auto_increment primary key, a text
field, a field to identify the sensor, the value read, and timestamp of
the recorded data.
Note: Since this sketch uses test data, we place the INSERT in the setup()
method so that it runs only once. Typically, you would have the
INSERT in the loop() method after your code to read from the sensor.
For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.
INSTRUCTIONS FOR USE
1) Create the database and table as shown above.
2) Change the address of the server to the IP address of the MySQL server
3) Change the user and password to a valid MySQL user and password
4) Connect a USB cable to your Arduino
5) Select the correct board and port
6) Compile and upload the sketch to your Arduino
7) Once uploaded, open Serial Monitor (use 115200 speed) and observe
8) After the sketch has run for some time, open a mysql client and issue
the command: "SELECT * FROM test_arduino.hello_sensor" to see the data
recorded. Note the field values and how the database handles both the
auto_increment and timestamp fields for us. You can clear the data with
"DELETE FROM test_arduino.hello_sensor".
Note: The MAC address can be anything so long as it is unique on your network.
Created by: Dr. Charles A. Bell
*/
#include <Ethernet.h>
#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>
byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here
char user[] = "root"; // MySQL user login username
char password[] = "secret"; // MySQL user login password
// Sample query
char INSERT_DATA[] = "INSERT INTO test_arduino.hello_sensor (message, sensor_num, value) VALUES ('%s',%d,%s)";
char query[128];
char temperature[10];
EthernetClient client;
MySQL_Connection conn((Client *)&client);
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial port to connect
Ethernet.begin(mac_addr);
Serial.println("Connecting...");
if (conn.connect(server_addr, 3306, user, password)) {
delay(1000);
// Initiate the query class instance
MySQL_Cursor *cur_mem = new MySQL_Cursor(&conn);
// Save
dtostrf(50.125, 1, 1, temperature);
sprintf(query, INSERT_DATA, "test sensor", 24, temperature);
// Execute the query
cur_mem->execute(query);
// Note: since there are no results, we do not need to read any data
// Deleting the cursor also frees up memory used
delete cur_mem;
Serial.println("Data recorded.");
}
else
Serial.println("Connection failed.");
conn.close();
}
void loop() {
}

View File

@@ -0,0 +1,106 @@
/*
MySQL Connector/Arduino Example : complex select
This example demonstrates how to issue a SELECT query with parameters that
we provide from code. Thus, it demonstrates how to build query parameters
dynamically.
Notice also the sketch demonstrates how to read columns and rows from
the result set. Study this example until you are familiar with how to
do this before writing your own sketch to read and consume query results.
For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.
NOTICE: You must download and install the World sample database to run
this sketch unaltered. See http://dev.mysql.com/doc/index-other.html.
INSTRUCTIONS FOR USE
1) Change the address of the server to the IP address of the MySQL server
2) Change the user and password to a valid MySQL user and password
3) Connect a USB cable to your Arduino
4) Select the correct board and port
5) Compile and upload the sketch to your Arduino
6) Once uploaded, open Serial Monitor (use 115200 speed) and observe
Note: The MAC address can be anything so long as it is unique on your network.
Created by: Dr. Charles A. Bell
*/
#include <Ethernet.h>
#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>
byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here
char user[] = "root"; // MySQL user login username
char password[] = "secret"; // MySQL user login password
// Sample query
//
// Notice the "%lu" - that's a placeholder for the parameter we will
// supply. See sprintf() documentation for more formatting specifier
// options
const char QUERY_POP[] = "SELECT name, population FROM world.city WHERE population > %lu ORDER BY population DESC;";
char query[128];
EthernetClient client;
MySQL_Connection conn((Client *)&client);
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial port to connect
Ethernet.begin(mac_addr);
Serial.println("Connecting...");
if (conn.connect(server_addr, 3306, user, password)) {
delay(1000);
}
else
Serial.println("Connection failed.");
}
void loop() {
delay(1000);
Serial.println("> Running SELECT with dynamically supplied parameter");
// Initiate the query class instance
MySQL_Cursor *cur_mem = new MySQL_Cursor(&conn);
// Supply the parameter for the query
// Here we use the QUERY_POP as the format string and query as the
// destination. This uses twice the memory so another option would be
// to allocate one buffer for all formatted queries or allocate the
// memory as needed (just make sure you allocate enough memory and
// free it when you're done!).
sprintf(query, QUERY_POP, 9000000);
// Execute the query
cur_mem->execute(query);
// Fetch the columns and print them
column_names *cols = cur_mem->get_columns();
for (int f = 0; f < cols->num_fields; f++) {
Serial.print(cols->fields[f]->name);
if (f < cols->num_fields-1) {
Serial.print(',');
}
}
Serial.println();
// Read the rows and print them
row_values *row = NULL;
do {
row = cur_mem->get_next_row();
if (row != NULL) {
for (int f = 0; f < cols->num_fields; f++) {
Serial.print(row->values[f]);
if (f < cols->num_fields-1) {
Serial.print(',');
}
}
Serial.println();
}
} while (row != NULL);
// Deleting the cursor also frees up memory used
delete cur_mem;
}

View File

@@ -0,0 +1,56 @@
/*
MySQL Connector/Arduino Example : connect
This example demonstrates how to connect to a MySQL server from an
Arduino using an Arduino-compatible Ethernet shield. Note that "compatible"
means it must conform to the Ethernet class library or be a derivative
with the same classes and methods.
For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.
INSTRUCTIONS FOR USE
1) Change the address of the server to the IP address of the MySQL server
2) Change the user and password to a valid MySQL user and password
3) Connect a USB cable to your Arduino
4) Select the correct board and port
5) Compile and upload the sketch to your Arduino
6) Once uploaded, open Serial Monitor (use 115200 speed) and observe
If you do not see messages indicating you have a connection, refer to the
manual for troubleshooting tips. The most common issues are the server is
not accessible from the network or the user name and password is incorrect.
Note: The MAC address can be anything so long as it is unique on your network.
Created by: Dr. Charles A. Bell
*/
#include <Ethernet.h>
#include <MySQL_Connection.h>
byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here
char user[] = "root"; // MySQL user login username
char password[] = "secret"; // MySQL user login password
EthernetClient client;
MySQL_Connection conn((Client *)&client);
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial port to connect
Ethernet.begin(mac_addr);
Serial.println("Connecting...");
if (conn.connect(server_addr, 3306, user, password)) {
delay(1000);
// You would add your code here to run a query once on startup.
}
else
Serial.println("Connection failed.");
conn.close();
}
void loop() {
}

View File

@@ -0,0 +1,63 @@
/*
MySQL Connector/Arduino Example : connect by hostname
This example demonstrates how to connect to a MySQL server resolving the
hostname for cases when you do not know the IP address of the server or
it changes because it is in the cloud.
For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.
INSTRUCTIONS FOR USE
1) Change the hostname variable to the hostname of the MySQL server
2) Change the user and password to a valid MySQL user and password
3) Connect a USB cable to your Arduino
4) Select the correct board and port
5) Compile and upload the sketch to your Arduino
6) Once uploaded, open Serial Monitor (use 115200 speed) and observe
If you do not see messages indicating you have a connection, refer to the
manual for troubleshooting tips. The most common issues are the server is
not accessible from the network or the user name and password is incorrect.
Note: The MAC address can be anything so long as it is unique on your network.
Created by: Dr. Charles A. Bell
*/
#include <Ethernet.h>
#include <MySQL_Connection.h>
#include <Dns.h>
byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
char hostname[] = "www.google.com"; // change to your server's hostname/URL
char user[] = "root"; // MySQL user login username
char password[] = "secret"; // MySQL user login password
IPAddress server_ip;
EthernetClient client;
MySQL_Connection conn((Client *)&client);
DNSClient dns_client; // DNS instance
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial port to connect
Ethernet.begin(mac_addr);
// Begin DNS lookup
dns_client.begin(Ethernet.dnsServerIP());
dns_client.getHostByName(hostname, server_ip);
Serial.println(server_ip);
// End DNS lookup
Serial.println("Connecting...");
if (conn.connect(server_ip, 3306, user, password)) {
delay(1000);
// You would add your code here to run a query once on startup.
}
else
Serial.println("Connection failed.");
conn.close();
}
void loop() {
}

View File

@@ -0,0 +1,55 @@
/*
MySQL Connector/Arduino Example : connect with default database
This example demonstrates how to connect to a MySQL server and specifying
the default database when connecting.
For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.
INSTRUCTIONS FOR USE
1) Change the address of the server to the IP address of the MySQL server
2) Change the user and password to a valid MySQL user and password
3) Connect a USB cable to your Arduino
4) Select the correct board and port
5) Compile and upload the sketch to your Arduino
6) Once uploaded, open Serial Monitor (use 115200 speed) and observe
If you do not see messages indicating you have a connection, refer to the
manual for troubleshooting tips. The most common issues are the server is
not accessible from the network or the user name and password is incorrect.
Note: The MAC address can be anything so long as it is unique on your network.
Created by: Dr. Charles A. Bell
*/
#include <Ethernet.h>
#include <MySQL_Connection.h>
byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here
char user[] = "root"; // MySQL user login username
char password[] = "secret"; // MySQL user login password
char default_db = "test_arduino;
EthernetClient client;
MySQL_Connection conn((Client *)&client);
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial port to connect
Ethernet.begin(mac_addr);
Serial.println("Connecting...");
if (conn.connect(server_addr, 3306, user, password, default_db)) {
delay(1000);
// You would add your code here to run a query once on startup.
}
else
Serial.println("Connection failed.");
conn.close();
}
void loop() {
}

View File

@@ -0,0 +1,62 @@
/*
MySQL Connector/Arduino Example : connect and disconnect (close)
This example demonstrates how to use the connection to open at the start
of a loop, perform some query, then close the connection. Use this technique
for solutions that must sleep for a long period or otherwise require
additional processing or delays. The connect/close pair allow you to
control how long the connection is open and thus reduce the amount of
time a connection is held open. It also helps for lossy connections.
This example demonstrates how to connect to a MySQL server and specifying
the default database when connecting.
For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.
INSTRUCTIONS FOR USE
1) Change the address of the server to the IP address of the MySQL server
2) Change the user and password to a valid MySQL user and password
3) Connect a USB cable to your Arduino
4) Select the correct board and port
5) Compile and upload the sketch to your Arduino
6) Once uploaded, open Serial Monitor (use 115200 speed) and observe
Created by: Dr. Charles A. Bell
*/
#include <Ethernet.h>
#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>
byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here
char user[] = "root"; // MySQL user login username
char password[] = "secret"; // MySQL user login password
EthernetClient client;
MySQL_Connection conn((Client *)&client);
MySQL_Cursor cur = MySQL_Cursor(&conn);
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial port to connect
Ethernet.begin(mac_addr);
}
void loop() {
Serial.println("Sleeping...");
delay(2000);
Serial.println("Connecting...");
if (conn.connect(server_addr, 3306, user, password)) {
delay(500);
Serial.println("Running a query");
cur.execute("SHOW DATABASES"); // execute a query
cur.show_results(); // show the results
cur.close(); // close the cursor
conn.close(); // close the connection
} else {
Serial.println("Connect failed. Trying again on next iteration.");
}
}

View File

@@ -0,0 +1,76 @@
/*
MySQL Connector/Arduino Example : connect by wifi
This example demonstrates how to connect to a MySQL server from an
Arduino using an Arduino-compatible Wifi shield. Note that "compatible"
means it must conform to the Ethernet class library or be a derivative
with the same classes and methods.
For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.
INSTRUCTIONS FOR USE
1) Change the address of the server to the IP address of the MySQL server
2) Change the user and password to a valid MySQL user and password
3) Change the SSID and pass to match your WiFi network
4) Connect a USB cable to your Arduino
5) Select the correct board and port
6) Compile and upload the sketch to your Arduino
7) Once uploaded, open Serial Monitor (use 115200 speed) and observe
If you do not see messages indicating you have a connection, refer to the
manual for troubleshooting tips. The most common issues are the server is
not accessible from the network or the user name and password is incorrect.
Note: The MAC address can be anything so long as it is unique on your network.
Created by: Dr. Charles A. Bell
*/
#include <WiFi.h> // Use this for WiFi instead of Ethernet.h
#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>
byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here
char user[] = "root"; // MySQL user login username
char password[] = "secret"; // MySQL user login password
// WiFi card example
char ssid[] = "horse_pen"; // your SSID
char pass[] = "noname"; // your SSID Password
WiFiClient client; // Use this for WiFi instead of EthernetClient
MySQL_Connection conn((Client *)&client);
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial port to connect. Needed for Leonardo only
// Begin WiFi section
int status = WiFi.begin(ssid, pass);
if ( status != WL_CONNECTED) {
Serial.println("Couldn't get a wifi connection");
while(true);
}
// print out info about the connection:
else {
Serial.println("Connected to network");
IPAddress ip = WiFi.localIP();
Serial.print("My IP address is: ");
Serial.println(ip);
}
// End WiFi section
Serial.println("Connecting...");
if (conn.connect(server_addr, 3306, user, password)) {
delay(1000);
}
else
Serial.println("Connection failed.");
conn.close();
}
void loop() {
}

View File

@@ -0,0 +1,75 @@
/*
MySQL Connector/Arduino Example : connect by wifi using WiFi 101 shield
This example demonstrates how to connect to a MySQL server from an
Arduino using using the new WiFi Shield 101 from arduino.cc.
For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.
INSTRUCTIONS FOR USE
1) Change the address of the server to the IP address of the MySQL server
2) Change the user and password to a valid MySQL user and password
3) Change the SSID and pass to match your WiFi network
4) Connect a USB cable to your Arduino
5) Select the correct board and port
6) Compile and upload the sketch to your Arduino
7) Once uploaded, open Serial Monitor (use 115200 speed) and observe
If you do not see messages indicating you have a connection, refer to the
manual for troubleshooting tips. The most common issues are the server is
not accessible from the network or the user name and password is incorrect.
Note: The MAC address can be anything so long as it is unique on your network.
Created by: Dr. Charles A. Bell
*/
#include <SPI.h>
#include <WiFi101.h>
#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>
byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here
char user[] = "root"; // MySQL user login username
char password[] = "secret"; // MySQL user login password
// WiFi card example
char ssid[] = "horse_pen"; // your SSID
char pass[] = "noname"; // your SSID Password
WiFiClient client;
MySQL_Connection conn((Client *)&client);
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial port to connect
// Begin WiFi section
int status = WiFi.begin(ssid, pass);
if ( status != WL_CONNECTED) {
Serial.println("Couldn't get a wifi connection");
while(true);
}
// print out info about the connection:
else {
Serial.println("Connected to network");
IPAddress ip = WiFi.localIP();
Serial.print("My IP address is: ");
Serial.println(ip);
}
// End WiFi section
Serial.println("Connecting...");
if (conn.connect(server_addr, 3306, user, password)) {
delay(1000);
}
else
Serial.println("Connection failed.");
conn.close();
}
void loop() {
}

View File

@@ -0,0 +1,71 @@
/*
MySQL Connector/Arduino Example : query with PROGMEM strings
This example demonstrates how to issue queries using strings stored in
PROGMEM. As you will see, you need only add a parameter to the execute()
method in the cursor class, const and PROGMEM to the string declaration
and add the #include <avr/pgmspace.h> directive.
For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.
NOTICE: You must download and install the World sample database to run
this sketch unaltered. See http://dev.mysql.com/doc/index-other.html.
INSTRUCTIONS FOR USE
1) Change the address of the server to the IP address of the MySQL server
2) Change the user and password to a valid MySQL user and password
3) Connect a USB cable to your Arduino
4) Select the correct board and port
5) Compile and upload the sketch to your Arduino
6) Once uploaded, open Serial Monitor (use 115200 speed) and observe
Note: The MAC address can be anything so long as it is unique on your network.
Created by: Dr. Charles A. Bell
*/
#include <Ethernet.h>
#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>
#include <avr/pgmspace.h>
byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here
char user[] = "root"; // MySQL user login username
char password[] = "secret"; // MySQL user login password
// Sample query
const char PROGMEM query[] = "SELECT * FROM world.city LIMIT 12";
EthernetClient client;
MySQL_Connection conn((Client *)&client);
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial port to connect
Ethernet.begin(mac_addr);
Serial.println("Connecting...");
if (conn.connect(server_addr, 3306, user, password)) {
delay(1000);
}
else
Serial.println("Connection failed.");
}
void loop() {
delay(2000);
Serial.println("\nRunning SELECT from PROGMEM and printing results\n");
// Initiate the query class instance
MySQL_Cursor *cur_mem = new MySQL_Cursor(&conn);
// Execute the query with the PROGMEM option
cur_mem->execute(query, true);
// Show the results
cur_mem->show_results();
// Deleting the cursor also frees up memory used
delete cur_mem;
}

View File

@@ -0,0 +1,90 @@
/*
MySQL Connector/Arduino Example : query results
This example demonstrates how to issue a SELECT query and how to read columns
and rows from the result set. Study this example until you are familiar with how to
do this before writing your own sketch to read and consume query results.
For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.
NOTICE: You must download and install the World sample database to run
this sketch unaltered. See http://dev.mysql.com/doc/index-other.html.
INSTRUCTIONS FOR USE
1) Change the address of the server to the IP address of the MySQL server
2) Change the user and password to a valid MySQL user and password
3) Connect a USB cable to your Arduino
4) Select the correct board and port
5) Compile and upload the sketch to your Arduino
6) Once uploaded, open Serial Monitor (use 115200 speed) and observe
Note: The MAC address can be anything so long as it is unique on your network.
Created by: Dr. Charles A. Bell
*/
#include <Ethernet.h>
#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>
byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here
char user[] = "root"; // MySQL user login username
char password[] = "secret"; // MySQL user login password
// Sample query
char query[] = "SELECT * FROM world.city LIMIT 12";
EthernetClient client;
MySQL_Connection conn((Client *)&client);
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial port to connect
Ethernet.begin(mac_addr);
Serial.println("Connecting...");
if (conn.connect(server_addr, 3306, user, password)) {
delay(1000);
}
else
Serial.println("Connection failed.");
}
void loop() {
delay(2000);
Serial.println("\nRunning SELECT and printing results\n");
// Initiate the query class instance
MySQL_Cursor *cur_mem = new MySQL_Cursor(&conn);
// Execute the query
cur_mem->execute(query);
// Fetch the columns and print them
column_names *cols = cur_mem->get_columns();
for (int f = 0; f < cols->num_fields; f++) {
Serial.print(cols->fields[f]->name);
if (f < cols->num_fields-1) {
Serial.print(", ");
}
}
Serial.println();
// Read the rows and print them
row_values *row = NULL;
do {
row = cur_mem->get_next_row();
if (row != NULL) {
for (int f = 0; f < cols->num_fields; f++) {
Serial.print(row->values[f]);
if (f < cols->num_fields-1) {
Serial.print(", ");
}
}
Serial.println();
}
} while (row != NULL);
// Deleting the cursor also frees up memory used
delete cur_mem;
}

View File

@@ -0,0 +1,82 @@
/*
MySQL Connector/Arduino Example : reboot if connection lost
This example demonstrates how to reboot an Arduino if connection to the
server is lost for a period of time.
For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.
INSTRUCTIONS FOR USE
1) Change the address of the server to the IP address of the MySQL server
2) Change the user and password to a valid MySQL user and password
3) Connect a USB cable to your Arduino
4) Select the correct board and port
5) Compile and upload the sketch to your Arduino
6) Once uploaded, open Serial Monitor (use 115200 speed) and observe
To test the reboot, unplug your Ethernet cable once you see "disconnected"
then wait for the timeout. Once the Arduino reboots, plug the cable in again
and you should see the query processing resume.
Created by: Dr. Charles A. Bell
*/
#include <Ethernet.h>
#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>
byte mac_addr[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress server_addr(10,0,1,35); // IP of the MySQL *server* here
char user[] = "root"; // MySQL user login username
char password[] = "secret"; // MySQL user login password
EthernetClient client;
MySQL_Connection conn((Client *)&client);
MySQL_Cursor cur = MySQL_Cursor(&conn);
void setup() {
Serial.begin(115200);
while (!Serial); // wait for serial port to connect
Ethernet.begin(mac_addr);
}
// Begin reboot code
int num_fails; // variable for number of failure attempts
#define MAX_FAILED_CONNECTS 5 // maximum number of failed connects to MySQL
void soft_reset() {
asm volatile("jmp 0");
}
// End reboot code
void loop() {
Serial.println("Sleeping...");
delay(1000);
if (conn.connected()) {
Serial.println("Running a query");
cur.execute("SHOW DATABASES"); // execute a query
cur.show_results(); // show the results
cur.close(); // close the cursor
conn.close(); // close the connection
num_fails = 0; // reset failures
delay(3000);
} else {
Serial.println("Connecting...");
if (conn.connect(server_addr, 3306, user, password)) {
delay(500);
} else {
num_fails++;
Serial.println("Connect failed!");
if (num_fails == MAX_FAILED_CONNECTS) {
Serial.println("Ok, that's it. I'm outta here. Rebooting...");
delay(2000);
// Here we tell the Arduino to reboot by redirecting the instruction
// pointer to the "top" or position 0. This is a soft reset and may
// not solve all hardware-related lockups.
soft_reset();
}
}
}
}

View File

@@ -0,0 +1,4 @@
The documentation for the connector has been moved to the wiki page on Github. For more information and documentation, visit the wiki:
https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki.

View File

@@ -0,0 +1,5 @@
connect KEYWORD2
execute KEYWORD2
show_results KEYWORD2
connected KEYWORD2
field_struct KEYWORD3

View File

@@ -0,0 +1,9 @@
name=MySQL Connector Arduino
version=1.2.0
author=Dr. Charles Bell <chuck.bell@oracle.com>
maintainer=Dr. Charles Bell <chuck.bell@oracle.com>
sentence=Connects Arduino using Arduino Ethernet-compatible shields including the Ethernet Shield and WiFi Shield.
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!
category=Communication
url=https://github.com/ChuckBell/MySQL_Connector_Arduino/wiki
architectures=*

View File

@@ -0,0 +1,116 @@
/*
Copyright (c) 2012, 2016 Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
MySQL_Connection.cpp - Library for communicating with a MySQL Server over
Ethernet. (formerly mysql.cpp)
Change History:
Version 1.0.0a Created by Dr. Charles A. Bell, April 2012.
Version 1.0.0b Updated by Dr. Charles A. Bell, October 2013.
Version 1.0.1b Updated by Dr. Charles A. Bell, February 2014.
Version 1.0.2b Updated by Dr. Charles A. Bell, April 2014.
Version 1.0.3rc Updated by Dr. Charles A. Bell, March 2015.
Version 1.0.4ga Updated by Dr. Charles A. Bell, July 2015.
Version 1.1.0a Created by Dr. Charles A. Bell, January 2016.
Version 1.1.1a Created by Dr. Charles A. Bell, January 2016.
Version 1.1.2b Created by Dr. Charles A. Bell, November 2016.
Version 1.2.0 Created by Dr. Charles A. Bell, March 2020.
*/
#include <Arduino.h>
#include <MySQL_Connection.h>
#include <MySQL_Encrypt_Sha1.h>
#define MAX_CONNECT_ATTEMPTS 3
#define CONNECT_DELAY_MS 500
#define SUCCESS 1
const char CONNECTED[] PROGMEM = "Connected to server version ";
const char DISCONNECTED[] PROGMEM = "Disconnected.";
/*
connect - Connect to a MySQL server.
This method is used to connect to a MySQL server. It will attempt to
connect to the server as a client retrying up to MAX_CONNECT_ATTEMPTS.
This permits the possibility of longer than normal network lag times
for wireless networks. You can adjust MAX_CONNECT_ATTEMPTS to suit
your environment.
server[in] IP address of the server as IPAddress type
port[in] port number of the server
user[in] user name
password[in] (optional) user password
db[in] (optional) default database
Returns boolean - True = connection succeeded
*/
boolean MySQL_Connection::connect(IPAddress server, int port, char *user,
char *password, char *db)
{
int connected = 0;
int retries = MAX_CONNECT_ATTEMPTS;
// Retry up to MAX_CONNECT_ATTEMPTS times.
while (retries--)
{
Serial.println("...trying...");
connected = client->connect(server, port);
if (connected != SUCCESS) {
Serial.print("...got: ");
Serial.print(connected);
Serial.println(" retrying...");
delay(CONNECT_DELAY_MS);
} else {
break;
}
}
if (connected != SUCCESS)
return false;
read_packet();
parse_handshake_packet();
send_authentication_packet(user, password, db);
read_packet();
if (get_packet_type() != MYSQL_OK_PACKET) {
parse_error_packet();
return false;
}
show_error(CONNECTED);
Serial.println(server_version);
free(server_version); // don't need it anymore
return true;
}
/*
close - cancel the connection
This method closes the connection to the server and frees up any memory
used in the buffer.
*/
void MySQL_Connection::close()
{
if (connected())
{
client->flush();
client->stop();
show_error(DISCONNECTED, true);
}
}

View File

@@ -0,0 +1,52 @@
/*
Copyright (c) 2012, 2016 Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
MySQL_Connection.h - Library for communicating with a MySQL Server over
Ethernet. (formerly mysql.h)
This header file defines a connection class for connecting to a MySQL
server.
Change History:
Version 1.0.0a Created by Dr. Charles A. Bell, April 2012.
Version 1.0.0b Updated by Dr. Charles A. Bell, October 2013.
Version 1.0.1b Updated by Dr. Charles A. Bell, February 2014.
Version 1.0.2b Updated by Dr. Charles A. Bell, April 2014.
Version 1.0.3rc Updated by Dr. Charles A. Bell, March 2015.
Version 1.0.4ga Updated by Dr. Charles A. Bell, July 2015.
Version 1.1.0a Created by Dr. Charles A. Bell, January 2016.
Version 1.1.1a Created by Dr. Charles A. Bell, January 2016.
Version 1.1.2b Created by Dr. Charles A. Bell, November 2016.
Version 1.2.0 Created by Dr. Charles A. Bell, March 2020.
*/
#ifndef MYSQL_CONNECTION_H
#define MYSQL_CONNECTION_H
#include <MySQL_Packet.h>
class MySQL_Connection : public MySQL_Packet {
public:
MySQL_Connection(Client *client_instance) :
MySQL_Packet(client_instance) {}
boolean connect(IPAddress server, int port, char *user, char *password,
char *db=NULL);
int connected() { return client->connected(); }
const char *version() { return MYSQL_VERSION_STR; }
void close();
};
#endif

View File

@@ -0,0 +1,542 @@
/*
Copyright (c) 2012, 2016 Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
MySQL_Cursor.cpp - Run queries on a MySQL Server
Change History:
Version 1.0.0a Created by Dr. Charles A. Bell, April 2012.
Version 1.0.0b Updated by Dr. Charles A. Bell, October 2013.
Version 1.0.1b Updated by Dr. Charles A. Bell, February 2014.
Version 1.0.2b Updated by Dr. Charles A. Bell, April 2014.
Version 1.0.3rc Updated by Dr. Charles A. Bell, March 2015.
Version 1.0.4ga Updated by Dr. Charles A. Bell, July 2015.
Version 1.1.0a Created by Dr. Charles A. Bell, January 2016.
Version 1.1.1a Created by Dr. Charles A. Bell, January 2016.
Version 1.1.2b Created by Dr. Charles A. Bell, November 2016.
Version 1.2.0 Created by Dr. Charles A. Bell, March 2020.
*/
#include <MySQL_Cursor.h>
const char BAD_MOJO[] PROGMEM = "Bad mojo. EOF found reading column header.";
const char ROWS[] PROGMEM = " rows in result.";
const char READ_COLS[] PROGMEM = "ERROR: You must read the columns first!";
const char NOT_CONNECTED[] PROGMEM = "ERROR: Class requires connected server.";
/*
Constructor
Requires an instance of the MySQL_Connection class to communicate with a
MySQL server.
connection[in] Connection to a MySQL server - must be connected.
*/
MySQL_Cursor::MySQL_Cursor(MySQL_Connection *connection) {
conn = connection;
#ifdef WITH_SELECT
columns.num_fields = 0;
for (int f = 0; f < MAX_FIELDS; f++) {
columns.fields[f] = NULL;
row.values[f] = NULL;
}
columns_read = false;
rows_affected = -1;
last_insert_id = -1;
#endif
}
/*
Destructor
*/
MySQL_Cursor::~MySQL_Cursor() {
#ifdef WITH_SELECT
close();
#endif
}
/*
execute - Execute a SQL statement
This method executes the query specified as a character array. It copies
the query to the local buffer then calls the execute_query() method to
execute the query.
If a result set is available after the query executes, the field
packets and rows can be read separately using the get_field() and
get_row() methods.
query[in] SQL statement (using normal memory access)
progmem[in] True if string is in program memory
Returns boolean - True = a result set is available for reading
*/
boolean MySQL_Cursor::execute(const char *query, boolean progmem)
{
int query_len; // length of query
if (!conn->connected()) {
conn->show_error(NOT_CONNECTED, true);
return false;
}
if (progmem) {
query_len = (int)strlen_P(query);
} else {
query_len = (int)strlen(query);
}
if (conn->buffer != NULL)
free(conn->buffer);
conn->buffer = (byte *)malloc(query_len+5);
// Write query to packet
if (progmem) {
for (int c = 0; c < query_len; c++)
conn->buffer[c+5] = pgm_read_byte_near(query+c);
} else {
memcpy(&conn->buffer[5], query, query_len);
}
// Send the query
return execute_query(query_len);
}
/*
execute_query - execute a query
This method sends the query string to the server and waits for a
response. If the result is a result set, it returns true, if it is
an error, it processes the error packet and prints the error via
Serial.print(). If it is an Ok packet, it parses the packet and
returns false.
query_len[in] Number of bytes in the query string
Returns boolean - true = result set available,
false = no result set returned.
*/
boolean MySQL_Cursor::execute_query(int query_len)
{
if (!conn->buffer)
return false;
// Reset the rows affected and last insert id before query.
rows_affected = -1;
last_insert_id = -1;
conn->store_int(&conn->buffer[0], query_len+1, 3);
conn->buffer[3] = byte(0x00);
conn->buffer[4] = byte(0x03); // command packet
// Send the query
conn->client->write((uint8_t*)conn->buffer, query_len + 5);
conn->client->flush();
// Read a response packet and check it for Ok or Error.
conn->read_packet();
int res = conn->get_packet_type();
if (res == MYSQL_ERROR_PACKET) {
conn->parse_error_packet();
return false;
} else if (res == MYSQL_OK_PACKET || res == MYSQL_EOF_PACKET) {
// Read the rows affected and last insert id.
int loc1 = conn->buffer[5]; // Location of rows affected
int loc2 = 5;
if (loc1 < 252) {
loc2++;
} else if (loc1 == 252) {
loc2 += 2;
} else if (loc1 == 253) {
loc2 += 3;
} else {
loc2 += 8;
}
rows_affected = conn->read_lcb_int(5);
if (rows_affected > 0) {
last_insert_id = conn->read_lcb_int(loc2);
}
return true;
}
// Not an Ok packet, so we now have the result set to process.
#ifdef WITH_SELECT
columns_read = false;
#endif
return true;
}
#ifdef WITH_SELECT
/*
Close
Takes care of removing allocated memory.
*/
void MySQL_Cursor::close() {
free_columns_buffer();
free_row_buffer();
}
/*
get_columns - Get a list of the columns (fields)
This method returns an instance of the column_names structure
that contains an array of fields.
Note: you should call free_columns_buffer() after consuming
the field data to free memory.
*/
column_names *MySQL_Cursor::get_columns() {
free_columns_buffer();
free_row_buffer();
num_cols = 0;
if (get_fields()) {
columns_read = true;
return &columns;
}
else {
return NULL;
}
}
/*
get_next_row - Iterator for reading rows from a result set
This method returns an instance of a structure (row_values)
that contains an array of strings representing the row
values returned from the server.
The caller can use the values however needed - by first
converting them to a specific type or as a string.
*/
row_values *MySQL_Cursor::get_next_row() {
int res = 0;
free_row_buffer();
// Read the rows
res = get_row_values();
if (res != MYSQL_EOF_PACKET) {
return &row;
}
return NULL;
}
/*
show_results - Show a result set from the server via Serial.print
This method reads a result from the server and displays it via the
via the Serial.print methods. It can be used in cases where
you may want to issue a SELECT or SHOW and see the results on your
computer from the Arduino.
It is also a good example of how to read a result set from the
because it uses the public methods designed to return result
sets from the server.
*/
void MySQL_Cursor::show_results() {
column_names *cols;
int rows = 0;
// Get the columns
cols = get_columns();
if (cols == NULL) {
return;
}
for (int f = 0; f < columns.num_fields; f++) {
Serial.print(columns.fields[f]->name);
if (f < columns.num_fields-1)
Serial.print(',');
}
Serial.println();
// Read the rows
while (get_next_row()) {
rows++;
for (int f = 0; f < columns.num_fields; f++) {
Serial.print(row.values[f]);
if (f < columns.num_fields-1)
Serial.print(',');
}
free_row_buffer();
Serial.println();
}
// Report how many rows were read
Serial.print(rows);
conn->show_error(ROWS, true);
free_columns_buffer();
// Free any post-query messages in queue for stored procedures
clear_ok_packet();
}
/*
clear_ok_packet - clear last Ok packet (if present)
This method reads the header and status to see if this is an Ok packet.
If it is, it reads the packet and discards it. This is useful for
processing result sets from stored procedures.
Returns False if the packet was not an Ok packet.
*/
bool MySQL_Cursor::clear_ok_packet() {
int num = 0;
do {
num = conn->client->available();
if (num > 0) {
conn->read_packet();
if (conn->get_packet_type() != MYSQL_OK_PACKET) {
conn->parse_error_packet();
return false;
}
}
} while (num > 0);
rows_affected = -1;
last_insert_id = -1;
return true;
}
/*
free_columns_buffer - Free memory allocated for column names
This method frees the memory allocated during the get_columns()
method.
NOTICE: Failing to call this method after calling get_columns()
and consuming the column names, types, etc. will result
in a memory leak. The size of the leak will depend on
the size of the combined column names (bytes).
*/
void MySQL_Cursor::free_columns_buffer() {
// clear the columns
for (int f = 0; f < MAX_FIELDS; f++) {
if (columns.fields[f] != NULL) {
free(columns.fields[f]->db);
free(columns.fields[f]->table);
free(columns.fields[f]->name);
free(columns.fields[f]);
}
columns.fields[f] = NULL;
}
num_cols = 0;
columns_read = false;
}
/*
free_row_buffer - Free memory allocated for row values
This method frees the memory allocated during the get_next_row()
method.
NOTICE: You must call this method at least once after you
have consumed the values you wish to process. Failing
to do will result in a memory leak equal to the sum
of the length of values and one byte for each max cols.
*/
void MySQL_Cursor::free_row_buffer() {
// clear the row
for (int f = 0; f < MAX_FIELDS; f++) {
if (row.values[f] != NULL) {
free(row.values[f]);
}
row.values[f] = NULL;
}
}
/*
read_string - Retrieve a string from the buffer
This reads a string from the buffer. It reads the length of the string
as the first byte.
offset[in] offset from start of buffer
Returns string - String from the buffer
*/
char *MySQL_Cursor::read_string(int *offset) {
char *str;
int len_bytes = conn->get_lcb_len(conn->buffer[*offset]);
int len = conn->read_int(*offset, len_bytes);
if (len == 251) {
// This is a null field.
str = (char *)malloc(5);
strncpy(str, "NULL", 4);
str[4] = 0x00;
*offset += len_bytes;
} else {
str = (char *)malloc(len+1);
strncpy(str, (char *)&conn->buffer[*offset+len_bytes], len);
str[len] = 0x00;
*offset += len_bytes+len;
}
return str;
}
/*
get_field - Read a field from the server
This method reads a field packet from the server. Field packets are
defined as:
Bytes Name
----- ----
n (Length Coded String) catalog
n (Length Coded String) db
n (Length Coded String) table
n (Length Coded String) org_table
n (Length Coded String) name
n (Length Coded String) org_name
1 (filler)
2 charsetnr
4 length
1 type
2 flags
1 decimals
2 (filler), always 0x00
n (Length Coded Binary) default
Note: the sum of all db, column, and field names must be < 255 in length
*/
int MySQL_Cursor::get_field(field_struct *fs) {
int len_bytes;
int len;
int offset;
// Read field packets until EOF
conn->read_packet();
if (conn->buffer && conn->buffer[4] != MYSQL_EOF_PACKET) {
// calculate location of db
len_bytes = conn->get_lcb_len(4);
len = conn->read_int(4, len_bytes);
offset = 4+len_bytes+len;
fs->db = read_string(&offset);
// get table
fs->table = read_string(&offset);
// calculate location of name
len_bytes = conn->get_lcb_len(offset);
len = conn->read_int(offset, len_bytes);
offset += len_bytes+len;
fs->name = read_string(&offset);
return 0;
}
return MYSQL_EOF_PACKET;
}
/*
get_row - Read a row from the server and store it in the buffer
This reads a single row and stores it in the buffer. If there are
no more rows, it returns MYSQL_EOF_PACKET. A row packet is defined as
follows.
Bytes Name
----- ----
n (Length Coded String) (column value)
...
Note: each column is store as a length coded string concatenated
as a single stream
Returns integer - MYSQL_EOF_PACKET if no more rows, 0 if more rows available
*/
int MySQL_Cursor::get_row() {
// Read row packets
conn->read_packet();
if (conn->buffer && conn->buffer[4] != MYSQL_EOF_PACKET)
return 0;
return MYSQL_EOF_PACKET;
}
/*
get_fields - reads the fields from the read buffer
This method is used to read the field names, types, etc.
from the read buffer and store them in the columns structure
in the class.
*/
boolean MySQL_Cursor::get_fields()
{
int num_fields = 0;
int res = 0;
if (conn->buffer == NULL) {
return false;
}
num_fields = conn->buffer[4]; // From result header packet
columns.num_fields = num_fields;
num_cols = num_fields; // Save this for later use
for (int f = 0; f < num_fields; f++) {
field_struct *field = (field_struct *)malloc(sizeof(field_struct));
res = get_field(field);
if (res == MYSQL_EOF_PACKET) {
conn->show_error(BAD_MOJO, true);
return false;
}
columns.fields[f] = field;
}
conn->read_packet(); // EOF packet
return true;
}
/*
get_row_values - reads the row values from the read buffer
This method is used to read the row column values
from the read buffer and store them in the row structure
in the class.
*/
int MySQL_Cursor::get_row_values() {
int res = 0;
int offset = 0;
// It is an error to try to read rows before columns
// are read.
if (!columns_read) {
conn->show_error(READ_COLS, true);
return MYSQL_EOF_PACKET;
}
// Drop any row data already read
free_row_buffer();
// Read a row
res = get_row();
if (res != MYSQL_EOF_PACKET) {
offset = 4;
for (int f = 0; f < num_cols; f++) {
row.values[f] = read_string(&offset);
}
}
return res;
}
#endif // WITH_SELECT

View File

@@ -0,0 +1,106 @@
/*
Copyright (c) 2012, 2016 Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
MySQL_Cursor.h - Run queries on a MySQL Server
This header file defines a cursor class for running queries on a MySQL
server. You can issue any command using SQL statements for inserting or
retrieving data.
Change History:
Version 1.0.0a Created by Dr. Charles A. Bell, April 2012.
Version 1.0.0b Updated by Dr. Charles A. Bell, October 2013.
Version 1.0.1b Updated by Dr. Charles A. Bell, February 2014.
Version 1.0.2b Updated by Dr. Charles A. Bell, April 2014.
Version 1.0.3rc Updated by Dr. Charles A. Bell, March 2015.
Version 1.0.4ga Updated by Dr. Charles A. Bell, July 2015.
Version 1.1.0a Created by Dr. Charles A. Bell, January 2016.
Version 1.1.1a Created by Dr. Charles A. Bell, January 2016.
Version 1.1.2b Created by Dr. Charles A. Bell, November 2016.
Version 1.2.0 Created by Dr. Charles A. Bell, March 2020.
*/
#ifndef MYSQL_QUERY_H
#define MYSQL_QUERY_H
#include <MySQL_Connection.h>
#define WITH_SELECT // Comment this if you don't need SELECT queries.
// Reduces memory footprint of the library.
#define MAX_FIELDS 0x20 // Maximum number of fields. Reduce to save memory. Default=32
#ifdef WITH_SELECT
// Structure for retrieving a field (minimal implementation).
typedef struct {
char *db;
char *table;
char *name;
} field_struct;
// Structure for storing result set metadata.
typedef struct {
int num_fields; // actual number of fields
field_struct *fields[MAX_FIELDS];
} column_names;
// Structure for storing row data.
typedef struct {
char *values[MAX_FIELDS];
} row_values;
#endif // WITH_SELECT
class MySQL_Cursor {
public:
MySQL_Cursor(MySQL_Connection *connection);
~MySQL_Cursor();
boolean execute(const char *query, boolean progmem=false);
private:
boolean execute_query(int query_len);
#ifdef WITH_SELECT
public:
void close();
column_names *get_columns();
row_values *get_next_row();
void show_results();
int get_rows_affected() { return rows_affected; }
int get_last_insert_id() { return last_insert_id; }
private:
void free_columns_buffer();
void free_row_buffer();
bool clear_ok_packet();
char *read_string(int *offset);
int get_field(field_struct *fs);
int get_row();
boolean get_fields();
int get_row_values();
column_names *query_result();
boolean columns_read;
int num_cols;
column_names columns;
row_values row;
int rows_affected;
int last_insert_id;
#endif
MySQL_Connection *conn;
};
#endif

View File

@@ -0,0 +1,141 @@
/*
* GNU GPL v3
*
* This file is part of the code entitled, "cryptosuite" available at
* https://code.google.com/p/cryptosuite/. The file was copied from that
* repository and renamed for use in Connector/Arduino to preserve
* compatibility and protect against namespace collisions for users who
* want to use the full cryptosuite functionality. For Connector/Arduino
* all that is needed is this one sha1 class.
*
* Note: #defines renamed to prevent collisions
*/
#include <string.h>
#include "MySQL_Encrypt_Sha1.h"
#define MYSQL_SHA1_K0 0x5a827999
#define MYSQL_SHA1_K20 0x6ed9eba1
#define MYSQL_SHA1_K40 0x8f1bbcdc
#define MYSQL_SHA1_K60 0xca62c1d6
const uint8_t sha1InitState[] PROGMEM = {
0x01,0x23,0x45,0x67, // H0
0x89,0xab,0xcd,0xef, // H1
0xfe,0xdc,0xba,0x98, // H2
0x76,0x54,0x32,0x10, // H3
0xf0,0xe1,0xd2,0xc3 // H4
};
void Encrypt_SHA1::init(void) {
memcpy_P(state.b,sha1InitState,HASH_LENGTH);
byteCount = 0;
bufferOffset = 0;
}
uint32_t Encrypt_SHA1::rol32(uint32_t number, uint8_t bits) {
return ((number << bits) | (number >> (32-bits)));
}
void Encrypt_SHA1::hashBlock() {
// SHA1 only for now
uint8_t i;
uint32_t a,b,c,d,e,t;
a=state.w[0];
b=state.w[1];
c=state.w[2];
d=state.w[3];
e=state.w[4];
for (i=0; i<80; i++) {
if (i>=16) {
t = buffer.w[(i+13)&15] ^ buffer.w[(i+8)&15] ^ buffer.w[(i+2)&15] ^ buffer.w[i&15];
buffer.w[i&15] = rol32(t,1);
}
if (i<20) {
t = (d ^ (b & (c ^ d))) + MYSQL_SHA1_K0;
} else if (i<40) {
t = (b ^ c ^ d) + MYSQL_SHA1_K20;
} else if (i<60) {
t = ((b & c) | (d & (b | c))) + MYSQL_SHA1_K40;
} else {
t = (b ^ c ^ d) + MYSQL_SHA1_K60;
}
t+=rol32(a,5) + e + buffer.w[i&15];
e=d;
d=c;
c=rol32(b,30);
b=a;
a=t;
}
state.w[0] += a;
state.w[1] += b;
state.w[2] += c;
state.w[3] += d;
state.w[4] += e;
}
void Encrypt_SHA1::addUncounted(uint8_t data) {
buffer.b[bufferOffset ^ 3] = data;
bufferOffset++;
if (bufferOffset == BLOCK_LENGTH) {
hashBlock();
bufferOffset = 0;
}
}
size_t Encrypt_SHA1::write(uint8_t data) {
++byteCount;
addUncounted(data);
return 1;
}
size_t Encrypt_SHA1::write(uint8_t* data, int length) {
for (int i=0; i<length; i++) {
write(data[i]);
}
return length;
}
void Encrypt_SHA1::pad() {
// Implement SHA-1 padding (fips180-2 §5.1.1)
// Pad with 0x80 followed by 0x00 until the end of the block
addUncounted(0x80);
while (bufferOffset != 56) addUncounted(0x00);
// Append length in the last 8 bytes
addUncounted(0); // We're only using 32 bit lengths
addUncounted(0); // But SHA-1 supports 64 bit lengths
addUncounted(0); // So zero pad the top bits
addUncounted(byteCount >> 29); // Shifting to multiply by 8
addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as
addUncounted(byteCount >> 13); // byte.
addUncounted(byteCount >> 5);
addUncounted(byteCount << 3);
}
uint8_t* Encrypt_SHA1::result(void) {
// Pad to complete the last block
pad();
// Swap byte order back
for (int i=0; i<5; i++) {
uint32_t a,b;
a=state.w[i];
b=a<<24;
b|=(a<<8) & 0x00ff0000;
b|=(a>>8) & 0x0000ff00;
b|=a>>24;
state.w[i]=b;
}
// Return pointer to hash (20 characters)
return state.b;
}
#define HMAC_IPAD 0x36
#define HMAC_OPAD 0x5c
Encrypt_SHA1 Sha1;

View File

@@ -0,0 +1,54 @@
/*
* GNU GPL v3
*
* This file is part of the code entitled, "cryptosuite" available at
* https://code.google.com/p/cryptosuite/. The file was copied from that
* repository and renamed for use in Connector/Arduino to preserve
* compatibility and protect against namespace collisions for users who
* want to use the full cryptosuite functionality. For Connector/Arduino
* all that is needed is this one sha1 class.
*/
#ifndef ENCRYPT_SHA1_H
#define ENCRYPT_SHA1_H
#include <inttypes.h>
#include "Print.h"
#define HASH_LENGTH 20
#define BLOCK_LENGTH 64
union _buffer {
uint8_t b[BLOCK_LENGTH];
uint32_t w[BLOCK_LENGTH/4];
};
union _state {
uint8_t b[HASH_LENGTH];
uint32_t w[HASH_LENGTH/4];
};
class Encrypt_SHA1 : public Print
{
public:
void init(void);
void initHmac(const uint8_t* secret, int secretLength);
uint8_t* result(void);
virtual size_t write(uint8_t);
virtual size_t write(uint8_t* data, int length);
using Print::write;
private:
void pad();
void addUncounted(uint8_t data);
void hashBlock();
uint32_t rol32(uint32_t number, uint8_t bits);
_buffer buffer;
uint8_t bufferOffset;
_state state;
uint32_t byteCount;
uint8_t keyBuffer[BLOCK_LENGTH];
uint8_t innerHash[HASH_LENGTH];
};
extern Encrypt_SHA1 Sha1;
#endif

View File

@@ -0,0 +1,573 @@
/*
Copyright (c) 2012, 2016 Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
MySQL_Packet.cpp - Packet library for communicating with a MySQL Server
Change History:
Version 1.0.0a Created by Dr. Charles A. Bell, April 2012.
Version 1.0.0b Updated by Dr. Charles A. Bell, October 2013.
Version 1.0.1b Updated by Dr. Charles A. Bell, February 2014.
Version 1.0.2b Updated by Dr. Charles A. Bell, April 2014.
Version 1.0.3rc Updated by Dr. Charles A. Bell, March 2015.
Version 1.0.4ga Updated by Dr. Charles A. Bell, July 2015.
Version 1.1.0a Created by Dr. Charles A. Bell, January 2016.
Version 1.1.1a Created by Dr. Charles A. Bell, January 2016.
Version 1.1.2b Created by Dr. Charles A. Bell, November 2016.
Version 1.2.0 Created by Dr. Charles A. Bell, March 2020.
*/
#include <Arduino.h>
#include <MySQL_Packet.h>
#include <MySQL_Encrypt_Sha1.h>
#define MYSQL_DATA_TIMEOUT 3000 // Wifi client wait in milliseconds
#define MYSQL_WAIT_INTERVAL 300 // WiFi client wait interval
/*
Constructor
Initialize the buffer and store client instance.
*/
MySQL_Packet::MySQL_Packet(Client *client_instance) {
buffer = NULL;
client = client_instance;
}
/*
show_error
Print the curent error message.
msg[in] Message to print
EOL[in] True if we print EOLN character
*/
void MySQL_Packet::show_error(const char *msg, bool EOL) {
#ifdef DEBUG
char pos;
while ((pos = pgm_read_byte(msg))) {
Serial.print(pos);
msg++;
}
if (EOL)
Serial.println();
#endif
}
/*
send_authentication_packet
This method builds a response packet used to respond to the server's
challenge packet (called the handshake packet). It includes the user
name and password scrambled using the SHA1 seed from the handshake
packet. It also sets the character set (default is 8 which you can
change to meet your needs).
Note: you can also set the default database in this packet. See
the code before for a comment on where this happens.
The authentication packet is defined as follows.
Bytes Name
----- ----
4 client_flags
4 max_packet_size
1 charset_number
23 (filler) always 0x00...
n (Null-Terminated String) user
n (Length Coded Binary) scramble_buff (1 + x bytes)
n (Null-Terminated String) databasename (optional)
user[in] User name
password[in] password
db[in] default database
*/
void MySQL_Packet::send_authentication_packet(char *user, char *password,
char *db)
{
if (buffer != NULL)
free(buffer);
buffer = (byte *)malloc(256);
int size_send = 4;
// client flags
buffer[size_send] = byte(0x0D);
buffer[size_send+1] = byte(0xa6);
buffer[size_send+2] = byte(0x03);
buffer[size_send+3] = byte(0x00);
size_send += 4;
// max_allowed_packet
buffer[size_send] = 0;
buffer[size_send+1] = 0;
buffer[size_send+2] = 0;
buffer[size_send+3] = 1;
size_send += 4;
// charset - default is 8
buffer[size_send] = byte(0x08);
size_send += 1;
for(int i = 0; i < 24; i++)
buffer[size_send+i] = 0x00;
size_send += 23;
// user name
memcpy((char *)&buffer[size_send], user, strlen(user));
size_send += strlen(user) + 1;
buffer[size_send-1] = 0x00;
// password - see scramble password
byte *scramble = (uint8_t *)malloc(20);
if (scramble_password(password, scramble)) {
buffer[size_send] = 0x14;
size_send += 1;
for (int i = 0; i < 20; i++)
buffer[i+size_send] = scramble[i];
size_send += 20;
buffer[size_send] = 0x00;
}
free(scramble);
if (db) {
memcpy((char *)&buffer[size_send], db, strlen(db));
size_send += strlen(db) + 1;
buffer[size_send-1] = 0x00;
} else {
buffer[size_send+1] = 0x00;
size_send += 1;
}
// Write packet size
int p_size = size_send - 4;
store_int(&buffer[0], p_size, 3);
buffer[3] = byte(0x01);
// Write the packet
client->write((uint8_t*)buffer, size_send);
client->flush();
}
/*
scramble_password - Build a SHA1 scramble of the user password
This method uses the password hash seed sent from the server to
form a SHA1 hash of the password. This is used to send back to
the server to complete the challenge and response step in the
authentication handshake.
password[in] User's password in clear text
pwd_hash[in] Seed from the server
Returns boolean - True = scramble succeeded
*/
boolean MySQL_Packet::scramble_password(char *password, byte *pwd_hash) {
byte *digest;
byte hash1[20];
byte hash2[20];
byte hash3[20];
byte pwd_buffer[40];
if (strlen(password) == 0)
return false;
// hash1
Sha1.init();
Sha1.print(password);
digest = Sha1.result();
memcpy(hash1, digest, 20);
// hash2
Sha1.init();
Sha1.write(hash1, 20);
digest = Sha1.result();
memcpy(hash2, digest, 20);
// hash3 of seed + hash2
Sha1.init();
memcpy(pwd_buffer, &seed, 20);
memcpy(pwd_buffer+20, hash2, 20);
Sha1.write(pwd_buffer, 40);
digest = Sha1.result();
memcpy(hash3, digest, 20);
// XOR for hash4
for (int i = 0; i < 20; i++)
pwd_hash[i] = hash1[i] ^ hash3[i];
return true;
}
/*
wait_for_bytes - Wait until data is available for reading
This method is used to permit the connector to respond to servers
that have high latency or execute long queries. The timeout is
set by MYSQL_DATA_TIMEOUT. Adjust this value to match the performance of
your server and network.
It is also used to read how many bytes in total are available from the
server. Thus, it can be used to know how large a data burst is from
the server.
bytes_need[in] Bytes count to wait for
Returns integer - Number of bytes available to read.
*/
int MySQL_Packet::wait_for_bytes(int bytes_need)
{
const long wait_till = millis() + MYSQL_DATA_TIMEOUT;
int num = 0;
long now = 0;
do
{
now = millis();
num = client->available();
if (num < bytes_need)
delay(MYSQL_WAIT_INTERVAL);
else
break;
} while (now < wait_till);
if (num == 0 && now >= wait_till)
client->stop();
return num;
}
/*
read_packet - Read a packet from the server and store it in the buffer
This method reads the bytes sent by the server as a packet. All packets
have a packet header defined as follows.
Bytes Name
----- ----
3 Packet Length
1 Packet Number
Thus, the length of the packet (not including the packet header) can
be found by reading the first 4 bytes from the server then reading
N bytes for the packet payload.
*/
void MySQL_Packet::read_packet() {
byte local[4];
if (buffer) {
free(buffer);
buffer = NULL;
}
// Read packet header
if (wait_for_bytes(4) < 4) {
show_error(READ_TIMEOUT, true);
return;
}
for (int i = 0; i < 4; i++)
local[i] = client->read();
// Get packet length
packet_len = local[0];
packet_len += (local[1] << 8);
packet_len += ((uint32_t)local[2] << 16);
// We must wait for slow arriving packets for Ethernet shields only.
/*
if (wait_for_bytes(packet_len) < packet_len) {
show_error(READ_TIMEOUT, true);
return;
}
*/
// Check for valid packet.
if (packet_len < 0) {
show_error(PACKET_ERROR, true);
packet_len = 0;
}
buffer = (byte *)malloc(packet_len+4);
if (buffer == NULL) {
show_error(MEMORY_ERROR, true);
return;
}
for (int i = 0; i < 4; i++)
buffer[i] = local[i];
for (int i = 4; i < packet_len+4; i++)
buffer[i] = client->read();
}
/*
parse_handshake_packet - Decipher the server's challenge data
This method reads the server version string and the seed from the
server. The handshake packet is defined as follows.
Bytes Name
----- ----
1 protocol_version
n (Null-Terminated String) server_version
4 thread_id
8 scramble_buff
1 (filler) always 0x00
2 server_capabilities
1 server_language
2 server_status
2 server capabilities (two upper bytes)
1 length of the scramble seed
10 (filler) always 0
n rest of the plugin provided data
(at least 12 bytes)
1 \0 byte, terminating the second part of
a scramble seed
*/
void MySQL_Packet::parse_handshake_packet() {
if (!buffer)
return;
int i = 5;
do {
i++;
} while (buffer[i-1] != 0x00);
server_version = (char *)malloc(i-5);
strncpy(server_version, (char *)&buffer[5], i-5);
// Capture the first 8 characters of seed
i += 4; // Skip thread id
for (int j = 0; j < 8; j++) {
seed[j] = buffer[i+j];
}
// Capture rest of seed
i += 27; // skip ahead
for (int j = 0; j < 12; j++) {
seed[j+8] = buffer[i+j];
}
}
/*
parse_error_packet - Display the error returned from the server
This method parses an error packet from the server and displays the
error code and text via Serial.print. The error packet is defined
as follows.
Note: the error packet is already stored in the buffer since this
packet is not an expected response.
Bytes Name
----- ----
1 field_count, always = 0xff
2 errno
1 (sqlstate marker), always '#'
5 sqlstate (5 characters)
n message
*/
void MySQL_Packet::parse_error_packet() {
#ifdef DEBUG
Serial.print("Error: ");
Serial.print(read_int(5, 2));
Serial.print(" = ");
if (!buffer)
return;
for (int i = 0; i < packet_len-9; i++)
Serial.print((char)buffer[i+13]);
Serial.println(".");
#endif
}
/*
get_packet_type - Returns the packet type received from the server.
Bytes Name
----- ----
1 (Length Coded Binary) field_count, always = 0
1-9 (Length Coded Binary) affected_rows
1-9 (Length Coded Binary) insert_id
2 server_status
2 warning_count
n (until end of packet) message
Returns integer - 0 = successful parse, packet type if not an Ok packet
*/
int MySQL_Packet::get_packet_type() {
if (!buffer)
return -1;
int type = buffer[4];
return type;
}
/*
get_lcb_len - Retrieves the length of a length coded binary value
This reads the first byte from the offset into the buffer and returns
the number of bytes (size) that the integer consumes. It is used in
conjunction with read_int() to read length coded binary integers
from the buffer.
Returns integer - number of bytes integer consumes
*/
int MySQL_Packet::get_lcb_len(int offset) {
if (!buffer)
return 0;
int read_len = buffer[offset];
if (read_len > 250) {
// read type:
byte type = buffer[offset+1];
if (type == 0xfc)
read_len = 2;
else if (type == 0xfd)
read_len = 3;
else if (type == 0xfe)
read_len = 8;
} else {
read_len = 1;
}
return read_len;
}
/*
read_int - Retrieve an integer from the buffer in size bytes.
This reads an integer from the buffer at offset position indicated for
the number of bytes specified (size).
offset[in] offset from start of buffer
size[in] number of bytes to use to store the integer
Returns integer - integer from the buffer
*/
int MySQL_Packet::read_int(int offset, int size) {
int value = 0;
int new_size = 0;
if (!buffer)
return -1;
if (size == 0)
new_size = get_lcb_len(offset);
if (size == 1)
return buffer[offset];
new_size = size;
int shifter = (new_size - 1) * 8;
for (int i = new_size; i > 0; i--) {
value += (buffer[i-1] << shifter);
shifter -= 8;
}
return value;
}
/*
store_int - Store an integer value into a byte array of size bytes.
This writes an integer into the buffer at the current position of the
buffer. It will transform an integer of size to a length coded binary
form where 1-3 bytes are used to store the value (set by size).
buff[in] pointer to location in internal buffer where the
integer will be stored
value[in] integer value to be stored
size[in] number of bytes to use to store the integer
*/
void MySQL_Packet::store_int(byte *buff, long value, int size) {
memset(buff, 0, size);
if (value < 0xff)
buff[0] = (byte)value;
else if (value < 0xffff) {
buff[0] = (byte)value;
buff[1] = (byte)(value >> 8);
} else if (value < 0xffffff) {
buff[0] = (byte)value;
buff[1] = (byte)(value >> 8);
buff[2] = (byte)(value >> 16);
} else if (value < 0xffffff) {
buff[0] = (byte)value;
buff[1] = (byte)(value >> 8);
buff[2] = (byte)(value >> 16);
buff[3] = (byte)(value >> 24);
}
}
/*
read_lcb_int - Read an integer with len encoded byte
This reads an integer from the buffer looking at the first byte in the offset
as the encoded length of the integer.
offset[in] offset from start of buffer
Returns integer - integer from the buffer
*/
int MySQL_Packet::read_lcb_int(int offset) {
int len_size = 0;
int size = 0;
int value = 0;
if (!buffer)
return -1;
len_size = buffer[offset];
if (len_size < 252) {
return buffer[offset];
} else if (len_size == 252) {
len_size = 2;
} else if (len_size == 253) {
len_size = 3;
} else {
len_size = 8;
}
int shifter = (len_size-1) * 8;
for (int i = len_size; i > 0; i--) {
value += (buffer[offset+i] << shifter);
shifter -= 8;
}
return value;
}
/*
print_packet - Print the contents of a packet via Serial.print
This method is a diagnostic method. It is best used to decipher a
packet from the server (or before being sent to the server). If you
are looking for additional program memory space, you can safely
delete this method.
*/
void MySQL_Packet::print_packet() {
if (!buffer)
return;
Serial.print("Packet: ");
Serial.print(buffer[3]);
Serial.print(" contains ");
Serial.print(packet_len+3);
Serial.println(" bytes.");
Serial.print(" HEX: ");
for (int i = 0; i < packet_len+3; i++) {
Serial.print(buffer[i], HEX);
Serial.print(' ');
}
Serial.println();
Serial.print("ASCII: ");
for (int i = 0; i < packet_len+3; i++)
Serial.print((char)buffer[i]);
Serial.println();
}

View File

@@ -0,0 +1,85 @@
/*
Copyright (c) 2012, 2016 Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
MySQL_Packet.h - Packet library for communicating with a MySQL Server
This header file defines the base packet handling code for connecting
to a MySQL server and executing queries.
Change History:
Version 1.0.0a Created by Dr. Charles A. Bell, April 2012.
Version 1.0.0b Updated by Dr. Charles A. Bell, October 2013.
Version 1.0.1b Updated by Dr. Charles A. Bell, February 2014.
Version 1.0.2b Updated by Dr. Charles A. Bell, April 2014.
Version 1.0.3rc Updated by Dr. Charles A. Bell, March 2015.
Version 1.0.4ga Updated by Dr. Charles A. Bell, July 2015.
Version 1.1.0a Created by Dr. Charles A. Bell, January 2016.
Version 1.1.1a Created by Dr. Charles A. Bell, January 2016.
Version 1.1.2b Created by Dr. Charles A. Bell, November 2016.
Version 1.2.0 Created by Dr. Charles A. Bell, March 2020.
*/
#ifndef MYSQL_PACKET_H
#define MYSQL_PACKET_H
#ifdef ARDUINO_ARCH_ESP32
#include <Arduino.h>
#elif ARDUINO_ARCH_ESP8266
#include <ESP8266WiFi.h>
#else
// #include <WiFi.h>
#include <Ethernet.h>
#endif
#define MYSQL_OK_PACKET 0x00
#define MYSQL_EOF_PACKET 0xfe
#define MYSQL_ERROR_PACKET 0xff
#define MYSQL_VERSION_STR "1.2.0"
#define DEBUG
const char MEMORY_ERROR[] PROGMEM = "Memory error.";
const char PACKET_ERROR[] PROGMEM = "Packet error.";
const char READ_TIMEOUT[] PROGMEM = "ERROR: Timeout waiting for client.";
class MySQL_Packet {
public:
byte *buffer; // buffer for reading packets
int packet_len; // length of current packet
Client *client; // instance of client class (e.g. EthernetClient)
char *server_version; // save server version from handshake
MySQL_Packet(Client *client_instance);
boolean complete_handshake(char *user, char *password);
void send_authentication_packet(char *user, char *password,
char *db=NULL);
void parse_handshake_packet();
boolean scramble_password(char *password, byte *pwd_hash);
void read_packet();
int get_packet_type();
void parse_error_packet();
int get_lcb_len(int offset);
int read_int(int offset, int size=0);
void store_int(byte *buff, long value, int size);
int read_lcb_int(int offset);
int wait_for_bytes(int bytes_count);
void show_error(const char *msg, bool EOL = false);
void print_packet();
private:
byte seed[20];
};
#endif