utilizing the SD card

Discussion, support, and resources for our noise making modules and kits.

Moderators: Kent, scottwilson

Post Reply
User avatar
spacenoodle
Veteran Wiggler
Posts: 709
Joined: Tue Mar 20, 2012 9:00 pm
Location: Madison, WI

utilizing the SD card

Post by spacenoodle » Mon Mar 02, 2015 10:15 am

I need to save values in two dimensional arrays to the SD card and read them back into the arrays when my sketch loads. Are there any examples of this for the b? If someone could point me in the right direction that would be great. I'm not sure how to read and write a file.

User avatar
scottwilson
Wiggling with Experience
Posts: 432
Joined: Mon Sep 16, 2013 12:54 pm

Post by scottwilson » Mon Mar 02, 2015 12:04 pm

Do you have any code we can look at?

There's three ways...

The first is to store binary data in a file that you write bytes to and read bytes from that gets cast to some memory space. This is the most efficient, but it's the hardest to debug.

The second would be to write the data out as strings in some random format and then parse the strings when you read it in. It's a little easier to debug and edit the files offline. The downside of this is that you have to write your own parsing routines.

The third would be to use the JSON library that we're already using to store data in plain text. The benefit is that it supports complex data structures out of the box. The downside is that JSON libraries are a bit awkward to use at first if you're not used to them, especially when they are pared down for use in embedded systems.

Scannerdarkly is using the JSON library to store config data on the card, so you can look at that code:

https://github.com/nw2s/b/blob/b-1.1.0- ... OfLife.cpp

Code: Select all

