Arduino SD card fails to write when used with another SPI device - arduino

I have an ADXL355 accelerometer attached to an Adafruit Feather Adalogger. I can configure and read the sensor. I can also write binary values to the SD card. The problem occurs when I try to read from the sensor and then write that data to the SD card. The only thing I can think of is I'm somehow messing up the SPI communication but I can't see where. I looked through pins_arduino.h for my board and the SD Card (pin 4) is on a different register than pin 10 so I don't see how I'm breaking things.
My operations proceed like this. Global sensor creation, Serial.begin, SD.begin, SPI.begin, Test sensor connection, Create file for output on SD card, Initialize sensor, Read sensor FIFO, Write to file, repeat last 2 forever.
The file is created but remains at 0 file size, ie nothing is actually written to the card.
The sensor can operate at 4 kHz which was hard to achieve using the digitalWrite functions so I switched to using the port registers on the Feather. I do it like this:
#include <SM_ADXL355_SPI_fast.h>
#include <SPI.h>
#include <SD.h>
#define cardSelect 4
ADXL355_SPIF adxl355(&DDRB, &PORTB, _BV(6)); // values taken from pins_arduino.h, tested and working on pin 10
void setup() {
Serial.begin(57600);
while(!Serial){
// wait for Serial
}
SD.begin(cardSelect);
SPI.begin();
while(!adxl355.TestConnection()){
delay(1000);
}
adxl355.OpenFile("TestSPI.bin");
adxl355.Initialize(1, 10, 0); // set range to 2g's, frequency to 4 Hz and filter to off
}
void loop() {
while(true){ // avoid Arduino overhead of their loop function
adxl355.ReadFIFO();
adxl355.WriteFIFOToFile();
}
}
Here is the ADXL constructor
ADXL355_SPIF::ADXL355_SPIF(volatile uint8_t * outReg, volatile uint8_t * outPort, uint8_t bitValue) : sensorOutReg(outReg), sensorPort(outPort), sensorBitValue(bitValue){
*sensorOutReg |= sensorBitValue;
*sensorPort |= sensorBitValue;
sensorWriteCount = 0;
}
TestConnection tests that the DeviceID reads 0xAD. Initialize sets the G range, sample rate in Hz and filter. I have tested these with serial output and they work properly.
OpenFile looks like this:
bool ADXL355_SPIF::OpenFile(const String& fileName){
sensorFile = SD.open(fileName, FILE_WRITE);
if (!sensorFile){
Serial.print("Could not create file: ");
Serial.println(fileName);
return false;
}
return true;
}
After running this a file does get created on the SD card called "TESTSPI.BIN" with 0 file size.
ReadFIFO reads the numbers of entries in FIFO, stored as fifoCount and then populates a buffer (sensorFIFO[32][3]) with the values from the FIFO. I've printed this buffer to Serial to show that it's working. Here is that function
void ADXL355_SPIF::ReadFIFO(){
ReadRegister(ADXL355_RA_FIFO_ENTRIES, 1);
fifoCount = buffer[0];
ReadFIFOInternal();
return;
}
void ADXL355_SPIF::ReadFIFOInternal(){
SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
*sensorPort &= ~sensorBitValue;
uint8_t spiCommand = ADXL355_RA_FIFO_DATA << 1 | ADXL355_READ;
SPI.transfer(spiCommand);
int i = 0;
unsigned long tempV;
unsigned long value;
while(i < fifoCount){
for (int ptr = 0; ptr < 3; ++ptr){
buffer[0] = SPI.transfer(0x0);
value = buffer[0];
value <<= 12;
tempV = SPI.transfer(0x0);
tempV <<= 4;
value |= tempV;
tempV = SPI.transfer(0x0);
tempV >>=4;
value |= tempV;
if (buffer[0] & 0x80) {
value |= 0xFFF00000;
}
long lValue = static_cast<long>(value);
sensorFIFO[i][ptr] = scaleFactor * lValue;
}
i += 3;
}
SPI.endTransaction();
*sensorPort |= sensorBitValue;
return;
}
Here is WriteFIFOToFile:
void ADXL355_SPIF::WriteFIFOToFile(){
if (fifoCount > 0){
sensorFile.write(reinterpret_cast<const char *>(&sensorFIFO), 4 * fifoCount);
}
sensorWriteCount += fifoCount;
if (sensorWriteCount >= 100){
sensorFile.flush();
sensorWriteCount = 0;
}
}
After allowing this to run for a while the file size is always 0. I tried a simple binary write function just to test the card. It looks like this and it worked.
#include <SD.h>
#define cardSelectPin 4
const float pi=3.14159;
File oFile;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
while(!Serial){
// wait for serial
}
SD.begin(cardSelectPin);
oFile = SD.open("Test.bin", FILE_WRITE);
Serial.println(sizeof(int));
Serial.println(sizeof(float));
float testFloat[32][3];
for (int i = 0; i < 32; ++i){
for (int j = 0; j < 3; ++j){
testFloat[i][j] = pi * (i + 1) + j;
}
}
oFile.write(reinterpret_cast<const char *>(&testFloat), sizeof(float) * 96);
oFile.close();
Serial.println("Finished writing file.");
}
void loop() {
// put your main code here, to run repeatedly:
}

