Arduino Examples
Example 1: Navigation Switch
This example demonstrates how to use the navigation switch and detect if the SD card is inserted. The !INT pin of the GPIO expander IC (PCA9554) triggers an interrupt any time a button is pressed or an SD card is inserted/removed.
The code for this example can be found in the Firmware folder of this repository. Alternatively, you can expand the link below and copy and paste the code into a shiny new Arduino sketch:
Example 1 Arduino Code
/*
Read the 5-way switch and card detect on the Portability Shield
By: Nathan Seidle
SparkFun Electronics
Date: November 24th, 2024
License: This code is public domain but you buy me a drink if you use this
and we meet someday (Drink license).
Feel like supporting our work? Buy a board from SparkFun!
https://www.sparkfun.com/products/27510
This example demonstrates how to read the 5-way nav switch and detect if the SD card is inserted.
The !INT pin of the GPIO expander IC (PCA9554) triggers an interrupt any time a button is pressed or
an SD card is inserted/removed.
Hardware Connections:
Connect the Portability shield to the RTK Postcard or other controller board
Open output window at 115200bps
Press button on navigation switch
*/
#include <SparkFun_I2C_Expander_Arduino_Library.h> // Click here to get the library: http://librarymanager/All#SparkFun_I2C_Expander_Arduino_Library
SFE_PCA95XX io(PCA95XX_PCA9554); // Create an instance with the PCA9554 IC
int gpioExpander_up = 0;
int gpioExpander_down = 1;
int gpioExpander_right = 2;
int gpioExpander_left = 3;
int gpioExpander_center = 4;
int gpioExpander_cardDetect = 5;
int gpioExpander_interrupt = 14; //INT of PCA9554 is connected to ESP14. Goes low when button is pressed.
bool buttonPressed = false;
void IRAM_ATTR gpioExpanderISR()
{
buttonPressed = true;
}
void setup()
{
Serial.begin(115200);
delay(250);
Serial.println("Portability Shield Example");
Wire.begin(7, 20); //SDA, SCL. I2C0 on Portability Shield
// Initialize the PCA95xx with its default I2C address
if (io.begin() == false)
{
Serial.println("PCA9554 not detected. Please check wiring. Freezing...");
while (1)
;
}
Serial.println("Portability Shield online!");
pinMode(gpioExpander_interrupt, INPUT_PULLUP);
io.pinMode(gpioExpander_up, INPUT);
io.pinMode(gpioExpander_down, INPUT);
io.pinMode(gpioExpander_left, INPUT);
io.pinMode(gpioExpander_right, INPUT);
io.pinMode(gpioExpander_center, INPUT);
io.pinMode(gpioExpander_cardDetect, INPUT);
uint8_t currentState = io.getInputRegister() & 0b00111111; //Ignore unconnected GPIO6/7
if (currentState & 0b00100000)
Serial.println("SD Card detected");
else
Serial.println("No card detected");
//Setup interrupt service routine (ISR)
attachInterrupt(gpioExpander_interrupt, gpioExpanderISR, CHANGE);
}
void loop()
{
if (buttonPressed == true)
{
//Get all the pins in one read
uint8_t currentState = io.getInputRegister() & 0b00111111; //Ignore unconnected GPIO6/7
// Pins are pulled high so when we see low, button is being pressed
if ((currentState & (1 << gpioExpander_up)) == 0)
Serial.print("Up");
if ((currentState & (1 << gpioExpander_down)) == 0)
Serial.print("Down");
if ((currentState & (1 << gpioExpander_left)) == 0)
Serial.print("Left");
if ((currentState & (1 << gpioExpander_right)) == 0)
Serial.print("Right");
if ((currentState & (1 << gpioExpander_center)) == 0)
Serial.print("Center");
Serial.println();
buttonPressed = false;
}
delay(100);
}
One thing to note is that you will also need to install the SparkFun I2C Expander Arduino Library if you haven't already. You can search the library from within the Arduino Library manager, download the zip from the GitHub Repository and install it manually, or you can click the link from within the code. Clicking the link will show something like the following:
Make sure you've selected the correct board (in this case, you will need to use ESP32 Dev Module from espressif) and port in the Tools menu and then hit the upload button. Once the code has finished uploading, go ahead and open a Serial Monitor. You should see something similar to the following.
Example 2: Fuel Gauge
This file demonstrates the simple API of the SparkFun MAX17043 Arduino library. Make sure you have a LiPo battery plugged into the JST port.
The code for this example can be found in the Firmware folder of this repository. Alternatively, you can expand the link below and copy and paste the code into a shiny new Arduino sketch:
Example 2 Arduino Code
/*
Example1_Simple
By: Paul Clark
Date: October 23rd 2020
Based extensively on:
MAX17043_Simple_Serial.cpp
SparkFun MAX17043 Example Code
Jim Lindblom @ SparkFun Electronics
Original Creation Date: June 22, 2015
This file demonstrates the simple API of the SparkFun MAX17043 Arduino library.
This example will print the gauge's voltage and state-of-charge (SOC) readings
to Serial (115200 baud)
This code is released under the MIT license.
Distributed as-is; no warranty is given.
*/
#include <Wire.h> // Needed for I2C
#include <SparkFun_MAX1704x_Fuel_Gauge_Arduino_Library.h> // Click here to get the library: http://librarymanager/All#SparkFun_MAX1704x_Fuel_Gauge_Arduino_Library
SFE_MAX1704X lipo(MAX1704X_MAX17048); // Create a MAX17048
double voltage = 0; // Variable to keep track of LiPo voltage
double soc = 0; // Variable to keep track of LiPo state-of-charge (SOC)
bool alert; // Variable to keep track of whether alert has been triggered
void setup()
{
Serial.begin(115200); // Start serial, to output debug data
while (!Serial)
; //Wait for user to open terminal
Serial.println(F("MAX17043 Example"));
Wire.begin(7, 20); //SDA, SCL
// Set up the fuel gauge:
if (lipo.begin() == false)
{
Serial.println(F("MAX17043 not detected. Please check wiring. Freezing."));
while (1)
;
}
// Quick start restarts the MAX17043 in hopes of getting a more accurate
// guess for the SOC.
lipo.quickStart();
// We can set an interrupt to alert when the battery SoC gets too low.
// We can alert at anywhere between 1% - 32%:
lipo.setThreshold(20); // Set alert threshold to 20%.
}
void loop()
{
// lipo.getVoltage() returns a voltage value (e.g. 3.93)
voltage = lipo.getVoltage();
// lipo.getSOC() returns the estimated state of charge (e.g. 79%)
soc = lipo.getSOC();
// lipo.getAlert() returns a 0 or 1 (0=alert not triggered)
alert = lipo.getAlert();
// Print the variables:
Serial.print("Voltage: ");
Serial.print(voltage); // Print the battery voltage
Serial.println(" V");
Serial.print("Percentage: ");
Serial.print(soc); // Print the battery state of charge
Serial.println(" %");
Serial.print("Alert: ");
Serial.println(alert);
Serial.println();
delay(500);
}
You will also need to install the SparkFun MAX1704x Fuel Gauge Arduino Library if you haven't already. You can search the library from within the Arduino Library manager, download the zip from the GitHub Repository and install it manually, or you can click the link from within the code. Clicking the link will show something like the following:
Make sure you've selected the correct board and port in the Tools menu and then hit the upload button. Once the code has finished uploading, go ahead and open a Serial Monitor at 115200 baud. You should see the gauge's voltage and state-of-charge (SOC) readings, similar to the following:
Example 3: Display
This example demonstrates how to display on the 1.3" OLED and read the fuel gauge. You should have the Portability Shield connected to the RTK Postcard or other controller and a LiPo battery plugged into the JST port.
The code for this example can be found in the Firmware folder of this repository. Alternatively, you can expand the link below and copy and paste the code into a shiny new Arduino sketch:
Example 3 Arduino Code
/*
Display a cube on the OLED at 400kHz. Query the fuel guage periodically.
By: Nathan Seidle
SparkFun Electronics
Date: November 24th, 2024
License: This code is public domain but you buy me a drink if you use this
and we meet someday (Drink license).
Feel like supporting our work? Buy a board from SparkFun!
https://www.sparkfun.com/products/27510
This example demonstrates how to display on the 1.3" OLED and read the fuel gauge.
Hardware Connections:
Connect the Portability shield to the RTK Postcard or other controller board
Open output window at 115200bps
*/
#include <Wire.h>
#include <SparkFun_Qwiic_OLED.h> //http://librarymanager/All#SparkFun_Qwiic_OLED
Qwiic1in3OLED myOLED;
int width;
int height;
// For simple frame rate calculations
long drawTotalTime = 0;
int numberOfDraws = 0;
float d = 3;
float px[] = { -d, d, d, -d, -d, d, d, -d};
float py[] = { -d, -d, d, d, -d, -d, d, d};
float pz[] = { -d, -d, -d, -d, d, d, d, d};
float p2x[8] = {0};
float p2y[8] = {0};
float r[3] = {0};
#define SHAPE_SIZE 950
#define ROTATION_SPEED 00
#include <SparkFun_MAX1704x_Fuel_Gauge_Arduino_Library.h> // Click here to get the library: http://librarymanager/All#SparkFun_MAX1704x_Fuel_Gauge_Arduino_Library
SFE_MAX1704X lipo(MAX1704X_MAX17048);
double voltage = 0;
double soc = 0;
unsigned long lastLipoCheck;
void setup()
{
Serial.begin(115200);
delay(250);
Serial.println("OLED + Fuel gauge test");
Wire.begin(7, 20); //SDA, SCL
Wire.setClock(400000); //Go to 400kHz for faster OLED response
//Wire.setClock(1000000); //Moar! It's not 1MHz but it does work.
if (myOLED.begin() == false)
{
Serial.println("Device begin failed. Freezing...");
while (true);
}
if (lipo.begin() == false)
{
Serial.println("MAX17043 not detected. Please check wiring. Freezing...");
while (1);
}
Serial.println("Begin success");
width = myOLED.getWidth();
height = myOLED.getHeight();
// For frame rate calc
drawTotalTime = 0;
numberOfDraws = 0;
}
void loop()
{
drawCube();
if (millis() - lastLipoCheck > 1000)
{
lastLipoCheck = millis();
voltage = lipo.getVoltage();
soc = lipo.getSOC();
Serial.print("Voltage: ");
Serial.print(voltage); // Print the battery voltage
Serial.println(" V");
Serial.print("Percentage: ");
Serial.print(soc); // Print the battery state of charge
Serial.println(" %");
Serial.println();
}
}
void drawCube()
{
r[0] = r[0] + PI / 180.0; // Add a degree
r[1] = r[1] + PI / 180.0; // Add a degree
r[2] = r[2] + PI / 180.0; // Add a degree
if (r[0] >= 360.0 * PI / 180.0)
r[0] = 0;
if (r[1] >= 360.0 * PI / 180.0)
r[1] = 0;
if (r[2] >= 360.0 * PI / 180.0)
r[2] = 0;
// This routine gets called often, so just make these statics
static float px2, py2, pz2, px3, py3, pz3, ax, ay, az;
for (int i = 0; i < 8; i++)
{
px2 = px[i];
py2 = cos(r[0]) * py[i] - sin(r[0]) * pz[i];
pz2 = sin(r[0]) * py[i] + cos(r[0]) * pz[i];
px3 = cos(r[1]) * px2 + sin(r[1]) * pz2;
py3 = py2;
pz3 = -sin(r[1]) * px2 + cos(r[1]) * pz2;
ax = cos(r[2]) * px3 - sin(r[2]) * py3;
ay = sin(r[2]) * px3 + cos(r[2]) * py3;
az = pz3 - 150;
p2x[i] = width / 2 + ax * SHAPE_SIZE / az;
p2y[i] = height / 2 + ay * SHAPE_SIZE / az;
}
// Calculate draw time
uint32_t startTime = millis();
myOLED.erase();
for (int i = 0; i < 3; i++)
{
myOLED.line(p2x[i], p2y[i], p2x[i + 1], p2y[i + 1]);
myOLED.line(p2x[i + 4], p2y[i + 4], p2x[i + 5], p2y[i + 5]);
myOLED.line(p2x[i], p2y[i], p2x[i + 4], p2y[i + 4]);
}
myOLED.line(p2x[3], p2y[3], p2x[0], p2y[0]);
myOLED.line(p2x[7], p2y[7], p2x[4], p2y[4]);
myOLED.line(p2x[3], p2y[3], p2x[7], p2y[7]);
myOLED.display();
// Write out our frame rate
drawTotalTime += millis() - startTime;
numberOfDraws++;
// Output framerate once every 120 frames
if (numberOfDraws % 120 == 0)
{
Serial.print("Frame rate: ");
Serial.println(numberOfDraws / (float)drawTotalTime * 1000.0);
numberOfDraws = 0;
drawTotalTime = 0;
}
}
You will also need both the SparkFun Qwiic OLED Arduino Library as well as the SparkFun MAX1704x Fuel Gauge Arduino Library. If you haven't already installed these, you can search them from within the Arduino Library manager and install them from there. Alternatively, you can download the zips from their respective GitHub Repositories here(Fuel Gauge) and here(Qwiic OLED), and install them manually.
Make sure you've selected the correct board and port in the Tools menu and then hit the upload button. Once the code has finished uploading, you should see the gauge's voltage and state-of-charge (SOC) readings, similar to the the output for Example 2. The 1.3" OLED display should show a bouncing box like so:
Example 4: SD Card
This example will mount an SD card, analyze the type of card, and output the analysis via the Serial Monitor.
Go ahead and connect the Portability Shield to the RTK Postcard or other controller board. Insert a microSD card into the socket. Cards up to 512GB should work.
The code for this example can be found in the Firmware folder of this repository. Alternatively, you can expand the link below and copy and paste the code into a shiny new Arduino sketch:
Example 4 Arduino Code
/*
Attempt to mount an SD card and analyze the type of card
By: Nathan Seidle
SparkFun Electronics
Date: November 24th, 2024
License: This code is public domain but you buy me a drink if you use this
and we meet someday (Drink license).
Feel like supporting our work? Buy a board from SparkFun!
https://www.sparkfun.com/products/27510
Hardware Connections:
Connect the Portability shield to the RTK Postcard or other controller board
Insert a microSD card into the socket. Cards up to 512GB should work.
Open output window at 115200bps
*/
#include "SdFat.h" // Click here to get the library: http://librarymanager/All#SdFat_SDXC by Bill Greiman
#include "sdios.h"
int pin_spiSCK = 32;
int pin_spiPICO = 26; //microSD SDI
int pin_spiPOCI = 25; //microSD SDO
int pin_microSD_CS = 27;
#define SD_CONFIG SdSpiConfig(pin_microSD_CS, SHARED_SPI, SD_SCK_MHZ(16))
//#define SD_CONFIG SdSpiConfig(pin_microSD_CS, DEDICATED_SPI, SD_SCK_MHZ(16))
const int8_t DISABLE_CS_PIN = -1;
//------------------------------------------------------------------------------
SdFs sd;
cid_t cid;
csd_t csd;
scr_t scr;
uint8_t cmd6Data[64];
uint32_t eraseSize;
uint32_t ocr;
static ArduinoOutStream cout(Serial);
//------------------------------------------------------------------------------
void cidDmp() {
cout << F("\nManufacturer ID: ");
cout << uppercase << showbase << hex << int(cid.mid) << dec << endl;
cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
cout << F("Product: ");
for (uint8_t i = 0; i < 5; i++) {
cout << cid.pnm[i];
}
cout << F("\nRevision: ") << cid.prvN() << '.' << cid.prvM() << endl;
cout << F("Serial number: ") << hex << cid.psn() << dec << endl;
cout << F("Manufacturing date: ");
cout << cid.mdtMonth() << '/' << cid.mdtYear() << endl;
cout << F("CID HEX: ");
hexDmp(&cid, sizeof(cid));
}
//------------------------------------------------------------------------------
void clearSerialInput() {
uint32_t m = micros();
do {
if (Serial.read() >= 0) {
m = micros();
}
} while (micros() - m < 10000);
}
//------------------------------------------------------------------------------
void csdDmp() {
eraseSize = csd.eraseSize();
cout << F("\ncardSize: ") << 0.000512 * csd.capacity();
cout << F(" MB (MB = 1,000,000 bytes)\n");
cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n");
cout << F("eraseSingleBlock: ");
if (csd.eraseSingleBlock()) {
cout << F("true\n");
} else {
cout << F("false\n");
}
cout << F("dataAfterErase: ");
if (scr.dataAfterErase()) {
cout << F("ones\n");
} else {
cout << F("zeros\n");
}
cout << F("CSD HEX: ");
hexDmp(&csd, sizeof(csd));
}
//------------------------------------------------------------------------------
void errorPrint() {
if (sd.sdErrorCode()) {
cout << F("SD errorCode: ") << hex << showbase;
printSdErrorSymbol(&Serial, sd.sdErrorCode());
cout << F(" = ") << int(sd.sdErrorCode()) << endl;
cout << F("SD errorData = ") << int(sd.sdErrorData()) << dec << endl;
}
}
//------------------------------------------------------------------------------
void hexDmp(void* reg, uint8_t size) {
uint8_t* u8 = reinterpret_cast<uint8_t*>(reg);
cout << hex << noshowbase;
for (size_t i = 0; i < size; i++) {
cout << setw(2) << setfill('0') << int(u8[i]);
}
cout << dec << endl;
}
//------------------------------------------------------------------------------
bool mbrDmp() {
MbrSector_t mbr;
bool valid = true;
if (!sd.card()->readSector(0, (uint8_t*)&mbr)) {
cout << F("\nread MBR failed.\n");
errorPrint();
return false;
}
cout << F("\nSD Partition Table\n");
cout << F("part,boot,bgnCHS[3],type,endCHS[3],start,length\n");
for (uint8_t ip = 1; ip < 5; ip++) {
MbrPart_t* pt = &mbr.part[ip - 1];
if ((pt->boot != 0 && pt->boot != 0X80) ||
getLe32(pt->relativeSectors) > csd.capacity()) {
valid = false;
}
cout << int(ip) << ',' << uppercase << showbase << hex;
cout << int(pt->boot) << ',';
for (int i = 0; i < 3; i++) {
cout << int(pt->beginCHS[i]) << ',';
}
cout << int(pt->type) << ',';
for (int i = 0; i < 3; i++) {
cout << int(pt->endCHS[i]) << ',';
}
cout << dec << getLe32(pt->relativeSectors) << ',';
cout << getLe32(pt->totalSectors) << endl;
}
if (!valid) {
cout << F("\nMBR not valid, assuming Super Floppy format.\n");
}
return true;
}
//------------------------------------------------------------------------------
void dmpVol() {
cout << F("\nScanning FAT, please wait.\n");
int32_t freeClusterCount = sd.freeClusterCount();
if (sd.fatType() <= 32) {
cout << F("\nVolume is FAT") << int(sd.fatType()) << endl;
} else {
cout << F("\nVolume is exFAT\n");
}
cout << F("sectorsPerCluster: ") << sd.sectorsPerCluster() << endl;
cout << F("fatStartSector: ") << sd.fatStartSector() << endl;
cout << F("dataStartSector: ") << sd.dataStartSector() << endl;
cout << F("clusterCount: ") << sd.clusterCount() << endl;
cout << F("freeClusterCount: ");
if (freeClusterCount >= 0) {
cout << freeClusterCount << endl;
} else {
cout << F("failed\n");
errorPrint();
}
}
//------------------------------------------------------------------------------
void printCardType() {
cout << F("\nCard type: ");
switch (sd.card()->type()) {
case SD_CARD_TYPE_SD1:
cout << F("SD1\n");
break;
case SD_CARD_TYPE_SD2:
cout << F("SD2\n");
break;
case SD_CARD_TYPE_SDHC:
if (csd.capacity() < 70000000) {
cout << F("SDHC\n");
} else {
cout << F("SDXC\n");
}
break;
default:
cout << F("Unknown\n");
}
}
//------------------------------------------------------------------------------
void printConfig(SdSpiConfig config) {
if (DISABLE_CS_PIN < 0) {
cout << F(
"\nAssuming the SD is the only SPI device.\n"
"Edit DISABLE_CS_PIN to disable an SPI device.\n");
} else {
cout << F("\nDisabling SPI device on pin ");
cout << int(DISABLE_CS_PIN) << endl;
pinMode(DISABLE_CS_PIN, OUTPUT);
digitalWrite(DISABLE_CS_PIN, HIGH);
}
cout << F("\nAssuming the SD chip select pin is: ") << int(config.csPin);
cout << F("\nEdit SD_CS_PIN to change the SD chip select pin.\n");
}
//------------------------------------------------------------------------------
void printConfig(SdioConfig config) {
(void)config;
cout << F("Assuming an SDIO interface.\n");
}
//-----------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
delay(250);
Serial.println("SPI test");
SPI.begin(pin_spiSCK, pin_spiPOCI, pin_spiPICO);
cout << F("SdFat version: ") << SD_FAT_VERSION_STR << endl;
printConfig(SD_CONFIG);
}
//------------------------------------------------------------------------------
void loop() {
// Read any existing Serial data.
clearSerialInput();
// F stores strings in flash to save RAM
cout << F("\ntype any character to start\n");
while (!Serial.available()) {
yield();
}
uint32_t t = millis();
if (!sd.cardBegin(SD_CONFIG)) {
cout << F(
"\nSD initialization failed.\n"
"Do not reformat the card!\n"
"Is the card correctly inserted?\n"
"Is there a wiring/soldering problem?\n");
if (isSpi(SD_CONFIG)) {
cout << F(
"Is SD_CS_PIN set to the correct value?\n"
"Does another SPI device need to be disabled?\n");
}
errorPrint();
return;
}
t = millis() - t;
cout << F("init time: ") << dec << t << " ms" << endl;
if (!sd.card()->readCID(&cid) || !sd.card()->readCSD(&csd) ||
!sd.card()->readOCR(&ocr) || !sd.card()->readSCR(&scr)) {
cout << F("readInfo failed\n");
errorPrint();
return;
}
printCardType();
cout << F("sdSpecVer: ") << 0.01 * scr.sdSpecVer() << endl;
cout << F("HighSpeedMode: ");
if (scr.sdSpecVer() > 101 && sd.card()->cardCMD6(0X00FFFFFF, cmd6Data) &&
(2 & cmd6Data[13])) {
cout << F("true\n");
} else {
cout << F("false\n");
}
cidDmp();
csdDmp();
cout << F("\nOCR: ") << uppercase << showbase;
cout << hex << ocr << dec << endl;
if (!mbrDmp()) {
return;
}
if (!sd.volumeBegin()) {
cout << F("\nvolumeBegin failed. Is the card formatted?\n");
errorPrint();
return;
}
dmpVol();
}
You will also need to install the SDFat Arduino Library if you haven't already. You can search the library from within the Arduino Library manager, download the zip from the GitHub Repository and install it manually, or you can click the link from within the code.
Make sure you've selected the correct board and port in the Tools menu and then hit the upload button. Once the code has finished uploading, go ahead and open a Serial Monitor at 115200 baud. Once you type in any key in the Message Field and send it, the code will analyze the SD card and output something similar to the following: