Create a database on an SD card

Hi everybody !

I have a project where I would like to write a database on an SD Card
connected to an Arduino Nano.

I know I could write a text file on an SD Card but I would like to know if it's possible to write a structured database on an SD Card instead of just a text file.
Is there a library that can do that ?

That would allow me to run queries much faster and easier than with a text file.
For instance, a lookup search on a text file would have to be sequential whereas on a structure database that would be much easier with a SQL query. Same things with deleting records.

I could copy the text file over wifi or Bluetooth or USB connection
and then convert it to a database. But I'm not keen to do that because I would end up with duplicates of the database in different locations instead of managing the database that is located on the SD Card.

I would welcome any suggestions.

Cheers !

Small data model:
Arduino Playground - DatabaseLibrary

GitHub - jwhiddon/EDB: A re-implementation of the Arduino database library to allow more than 256 records

I have not used the dB.
I have seen posts in forum where Ops have had issues; unknown if they were resolved.

That's great. Thanks.
I will test it out.

True, but creating and maintaining a data base on SD card will be REALLY slow.

Study database design, how to design the datasets in order to search and find objects. Index files pointing into the mass data could do a lot.
In order to be able to search for data the data needs to be organised to fit the search engine. Random data in the base calls for sequentially reading the entire database and it takes time, lots of time.

1 Like

Hi @lguapo,

sounds as if you would like to use SQL statements on your Arduino ... ?

SQL Databases use tables, queries and transactions to store, retrieve, filter and combine data ... This is not an application for microcontrollers but PCs or Servers.

I am with @Railroader. If you can come up with a reasonable data structure you can speed up finding data on SD card with the DB that @mrburnette mentioned in post #2 on Github, possibly with the assistance of lookup tables!

But it will not be a SQL DB, only a tool to handle a hugh amount of data of the same structure.

A little more information on the data might also be helpful ...

ec2021

P.S.: If you really require SQL you could of course use an ESP32. There is actually a lib for sqlite:

https://github.com/siara-cc/esp32_arduino_sqlite3_lib

1 Like

"Structured" is an interesting word ... like, "pre-sorted" data? Then, yes, you can and I did an ESP8266 SPIFFS (could be SD) that put the entire OUI database (about 680 KB) into the file system for querry. The indexing scheme is done at power-on, there after querries resolve in < 1 second.

/*
   Arduino 1.6.13 on Linux Mint 17.3 (1.8.1 tested)
   Open Source demo source by M. Ray Burnette (ray.burne) @ https://www.hackster.io/rayburne/projects
*/


#define maxBlks     7680                                                // determined by direct analysis

#include "FS.h"
#include <ESP8266WiFi.h>

const char* ssid     = "ABCDEFG";
const char* password = "010203040506";

unsigned char ASCII2HEX( char c)                                        // http://stackoverflow.com/questions/2499000/how-to-convert-an-ascii-hex-character-to-its-value-0-15
{
  return ( c <= '9' ) ? ( c - '0' ) : ( (c | '\x60') - 'a' + 10 ) ;
}

struct        OUI {                                                     // Custom data structure to hold linked list of O1::02 file pointers
  unsigned char c_Octet1 ;
  unsigned char c_Octet2 ;
  uint16_t      f_offset ;                                              // linked list: beginning of current record TO beginning of next line /n included
}  ;

OUI oui_array[maxBlks] ;                                                // define the array type and size

String        OUI_matchup ;
char          cArrayOUI[7] ;
char          currentCharacter ;
char          * pEnd ;

unsigned char cOctet1 ;
unsigned char cOctet2 ;
unsigned char cOctet3 ;

unsigned long fileByteOffset    = 0 ;
unsigned long iSum              = 0 ;
unsigned long previousPosition  = 0 ;
unsigned long currentPosition   = 0 ;
unsigned long timeMarker ;

uint16_t      iRecIdx = 0 ;
uint8_t       OUI_match_Index = 0 ;
uint8_t       io1 ;
uint8_t       io2 ;
uint8_t       io3 ;

bool          ok ;
bool          lFlag = true ;
bool          lFound = true ;;

File          FileRead ;
WiFiServer    server(80);