The problem was that flush was not being called correctly. I had created a buffer to hold data from the FIFO and it would flush the card when it would get full enough such that a subsequent read would overflow. At that time it would call flush. This is what was intended with the variable sensorWriteCount. This variable was of type uint8_t when it should have been a uint16_t.
Changing to the correct type fixed the problem. I would have deleted this question because it boils down to a typo, but once an answer has been posted the system doesn't allow that.

The only difference between the not-working sketch and the working one is the closing of the sd card. The sd card MUST be closed, I had the same problem you have and I assume that the file gets its boundaries written in its filesystem at file close call.
To solve your issue, use a push button. When you push it, it will close the file and stop reading/processing sensors. You can also use this button to start reading and recording sensors data on sd card again (toggle).

Related

STM32H7 | BlockDevice & FatFs | SD card writing is very slow

I'm trying to implement SD card functionality on a portenta H7 (STM32H7). The SD card must save all ADC's data in real time (4Mbyte/sec). I saw the maximum data rate for a portenta is 200MByte/sec but I can't even reach 1Mbyte/s.
I'm using Arduino IDE, SDMMCBlockDevice.h and FATFileSystem.h librarys. I try to work with the MMC bus with 4-bit width. When I check the register's bits, my configuration is good. I tried to increase the clock speed but no result.
I don't have lot of skills in C++ so I think I miss something. Someone could help me?
Below my test code:
#include "SDMMCBlockDevice.h"
#include "FATFileSystem.h"
SDMMCBlockDevice block_device;
mbed::FATFileSystem fs("fs");
const int len = 8192;
uint16_t tab[len];
long int Time = 0;
void setup() {
for(int i=0; i<len; i++){
tab[i]=i;
}
Serial.begin(115200);
while (!Serial);
delay(1000);
Serial.println("Mounting SDCARD...");
int err = fs.mount(&block_device);
if (err) {
Serial.println("No SD Card filesystem found, please check SD Card on computer and manually format if needed.");
}
Serial.println(SDMMC2->CLKCR,BIN);
//SDMMC2->CLKCR |= SDMMC_CLKCR_WIDBUS_0 & ~SDMMC_CLKCR_WIDBUS_1;
//SDMMC2->CLKCR |= SDMMC_CLKCR_BUSSPEED;
//SDMMC2->CLKCR |= SDMMC_CLKCR_DDR;
//Serial.println(SDMMC2->CLKCR,BIN);
}
void loop() {
mkdir("fs/Serie",0777);
char myFileName[] = "fs/Serie/data.bin";
FILE *myFile = fopen(myFileName, "a");
Time = micros();
fwrite(&tab[0], 2, len, myFile);
Time = micros()-Time;
fclose(myFile);
Serial.print("Time : ");
Serial.println(Time);
delay(5000); // wait a bit
}

'class SoftSerial' has no member named 'readString' ! Arduino DigiSpark attiny85 SoftSerial.h SoftwareSerial.h NeoPixel

