SdFat to SerialFlash

I need help with the SerialFlash

I am trying to adapt Pepijndevos's project (https://github.com/pepijndevos/bicycle_gps), which reads data from a binary file representing a map using an RTree.

The project was developed for the Arduino Mega, and I am trying to use a Teensy 4 with SerialFlash, but I can't correctly read the data. I tried using a Teensy 3.6 with the SdFat library, but the current library doesn't have some functions used in the Arduino AVR version, such as the "readBlock" used on line #82 (sd.card()->readBlock(block, nb.buf);) of the rtree.ino code.

Could someone help me adapt the code to use SerialFlash?

Follow the codes:

bicycle_gps.ino
C:
#include <stdio.h>
#include <math.h>
#include <Wire.h>
#include <SPI.h>
#include <SdFat.h>
#include "Adafruit_MPL3115A2.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include "Adafruit_GPS.h"
#include "MemoryFree.h"
#include "SoftwareSerial.h" // not used
#include "types.h"


// For the Adafruit shield, these are the default.
#define TFT_DC 9
#define TFT_CS 10
#define SD_CS 4
#define JOYSTICK 18
#define JOYSTICK_X 14
#define JOYSTICK_Y 15

// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

// Create an instance of the pressure object
Adafruit_MPL3115A2 baro = Adafruit_MPL3115A2();

// Set up GPS
Adafruit_GPS GPS(&UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2);
ISR(USART2_RX_vect)
{
  GPS.read();
}
ISR(USART2_UDRE_vect)
{
  GPS.write();
}

// joystick center
int x_centre, y_centre;
double zoom = 10000;
bool streetnames = false;

// file system
SdFat sd;
// test file
SdFile file;
// file extent
uint32_t bgnBlock, endBlock;

void setup()
{
  Wire.begin();       // Join i2c bus
  Serial.begin(9600); // Start serial for output
  baro.begin();       // Get sensor online
  tft.begin();        // Start TFT
  GPS.begin(9600);    // Start GPS
  // Mount SD card
  if (!sd.begin(SD_CS, SPI_FULL_SPEED)) sd.initErrorHalt();
 
  // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  // uncomment this line to turn on only the "minimum recommended" data
  //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY);
  // For parsing data, we don't suggest using anything but either RMC only or RMC+GGA since
  // the parser doesn't care about other sentences at this time

  // Set the update rate
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);   // 1 Hz update rate
  // For the parsing code to work nicely and have time to sort thru the data, and
  // print it out we don't suggest using anything higher than 1 Hz

  // Request updates on antenna status, comment out to keep quiet
  //GPS.sendCommand(PGCMD_ANTENNA);
 
  // joystick
  pinMode(JOYSTICK, INPUT);
  x_centre = analogRead(JOYSTICK_X);
  y_centre = analogRead(JOYSTICK_Y);

  tft.setTextSize(1);
  tft.fillScreen(ILI9341_BLACK);
  tft.setRotation(1);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextWrap(false);
}

void loop()
{
  Serial.print("freeMemory()=");
  Serial.println(freeMemory());
  float pressure = baro.getPressure();
  float temperature = baro.getTemperature();
  // if a sentence is received, we can check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    // this also sets the newNMEAreceived() flag to false
    GPS.parse(GPS.lastNMEA());
  }
 
  // logging stuff
  /*
  char filename[13];
  snprintf(filename, 13, "%d-%d-%d.log", GPS.year, GPS.month, GPS.day);
  File dataFile = SD.open(filename, FILE_WRITE);
 
  if (dataFile) {
    dataFile.println(String() +
    GPS.latitude_fixed + "," + GPS.lat + "," +
    GPS.longitude_fixed + "," + GPS.lon + "," +
    pressure + "," + temperature + "," + cycle_time
    );
    dataFile.close();
  }*/
 
  if (GPS.fix) {
    // this is not correct
    double lat_zoom = zoom / ((double)tft.width() / tft.height());
    double lon_zoom = zoom / lon_scale(GPS.latitude_fixed);
    Serial.println(lon_scale(GPS.latitude_fixed));
    Serial.println((double)tft.width() / tft.height());
    
    Rect bounds = {.sub = 0,
                   .x0 = GPS.longitude_fixed - lon_zoom,
                   .y0 = GPS.latitude_fixed - zoom,
                   .x1 = GPS.longitude_fixed + lon_zoom,
                   .y1 = GPS.latitude_fixed + zoom};
    rtree_lookup(&bounds);
    tft.fillCircle(tft.width()/2, tft.height()/2, 4, ILI9341_BLUE);
  }
 
  tft.setCursor(0, 0);
  tft.setTextSize(1);
  tft.setTextColor(ILI9341_WHITE);
  tft.print(pressure, 2);
  tft.println(" Pa");
  tft.print(temperature, 2);
  tft.println(" C");
 
  tft.setCursor(250, 0);
  tft.setTextSize(2);
  tft.print(GPS.hour);
  tft.print(":");
  tft.print(GPS.minute);

  tft.setTextSize(3);
  tft.setCursor(100, 200);
  tft.print(GPS.speed * 1.852, 2);
    tft.println(" Km/h");

  unsigned long time = millis() + 10000;
  bool streetnames_ = streetnames;
  while (millis() < time && streetnames_ == streetnames) {
    int y = y_centre - analogRead(JOYSTICK_Y);
    int btn = digitalRead(JOYSTICK);
    if (y > 20) {
      zoom *= 0.8;
      break;
    } else if (y < -20) {
      zoom *= 1.3;
      break;
    } else if (btn == LOW) {
      streetnames = !streetnames;
    }
  }
  /*tft.println("Joystick:");
  tft.print("X: ");
  tft.print(x_centre - analogRead(JOYSTICK_X));
  tft.print(" Y: ");
  tft.print(y_centre - analogRead(JOYSTICK_Y));*/
  tft.fillScreen(ILI9341_BLACK);
  tft.setCursor(0, 0);
  tft.setTextSize(1);
}