void setup() {
  Serial.begin(115200) ;
  delay(100) ;
  Serial.print("\n\r ***** Starting Up *****\r\n") ;
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  
  server.begin();                                                       // Start the server
  Serial.println("Server started");
  Serial.println(WiFi.localIP());                                       // Print the IP address
  
  OUI_matchup.reserve(8) ;                                              // xx-xx-xx
  ok = SPIFFS.begin() ;                                                 // https://github.com/esp8266/Arduino/blob/master/doc/filesystem.md

  oui_array[0].c_Octet1 = 0x0000 ;                                      // initialize array as a placeholder
  oui_array[0].f_offset = 0 ;
  previousPosition      = 0 ;                                           // Walking var for varying length descriptions field
  iRecIdx               = 1 ;                                           // Beginning array indexpreviousPosition

  Serial.print("\r\nBegin reading OUI.txt located in SPIFFS and building array... about 50 seconds\r\n") ;
  timeMarker = millis() + 5000;
  FileRead = SPIFFS.open("/oui.txt", "r") ;
  FileRead.seek(fileByteOffset, SeekSet) ;                              // Seek to record

    while (ok && FileRead.available() > 0)                              // While not EOF
    {
      currentCharacter = FileRead.read() ;

      if (OUI_match_Index < 8 )  {                                      // gather the first 8 characters of a line as: xx-xx-xx
        cArrayOUI[OUI_match_Index] = currentCharacter ;
        ++OUI_match_Index ;
      }

      if (currentCharacter == 9) {                                      // TAB ?
          cOctet1 = (ASCII2HEX(cArrayOUI[1])) + (ASCII2HEX(cArrayOUI[0]) * 16) ;
          cOctet2 = (ASCII2HEX(cArrayOUI[4])) + (ASCII2HEX(cArrayOUI[3]) * 16) ;
          // the magic in the test below is only possible because the imported OUI list is in order by 1st::2nd octet
          if ((cOctet2 > oui_array[iRecIdx - 1].c_Octet2) || ( cOctet1 > oui_array[iRecIdx - 1].c_Octet1)) 
          {
            oui_array[iRecIdx].c_Octet1 = cOctet1 ;
            oui_array[iRecIdx].c_Octet2 = cOctet2 ;                     // iRecIdx points to last-built array elements
            oui_array[iRecIdx].f_offset = (currentPosition - previousPosition) ;
            previousPosition = currentPosition ;


            if( millis() > timeMarker ) {
              Serial.print(".") ;                                       // Let user know something is happening
              timeMarker = millis() + 5000 ;
            }
            OUI_match_Index = 0 ;                                       // prepare for next OUI parsing
            OUI_matchup = "" ;                                          // null out the capture String;
            ++iRecIdx ;                                                 // Block count
        }
      }

      if (currentCharacter == 10)                                       // End-of-line ?
      {
          // previousPosition  = currentPosition ;                         // Delta
          currentPosition   = FileRead.position() ;                     // DO NOT nest this function
          OUI_match_Index = 0 ;                                         // prepare for next OUI parsing
          OUI_matchup = "" ;                                            // null out the capture String;
      }
    }                                                                   // EOF

    Serial.print( "\n\rReached End Of File\n\r") ;
}


Spiff_OUI-13.zip (244.3 KB)

The code is commented if you wish to review, but too large to dump into this post, so Zip attached.

1 Like

Take a look at this one and maybe figure out why it doesn't work.
[DatabaseOn SD] (DatabaseOnSD - Arduino Reference)

The reason I was looking to create a database
was to have a timestamp record sorted out chronologically.
So I could plot a graph later.

The structure of the data is actually simple:

timestamp (that would serve as primary key too)
+ value from sensor 1 + value from sensor 2 + value from sensor 3

and that's pretty much it, really.

This part is easy because the record would be taken
chronologically anyway.

But I want to be able to add records manually later.
Kind of like a manual adjustment of the data collected.
And adding observation too in form of notes.

Thinking about it now, I could actually use a text file (instead of a DB)
and use a temp file to sort out the data including the newly
added manual record + notes.

But that would mean going through the text file sequentially
from top to bottom every time there is a record added or a note.

Same thing when deleting a record, I would have to recreate
the whole text file.

I'm probably overthinking but would a database be faster?

I was under the impression I could create a SQL DB
and then use SQL queries.

If I remember correctly SQL uses Dichotomic search
and if I'm not completely wrong, this principle uses sequential search within indexes
with an efficient strategy of divide and conquer.
But at least, you don't have to deal with that, so that's one piece of code less to write.

That's interesting.

Just out of curiosity, why is it possible to use SQL on an ESP32 and not on an Arduino ?
Other than the fact that the sqlite library is already available for the ESP32.

It is because it's got a faster microcontroller than the Arduino ?

Yes, you got it right.
I was really just interested in having pre-sorted data.
But I want to add data manually too later.
And I want to be able to delete some records too.

Usually when talking about "Arduino" it is about relatively low clocked controllers with with very limited memory compared to a PC.

Therefore a lot of functionality we know from PCs are not available in microcontrollers. They are made for different purposes and do have their specific advantages over "computers".

The ESP32 is a very powerful microcontroller compared to an Uno or Nano and the like. Just look up the datasheets and compare memory and clock speed.

Today there is no such thing as "an Arduino" anymore as there are plenty of boards using quite different controllers/processors . So it is not unlikely that one if the more powerful can also handle SQLite. A Nano is definitely not the choice. It can write data in a format that a SQL application can read it, but not more...

Would be a nice attempt to write a SQL application for it making intensive use of the SD Card to read and store partial data and even use dynamically loaded code... :wink: But it would quite likely be pretty slow...

ec2021

Microcontrollers are excellent for data capture and real-time manipulation of the value(s); but, post-processing is best for a capable PC with an OS and highly versitle tools: Access, Excel, or real databases.

Why copy? Just move the database elements as JSON and let a capable product such as NodeRED digest the serial stream (serial-USB or BT-serial.)

No sneaker-Net from SD-card transfer, no silliness from attempting to run everything on a microcontroller. And many associated benefits from NodeRED including running on multiple platforms, real webserver, and data graphing and analysis tools. Node-RED (nodered.org)

I implemented on a RPi for one project but found that an old Intel HP netbook running the x86 Linux provided ample horsepower on a PC destined for the recycle-center.

Serial output directly into NodeRED - Arduino for STM32 (stm32duino.com)

IMO, editing data on SD is a data corruption just waiting to happen.
Raspberry Pi Desktop for PC and Mac – Raspberry Pi

Note: As Internet connectivity is not required, an old Win-XP PC can easily be repurposed.

1 Like

you made some good points:
-cross platform capabilities.
-lots of CPU power
-lowering the risk of data corruption

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.