i'm using Arduino IDE 1.8.12, it's setup to use digispark libs (i think), don't remember exactly what i did, watched a video online some many moths ago.
i have an issue with my sketch, when i try to compile it it gives me this error:
error: 'class SoftSerial' has no member named 'readString'
dataIn = bluetooth.readString();
^
exit status 1
'class SoftSerial' has no member named 'readString'
I am not that surprised because the original code was written for an Arduino and it used SoftwareSerial.h instead of SoftSerial.h but for some strange reason i can't load or get to work with softwareserial now, i have the libs in arduino folder but i don't know if it's even possible to load them and work with them since i've set the IDE to work with digispark.
The other solution would be to find a way to do "readString" in a way that SoftSerial recognises, i'm not sure if that is possible either.
PLEASE HELP!
#include <Adafruit_NeoPixel.h> // NeoPixel Lib
#include <SoftSerial.h> // Serial Lib
#define LED_PIN 1
#define LED_COUNT 30
SoftSerial bluetooth(3, 4); // RX TX
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
int brightness = 100; //
int redColor = 0;
int greenColor = 0;
int blueColor = 0;
String dataIn = ""; //
unsigned long timer1 = 0;
void setup()
{
Serial.begin(9600);
bluetooth.begin (9600);
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(brightness); // Set BRIGHTNESS to about 1/5 (max = 255)
}
void loop(){
if (bluetooth.available() > 0);{
dataIn = bluetooth.readString();
delay(20);
// Serial.println(dataIn);
if (dataIn.startsWith("1")){
delay(10);
String R = dataIn.substring(dataIn.indexOf("R") + 1, dataIn.indexOf("G"));
redColor = R.toInt();
Serial.println(R);
String G = dataIn.substring(dataIn.indexOf("G") + 1, dataIn.indexOf("B"));
greenColor = G.toInt();
Serial.println(G);
String B = dataIn.substring(dataIn.indexOf("B") +1, dataIn.indexOf("E"));
blueColor = B.toInt();
Serial.println(B);
}
else if (dataIn.startsWith("2")){
String stringBrightness = dataIn.substring(dataIn.indexOf("2") + 1, dataIn.length());
brightness = stringBrightness.toInt();
strip.setBrightness(brightness);
Serial.println(brightness);
}
for (int i = 0; i < LED_COUNT; i++){
strip.setPixelColor(i, strip.Color(greenColor, redColor, blueColor));
}
}
strip.show();
delay(20);
}
Simply implement the same functionality in your sketch using read().
readString blocks for a set amount of time. It freezes your sketch waiting on the transmission to complete for 1 second. This isn't great for most sketches. Most of the time it would be better to implement a non-blocking read. There are also big issues with using the String class on smaller microcontrollers like the UNO and especially the tiny85 in the digiSpark. It would be best on that platform to stick to c-style strings. But here's how to mimic readString if that's really what you want to do.
String myReadString() {
unsigned long startTime = millis();
char c = 0;
String message = "";
while (c!= '\n' && (millis() - startTime <= 1000)){
if(bluetooth.available()){
c = bluetooth.read();
message += c;
}
}
return message;
And then use it like this:
if (bluetooth.available() > 0);{
dataIn = myReadString();
Again, this isn't a particularly good thing to do because all of that string concatenation will shoot holes in the tiny amount of memory you have on the 85. That's probably why they didn't implement it in the libraries for it. If I were you I'd spend some time learning how to work with serial data. Google "Robin2 serial input basics" for one good place to read from the Arduino forum. But if you really insist on String class then there's a function that will do what readString does.

I'm having trouble implementing the Atmega328 timer into my arduino networking

I am trying to implement error correction over an r/f communication between two arduinos. I tried adding a timer to it, in order to create a packet resend, but whenever it gets past the first send, it starts printing garbage ad infinity instead of doing the timer interrupt.
I tried messing around with the inside loop conditions some as well as trying to figure out what was wrong with the timer, but I couldn't figure it out. The problem seems to happen right around the first serial print, which is strange, because that part of the code is mostly unchanged.
(packets is a structure of two ints)
#include <ELECHOUSE_CC1101.h>
#include "packets.h"
// These examples are from the Electronics Cookbook by Simon Monk
// Connections (for an Arduino Uno)
// Arduino CC1101
// GND GND
// 3.3V VCC
// 10 CSN/SS **** Must be level shifted to 3.3V
// 11 SI/MOSI **** Must be level shifted to 3.3V
// 12 SO/MISO
// 13 SCK **** Must be level shifted to 3.3V
// 2 GD0
const int n = 61;
unsigned short int sequence = 0;
byte buffer[n] = "";
void setup() {
Serial.begin(9600);
Serial.println("Set line ending to New Line in Serial Monitor.");
Serial.println("Enter Message");
ELECHOUSE_cc1101.Init(F_433); // set frequency - F_433, F_868, F_965 MHz
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 0xFFFF; // Max value for overflow for now
TCCR1B |= (1 << CS12); // 256 prescaler
interrupts(); // enable all interrupts
}
Packet pckt, recieve;
ISR(TIMER1_OVR_vect){ // timer compare interrupt service routine
//Resend packet
ELECHOUSE_cc1101.SendData(buffer, pckt.data + pckt.seqNum);
int len = ELECHOUSE_cc1101.ReceiveData(buffer);
buffer[len] = '\0';
recieve.seqNum = buffer[n];
Serial.println("Interrupt");
}
void loop() {
if (Serial.available()) {
pckt.data = Serial.readBytesUntil('\n', buffer, n);
pckt.seqNum = sequence;
buffer[pckt.data] = '\0';
buffer[n-1] = pckt.seqNum;
Serial.println((char *)buffer);
ELECHOUSE_cc1101.SendData(buffer, pckt.data + pckt.seqNum);
TCNT1 = 0; // clear timer
TIMSK1 |= (1 << TOIE0); // enable timer compare interrupt
int len = ELECHOUSE_cc1101.ReceiveData(buffer);
while (recieve.seqNum <= sequence) {
}
TIMSK1 &= ~(1 << TOIE0); // turn off the timer interrupt
}
}
Sending data takes too long for interrupts. You should keep calls to send and receive buffers of data within the loop() function call tree. For example, sending a 12 bytes message via UART at 9600 bauds can take up to about 12ms.
You can use the timer interrupt to decrement a timeout counter, as is usually done on micro controllers, or use the millis() function to handle timings, as is easily done on Arduino.
I suggest you use the millis() function to compute timeouts.
example:
/* ... */
// I could not figure out what you were trying to do with
// pckt.seqNum.... Putting it at the end of the buffer
// makes no sense, so I've left it out.
// Moreover, its size is 2, so placing it at buffer[n-1] overflows the buffer...
enum machineState {
waitingForSerial,
waitingForResponse,
};
unsigned int time_sent; // Always use unsigned for variables holding millis()
// can use unsigned char for timeouts of 255
// milliseconds or less. unsigned int is good for about
// 65.535 seconds or less.
machineState state = waitingForSerial;
void loop()
{
switch(state)
{
case waitingForSerial:
pckt.data = Serial.readBytesUntil('\n', buffer, sizeof(buffer));
if (pckt.data > 0)
{
++pckt.seqNum;
Serial.write(buffer, pckt.data);
ELECHOUSE_cc1101.SetReceive();
ELECHOUSE_cc1101.SendData(buffer, pckt.data);
time_sent = millis();
state = waitingForResponse;
}
break;
case waitingForResponse:
if (ELECHOUSE_cc1101.CheckReceiveFlag())
{
auto len = ELECHOUSE_cc1101.ReceiveData(buffer)) // can use C++17 with duinos!!!
Serial.print("cc1101: ");
Serial.write(buffer, len);
state = waitingForSerial; // wait for another command from PC
}
// 1 second timeout, note the cast and subtraction, this is to avoid any
// issues with rollover of the millis() timestamp.
else if ((unsigned int)millis() - time_sent > 1000)
{
// resend ... stays stuck this way.
Serial.println("Retrying :(");
ELECHOUSE_cc1101.SendData(buffer, pckt.data);
time_sent = millis();
}
break;
default:
state = waitingForSerial;
Serial.println("unhandled state");
break;
}
}

Serial.write causing void loop program to stop (Digital Pins stop responding)

I have an issue trying to send some serial data through tx and rx to another arduino through a HC05 bluetooth module.
The overall project is developing a hybrid go kart and using the arduino as a simple ECU unit with a PID speed control over the PWM output controlling a DC motor. I have been working the project in steps and have go as far as setting up a footpedal with the arduino and controlling the electronic speed controller (ESC) directly. I have added a simple PID function to this along with a simple hall sensor to detect the speed and does require tuning but works great so far. Now the problem comes when I try to send data across over serial ports.
I have had the bluetooth modules connected with to separate arduinos and have successfully managed to send over data from one arduino with a pot input to another with a 3.5 inch TFT screen. When I try to integrate the master side of the project to the PID controlled DC motor the system freezes. I have since then removed the PID control and gone back to direct control and it still fails, i have tried commenting out the interrupt sequence for the encoder and put a static value for RPM and still freezes. the sending sequence works if I don't attempt to use any digital outputs. I am really confused. The code I have gone down to to try and debug this can be found below. This is not the full code and has been chopped to pieces to try and eliminate this fault. however in this code below, if I comment out the sendData function the system works and the motor spins with relative speed to the pedal input. as soon as I try to send the data the system runs for a seconds then freezes. the data is still being sent and the static readings are showing just the digital output seizes to work.
#include <TimerOne.h>
int previous = 0;
int Tavg = 0; // the average
int Tout = 0;
int throttle = A0;
const int numReadings = 10;
int readings[numReadings]; // the readings from the analog input
int readIndex = 0; // the index of the current reading
int total = 0; // the running total
int ESCPin = 5;
unsigned int counter=0;
int RPM;
long Time = 0;
long ReadInt = 0;
void docount() // counts from the speed sensor
{
counter++; // increase +1 the counter value
}
void timerIsr()
{
Timer1.detachInterrupt(); //stop the timer
Serial.print("Motor Speed: ");
RPM = (counter*75 ); // RPM= counterx10*60/8 (x10 for second, 8 counts in encoder, 60 minutes === 75x counter)
Serial.print(RPM);
Serial.println(" Rotation per min"); Serial.print(Tout);Serial.print("= "); Serial.print(Tout*0.01961);Serial.println("V");
counter=0; // reset counter to zero
Timer1.attachInterrupt( timerIsr ); //enable the timer
}
void ReadEnc (){
Timer1.initialize(100000); // set timer for 0.1sec
attachInterrupt(0, docount, RISING); // increase counter when speed sensor pin goes High
Timer1.attachInterrupt( timerIsr ); // enable the timer
}
void sendData(){
if (Serial.available()>0) {
if (Serial.read() == 0){
//Serial.println(sendChars);
int RPMout = RPM;
Serial.write("<");
//delay(2);
//Serial.write(sendChars);
Serial.write("Data is,");
//delay(2);
Serial.write( itoa (RPMout, 4,10));
//delay(2);
Serial.write(", 30, 48.35");
//delay(2);
Serial.write(">");
//delay(10);
Serial.println("");
}
}
}
void setup()
{
Serial.begin(9600);
pinMode(2, INPUT_PULLUP); // internal pullup input pin 2
pinMode(ESCPin, OUTPUT);
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readings[thisReading] = 0; }
Time = millis();
ReadInt = -100;
}
void ReadSensor (){
// get the sensor value
total = total - readings[readIndex];
// read from the sensor:
readings[readIndex] = analogRead(throttle);
//Serial.println(readings[readIndex]);
// add the reading to the total:
total = total + readings[readIndex];
// advance to the next position in the array:
readIndex = readIndex + 1;
// if we're at the end of the array...
if (readIndex >= numReadings) {
// ...wrap around to the beginning:
readIndex = 0;
}
// calculate the average:
Tavg = total / numReadings;
}
void loop(){
ReadSensor();
ReadEnc();
RPM = 1800;
Tout = map(Tavg, 180, 860, 0, 200);
if (Tout>0){
analogWrite(ESCPin, Tout);
}
if (Time > ReadInt + 5000) {
sendData (); // when this is commented it works fine, tried moving it everywhere
ReadInt = Time;
}
Time = millis();
}
If anyone has any ideas please let me know, and I know I probably haven't explained my problem well so if their is any questions or more details needed please ask.
Second parameter of itoa should be a pointer to output bufffer. but you do not need itoa. use Serial.print(RPM);. For string the print and write functions are the same, but for number print has a version for int

