i have a one master four slave arduino code, communicating over i2c.
communication goes as follows:
master transmits <command,datain>
master requests from slave
slave responds <success, dataout>
however, this doesn't work. i have weird issues where they lock up, slave doesnt recieve command, slave doesnt transmit command, etc. i have removed all non-essential code and it still doesnt work reliably. it is not a hardware issue, as i can simply transmit and have slave recieve for hours without issue. the problem seems to be the request from (atleast i think?). here are the transmit and receive functions.
my question is: what is wrong/could be wrong with my code to cause this issue
EDIT EDIT EDIT:
New Problem:
so I've narrowed my problem down to an issue with trying to have the device have both master and slave capabilities. so i took a new approach, and now i can get it to work perfectly as either master or slave, but not both. i know you cant do both at the same time, but i had assumed you could switch between them, in an OR configuration. however, i am having issues with that. is supposed to select between master and slave mode. however Wire.onReceive(receiveEvent) & Wire.onRequest(requestEvent) only work when initialized in the setup routine, and once the device is switched into master mode, it ceased ever being able to function as a slave (until it is reset). does anyone know a workaround to this? microcontroller is a SAMD21G18A. Its on a custom board, so using a second sercom is not possible without a revised board.
EDIT EDIT EDIT:
Modified a module for:
#define slaveSDA 11
#define slaveSCL 13
still doesnt work as slave. tried both 6K and 1K pullups. seems to just lockup after a short while.
so i tried another approach: hardcoding the interface into the variant file... and it works?! so i think i have pretty much proven that onReceive and onRequest only work in the hard defined i2c pins?? why might this be? is there a workaround? (modified varient file in comments)
here is the full code, not just code snippets.
SLAVE:
/*********************************************************************************************************************
List of accepted commands:
<matrix,set1,set2,set3,set4> - sets the matrix. returns either <success> or <fail,status1,status2,status3,status4>
<matrixscalpel,pin,seto> - sets a single channel of the matrix. returns either <success> or <fail>
<powerset,float voltage> - sets the module voltage. also turns off voltage. returns either <success,difference> or <fail>
<powertoggle,bool> - toggles power on or off. returns either <success> or <fail>
<currmeasure> - measures the current draw of the V_ADJ supply. returns either <success, float current> or <fail>
<*idn?> - module identification. returns < firmare_version, hardware_version>
<adcread,pin> - reads the ADC value of a pin. returns either <success, float adcvalue> or <fail>
<dacset,pin,float voltage> - sets the DAC voltage level of a pin. can be 0 to 5.5V. returns either <success> or <fail>
<selftest,status> - this returns the self test status, either <success> or <fail>, simply returns the status of a previous self test.
<selftest,runtest> - this returns the self test status, either <success> or <fail>, but also runs a full new self test of the module.
<resetbreaker> - resets the MIC2045 circuit breaker. returns either <success> or <fail>
*********************************************************************************************************************/
#include <Wire.h>
#include <Adafruit_SleepyDog.h>
#define FIRMWARE_VERSION "2.0"
#define HARDWARE_VERSION "B"
//enable the digital pins
#define en_dio 7
//error LED
#define faultLED A5
//i2c data interupt
#define I2C_DATA_INCOMING_INTERUPT 24
bool selfTest = true; //default as passed self test.
//#define Serial SerialUSB
String responseToSend; // This will hold the response to be sent
String command = "";
/*********************************************************************************************************************
Setup and initilization function
*********************************************************************************************************************/
void setup() {
Watchdog.enable(10000); // turn on watchdog timer with 10 seconds
Serial.begin(9600);
// while (!Serial) delay(10); // pause the serial port
//set pinmode of fault LED to output
pinMode(faultLED,OUTPUT);
// Initial state of selfTest is true
selfTest = true;
// Call main self testing function
//just calls all the individual self test and initilization functions
//this function and call is mandantory. remove it, and the code wont work.
selfTestMain();
switchI2CMode(false); //as slave
//Wire.begin(0x20); // join i2c bus with address 0x20
//Wire.onReceive(receiveEvent); // register event
//Wire.onRequest(requestEvent); // Register the onRequest event handler
//pinMode(I2C_DATA_INCOMING_INTERUPT,INPUT);
pinMode(en_dio,OUTPUT);
digitalWrite(en_dio,HIGH);
}
void loop() {
Watchdog.reset(); //resets the watchdog timer. basically, this makes sure the code doesnt lock up.
}
void switchI2CMode(bool asMaster) {
Wire.end(); // End current I2C operation to reconfigure
if (asMaster) {
// Configure as I2C master
Wire.begin(); // Start I2C as master
}
else {
// Configure as I2C slave
Wire.begin(0x20); // join i2c bus with address 0x20 as a slave
Wire.onReceive(receiveEvent); // register event
Wire.onRequest(requestEvent); // Register the onRequest event handler
}
}
/*********************************************************************************************************************
just runs the setup and self test functions.
this function MUST be called in setup() otherwise the rest of the code wont work
this is because this code also initilizes all the ADC DAC ETC objects in the code
*********************************************************************************************************************/
void selfTestMain(){
// Call setup functions and check their return values
if (setupPinMatrix() != 0) selfTest = false; // Set selfTest to false if setupPinMatrix fails
if (setupVoltage() != 0) selfTest = false; // Set selfTest to false if setupVoltage fails
if (adcPinSetup() != 0) selfTest = false; // Set selfTest to false if adcPinSetup fails
if (dacPinSetup() != 0) selfTest = false; // Set selfTest to false if dacPinSetup fails
// Call the selfTestPinMatrix and check its return value
//this pretty much is the main self test routine
if (selfTestPinMatrix() != 0) selfTest = false; // Set selfTest to false if selfTestPinMatrix fails
// Serial.println("testy");
digitalWrite(faultLED,!selfTest); //turn fault LED ON if there is an error
}
/*********************************************************************************************************************
i2c Slave Request and Recieve functions
*********************************************************************************************************************/
// Function to run when data requested from master
void requestEvent() {
Serial.println("In Request");
Serial.print("Command Sent: ");
Serial.println(responseToSend);
String responseWithTermination = responseToSend + "\n";
Wire.write(responseWithTermination.c_str(), responseWithTermination.length()); // Send the response
}
// I2C communication function for receiving data
void receiveEvent(int howMany) {
Serial.println("In Recieve");
if (Wire.available() > 0) {
command = "";
while (Wire.available()) {
char c = Wire.read();
command += c;
}
Serial.print("Command Reieved: ");
Serial.println(command);
responseToSend = processCommand(command); // Process the command and store the response
}
}
/*********************************************************************************************************************
i2c command handling functions
*********************************************************************************************************************/
// Process received command
String processCommand(String command) {
delay(10); // Adjust this delay based on the expected processing time
//return "<success,1.0>";
//give priority to selftest commands. this is important as the next condition voids the main command selection if self test is in error.
if (command.startsWith("<selftest,")) return processSelfTestCommand(command);
//if it has failed a self test, do not proceed. theoretically the master should not have proceeded with a module failing a self test, so this is basically redundant.
//however, with the watchdog, it could pass an initial self test and fail the next without master knowing. this is an edge case but worth noting.
if (selfTest == false) return "<fail,selftest>";
//process which command was recieved and call the appropriate function to interpret.
if (command.startsWith("<matrix,")) return processSetMatrixCommand(command);
else if (command.startsWith("<matrixscalpel,")) return processScalpelMatrixCommand(command);
else if (command.startsWith("<powerset,")) return processSetPowerCommand(command);
else if (command.startsWith("<powertoggle,")) return processTogglePowerCommand(command);
else if (command.startsWith("<dacset,")) return processDacSetCommand(command);
else if (command.startsWith("<adcread,")) return processAdcReadCommand(command);
else if (command == "<currmeasure>") return processCurrentMeasureCommand();
else if (command == "<resetbreaker>") return processCircuitBreakerCommand();
else if (command == "<*idn?>") return processIdnCommand();
else return "<fail,Invalid Command>";
}
/*********************************************************************************************************************
Set Matrix Command
<matrix,set1,set2,set3,set4> - sets the matrix. returns either <success> or <fail,status1,status2,status3,status4>
*********************************************************************************************************************/
// Command processing functions
String processSetMatrixCommand(String command) {
// Parse the command
//int matrix = command.charAt(8) - '0'; // assuming single digit matrix number
int matrix = 1;
int lastComma = command.indexOf(',', 7); // Start after "<matrix,"
int nextComma;
String settings[4];
uint8_t status[4];
for (int i = 0; i < 4; i++) {
nextComma = command.indexOf(',', lastComma + 1);
if (nextComma == -1) { // If it's the last setting
settings[i] = command.substring(lastComma + 1, command.length() - 1); // Exclude '>'
}
else {
settings[i] = command.substring(lastComma + 1, nextComma);
}
status[i] = setPinMatrix(matrix, 1, settings[i]); //1 means external mode
lastComma = nextComma;
// nextComma = command.indexOf(',', lastComma + 1);
matrix++;
}
// Check status of each call and form response
String response = "<fail,";
bool success = true;
for (int i = 0; i < 4; i++) {
if (status[i] != 0) {
success = false;
response += String(status[i]) + ",";
}
}
response = success ? "<success>" : response.substring(0, response.length() - 1) + ">";
return response;
}
/*********************************************************************************************************************
Set Matrix Scalpel Command
<matrixscalpel,pin,seto> - sets a single channel of the matrix. returns either <success> or <fail>
*********************************************************************************************************************/
String processScalpelMatrixCommand(String command) {
// Remove command identifier and split the remaining part
String commandBody = command.substring(15, command.length() - 1); // Remove "<matrixscalpel," and '>'
// Find the comma separating the values
int commaIndex = commandBody.indexOf(',');
// Extract PIN and SETO
int pin = commandBody.substring(0, commaIndex).toInt();
String seto = commandBody.substring(commaIndex + 1);
// Call setPinMatrix function
int status = setPinMatrix(pin, 1, seto); //1 means external mode
// Form the response based on the status
String response = (status == 0) ? "<success>" : "<fail>";
return response;
}
/*********************************************************************************************************************
Set Voltage Level Command
<powerset,float voltage> - sets the module voltage. also turns off voltage. returns either <success,difference> or <fail>
*********************************************************************************************************************/
String processSetPowerCommand(String command) {
// Parse the command
int commaIndex = command.indexOf(',');
float voltage = command.substring(commaIndex + 1).toFloat();
// Call setVoltage and check the status
float status = setVoltage(voltage);
String response = (status == -1) ? "<fail>" : "<success," + String(status) + ">";
return response;
}
/*********************************************************************************************************************
Toggle Power Command
<powertoggle,bool> - toggles power on or off. returns either <success> or <fail>
*********************************************************************************************************************/
String processTogglePowerCommand(String command) {
// Extract the boolean part of the command
int commaIndex = command.indexOf(',');
String boolStr = command.substring(commaIndex + 1, command.length() - 1); // Exclude '>'
// Convert the extracted string to boolean
bool state = boolStr == "true";
// Call voltageToggle and check the status
int status = voltageToggle(state);
// Form the response based on the status
String response = (status == 0) ? "<success>" : "<fail>";
return response;
}
/*********************************************************************************************************************
Reset Circuit Breaker Command
<resetbreaker> - resets the MIC2045 circuit breaker. returns either <success> or <fail>
*********************************************************************************************************************/
String processCircuitBreakerCommand() {
// Call the voltageResetCircuitBreaker function
int status = voltageResetCircuitBreaker(); //voltageResetCircuitBreaker() doesn't require arguments
// Form the response based on the status
String response = (status == 0) ? "<success>" : "<fail>";
return response;
}
/*********************************************************************************************************************
ADC Read Command
<adcread,pin> - reads the ADC value of a pin. returns either <success, float adcvalue> or <fail>
*********************************************************************************************************************/
String processAdcReadCommand(String command) {
// Parse the command
uint8_t pin = command.substring(command.indexOf(',') + 1).toInt();
// Call readAdcValue and check the status
float adcValue = adcGetPinValue(pin);
String response = (adcValue == -1) ? "<fail>" : "<success," + String(adcValue) + ">";
return response;
}
/*********************************************************************************************************************
DAC Voltage Set Command
<dacset,pin,float voltage> - sets the DAC voltage level of a pin. can be 0 to 5.5V. returns either <success> or <fail>
*********************************************************************************************************************/
String processDacSetCommand(String command) {
// Extract pin number and voltage from the command
int firstCommaIndex = command.indexOf(',');
int secondCommaIndex = command.lastIndexOf(',');
int pin = command.substring(firstCommaIndex + 1, secondCommaIndex).toInt();
float voltage = command.substring(secondCommaIndex + 1, command.length() - 1).toFloat();
// Call dacSetPinValue function and check the status
float status = dacSetPinValue(pin, voltage);
String response = (status == -1) ? "<fail>" : "<success>";
return response;
}
/*********************************************************************************************************************
Identify Command
<*idn?> - module identification. returns < firmare_version, hardware_version>
*********************************************************************************************************************/
String processIdnCommand() {
return "<" + String(FIRMWARE_VERSION) + "," + String(HARDWARE_VERSION) + ">";
}
/*********************************************************************************************************************
Current Measure Command
<currmeasure> - measures the current draw of the V_ADJ supply. returns either <success, float current> or <fail>
*********************************************************************************************************************/
String processCurrentMeasureCommand() {
// Call the currentGetPinValue function
float current = currentGetPinValue(); //currentGetPinValue() doesn't require arguments
// Form the response based on the status
String response = (current == -1) ? "<fail>" : "<success," + String(current) + ">";
return response;
}
/*********************************************************************************************************************
Self Test Command
Accepts two commands:
<selftest,status> - this returns the self test status, either <success> or <fail>, simply returns the status of a previous self test.
<selftest,runtest> - this returns the self test status, either <success> or <fail>, but also runs a full new self test of the module.
*********************************************************************************************************************/
String processSelfTestCommand(String command) {
//Return the current selfTest status
if (command == "<selftest,status>") return selfTest ? "<success>" : "<fail>";
//Run a new self test
else if (command == "<selftest,runtest>") {
selfTestMain();
// Return the updated selfTest status
return selfTest ? "<success>" : "<fail>";
}
//Handle invalid self test command
else return "<fail,Invalid Command>";
}
#include <Adafruit_ADS1X15.h>
#include <Wire.h>
//ads1115 address
#define adcaddr 0x48
//create the ADC object
Adafruit_ADS1115 ads;
/*********************************************************************************************************************
ADC Setup Function. Initilizes the ADC and sets the gain value.
*********************************************************************************************************************/
int adcPinSetup(){
switchI2CMode(true); //as master
// Try to initialize!
if (!ads.begin(adcaddr)) {
Serial.println("Failed to initialize ADS.");
switchI2CMode(false); //as slave
return 1; //error
}
ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 0.1875mV (default)
switchI2CMode(false); //as slave
return 0; //no error
}
/*********************************************************************************************************************
Main ADC Get Value Function. Gets the ADC value on the pin you want. accpets pin values 1 - 4.
*********************************************************************************************************************/
float adcGetPinValue(int pin){
int16_t adc = 1;
float adcValue;
switchI2CMode(true); //as master
switch (pin){
case 1:
adcValue = ads.computeVolts(ads.readADC_SingleEnded(0));
break;
case 2:
adcValue = ads.computeVolts(ads.readADC_SingleEnded(1));
break;
case 3:
adcValue = ads.computeVolts(ads.readADC_SingleEnded(2));
break;
case 4:
adcValue = ads.computeVolts(ads.readADC_SingleEnded(3));
break;
default:
break;
}
switchI2CMode(false); //as slave
return adcValue; //return the read value
}
//MCP4728 (default address 0x60) or MCP4728A4 (default address 0x64)
#include <Adafruit_MCP4728.h>
#include <Wire.h>
//mcp4728 address
#define dacaddr 0x60
//create the DAC object
Adafruit_MCP4728 mcp;
/*********************************************************************************************************************
DAC Setup Function. Initilizes the DAC and sets the VREF value.
*********************************************************************************************************************/
int dacPinSetup(){
switchI2CMode(true); //as master
// Try to initialize!
if (!mcp.begin(dacaddr)) {
Serial.println("Failed to find MCP4728 chip");
switchI2CMode(false); //as slave
return 1; //error
}
ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 0.1875mV (default)
//initilize to known state
//VDD = 5.5V
// Vref = MCP_VREF_VDD, value = 0, 0V
mcp.setChannelValue(MCP4728_CHANNEL_A, 0);
mcp.setChannelValue(MCP4728_CHANNEL_B, 0);
mcp.setChannelValue(MCP4728_CHANNEL_C, 0);
mcp.setChannelValue(MCP4728_CHANNEL_D, 0);
mcp.saveToEEPROM();
switchI2CMode(false); //as slave
return 0; //no error
}
/*********************************************************************************************************************
Main DAC SET Value Function. Sets the DAC value on the pin you want. accpets pin values 1 - 4.
*********************************************************************************************************************/
float dacSetPinValue(int pin, float voltage){
// Calculate the voltage increment per level for a 12-bit DAC with VREF = 5.5V
float increment = 5.5 / 4096;
// Convert the desired voltage to the corresponding 12-bit value
int voltageAdjusted = voltage / increment;
// Ensure voltageAdjusted is within the 12-bit range
if (voltageAdjusted < 0) {
voltageAdjusted = 0;
} else if (voltageAdjusted > 4095) {
voltageAdjusted = 4095;
}
switch (pin){
case 1:
switchI2CMode(true); //as master
mcp.setChannelValue(MCP4728_CHANNEL_A, voltageAdjusted);
switchI2CMode(false); //as slave
return 0;
break;
case 2:
switchI2CMode(true); //as master
mcp.setChannelValue(MCP4728_CHANNEL_B, voltageAdjusted);
switchI2CMode(false); //as slave
return 0;
break;
case 3:
switchI2CMode(true); //as master
mcp.setChannelValue(MCP4728_CHANNEL_C, voltageAdjusted);
switchI2CMode(false); //as slave
return 0;
break;
case 4:
switchI2CMode(true); //as master
mcp.setChannelValue(MCP4728_CHANNEL_D, voltageAdjusted);
switchI2CMode(false); //as slave
return 0;
break;
default:
return 1;
}
}
#include <Adafruit_MCP23X17.h>
//mcp23017 address
#define mcpioaddr 0x22
//create the IO Expander object
Adafruit_MCP23X17 mcpio;
//switch matrix pins low-power (microcontroller)
#define switch1 A4
#define switch2 8
#define switch3 A3
#define switch4 4
#define switch5 A2
#define switch6 3
#define switch7 A1
#define switch8 9
//switch matrix pins high-power (MCP23017)
#define annodeSwitch1 0 //out enable channel 1
#define annodeSwitch2 1 //gnd out channel 1
#define annodeSwitch3 2 //v_adj out channel 1
#define annodeSwitch4 3 //out enable channel 2
#define annodeSwitch5 4 //gnd out channel 2
#define annodeSwitch6 5 //v_adj out channel 2
#define annodeSwitch7 6 //out enable channel 3
#define annodeSwitch8 7 //gnd out channel 3
#define annodeSwitch9 8 //v_adj out channel 3
#define annodeSwitch10 9 //out enable channel 4
#define annodeSwitch11 10 //gnd out channel 4
#define annodeSwitch12 11 //v_adj out channel 4
/*********************************************************************************************************************
Pin Matrix Setup Function. Initilizes the Pin Matrix and sets the Default value (all OFF)
*********************************************************************************************************************/
int setupPinMatrix(){
//setup pinmodes for the microcontroller controlled switches
pinMode(switch1,OUTPUT);
pinMode(switch2,OUTPUT);
pinMode(switch3,OUTPUT);
pinMode(switch4,OUTPUT);
pinMode(switch5,OUTPUT);
pinMode(switch6,OUTPUT);
pinMode(switch7,OUTPUT);
pinMode(switch8,OUTPUT);
switchI2CMode(true); //as master
// Try to initialize!
if (!mcpio.begin_I2C(mcpioaddr)) {
Serial.println("Failed to initialize IO Expander.");
switchI2CMode(false); //as slave
return 1; //error
}
mcpio.pinMode(annodeSwitch1, OUTPUT);
mcpio.pinMode(annodeSwitch2, OUTPUT);
mcpio.pinMode(annodeSwitch3, OUTPUT);
mcpio.pinMode(annodeSwitch4, OUTPUT);
mcpio.pinMode(annodeSwitch5, OUTPUT);
mcpio.pinMode(annodeSwitch6, OUTPUT);
mcpio.pinMode(annodeSwitch7, OUTPUT);
mcpio.pinMode(annodeSwitch8, OUTPUT);
mcpio.pinMode(annodeSwitch9, OUTPUT);
mcpio.pinMode(annodeSwitch10, OUTPUT);
mcpio.pinMode(annodeSwitch11, OUTPUT);
mcpio.pinMode(annodeSwitch12, OUTPUT);
//set the pin matrix to safe values. ADC is considered "safe" as it is high impedence and wont let too much current flow.
//we want to guerentee that a overcurrent condition cannot happen. the switches can only handle 400mA RMS 600mA PEAK
setPinMatrix(1,0,"off"); //0 for internal mode
setPinMatrix(2,0,"off");
setPinMatrix(3,0,"off");
setPinMatrix(4,0,"off");
switchI2CMode(false); //as slave
return 0; //no error
}
/*********************************************************************************************************************
sets the pin matrix. accepts pin value 1-4 and string values VADJ GND DIO ADC DAC NULL OFF
For safety, default of everything is ADC mode, i.e. when set to V_ADJ mode, due to the switching archetecture, ADC must also be ON
mode. 0 = internal 1 = external. 0 is used exclusively for self-test.
truth table:
SW1 SW2 Anode1 Anode2 Anode3 Output
0 1 0 0 0 off
0 1 1 0 0 adc
1 x 1 0 0 dac
0 0 1 0 0 dio
0 1 1 1 0 gnd
0 1 1 0 1 v_adj
*********************************************************************************************************************/
int setPinMatrix(int pin, int mode, String seto){
if (pin < 1 || pin > 4) return 1; //make sure pin is in range of 1-4
uint8_t sw1;
uint8_t sw2;
uint8_t an1;
uint8_t an2;
uint8_t an3;
if (seto == "null") return 0; //if pin value null exit function
//set the 5 switch values according to the truth table.
//these will then be applied to the appropriate switch pins.
//Determine the switch states based on the mode
if (seto == "off") {
sw1 = 0; sw2 = 1; an1 = 0; an2 = 0; an3 = 0;
} else if (seto == "adc") {
sw1 = 0; sw2 = 1; an1 = 1; an2 = 0; an3 = 0;
} else if (seto == "dac") {
sw1 = 1; sw2 = 0; an1 = 1; an2 = 0; an3 = 0;
} else if (seto == "dio") {
sw1 = 0; sw2 = 0; an1 = 1; an2 = 0; an3 = 0;
} else if (seto == "gnd") {
sw1 = 0; sw2 = 1; an1 = 1; an2 = 1; an3 = 0;
} else if (seto == "vadj") {
sw1 = 0; sw2 = 1; an1 = 1; an2 = 0; an3 = 1;
} else {
return 1; // Invalid mode
}
if (mode == 0) an1 = 0;
switchI2CMode(true); //as master
switch (pin){
case 1:
//First, put the high-power switches in safety OFF
mcpio.digitalWrite(annodeSwitch1, LOW);
mcpio.digitalWrite(annodeSwitch2, LOW);
mcpio.digitalWrite(annodeSwitch3, LOW);
//Next, switch the low-power switches to desired position
digitalWrite(switch1,sw1);
digitalWrite(switch2,sw2);
//Finally, switch the high-power switches to desired position
mcpio.digitalWrite(annodeSwitch1, an1);
mcpio.digitalWrite(annodeSwitch2, an2);
mcpio.digitalWrite(annodeSwitch3, an3);
switchI2CMode(false); //as slave
return 0;
break;
case 2:
//First, put the high-power switches in safety OFF
mcpio.digitalWrite(annodeSwitch4, LOW);
mcpio.digitalWrite(annodeSwitch5, LOW);
mcpio.digitalWrite(annodeSwitch6, LOW);
//Next, switch the low-power switches to desired position
digitalWrite(switch3,sw1);
digitalWrite(switch4,sw2);
//Finally, switch the high-power switches to desired position
mcpio.digitalWrite(annodeSwitch4, an1);
mcpio.digitalWrite(annodeSwitch5, an2);
mcpio.digitalWrite(annodeSwitch6, an3);
switchI2CMode(false); //as slave
return 0;
break;
case 3:
//First, put the high-power switches in safety OFF
mcpio.digitalWrite(annodeSwitch7, LOW);
mcpio.digitalWrite(annodeSwitch8, LOW);
mcpio.digitalWrite(annodeSwitch9, LOW);
//Next, switch the low-power switches to desired position
digitalWrite(switch5,sw1);
digitalWrite(switch6,sw2);
//Finally, switch the high-power switches to desired position
mcpio.digitalWrite(annodeSwitch7, an1);
mcpio.digitalWrite(annodeSwitch8, an2);
mcpio.digitalWrite(annodeSwitch9, an3);
switchI2CMode(false); //as slave
return 0;
break;
case 4:
//First, put the high-power switches in safety OFF
mcpio.digitalWrite(annodeSwitch10, LOW);
mcpio.digitalWrite(annodeSwitch11, LOW);
mcpio.digitalWrite(annodeSwitch12, LOW);
//Next, switch the low-power switches to desired position
digitalWrite(switch7,sw1);
digitalWrite(switch8,sw2);
//Finally, switch the high-power switches to desired position
mcpio.digitalWrite(annodeSwitch10, an1);
mcpio.digitalWrite(annodeSwitch11, an2);
mcpio.digitalWrite(annodeSwitch12, an3);
switchI2CMode(false); //as slave
return 0;
break;
default:
switchI2CMode(false); //as slave
return 1;
}
}
/*********************************************************************************************************************
Pin Matrix Self Test Function.
Does the following test steps:
-turns output OFF
-puts low-power matrix into ADC mode
-puts high-power matrix into vadj mode
-sets voltage to 2.0V, turns supply ON, measures voltage and current.
-sets voltage to 3.0V, turns supply ON, measures voltage and current.
-sets voltage to 4.0V, turns supply ON, measures voltage and current.
-puts high-power matrix into GND mode
-measures GND value. should be 0.0
if all these are within expected values self test passed (return 0) if not its a fail (return 1)
*********************************************************************************************************************/
#define TOLERANCE 0.10 // Define the tolerance level
#define MAXCURRENT 0.05 //define max current. should be 0 as there is no load.
bool selfTestPinMatrix() {
// Step 1: Turn output OFF
voltageToggle(false);
// Step 2 & 3: Set high-power matrix to VADJ mode, set low-power matrix into ADC mode
setPinMatrix(1, 0, "vadj"); //0 for internal mode
setPinMatrix(2, 0, "vadj");
setPinMatrix(3, 0, "vadj");
setPinMatrix(4, 0, "vadj");
// Helper function to perform voltage test
auto voltageTest = [](float testVoltage) {
setVoltage(testVoltage); // Set voltage
voltageToggle(true); // Turn power ON
delay(1000); // Wait for stabilization
bool allChannelsPass = true;
for (int channel = 1; channel <= 4; ++channel) {
float measuredVoltage = adcGetPinValue(channel); // Measure voltage on each channel
// Serial.print("measuredVoltage: ");
// Serial.println(measuredVoltage);
if (abs(measuredVoltage - testVoltage) > TOLERANCE) {
allChannelsPass = false; // If any channel is out of tolerance, mark test as failed
break;
}
}
float measuredCurrent = currentGetPinValue(); // Measure current
// Serial.print("measuredCurrent: ");
// Serial.println(measuredCurrent);
voltageToggle(false); // Turn power OFF
// Check if all channels are within acceptable range and current is within limit
return allChannelsPass && (measuredCurrent < MAXCURRENT);
};
// Step 4, 5, 6: Test with 2.0V, 3.0V, and 4.0V
if (!voltageTest(2.0) || !voltageTest(3.0) || !voltageTest(4.0)) {
return false; // Test failed
}
// Step 7: Put high-power matrix into GND mode and measure
setPinMatrix(1, 0, "gnd"); //0 for internal mode
setPinMatrix(2, 0, "gnd");
setPinMatrix(3, 0, "gnd");
setPinMatrix(4, 0, "gnd");
bool groundTestPass = true;
for (int channel = 1; channel <= 4; ++channel) {
float groundValue = adcGetPinValue(channel); // Measure GND value on each channel
if (abs(groundValue) > TOLERANCE) {
groundTestPass = false; // If any channel is out of tolerance, mark test as failed
break;
}
}
// If all tests passed
return groundTestPass && voltageTest(3.3) && voltageTest(3.0) && voltageTest(4.0);
}
#include "isl28022DL.h"
//power supply mosfets
#define mfet1 22
#define mfet2 2
#define mfet3 5
#define mfet4 11
#define mfet5 13
#define mfet6 10
#define mfet7 12
#define mfet8 6
//LOW = OFF
#define en_psu 23
//io expander + MIC2045-1 pins
#define pwrgdpsu 12 //this is basically not used / redundant. if we wanted to know if power is good, just use the ISL28022
#define en_psu_mic2045 13
#define faultpsu 14
//power supply monitor address i2c
//TB008 HWRev.A requires modified PCB for this address.
//without PCB modification address is 0x4F
#define addrPwrMonitor 0x40
//create power monitor object
power_isl28022 power_monitor(addrPwrMonitor);
const float VoltageLookupTable[][9] = {
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.2},
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.2600660066006601},
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.86006600660066},
{0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.35},
{0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.9499999999999997},
{0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 2.01006600660066},
{0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.61006600660066},
{0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.44985835694051},
{0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 2.04985835694051},
{0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 2.10992436354117},
{0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 2.7099243635411696},
{0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 2.1998583569405095},
{0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 2.799858356940509},
{0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 2.859924363541169},
{0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 3.45992436354117},
{0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.5933774834437084},
{0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 2.1933774834437085},
{0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 2.2534434900443685},
{0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 2.8534434900443686},
{0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 2.3433774834437084},
{0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 2.9433774834437085},
{0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 3.003443490044368},
{0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 3.603443490044368},
{0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 2.4432358403842187},
{0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 3.0432358403842183},
{0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 3.103301846984878},
{0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 3.703301846984878},
{0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 3.1932358403842183},
{0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 3.7932358403842192},
{0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 3.8533018469848788},
{0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 4.453301846984879},
{0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.7881188118811882},
{0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 2.3881188118811885},
{0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.448184818481848},
{0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 3.048184818481848},
{0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 2.538118811881188},
{0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 3.138118811881189},
{0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 3.1981848184818484},
{0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 3.798184818481849},
{0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 2.637977168821698},
{0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 3.2379771688216987},
{0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 3.2980431754223587},
{0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 3.8980431754223583},
{0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 3.3879771688216973},
{0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 3.9879771688216987},
{0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 4.048043175422359},
{0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 4.648043175422358},
{0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 2.7814962953248967},
{0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 3.381496295324897},
{0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 3.4415623019255577},
{0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 4.041562301925557},
{0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 3.5314962953248967},
{0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 4.131496295324898},
{0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 4.191562301925557},
{0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 4.791562301925557},
{0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 3.6313546522654074},
{0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 4.231354652265408},
{0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 4.291420658866067},
{0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 4.891420658866066},
{0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 4.381354652265407},
{0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 4.981354652265407},
{0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 5.041420658866067},
{0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.074201474201474},
{0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 2.674201474201474},
{0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.7342674808021337},
{0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 3.334267480802134},
{0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 2.8242014742014745},
{0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 3.424201474201475},
{0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 3.484267480802133},
{0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 4.084267480802134},
{0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 2.9240598311419834},
{0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 3.5240598311419844},
{0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 3.5841258377426435},
{0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 4.184125837742644},
{0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 3.674059831141983},
{0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 4.274059831141983},
{0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 4.3341258377426435},
{0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 4.934125837742645},
{0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 3.0675789576451824},
{0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 3.6675789576451834},
{0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 3.727644964245844},
{0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 4.327644964245844},
{0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 3.8175789576451824},
{0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 4.417578957645183},
{0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 4.477644964245843},
{0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 5.077644964245844},
{0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 3.9174373145856927},
{0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 4.517437314585693},
{0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 4.577503321186351},
{0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 5.177503321186355},
{0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 4.667437314585692},
{0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.262320286082663},
{0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 3.862320286082662},
{0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 3.922386292683322},
{0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 4.522386292683323},
{0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 4.012320286082662},
{0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 4.612320286082661},
{0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 4.672386292683322},
{0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 4.112178643023173},
{0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 4.712178643023171},
{0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 4.772244649623832},
{0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 4.862178643023172},
{0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 4.255697769526371},
{0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 4.8556977695263726},
{0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 4.915763776127032},
{0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 5.00569776952637},
{0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 5.1055561264668805},
{1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.541747572815534},
{1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 3.1417475728155333},
{1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 3.2018135794161933},
{1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 3.801813579416195},
{1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 3.291747572815534},
{1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 3.891747572815533},
{1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 3.951813579416194},
{1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 4.5518135794161925},
{1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 3.391605929756045},
{1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 3.9916059297560453},
{1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 4.051671936356703},
{1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 4.6516719363567045},
{1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 4.141605929756045},
{1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 4.7416059297560444},
{1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 4.801671936356704},
{1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 3.5351250562592416},
{1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 4.135125056259241},
{1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 4.1951910628599025},
{1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 4.795191062859902},
{1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 4.2851250562592424},
{1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 4.885125056259242},
{1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 4.9451910628599025},
{1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 4.384983413199751},
{1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 4.9849834131997515},
{1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 5.045049419800411},
{1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 5.134983413199753},
{1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.729866384696722},
{1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 4.329866384696722},
{1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 4.389932391297382},
{1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 4.989932391297382},
{1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 4.479866384696723},
{1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 5.0798663846967225},
{1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 5.139932391297382},
{1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 4.579724741637232},
{1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 5.179724741637232},
{1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 4.723243868140431},
{1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.015949047017008},
{1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 4.6159490470170095},
{1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 4.676015053617669},
{1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 4.765949047017008},
{1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 4.86580740395752},
{1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 5.009326530460719},
};
/*********************************************************************************************************************
Programable PSU Setup Function. Initilizes the supply control pins & power monitor.
*********************************************************************************************************************/
int setupVoltage(){
// Initialize MOSFET pins as outputs
pinMode(mfet1, OUTPUT);
pinMode(mfet2, OUTPUT);
pinMode(mfet3, OUTPUT);
pinMode(mfet4, OUTPUT);
pinMode(mfet5, OUTPUT);
pinMode(mfet6, OUTPUT);
pinMode(mfet7, OUTPUT);
pinMode(mfet8, OUTPUT);
pinMode(en_psu,OUTPUT);
switchI2CMode(true); //as master
//io expander + MIC2045-1 pins set pinmode
// mcpio.pinMode(pwrgdpsu, INPUT); //this is basically not used / redundant. if we wanted to know if power is good, just use the ISL28022
mcpio.pinMode(en_psu_mic2045, OUTPUT);
mcpio.pinMode(faultpsu, INPUT_PULLUP);
//disable PSU
digitalWrite(en_psu,LOW);
//set voltage to a known value
setVoltage(2.0);
// Start up the ISL28022 Power monitor ICs
power_monitor.begin();
switchI2CMode(false); //as slave
return 0;
}
/*********************************************************************************************************************
Voltage Set Function.
Accepts a float value of the desired voltage. then, tries to find a combination of resistors that get close to the desired value.
This function also turns the supply OFF. it does not turn it back ON.
Returns the difference between the set and expected values, or -1 for error.
*********************************************************************************************************************/
float setVoltage(float desiredVoltage) { //no direct i2c
digitalWrite(en_psu,LOW);
if (desiredVoltage < 1.2 || desiredVoltage > 5.2) {
Serial.println("Desired voltage out of range.");
return -1;
}
int bestIndex = -1;
float minDifference = 9999;
for (int i = 0; i < sizeof(VoltageLookupTable) / sizeof(VoltageLookupTable[0]); ++i) {
float thisVoltage = VoltageLookupTable[i][8];
float thisDifference = abs(desiredVoltage - thisVoltage);
if (thisDifference < minDifference && thisVoltage >= 1.2 && thisVoltage <= 5.2) {
bestIndex = i;
minDifference = thisDifference;
}
}
if (bestIndex == -1) {
Serial.println("No suitable resistor combination found.");
return -1;
}
// Turn on/off MOSFETs based on the best combination
digitalWrite(mfet1, VoltageLookupTable[bestIndex][0]);
digitalWrite(mfet2, VoltageLookupTable[bestIndex][1]);
digitalWrite(mfet3, VoltageLookupTable[bestIndex][2]);
digitalWrite(mfet4, VoltageLookupTable[bestIndex][3]);
digitalWrite(mfet5, VoltageLookupTable[bestIndex][4]);
digitalWrite(mfet6, VoltageLookupTable[bestIndex][5]);
digitalWrite(mfet7, VoltageLookupTable[bestIndex][6]);
digitalWrite(mfet8, VoltageLookupTable[bestIndex][7]);
return desiredVoltage - VoltageLookupTable[bestIndex][8];
}
/*********************************************************************************************************************
Voltage Toggle Function.
Performs a check to see if the PSU circuit breaker is in an error state (i.e. overcurrent, short circuit, etc.).
Then toggles the power supply according to the bool toggle state.
*********************************************************************************************************************/
//the minimum is always 32mS (MIC2045 limited)
#define wait_for_settle 100
int voltageToggle(bool toggle) {
switchI2CMode(true); //as master
// Check the fault status of the PSU
if (mcpio.digitalRead(faultpsu) == LOW) {
// Disable the PSU and return an error code
digitalWrite(en_psu, LOW);
mcpio.digitalWrite(en_psu_mic2045, LOW);
switchI2CMode(false); //as slave
return 1; // Error code
}
// Set the PSU on or off according to the toggle value
if (toggle == true) {
digitalWrite(en_psu, HIGH);
mcpio.digitalWrite(en_psu_mic2045, HIGH);
// Wait for PSU to settle
delay(wait_for_settle);
// Perform additional checks here after settling
// Check if the PSU is still okay
if (mcpio.digitalRead(faultpsu) == LOW) {
// Disable the PSU and return an error code
digitalWrite(en_psu, LOW);
mcpio.digitalWrite(en_psu_mic2045, LOW);
switchI2CMode(false); //as slave
return 1; // Error code
}
} else {
digitalWrite(en_psu, LOW);
mcpio.digitalWrite(en_psu_mic2045, LOW);
}
switchI2CMode(false); //as slave
return 0; // No error
}
/*********************************************************************************************************************
Reset Circuit Breaker Function
this function resets the circuit breaker (i.e. MIC2045) if there is overcurrent condition.
basically useless, however sudden capacitive loads can trigger the circuit breaker in error, so may be usefull.
simply does the following:
-turns ON (high) en_psu which is the switchers native enable
-toggles en_psu_mic2045 on - off - on
-waits wait_for_settle mS to let everything settle
-checks faultpsu for fault. LOW = error
if error, return 1
if no error, return 0
*********************************************************************************************************************/
int voltageResetCircuitBreaker() {
switchI2CMode(true); //as master
// Turn on the native PSU enable
digitalWrite(en_psu, HIGH);
// Toggle the MIC2045 enable pin
mcpio.digitalWrite(en_psu_mic2045, HIGH);
delay(10); // Short delay to ensure the state change is registered
mcpio.digitalWrite(en_psu_mic2045, LOW);
delay(10); // Short delay
mcpio.digitalWrite(en_psu_mic2045, HIGH);
// Wait for the system to settle
delay(wait_for_settle);
// Check for a fault condition
if (mcpio.digitalRead(faultpsu) == LOW) {
// If there's a fault, disable the PSU and return error
digitalWrite(en_psu, LOW);
mcpio.digitalWrite(en_psu_mic2045, LOW);
switchI2CMode(false); //as slave
return 1; // Error code
}
switchI2CMode(false); //as slave
// If no fault, return 0 indicating success
return 0;
}
/*********************************************************************************************************************
Current Measure Command. Simply measures the output of the PSU. does not care if PSU is on or not. use wisely and in context
perhaps add checks in the future. returning a -1 means it has failed, so we could use that. use the "circuit breaker" IC?
*********************************************************************************************************************/
float currentGetPinValue(){
switchI2CMode(true); //as master
float current = power_monitor.readRegister(CURRENTREG);
switchI2CMode(false); //as slave
return current;
}
MASTER CODE (simplified for testing, normally runs a GUI, but this is the actual code im using):
#include <Adafruit_GFX.h> // Core graphics library
#include <SPI.h> // This is needed for display
#include <Adafruit_ILI9341.h>
#include <Wire.h> // This is needed for FT6206
#include <Adafruit_FT6206.h>
#include "bitmaps.h"
#include "isl28022DL.h"
#include <Adafruit_SleepyDog.h>
// Define the pin States
enum PinSetting { PIN_3V3, PIN_5V, PIN_GND, PIN_ADC, PIN_DAC, PIN_DIO_INPUT, PIN_DIO_OUTPUT, PIN_NULL, PIN_OFF };
//DIO Pins
#define DIO1 7
#define DIO2 6
#define DIO3 12
#define DIO4 13
#define DIO5 11
#define DIO6 5
#define DIO7 2
#define DIO8 0
#define DIO9 1
#define DIO10 3
#define DIO11 4
#define DIO12 A5
#define DIO13 8
#define DIO14 A3
#define DIO15 A2
#define DIO16 A1
const byte DIO_PINS[16] = {DIO1, DIO2, DIO3, DIO4, DIO5, DIO6, DIO7, DIO8, DIO9, DIO10, DIO11, DIO12, DIO13, DIO14, DIO15, DIO16};
//fan pins
#define fan_foo 43 //PA08
#define pwm_fan 44 //PA09 requires you to modify the line in variant.cpp in order to use PWM { PORTA, 9, PIO_COM, PIN_ATTR_PWM_E, No_ADC_Channel, TC0_CH1, NOT_ON_TIMER, EXTERNAL_INT_9 },
//power supply monitor address i2c
#define addr9v 0x4f
#define addr3v3 0x4e
#define addr5v5 0x4d
//create power monitor objects
power_isl28022 power_monitor_9v(addr9v);
power_isl28022 power_monitor_3v3(addr3v3);
power_isl28022 power_monitor_5v5(addr5v5);
//USB vs WALL selector pins (LTC4415 IC)
//IN1 is VJACK IN2 is VUSB. VJACK has priority and is allowed to supply more current. 2 Amp USB 4 Amp VJACK.
//stat indicators are low when conducting, i.e. if VJACK & USB plugged in, stat1 will be LOW, stat2 will be HIGH.
//WARN Pulls down when diode current exceeds its current limit or die temperature is close to thermal shutdown.
//PB31 and PB23 requires you to add the lines in variant.cpp in order to use. Add them at the bottom of the pin definitions.
#define warn1 47 //PB31 VJACK { PORTB, 31, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_15 },
#define warn2 48 //PB23 VUSB { PORTB, 23, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_7 },
#define stat1 46 //PA11 VJACK { PORTA, 11, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_11 },
#define stat2 41 //PB10 VUSB { PORTB, 10, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_10 },
/*********************************************************************************************************************
Setup and initilization function
*********************************************************************************************************************/
// Initialize the MCP23017
void setup() {
delay(5000);
pinModulesSetup();
// Watchdog.enable(60000); // turn on watchdog timer with 60 seconds
Wire.begin(); // Start I2C
Serial.begin(9600);
// while (!Serial) delay(10); // pause the serial port
Serial.println(F("master test"));
// Start up the ISL28022 Power monitor ICs
power_monitor_9v.begin();
power_monitor_3v3.begin();
power_monitor_5v5.begin();
pinMode(pwm_fan,OUTPUT);
analogWrite(pwm_fan,100);
//setup USB vs WALL selector inputs
pinMode(warn1,INPUT);
pinMode(warn2,INPUT);
pinMode(stat1,INPUT);
pinMode(stat2,INPUT);
}
/*********************************************************************************************************************
Loop Function. Main code.
*********************************************************************************************************************/
void loop() {
// Watchdog.reset();
delay(2500);
//Serial.println(setPinScalpel(1,"gnd"));
//delay(2500);
//Serial.println(setPinScalpel(2,"vadj"));
//delay(2500);
Serial.println(setModulePower(3.3,1));
//delay(2500);
Serial.println(toggleModulePower("true",1));
delay(2500);
}
/*********************************************************************************************************************
List of accepted commands:
<matrix,set1,set2,set3,set4> - sets the matrix. returns either <success> or <fail,status1,status2,status3,status4>
<matrixscalpel,pin,seto> - sets a single channel of the matrix. returns either <success> or <fail>
<powerset,float voltage> - sets the module voltage. also turns off voltage. returns either <success,difference> or <fail>
<powertoggle,bool> - toggles power on or off. returns either <success> or <fail>
<currmeasure> - measures the current draw of the V_ADJ supply. returns either <success, float current> or <fail>
<*idn?> - module identification. returns < firmare_version, hardware_version>
<adcread,pin> - reads the ADC value of a pin. returns either <success, float adcvalue> or <fail>
<dacset,pin,float voltage> - sets the DAC voltage level of a pin. can be 0 to 5.5V. returns either <success> or <fail>
<selftest,status> - this returns the self test status, either <success> or <fail>, simply returns the status of a previous self test.
<selftest,runtest> - this returns the self test status, either <success> or <fail>, but also runs a full new self test of the module.
<resetbreaker> - resets the MIC2045 circuit breaker. returns either <success> or <fail>
*********************************************************************************************************************/
#include <Wire.h>
/*********************************************************************************************************************
Pin Modules Setup Code
*********************************************************************************************************************/
//all four pin modules share an address. They are selected using a digital pin going "HIGH". This triggers an interupt in the selected slave and lets it recieve commands.
uint8_t pinmoduleaddress;
#define pinmoduleaddress1 0x20
#define pinmoduleaddress2 0x21
#define pinmoduleaddress3 0x22
#define pinmoduleaddress4 0x23
#define pinmodule3 49 //PA07 { PORTA, 7, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_7 },
#define pinmodule2 50 //PA15 { PORTA, 15, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_7 },
#define pinmodule1 51 //PB00 { PORTB, 0, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_7 },
#define pinmodule4 52 //PB01 { PORTB, 1, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_7 },
void pinModulesSetup(){
//Wire.begin(); // Start I2C
pinMode(pinmodule1,OUTPUT);
pinMode(pinmodule2,OUTPUT);
pinMode(pinmodule3,OUTPUT);
pinMode(pinmodule4,OUTPUT);
selectModule(0); //deselect all modules
}
/*********************************************************************************************************************
Module Select Function
*********************************************************************************************************************/
int selectModule(int selection) {
// Set all module pins to LOW initially
digitalWrite(pinmodule1, LOW);
digitalWrite(pinmodule2, LOW);
digitalWrite(pinmodule3, LOW);
digitalWrite(pinmodule4, LOW);
// Select the appropriate module based on the input
switch (selection) {
case 1: digitalWrite(pinmodule1, HIGH); pinmoduleaddress = pinmoduleaddress1; break;
case 2: digitalWrite(pinmodule2, HIGH); pinmoduleaddress = pinmoduleaddress2; break;
case 3: digitalWrite(pinmodule3, HIGH); pinmoduleaddress = pinmoduleaddress3; break;
case 4: digitalWrite(pinmodule4, HIGH); pinmoduleaddress = pinmoduleaddress4; break;
case 0: return 0; // Deselection
default: return 1; // Invalid selection, return error
}
return 0; // Successful selection
}
/*********************************************************************************************************************
Module Initilize Function
*********************************************************************************************************************/
String pinINITILIZE(){
//this function will query and initilize all four pinmodules, checking for errors.
//it will then return a message that shows the errors, if any.
//perhaps should return "good" if all good, so that the program knows to halt if not good.
String message = "Dummy Pin Initilize Pass";
return message;
}
/*********************************************************************************************************************
ADC Read Command
<adcread,pin> - reads the ADC value of a pin. returns either <success, float adcvalue> or <fail>
*********************************************************************************************************************/
float pinADC(int pin) {
if (pin < 1 || pin > 16) {
Serial.println("Invalid pin number");
return -1; // Early return on invalid pin
}
int moduleNumber = (pin - 1) / 4 + 1; // Determine module number
int pinToSend = (pin - 1) % 4 + 1; // Normalize pin number for module
selectModule(moduleNumber);
// Send ADC read command to the correct slave module
String command = "<adcread," + String(pinToSend) + ">";
Wire.beginTransmission(pinmoduleaddress);
Wire.write(command.c_str());
Wire.endTransmission();
delay(200); // Increase the delay to ensure the slave has time to prepare the response
// Request and read response from slave
Wire.requestFrom(pinmoduleaddress, 32); // Request enough bytes to cover the expected response
String response = "";
while (Wire.available()) {
char c = Wire.read();
response += c;
if (c == '\n') break; // Stop reading if termination character is found
}
response.trim(); // Remove any extraneous whitespace or non-printable characters
// Validate and parse the response
if (response.length() > 0 && response.startsWith("<") && response.endsWith(">")) {
if (response.startsWith("<success,")) {
int startIndex = response.indexOf(',') + 1;
int endIndex = response.indexOf('>', startIndex);
String adcValueStr = response.substring(startIndex, endIndex);
selectModule(0); //deselect all modules
return adcValueStr.toFloat(); //return the ADC value for the pin
}
else {
selectModule(0); //deselect all modules
return -1; //return error
}
}
else {
selectModule(0); //deselect all modules
return -1; //return error
}
}
/*********************************************************************************************************************
Current Measure Command
<currmeasure> - measures the current draw of the V_ADJ supply. returns either <success, float current> or <fail>
*********************************************************************************************************************/
float pinCURRENT(int module){
if (module < 1 || module > 4) {
Serial.println("Invalid module number");
return -1;
}
selectModule(module);
Wire.beginTransmission(pinmoduleaddress);
Wire.write("<currmeasure>");
Wire.endTransmission();
delay(200);
Wire.requestFrom(pinmoduleaddress, 32);
String response = "";
while (Wire.available()) {
char c = Wire.read();
response += c;
if (c == '\n') break;
}
response.trim();
selectModule(0);
if (response.startsWith("<success,")) {
int startIndex = response.indexOf(',') + 1;
int endIndex = response.indexOf('>', startIndex);
return response.substring(startIndex, endIndex).toFloat();
} else {
return -1;
}
}
/*********************************************************************************************************************
Pin Self Test Code
accepts two commands:
<selftest,status> - this returns the self test status, either <success> or <fail>, simply returns the status of a previous self test.
<selftest,runtest> - this returns the self test status, either <success> or <fail>, but also runs a full new self test of the module.
*********************************************************************************************************************/
int pinModuleSelfTest(int module, String command) {
// Check if the module number is valid
if (module < 1 || module > 4) {
Serial.println("Invalid module number");
return -1; // Error code for invalid module number
}
// Validate the command parameter
if (command != "status" && command != "runtest") {
Serial.println("Invalid command");
return -2; // Error code for invalid command
}
// Prepare the self-test command
String selfTestCommand = "<selftest," + command + ">";
//transmit command to slave. recieve response.
String response = requestRecieve(selfTestCommand, module);
// Interpret the response
if (response.startsWith("<success")) {
return 0; // 0 for successful operation
} else if (response.startsWith("<fail")) {
return 1; // 1 for failed operation
} else {
// If the response is not as expected
Serial.println("Unexpected response: " + response);
return -3; // Error code for unexpected response
}
}
/*********************************************************************************************************************
Pin DAC Code
<dacset,pin,float voltage> - sets the DAC voltage level of a pin. can be 0 to 5.5V. returns either <success> or <fail>
*********************************************************************************************************************/
int pinDacSet(int pin, float voltage){
if (pin < 1 || pin > 16 || voltage < 0 || voltage > 5.5) {
Serial.println("Invalid parameters for DAC set");
return -1;
}
int moduleNumber = (pin - 1) / 4 + 1;
int pinToSend = (pin - 1) % 4 + 1;
selectModule(moduleNumber);
String dacCommand = "<dacset," + String(pinToSend) + "," + String(voltage) + ">";
Wire.beginTransmission(pinmoduleaddress);
Wire.write(dacCommand.c_str());
Wire.endTransmission();
delay(200);
if (!checkModuleResponse()) {
selectModule(0);
return -1;
}
selectModule(0);
return 0;
}
/*********************************************************************************************************************
Pin Matrix All Set Code
<matrix,set1,set2,set3,set4> - sets the matrix. returns either <success> or <fail,status1,status2,status3,status4>
Also sets the voltage level of each module.
*********************************************************************************************************************/
// Convert PinSetting to string command for I2C communication
String convertPinSettingToCommand(PinSetting setting) {
switch(setting) {
case PIN_3V3:
case PIN_5V: return "vadj";
case PIN_GND: return "gnd";
case PIN_ADC: return "adc";
case PIN_DAC: return "dac";
case PIN_DIO_INPUT:
case PIN_DIO_OUTPUT: return "dio";
case PIN_NULL: return "null";
}
return "";
}
float voltageForSetting(PinSetting setting) {
switch(setting) {
case PIN_3V3: return 3.3;
case PIN_5V: return 5.0;
default: return 0.0; // GND, ADC, DIO_INPUT, DIO_OUTPUT
}
}
int setPins(PinSetting pinConfig[16]) {
//first, set the pinmodes of the DIO pins locally
for (int i = 0; i < 16; i++) {
if (pinConfig[i] == PIN_DIO_INPUT) {
pinMode(DIO_PINS[i], INPUT);
}
else if (pinConfig[i] == PIN_DIO_OUTPUT) {
pinMode(DIO_PINS[i], OUTPUT);
}
}
//Next, set the matrix switches and power supply voltages
String matrixCommands[4] = {"", "", "", ""};
float voltageSet[4] = {0.0, 0.0, 0.0, 0.0}; // Store the voltage for each module
// Generate matrix commands and check for voltage conflicts
for (int i = 0; i < 16; i++) {
int moduleIndex = i / 4;
matrixCommands[moduleIndex] += convertPinSettingToCommand(pinConfig[i]);
if (i % 4 != 3) matrixCommands[moduleIndex] += ",";
float pinVoltage = voltageForSetting(pinConfig[i]);
if (pinVoltage > 0) {
if (voltageSet[moduleIndex] == 0) {
voltageSet[moduleIndex] = pinVoltage;
} else if (voltageSet[moduleIndex] != pinVoltage) {
Serial.println("Voltage conflict detected");
return -1; // Error due to voltage conflict
}
}
}
bool errorFlag = false;
for (int module = 0; module < 4; module++) {
// Set the matrix
String matrixCommand = "<matrix," + matrixCommands[module] + ">";
selectModule(module + 1);
Wire.beginTransmission(pinmoduleaddress);
Wire.write(matrixCommand.c_str());
Wire.endTransmission();
if (!checkModuleResponse()) {
Serial.print("Bad response from module matrix: ");
Serial.println(module + 1);
errorFlag = true;
break;
}
// Send the powerset command only if voltage is greater than 0
if (voltageSet[module] > 0) {
String powerCommand = "<powerset," + String(voltageSet[module]) + ">";
Wire.beginTransmission(pinmoduleaddress);
Wire.write(powerCommand.c_str());
Wire.endTransmission();
if (!checkModuleResponse()) {
Serial.print("Bad response from module power: ");
Serial.print(module + 1);
errorFlag = true;
break;
}
}
}
selectModule(0); // Deselect all modules
// Set pin modes for DIO pins
for (int i = 0; i < 16; i++) {
if (pinConfig[i] == PIN_DIO_INPUT) {
pinMode(DIO_PINS[i], INPUT);
} else if (pinConfig[i] == PIN_DIO_OUTPUT) {
pinMode(DIO_PINS[i], OUTPUT);
}
}
return errorFlag ? -1 : 0;
}
/*********************************************************************************************************************
Pin Matrix Single Set Code
<matrixscalpel,pin,seto> - sets a single channel of the matrix. returns either <success> or <fail>
Sets the matrix setting, but not the voltage. vadj not PIN_3V3 ETC.
Voltage must be set seperately.
*********************************************************************************************************************/
int setPinScalpel(int pin, String seto) {
if (pin < 1 || pin > 16) {
Serial.println("Invalid pin number");
return -1;
}
int module = (pin - 1) / 4 + 1;
int pinToSend = (pin - 1) % 4 + 1;
// Create the pin scalpel command
String matrixScalpelCommand = "<matrixscalpel," + String(pinToSend) + "," + seto + ">";
//transmit command to slave. recieve response.
String response = requestRecieve(matrixScalpelCommand, module);
return requestRecieveCheckResponse(response) ? 0 : -1;
}
/*********************************************************************************************************************
Power supply set voltage function
Calling this function turns the supply OFF. Must be turned back on seperatly using toggleModulePower.
<powerset,float voltage> - sets the module voltage. also turns off voltage. returns either <success,difference> or <fail>
*********************************************************************************************************************/
int setModulePower (float voltage, int module){
if (module < 1 || module > 4 || voltage < 0 || voltage > 5.5) {
Serial.println("Invalid parameters for setModulePower");
return -1;
}
// Create the power set command
String powerSetCommand = "<powerset," + String(voltage) + ">";
//transmit command to slave. recieve response.
String response = requestRecieve(powerSetCommand, module);
return requestRecieveCheckResponse(response) ? 0 : -1;
}
/*********************************************************************************************************************
Power supply toggle function
<powertoggle,bool> - toggles power on or off. returns either <success> or <fail>
*********************************************************************************************************************/
int toggleModulePower(bool state, int module) {
bool errorFlag = false;
if (module < 1 || module > 4) {
Serial.println("Invalid parameters for toggleModulePower");
return -1;
}
// Create the toggle power command
String powerToggleCommand = "<powertoggle," + String(state ? "true" : "false") + ">";
//transmit command to slave. recieve response.
String response = requestRecieve(powerToggleCommand, module);
return requestRecieveCheckResponse(response) ? 0 : -1;
}
/*********************************************************************************************************************
Power supply reset circuit breaker function
Calling this function resets the circuit breaker of the selected module. the MIC2045 automatically "resets" aswell, however this can be usefull for capacitive loads.
<resetbreaker> - resets the MIC2045 circuit breaker. returns either <success> or <fail>
*********************************************************************************************************************/
int resetModuleBreaker (int module){
if (module < 1 || module > 4) {
Serial.println("Invalid module number");
return -1;
}
selectModule(module);
Wire.beginTransmission(pinmoduleaddress);
Wire.write("<resetbreaker>");
Wire.endTransmission();
delay(200); // Adjust delay as necessary
return checkModuleResponse() ? 0 : -1;
}
/*********************************************************************************************************************
Module IDN function
<*idn?> - module identification. returns < firmare_version, hardware_version>
*********************************************************************************************************************/
String queryModuleIdn (int module){
if (module < 1 || module > 4) {
Serial.println("Invalid module number");
return "Invalid module";
}
selectModule(module);
Wire.beginTransmission(pinmoduleaddress);
Wire.write("*idn?");
Wire.endTransmission();
//delay(200); // Adjust delay as necessary
Wire.requestFrom(pinmoduleaddress, 32);
String response = "";
while (Wire.available()) {
char c = Wire.read();
response += c;
if (c == '\n') break;
}
response.trim();
selectModule(0);
return response.startsWith("<") ? response : "Invalid response";
}
/*********************************************************************************************************************
Response from slave verification function
*********************************************************************************************************************/
bool checkModuleResponse() {
Wire.requestFrom(pinmoduleaddress, 32);
String response = "";
while (Wire.available()) {
char c = Wire.read();
response += c;
if (c == '\n') break;
}
response.trim();
if (response.length() > 0 && response.startsWith("<") && response.endsWith(">")) {
if (response.startsWith("<success")) {
selectModule(0);
return true;
} else {
selectModule(0);
return false;
}
} else {
selectModule(0);
return false;
}
}
/*********************************************************************************************************************
requestrecieve function
*********************************************************************************************************************/
String requestRecieve(String message, int module){
// Select the appropriate module
selectModule(module);
delay(10);
// Send self-test command to the module
Wire.beginTransmission(pinmoduleaddress);
Wire.write(message.c_str(), message.length());
Wire.endTransmission();
// Wait for the module to process the command
delay(500); // Adjust this delay based on the expected processing time
// Check for response from the module
Wire.requestFrom(pinmoduleaddress, 32, true); // Assuming 32 is the maximum length of response
String response = "";
while (Wire.available()) {
char c = Wire.read();
response += c;
if (c == '\n') break; // End of response
}
// Wait for the module to process the command
//delay(200); // Adjust this delay based on the expected processing time
response.trim(); // Remove any extraneous whitespace
// Deselect all modules
selectModule(0);
return response;
}
//basic function to check if module responded correctly
bool requestRecieveCheckResponse(String response){
response.trim();
if (response.length() > 0 && response.startsWith("<") && response.endsWith(">")) {
if (response.startsWith("<success")) {
return true;
} else {
return false;
}
} else {
return false;
}
}