void GameOfLife::saveConfig()
{
	aJsonObject *root;
	root = aJson.createObject();
	
	aJsonObject* jsonArray;

	jsonArray = aJson.createArray();
	for &#40;int i = 0; i < 14; i++&#41;
	&#123;
		aJsonObject* item = aJson.createItem&#40;config.gateMode&#91;i&#93;&#41;;
		aJson.addItemToArray&#40;jsonArray, item&#41;;
	&#125;
	aJson.addItemToObject&#40;root, "gateMode", jsonArray&#41;;	

	jsonArray = aJson.createArray&#40;&#41;;
	for &#40;int i = 0; i < 16; i++&#41;
	&#123;
		aJsonObject* item = aJson.createItem&#40;config.cvMode&#91;i&#93;&#41;;
		aJson.addItemToArray&#40;jsonArray, item&#41;;
	&#125;
	aJson.addItemToObject&#40;root, "cvMode", jsonArray&#41;;	

	jsonArray = aJson.createArray&#40;&#41;;
	for &#40;int i = 0; i < 16; i++&#41;
	&#123;
		aJsonObject* item = aJson.createItem&#40;config.cvRangeMin&#91;i&#93;&#41;;
		aJson.addItemToArray&#40;jsonArray, item&#41;;
	&#125;
	aJson.addItemToObject&#40;root, "cvRangeMin", jsonArray&#41;;	

	jsonArray = aJson.createArray&#40;&#41;;
	for &#40;int i = 0; i < 16; i++&#41;
	&#123;
		aJsonObject* item = aJson.createItem&#40;config.cvRangeMax&#91;i&#93;&#41;;
		aJson.addItemToArray&#40;jsonArray, item&#41;;
	&#125;
	aJson.addItemToObject&#40;root, "cvRangeMax", jsonArray&#41;;	
	
	jsonArray = aJson.createArray&#40;&#41;;
	for &#40;int i = 0; i < 16; i++&#41;
	&#123;
		aJsonObject* item = aJson.createItem&#40;config.noteScale&#91;i&#93;&#41;;
		aJson.addItemToArray&#40;jsonArray, item&#41;;
	&#125;
	aJson.addItemToObject&#40;root, "noteScale", jsonArray&#41;;
	
	aJsonStringStream stringStream&#40;NULL, jsonBuffer, 512&#41;;
	aJson.print&#40;root, &stringStream&#41;;

	Serial.println&#40;"--- config JSON created ----------------------------"&#41;;
	Serial.println&#40;jsonBuffer&#41;;
	Serial.println&#40;"----------------------------------------------------"&#41;;
	
	SdFile rootFile = b&#58;&#58;getSDRoot&#40;&#41;; 
	SdFile configFolder;
	if &#40;!configFolder.open&#40;rootFile, CONFIG_FOLDER, O_READ&#41;&#41;
	&#123;
	    if &#40;configFolder.makeDir&#40;rootFile, CONFIG_FOLDER&#41;&#41;
		&#123;
			Serial.println&#40;"Config folder not found, created"&#41;;
		&#125;
		else
		&#123;
			Serial.println&#40;"Config folder not found and could not create one"&#41;;
			return;
		&#125;
		if &#40;!configFolder.open&#40;rootFile, CONFIG_FOLDER, O_READ&#41;&#41;
		&#123;
			Serial.println&#40;"Could not open config folder after creating one"&#41;;
		&#125;
	&#125;
	
	SdFile configFile;
	if &#40;configFile.open&#40;configFolder, GAME_OF_LIFE_CONFIG_FILE_NAME, O_CREAT | O_WRITE | O_TRUNC&#41;&#41;
	&#123;
		configFile.println&#40;jsonBuffer&#41;;
		configFile.close&#40;&#41;;
		Serial.println&#40;"Config saved!"&#41;;
	&#125;
	else
	&#123;
	    Serial.print&#40;"Could not write config, error opening config file for writing "&#41;;
		Serial.println&#40;GAME_OF_LIFE_CONFIG_FILE_NAME&#41;;
	&#125;
	
	aJson.deleteItem&#40;root&#41;;
&#125;


User avatar
spacenoodle
Veteran Wiggler
Posts: 709
Joined: Tue Mar 20, 2012 9:00 pm
Location: Madison, WI

Post by spacenoodle » Tue Mar 03, 2015 1:31 am

Thanks. Here's one example. I've got a function that edits the values in this array. How the values are formatted in the config file visually is pretty important because they are representative of x/y coordinates on a video screen. I'm calibrating the b with all the features of Ming Mecca so that things like collision are being handled on the b side.

Option 2 or 3 should work. JSON isn't in my comfort zone yet but that looks like it might work well for me in this case. Do you agree?

Code: Select all

int tileIndex&#91;12&#93;&#91;10&#93; = &#123; //need to store/read these values on card
  &#123;2288,  2311,	2334,	2357,	2370,	2385,	2406,	2434,	2452,	2485&#125;,
  &#123;2080,  2093,	2121,	2139,	2172,	2195,	2213,	2231,	2248,	2274&#125;,
  &#123;1877,  1895,	1918,	1941,	1961,	1977,	2005,	2023,	2035,	2064&#125;,
  &#123;1664,  1687,	1710,	1723,	1746,	1769,	1785,	1806,	1828,	1851&#125;,
  &#123;1456,  1479,	1502,	1525,	1547,	1561,	1580,	1607,	1620,	1643&#125;,
  &#123;1248,  1271,	1294,	1317,	1340,	1363,	1381,	1399,	1418,	1435&#125;,
  &#123;1040,  1063,	1086,	1099,	1127,	1145,	1173,	1191,	1214,	1237&#125;,
  &#123;832,   855,	878,	901,	924,	942,	960,	980,	1006,	1029&#125;,
  &#123;624,   647,	670,	693,	716,	736,	752,	772,	789,	811&#125;,
  &#123;416,   439,	462,	485,	508,	531,	554,	567,	590,	610&#125;,
  &#123;213,   234,	254,	277,	300,	318,	346,	359,	388,	408&#125;,
  &#123;0,     26,   46,     69,     92,     115,	135,	151,	184,	202&#125;
&#125;

void calibration&#40;&#41;
&#123;
  bool left = ps3->getButtonPress&#40;LEFT&#41;;
  bool right = ps3->getButtonPress&#40;RIGHT&#41;;

  if &#40;left == true && left_released == true || left == true && scrolling == true&#41; &#123;
    last_indx = indx;

    if &#40;indx > 1&#41; &#123;
      indx--;
      cal_printed = false;
    &#125;

    left_released = false;
  &#125;

  if &#40;right == true && right_released == true || right == true && scrolling == true&#41; &#123;
    last_indx = indx;

    if &#40;indx < 120&#41; &#123;
      indx++;
      cal_printed = false;
    &#125;

    right_released = false;
  &#125;

  if &#40;bool up = ps3->getButtonPress&#40;UP&#41; == true && up_released == true&#41; &#123;
    tileIndex&#91;row&#93;&#91;column&#93;++;
    cal_printed = false;
    up_released = false;
  &#125;

  if &#40;bool down = ps3->getButtonPress&#40;DOWN&#41; == true && down_released == true&#41; &#123;
    tileIndex&#91;row&#93;&#91;column&#93;--;
    cal_printed = false;
    down_released = false;
  &#125;

  if &#40;bool left = ps3->getButtonPress&#40;LEFT&#41; == false&#41; &#123;
    left_released = true;
  &#125;

  if &#40;bool right = ps3->getButtonPress&#40;RIGHT&#41; == false&#41; &#123;
    right_released = true;
  &#125;

  if &#40;bool up = ps3->getButtonPress&#40;UP&#41; == false&#41; &#123;
    up_released = true;
  &#125;

  if &#40;bool down = ps3->getButtonPress&#40;DOWN&#41; == false&#41; &#123;
    down_released = true;
  &#125;

  if &#40;left_released == false || right_released == false&#41; &#123;
    if &#40;scroll_waiting == false&#41; &#123;
      wait = millis&#40;&#41;;
      scroll_waiting = true;
    &#125;
    else &#123;
      start = millis&#40;&#41;;

      if &#40;start - wait > 700&#41; &#123;
        scrolling = true;
      &#125;
    &#125;
  &#125;
  else &#123;
    scroll_waiting = false;
    scrolling = false;
  &#125;

  row = &#40;indx / 10&#41;;
  column = indx % 10;
  if &#40;column == 0&#41; &#123;
    column = 10;
    row--;
  &#125;
  column--;

  row = abs&#40;row - 11&#41;;

  if &#40;cal_printed == false&#41; &#123;
    cal_printed = true;
    printTileIndex&#40;indx, cal, column, row&#41;;
  &#125;
  else if &#40;last_cal != cal&#41; &#123;
    printTileIndex&#40;indx, cal, column, row&#41;;
  &#125;

  last_cal = cal;

  An06->outputCV&#40;tileIndex&#91;row&#93;&#91;column&#93; + comp06&#41;;
&#125;

User avatar
scottwilson
Wiggling with Experience
Posts: 432
Joined: Mon Sep 16, 2013 12:54 pm

Post by scottwilson » Wed Mar 04, 2015 10:50 am

Yeah in your case, the JSON looks like it'll be pretty easy. The sample code stores a number of single dimensional arrays and you will be able to use something like this:

Code: Select all

   jsonArray = aJson.createArray&#40;&#41;; 
   for &#40;int i = 0; i < 14; i++&#41; 
   &#123; 
      aJsonObject* item = aJson.createItem&#40;config.gateMode&#91;i&#93;&#41;; 
      aJson.addItemToArray&#40;jsonArray, item&#41;; 
   &#125; 
   aJson.addItemToObject&#40;root, "gateMode", jsonArray&#41;;    
but create an inner loop to do the second dimension of array storage.

Then when you save it, I assume it won't be pretty printed, you can use something like http://jsonlint.com/ to pretty print it for you to help debugging.

s

User avatar
spacenoodle
Veteran Wiggler
Posts: 709
Joined: Tue Mar 20, 2012 9:00 pm
Location: Madison, WI

Post by spacenoodle » Mon Mar 09, 2015 2:15 am

Thank you. Everything seems to be working as far as loading and saving. Which is wonderful. But my first attempt at a jSON 2d array puts the saved data on a single line with no brackets to define rows. What am I doing wrong here?

This is how I'm reading:

Code: Select all

  for &#40;y = 0; y < 12; y++&#41;
  &#123;
    for &#40;x = 0; x < 10; x++&#41;
    &#123;
      aJsonObject* item = aJson.getArrayItem&#40;jsonArray, &#40;x + &#40;y*10&#41;&#41;&#41;;
      calTileIndex&#91;y&#93;&#91;x&#93; = item->valueint;
    &#125;
  &#125;
And saving:

Code: Select all

jsonArray = aJson.createArray&#40;&#41;;
  for &#40;int y = 0; y < 12; y++&#41;
  &#123;
    for &#40;int x = 0; x < 10; x++&#41;
    &#123;
      aJsonObject* item = aJson.createItem&#40;calTileIndex&#91;y&#93;&#91;x&#93;&#41;;
      aJson.addItemToArray&#40;jsonArray, item&#41;;
    &#125;
  &#125;
  aJson.addItemToObject&#40;root, "tileIndex", jsonArray&#41;;
The saved file looks like:

Code: Select all

&#123;"tileIndex"&#58;&#91;2288,2311,2334,2357,2370,2385,2406,2434,2452,2485,2080,2093,2121,2139,2172,2185,2213,2231,2248,2274,1877,1895,1918,1941,1961,1977,2005,2023,2035,2054,1664,1687,1710,1723,1746,1759,1785,1806,1828,1851,1456,1479,1502,1525,1547,1561,1580,1607,1620,1643,1248,1271,1294,1317,1340,1363,1381,1399,1418,1435,1040,1063,1086,1099,1127,1145,1173,1191,1214,1237,832,855,878,901,924,942,960,980,1006,1029,624,647,670,693,716,736,752,772,789,811,416,439,462,485,508,531,554,567,590,610,213,234,254,277,300,318,346,359,388,408,0,26,46,69,92,115,135,151,184,202&#93;&#125;
EDIT: I guess the formatting is not a huge deal, as it is working. I was just hoping it could look more like columns and rows.

User avatar
scottwilson
Wiggling with Experience
Posts: 432
Joined: Mon Sep 16, 2013 12:54 pm

Post by scottwilson » Tue Mar 10, 2015 8:17 am

It works since you have a fixed array size that is known before reading. It wouldn't work as well if the array sizes were arbitrary.

Even though you are looping through the x and y dimensions, you are only creating a single array.

You can update to something like this for saving:

Code: Select all

  jsonArrayY = aJson.createArray&#40;&#41;; 
  for &#40;int y = 0; y < 12; y++&#41; 
  &#123; 

    jsonArrayX = aJson.createArray&#40;&#41;; 
    
    aJson.addItemToArray&#40;jsonArrayY, jsonArrayX&#41;;

    for &#40;int x = 0; x < 10; x++&#41; 
    &#123; 
      aJsonObject* item = aJson.createItem&#40;calTileIndex&#91;y&#93;&#91;x&#93;&#41;; 
      aJson.addItemToArray&#40;jsonArrayX, item&#41;; 
    &#125; 
  &#125; 
  aJson.addItemToObject&#40;root, "tileIndex", jsonArray&#41;; 
and for reading:

Code: Select all

  for &#40;y = 0; y < 12; y++&#41; 
  &#123; 
    for &#40;x = 0; x < 10; x++&#41; 
    &#123; 
      aJsonObject* row = aJson.getArrayItem&#40;jsonArray, y&#41;; 
      aJsonObject* item = aJson.getArrayItem&#40;row, x&#41;; 
      calTileIndex&#91;y&#93;&#91;x&#93; = item->valueint; 
    &#125; 
  &#125;

User avatar
spacenoodle
Veteran Wiggler
Posts: 709
Joined: Tue Mar 20, 2012 9:00 pm
Location: Madison, WI

Post by spacenoodle » Tue Mar 17, 2015 9:45 am

Thanks Scott. I do appreciate your scripting advice immensely.

Post Reply

Return to “nw2s”