Arduino: How to read SD card text file line by line

I'm working on a project to read "current" in "data.txt" from SDcard. Goal is to read it line by line and input to my int "TargetCur".
Code structure:
1. Open "data.txt" from SDcard
2. Read first line data
3. Input read data into int "TargetCur"
4. Arduino perform action
5. Once action above completed, read second line data from "data.txt"
6. Repeat step 3 to 5 above
"data.txt" look like this:
Current
2.179
3.179
2.659
2.859
#include <SPI.h>
#include <SD.h>
File Data;
// Declare the pins used:
int TargetCur = 0;
void setup() {
Serial.begin(9600); // initialize serial communications at 9600 bps:
TCCR1B = TCCR1B & B11111000 | B00000001;
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
myFile = SD.open ("data.txt", FILE_WRITE);
}
void loop() {
TargetCur = Serial.write(myFile.read());
//perform action//
}
To read line by line, create a String readLine () function that reads char by char until the end of the line.
You can always create your own function that suits you needs. If you know the max line size than a statically declared char[] will work best like so.
int index = 0;
char stringArray[MAX_LINE_LEN];
while ((int next = myFile.read()) != -1)
{
char nextChar = (char) next;
if (nextChar == '\n')
{
stringArray[index] = '\0';
/* Do something with char array */
index = 0;
}
else
{
stringArray[index] = nextChar;
index += 1;
}
}
If you don't know the max line size that this becomes a bit harder and you need to use malloc() to dynamically grow the size of the buffer.

Resources