I am trying to get the master (Heltec ESP 32 Wifi Kit V3) to send 2 bytes to the slave (Arduino Nano). I am trying to send 2 commands A1 and A2 but they are not being received correctly by the slave, often being in the wrong order or having different commands all together.
I have attempted to adjust the clock speed and add delays but nothing has seemed to work. Using SPI.setClockDivider(SPI_CLOCK_DIV8) has not worked either. I have added my master and slave code below:
Master:
#include <SPI.h>
#include <SD.h>
#define SD_CS_PIN 5
#define SD_SCK_PIN 33
#define SD_MISO_PIN 35
#define SD_MOSI_PIN 34
#define SS_PIN_1 41 // Nano 1
#define SCK_PIN 46
#define MISO_PIN 42
#define MOSI_PIN 45
#define BUTTON_PIN 19
#define BUTTON_PIN2 7
unsigned long recordStartTime = 0;
bool isRecording = false;
char currentFilename[32] = "";
void setup() {
Serial.begin(115200);
pinMode(SS_PIN_1, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(BUTTON_PIN2, INPUT_PULLUP);
digitalWrite(SS_PIN_1, LOW);
SPI.beginTransaction(SPISettings(400000, MSBFIRST, SPI_MODE0)); //
SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, SS_PIN_1);
Serial.println("SPI initialized for sensor communication.");
delay(10); // Short delay to let SPI initialize
}
String generateFilename() {
String baseFilename = "/FlowData";
int fileNumber = 0;
String filename;
do {
filename = baseFilename + (fileNumber > 0 ? "_" + String(fileNumber) : "") + ".txt";
fileNumber++;
} while (SD.exists(filename));
filename.toCharArray(currentFilename, sizeof(currentFilename));
return filename;
}
void loop() {
static float flowData1[50] = { 0 };
if (digitalRead(BUTTON_PIN) == LOW) {
ESP.restart();
}
if (digitalRead(BUTTON_PIN2) == LOW) {
if (!isRecording) {
Serial.println("Master: Starting RECORD command to Nano 1...");
isRecording = true;
recordStartTime = millis();
while (digitalRead(BUTTON_PIN2) == LOW) {
delay(10); // Small debounce delay
}
delay(50); // Small delay to ensure button state change
}
}
if (isRecording) {
if (millis() - recordStartTime < 2500) {
collectData(SS_PIN_1, flowData1, "Nano 1");
stopRecordCommand(SS_PIN_1);
delay(200); // Brief delay for synchronization
} else {
Serial.println("Master: Stopping RECORD command to Nano 1...");
isRecording = false;
saveFlowData(flowData1, 50);
}
}
delay(50);
}
void collectData(int ssPin, float* dataArray, String nanoName) {
Serial.println("Collecting data from " + nanoName + "...");
volatile byte commandData[] = {0xA1,0xA2}; // Command bytes to send
volatile byte myData[2]; // Array to store received data
int validDataCount = 0;
while (validDataCount < 50) {
digitalWrite(ssPin, LOW); // Start SPI communication
// Send the command and receive data
for (int i = 0; i < 2; i++) {
myData[i] = SPI.transfer(commandData[i]); // Send command and receive response
}
digitalWrite(ssPin, HIGH); // End SPI communication
// Debugging output
Serial.print("Received Hex Values: 0x");
Serial.print(myData[0], HEX);
Serial.print(" 0x");
Serial.println(myData[1], HEX);
// Combine the two bytes into a 16-bit integer
int scaledFlow = (int(myData[0]) << 8) | int(myData[1]);
if (scaledFlow == 0xFFFF || scaledFlow == 0x0000) {
dataArray[validDataCount] = NAN; // Invalid data
} else {
dataArray[validDataCount] = scaledFlow;
Serial.printf("Data %d: %s: %.2f g/min\n", validDataCount, nanoName.c_str(), dataArray[validDataCount]);
}
validDataCount++;
delay(50); // Short delay between data points to avoid overloading the bus
}
}
void saveFlowData(float* data1, int dataPoints) {
digitalWrite(SS_PIN_1, HIGH);
SPI.end();
SPI.begin(SD_SCK_PIN, SD_MISO_PIN, SD_MOSI_PIN, SD_CS_PIN);
if (!SD.begin(SD_CS_PIN)) {
Serial.println("SD Card initialization failed!");
return;
}
String filename = generateFilename();
File file = SD.open(filename, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
file.println("Flow Data (g/min):");
file.println("Index Nano 1");
for (int i = 1; i < dataPoints; i++) {
if (isnan(data1[i])) continue;
file.printf("%-9d %-10.2f\n", i, data1[i]);
}
file.println();
file.close();
Serial.println("Data saved to " + filename);
SPI.end();
SPI.begin(SD_SCK_PIN, SD_MISO_PIN, SD_MOSI_PIN, SD_CS_PIN);
}
void stopRecordCommand(int ssPin) {
volatile byte stopCommand[] = { 0x00, 0x00 }; // 2-byte stop recording command
digitalWrite(ssPin, LOW);
delay(10);
// Send the 2-byte stop recording command
SPI.transfer(stopCommand[0]);
SPI.transfer(stopCommand[1]);
digitalWrite(ssPin, HIGH);
}
Slave:
#include <SPI.h>
#include <Wire.h>
#include <SensirionI2CSfm3000.h>
#define SS_PIN 10 // Chip Select (D10 for Nano)
#define BUTTON_PIN 5
SensirionI2CSfm3000 sfm;
volatile byte commandData[2]; // 2-byte command data received from master
volatile byte responseData[2]; // 2-byte response data to send back
int sensorDataIndex = 0;
float sensorValues[50];
float scalingFactor = 140.0;
float offset = 32000;
volatile bool commandReceived = false;
void setup() {
Serial.begin(115200);
pinMode(SS_PIN, INPUT_PULLUP); // Configure SS pin as input
pinMode(MISO, OUTPUT); // Configure MISO pin as output
pinMode(BUTTON_PIN, INPUT_PULLUP);
bitSet(SPCR, SPE); // Enable SPI
bitClear(SPCR, MSTR); // Ensure this device is a Slave
SPI.attachInterrupt(); // Attach SPI interrupt for handling communication
Wire.begin();
sfm.begin(Wire, 0x40);
uint16_t error = sfm.startContinuousMeasurement();
if (error) {
Serial.print("Error starting continuous measurement: ");
Serial.println(error);
}
Serial.println("Slave Ready (Responding to 2-byte command)");
}
ISR(SPI_STC_vect) {
static byte byteIndex = 0;
// Read the byte from the SPI Data Register (SPDR) and store it in the commandData array
commandData[byteIndex] = SPDR;
byteIndex++;
if (byteIndex >= 2) { // Only process when both bytes have been received
byteIndex = 0; // Reset index for next command
// Display received command bytes
Serial.print("Command Received: 0x");
Serial.print(commandData[0], HEX);
Serial.print(" 0x");
Serial.print(commandData[1], HEX);
// Combine the two bytes into a 16-bit integer (for debugging)
int receivedCommand = (int(commandData[0]) << 8) | int(commandData[1]);
Serial.print(" (Combined: 0x");
Serial.print(receivedCommand, HEX);
Serial.print(" / Decimal: ");
Serial.print(receivedCommand);
Serial.println(")");
// Check for a valid command
if (commandData[0] == 0xA1 && commandData[1] == 0xA2) { // Example command
if (sensorDataIndex < 50) {
float flow = sensorValues[sensorDataIndex];
float rawFlow = flow; // Store raw flow before modifications
flow = fabs(flow); // Ensure positive flow value
// Round and scale the flow value by 100 (to handle two decimal places)
flow = round(flow * 100) / 100.0;
// Scale the flow value
int scaledFlow = int(flow * 100); // Scaled value for transmission (x100)
// Split the scaled value into high and low bytes
responseData[0] = (scaledFlow >> 8) & 0xFF; // High byte
responseData[1] = scaledFlow & 0xFF; // Low byte
// Load the response into SPI data register (SPDR)
SPDR = responseData[0]; // First send high byte
SPDR = responseData[1]; // Then send low byte
sensorDataIndex++; // Increment the index to move to the next flow value
} else {
sensorDataIndex = 0; // Reset index when all values are sent
}
}
}
}
void loop() {
while (sensorDataIndex < 50) {
float flow;
uint16_t error = sfm.readMeasurement(flow, scalingFactor, offset);
if (error) {
Serial.print("Error reading flow measurement: ");
Serial.println(error);
flow = 0; // If an error occurs, set flow to 0
}
flow = fabs(flow); // Ensure positive flow value
// Round the flow value to 2 decimal points before scaling
flow = round(flow * 100) / 100.0;
// Store the unscaled flow value
sensorValues[sensorDataIndex] = flow;
// Display unscaled and scaled flow values
Serial.print("Unscaled Flow (raw): ");
Serial.print(flow, 2);
Serial.print(" g/min, ");
// Scale the rounded value by 100
int scaledFlow = int(flow * 100); // Scaled value for SPI (x100)
Serial.print("Scaled Flow Value (x100): ");
Serial.println(scaledFlow);
sensorDataIndex++; // Move to next index
delay(500); // Delay between measurements
}
}
Examples of what is being received is:
14:12:53.262 -> Command Received: 0x0 0xA1 (Combined: 0xA1 / Decimal: 161)
14:12:53.356 -> Command Received: 0xA1 0xA1 (Combined: 0xFFFFA1A1 / Decimal: -24159)
14:12:53.391 -> Command Received: 0xA1 0xA2 (Combined: 0xFFFFA1A2 / Decimal: -24158)
14:12:53.498 -> Command Received: 0x68 0xA1 (Combined: 0x68A1 / Decimal: 26785)
14:12:53.571 -> Command Received: 0xA2 0xA1 (Combined: 0xFFFFA2A1 / Decimal: -23903)
14:12:53.610 -> Command Received: 0xA1 0xA2 (Combined: 0xFFFFA1A2 / Decimal: -24158)
14:12:53.656 -> Command Received: 0xA1 0xA2 (Combined: 0xFFFFA1A2 / Decimal: -24158)