//radians = (degrees * 71) / 4068
//degrees = (radians * 4068) / 71
double lon_scale(int32_t lat) {
  double dlat = lat / 10000000.0;
  double rad = dlat * 0.0174532925;
  return cos(rad);
}

rtree.ino
C:
int overlap(Rect* r, Rect* s) {
  return r->x0 < s->x1 &&
         r->y0 < s->y1 &&
         r->x1 > s->x0 &&
         r->y1 > s->y0;
}

int32_t scale_to_width(int32_t x, Rect* bounds) {
  int32_t bwidth = bounds->x1 - bounds->x0;
  return (x - bounds->x0) / (bwidth / tft.width());
}

int32_t scale_to_height(int32_t y, Rect* bounds) {
  int32_t bheight = bounds->y1 - bounds->y0;
  int32_t height = (y - bounds->y0) / (bheight / tft.height());
  return -height + tft.height();
}

void draw_line(Rect* bounds, int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint16_t color) {
  x0 = scale_to_width(x0, bounds);
  y0 = scale_to_height(y0, bounds);
  x1 = scale_to_width(x1, bounds);
  y1 = scale_to_height(y1, bounds);
 
  tft.drawLine(x0, y0, x1, y1, color);
}

void draw_name(char* wayname, Point* point, Rect* bounds) {
  int x = scale_to_width(point->x, bounds);
  int y = scale_to_height(point->y, bounds);
  tft.setCursor(x, y);
  tft.println(wayname);
}

void draw_points(NodeBuffer* nb, Rect* bounds, uint32_t block) {
  // WARNIG: fence poles ahead.
  // the first points are in the node,
  // this means we can satisfy most without aditional reads
  // but outliers should be read block-by-block.
  // max |        avg         
  //-----+--------------------
  // 784 | 4.8219990810608790
  Way* w = &nb->n.sub.way;
  int len = w->pointlen - 1; // minus last node
  int namelen = w->namelen;
  char* wayname = &w->wayname;
  // tricky allignment code, includes padding and string lengths
  // to make sure a point does not cross a block boundary
  Point* points = (Point*)((uintptr_t)nb + (offsetof(NodeBuffer, n.sub.way.wayname) + namelen + 8 & ~7));

  uint16_t color = ILI9341_YELLOW;
  if (w->flags & 1) {
    color = ILI9341_GREEN;
  } else if (w->flags & 2) {
    color = ILI9341_RED;
  } else if (w->flags & 4) {
    color = ILI9341_BLUE;
  }
  uintptr_t bufend = (uintptr_t)nb + sizeof(NodeBuffer);
  int i = 0;
  while (len--) {
    Point p1 = points[i];
    i++;
    if ((uintptr_t)&points[i] >= bufend) {
      i = 0;
      block++;
      sd.card()->readBlock(block, nb->buf);
      points = nb->points;
      namelen = 0; // the name is no longer in memory, too lazy to copy
    }
    Point p2 = points[i];
    draw_line(bounds, p1.x, p1.y, p2.x, p2.y, color);
  }
 
  if (streetnames && namelen) draw_name(wayname, &points[len/2], bounds);
}

void inner_lookup(Rect* bounds, int32_t index) {
  NodeBuffer nb;
  uint32_t block = bgnBlock + (index / 512);
  sd.card()->readBlock(block, nb.buf);
 
  if (!nb.n.len) {
    draw_points(&nb, bounds, block);
    return;
  }
  for (int i=0; i<nb.n.len; i++) {
    if (overlap(&nb.n.sub.nodes[i], bounds)) {
      inner_lookup(bounds, nb.n.sub.nodes[i].sub);
    }
  }
}

void rtree_lookup(Rect* bounds) {
  tft.setTextSize(1);
  tft.setTextColor(38066);
  if (!file.open(sd.vwd(), "data.bin", O_READ)) {
    Serial.println("No data file");
    return;
  }
  // get the location of the file's blocks
  if (!file.contiguousRange(&bgnBlock, &endBlock)) {
    Serial.println("File not contiguous");
    return;
  }

  uint32_t index;
  file.read(&index, sizeof(index));
 
  inner_lookup(bounds, index);
 
  file.close();
}

types.h
C:
#define DEGREE 25

typedef struct {
  int32_t x, y;
} Point;

typedef struct {
  uint32_t sub;
  int32_t x0, y0, x1, y1;
} Rect;

typedef struct {
  uint16_t pointlen;
  uint8_t namelen;
  uint8_t flags;
  char wayname;
} Way;

typedef union {
  Rect nodes[DEGREE];
  Way way;
} Spatial;

typedef struct {
  uint8_t len;
  Spatial sub;
} Node;

typedef union {
  Node n;
  uint8_t buf[512];
  Point points[512/sizeof(Point)];
} NodeBuffer;
 
Back
Top