Float arguments not accepted in Void function

Hello - I feel guilty for abusing this forum but I have a question stemming from a previous issue I had much help with (thank you johnwasser) but have become kind of stuck again.

I am programming something to be used in conjunction with a touchscreen that utilizes both cores of an ESP32 (one side for interpreting whatever comes through the screen and the other for executing commands made), and as such I need void functions for their respective pages so they can be selected and run when needed.

In my previous plea for help, an admin here graciously went and coded the whole thing - though I had no idea the structure he chose to use (arguments within floats) would present issues when trying to stuff everything into a need little function to be added into the larger project file.

Here's the entire code in its current state:

int calcInt = 4;
const float Fills[] = {0.62, 0.72, 0.785, 0.907, 1.00};

float fill_a = 100; //62, 72, 78.5, 90.7, 100
float fill = fill_a / 100;

const float inchesPerMeter = 1000 / 25.4 ;
const float INCHES_PER_1000FT = 1000 * 12 ;
const float COPPER_RESISTIVITY = 1.724e-8 ;

//  const char *name;
  float L = 1.315;
  float W = .1875;
  float H = .340;
  float flange = .975;
  int AWG = 42;
  int Ohms = 5250;
  int windings = 10000;
  
struct Wire
{
  int AWG;
  float bare;
  float singleInsulated;
  float dooubleInsulated;
} const WireTable[] =
{
  {24, 0.0201, 0.0213, 0.0233},
  {25, 0.0179, 0.0190, 0.0199},
  {26, 0.0159, 0.0170, 0.0178},
  {27, 0.0142, 0.0153, 0.0161},
  {28, 0.0126, 0.0137, 0.0144},
  {29, 0.0113, 0.0123, 0.0130},
  {30, 0.0100, 0.0109, 0.0116},
  {31, 0.0089, 0.0097, 0.0105},
  {32, 0.0080, 0.0088, 0.0095},
  {33, 0.0071, 0.0078, 0.0084},
  {34, 0.0063, 0.0069, 0.0075},
  {35, 0.0056, 0.0062, 0.0067},
  {36, 0.0050, 0.0056, 0.0060},
  {37, 0.0045, 0.0050, 0.0055},
  {38, 0.0040, 0.0045, 0.0049},
  {39, 0.0035, 0.0039, 0.0043},
  {40, 0.0031, 0.0035, 0.0038},
  {41, 0.0028, 0.0031, 0.0034},
  {42, 0.0025, 0.0028, 0.0030},
  {43, 0.0022, 0.0025, 0.0027},
  {44, 0.0020, 0.0022, 0.0025},
  {45, 0.00176, 0.00192, 0.00215},
  {46, 0.00157, 0.00173, 0.00196},
  {47, 0.00140, 0.00158, 0.00178},
  {48, 0.00124, 0.00140, 0.00155},
  {49, 0.00111, 0.00124, 0.00139},
  {50, 0.00099, 0.00113, 0.00128},
  {51, 0.00088, 0.00103, 0.00117},
  {52, 0.00078, 0.00093, 0.00107},
  {53, 0.00070, 0.00079, 0.00090},
  {54, 0.00062, 0.00070, 0.00082},
  {55, 0.00055, 0.00064, 0.00075},
};



float calcMaxLayers(float wireDia)
{
  return ((flange - W) / 2.0) / wireDia;
}

float circularArea(float diameter)
{
  float radius = (diameter * Fills[calcInt]) / 2.0 ;
  return radius * radius * PI;
}

float Rin (float dia)
{
  return COPPER_RESISTIVITY / (12.0 * circularArea(dia));
}

float R1000ft(float dia)
{
  return (COPPER_RESISTIVITY * inchesPerMeter * INCHES_PER_1000FT) / circularArea(dia);
}


float feetLengthFromResistance(float totalResistance, float bareDiameter)
{

  float resistancePerThousandFeet = R1000ft(bareDiameter);
  return (totalResistance / resistancePerThousandFeet) * 1000.0;
}

float quadraticRoot(float a, float b, float c)
{
  float disc = b * b - 4 * a * c;

  int sgnb = (b > 0 ? 1 : -1);
  float temp = -0.5 * (b + sgnb * sqrt(disc));

  float r1 = temp / a ;
  float r2 = c / temp ;

  return r1 < 0 ? r2 : r1;
}

float layersFromLength(float wireLengthFeet, float insulatedDiameter)
{
  float lenInches = wireLengthFeet * 12.0;
  float insulatedDiameter2 = insulatedDiameter / Fills[calcInt];
  
  float a = PI * insulatedDiameter2;
  float b = (2.0 * L) + ((PI - 2.0) * W) + (PI * (insulatedDiameter2));
  float c = -((lenInches * insulatedDiameter2) / H);

  return quadraticRoot(a, b, c);
}


void calcPrint() {

/*
  float L = 1.315;
  float W = .1875;
  float H = .340;
  float flange = .975;
  int AWG = 42;
  int Ohms = 5250;
  int windings = 10000;
*/

    Serial.println("-----------------------");
    Serial.println("-----Bobbin Specs------");
    Serial.print("Length: ");
    Serial.print(L);
    Serial.print(" | ");
    Serial.print("Width: ");
    Serial.print(W);
    Serial.print(" | ");
    Serial.print("Height: ");
    Serial.print(H);
    Serial.print(" | ");
    Serial.print("Flange: ");
    Serial.print(flange);
    Serial.println();
    Serial.println("-----------------------");
    
    Wire w = WireTable[AWG - 24];

    Serial.print("AWG:\t");
    Serial.println(w.AWG);

    Serial.print("Bare Wire Diameter:\t");
    Serial.println(w.bare, 5);

    Serial.print("Insulated Diameter:\t");
    Serial.println(w.singleInsulated, 5);

    Serial.print("Ohms:\t");
    Serial.println(Ohms);


    Serial.print("Ohms/1000ft:\t");
    Serial.println(R1000ft(w.singleInsulated));

      float wireFeet = feetLengthFromResistance(Ohms, w.bare);
      Serial.print("Wire feet:\t");
      Serial.println(wireFeet);
      
  for (int i = 0; i < 5; i++) {
  calcInt = i;
//    Serial.print("\n\nBobbin Name:\t");
//    Serial.println(b.name);

    Serial.println("-----------------------");

    Serial.print("Fill:\t");
    Serial.print(Fills[calcInt] * 100);
    Serial.println("%");
    

/*
    Serial.print("Windings(nom.):\t");
    Serial.println(b.windings);
*/
    


    float layers = layersFromLength(wireFeet,  w.singleInsulated);
    Serial.print("Layers:\t");
    Serial.println(layers);
    
    float windingsPerLayer = ((H / w.singleInsulated) * Fills[calcInt]);
    Serial.print("Windings per Layer:\t");
    Serial.println(windingsPerLayer);

    float windings = layers * windingsPerLayer;
    Serial.print("Windings:\t");
    Serial.println(windings);

    float maxLayers = (calcMaxLayers(w.singleInsulated) * Fills[calcInt]);
    Serial.print("MaxLayers:\t");
    Serial.println(maxLayers);

    if (layers > maxLayers)
    {
      Serial.println("Warning: Maximum layers exceeded.");
    }
    float maxWinds = maxLayers * windingsPerLayer;
    Serial.print("maxWinds:\t");
    Serial.println(maxWinds);

  }; //print loop
};


void setup()
{
  Serial.begin(115200);
  delay(200);
  calcPrint();
};

void loop() {}

Since I need to execute bits and pieces as needed, I tried stuffing all those floats with arguments (ex: float thing1(float num2, float num3) {exciting math things}) into a void function of its own to be referenced by another, and that one to be called upon from the other core when a touchscreen action is made. Arduino did not like this when compiling.

I tried going back and breaking everything into longer mathematical calculations that used floats without arguments, but the math came out wrong...

Anyway, I suppose my question is - can anyone see a better way to approach this? Is avoiding the float arguments necessary or is there another function type that can be called that will handle them?

Could you maybe stop hand-waving, and provide detail, like actual error messages?

I wasn't waving my hand at you particularly, don't get too excited Shannon. When compiling a void containing a float with arguments in it, like the example seen in the previous message, you receive this error:











/Users/jtb/Downloads/CG3-Nex-Backup/CG3-Nex-Backup.ino: In function 'void thing()':
CG3-Nex-Backup:68:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:73:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:79:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:84:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:90:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:97:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:110:1: error: a function-definition is not allowed here before '{' token
 {
 ^
/Users/jtb/Downloads/CG3-Nex-Backup/CG3-Nex-Backup.ino: In function 'void calcPrint()':
CG3-Nex-Backup:166:45: error: 'R1000ft' was not declared in this scope
     Serial.println(R1000ft(w.singleInsulated));
                                             ^
CG3-Nex-Backup:168:61: error: 'feetLengthFromResistance' was not declared in this scope
       float wireFeet = feetLengthFromResistance(Ohms, w.bare);
                                                             ^
CG3-Nex-Backup:191:65: error: 'layersFromLength' was not declared in this scope
     float layers = layersFromLength(wireFeet,  w.singleInsulated);
                                                                 ^
CG3-Nex-Backup:203:55: error: 'calcMaxLayers' was not declared in this scope
     float maxLayers = (calcMaxLayers(w.singleInsulated) * Fills[calcInt]);
                                                       ^
exit status 1
a function-definition is not allowed here before '{' token

Shannon is a member category, not a user name. :slight_smile:

1 Like

Now here's some useful information :sunglasses:

How about showing us the actual code that you tried, then? The code that produced the error message you posted? There is no function "thing()" in what you posted.

OK I'll just dump what I've come up with to try to skirt the issue:

int calcInt = 4;
const float Fills[] = {0.62, 0.72, 0.785, 0.907, 1.00};

float fill_a = 100; //62, 72, 78.5, 90.7, 100
float fill = fill_a / 100;

const float inchesPerMeter = 1000 / 25.4 ;
const float INCHES_PER_1000FT = 1000 * 12 ;
const float COPPER_RESISTIVITY = 1.724e-8 ;

//  const char *name;
  float L = 1.315;
  float W = .1875;
  float H = .340;
  float flange = .975;
  int AWG = 42;
  int Ohms = 5250;
  int windings = 10000;
  
struct Wire
{
  int AWG;
  float bare;
  float singleInsulated;
  float dooubleInsulated;
} const WireTable[] =
{
  {24, 0.0201, 0.0213, 0.0233},
  {25, 0.0179, 0.0190, 0.0199},
  {26, 0.0159, 0.0170, 0.0178},
  {27, 0.0142, 0.0153, 0.0161},
  {28, 0.0126, 0.0137, 0.0144},
  {29, 0.0113, 0.0123, 0.0130},
  {30, 0.0100, 0.0109, 0.0116},
  {31, 0.0089, 0.0097, 0.0105},
  {32, 0.0080, 0.0088, 0.0095},
  {33, 0.0071, 0.0078, 0.0084},
  {34, 0.0063, 0.0069, 0.0075},
  {35, 0.0056, 0.0062, 0.0067},
  {36, 0.0050, 0.0056, 0.0060},
  {37, 0.0045, 0.0050, 0.0055},
  {38, 0.0040, 0.0045, 0.0049},
  {39, 0.0035, 0.0039, 0.0043},
  {40, 0.0031, 0.0035, 0.0038},
  {41, 0.0028, 0.0031, 0.0034},
  {42, 0.0025, 0.0028, 0.0030},
  {43, 0.0022, 0.0025, 0.0027},
  {44, 0.0020, 0.0022, 0.0025},
  {45, 0.00176, 0.00192, 0.00215},
  {46, 0.00157, 0.00173, 0.00196},
  {47, 0.00140, 0.00158, 0.00178},
  {48, 0.00124, 0.00140, 0.00155},
  {49, 0.00111, 0.00124, 0.00139},
  {50, 0.00099, 0.00113, 0.00128},
  {51, 0.00088, 0.00103, 0.00117},
  {52, 0.00078, 0.00093, 0.00107},
  {53, 0.00070, 0.00079, 0.00090},
  {54, 0.00062, 0.00070, 0.00082},
  {55, 0.00055, 0.00064, 0.00075},
};

/* calcMath Globals */

float R1000ft;
float R1000ftB;
float wireFeet;
float layersFromLength;
float maxLayers;

void calcMath() {

  Wire w = WireTable[AWG - 24];
/* Prints: Max Layers */
/* calcMaxLayers */


float wireDia = w.singleInsulated; //preCalc choice - correct this to contain diameter choice

/* --- */

maxLayers = (((flange - W) / 2.0) / wireDia) * Fills[calcInt];





/* Prints: Ohms/1000ft / Wire Feet */
/* feetLengthFromResistance -> R1000ft -> Rin -> circularArea */


/* calcs from wireDia and w.bare */

/* --- */

float radius = (wireDia * Fills[calcInt]) / 2.0;
float circularArea = radius * radius * PI;
float Rin = COPPER_RESISTIVITY / (12.0 * circularArea);
R1000ft = (COPPER_RESISTIVITY * inchesPerMeter * INCHES_PER_1000FT) / circularArea;
float radiusB = (w.bare * Fills[calcInt]) / 2.0;
float circularAreaB = radiusB * radiusB * PI;
float RinB = COPPER_RESISTIVITY / (12.0 * circularAreaB);
R1000ftB = (COPPER_RESISTIVITY * inchesPerMeter * INCHES_PER_1000FT) / circularAreaB;

wireFeet = (Ohms / R1000ft) * 1000.0;


/* Prints: Layers */
/* layersFromLength -> quadraticRoot */

/* calcs from wireFeet and wireDia */

  float lenInches = wireFeet * 12.0;
  float insulatedDiameter = wireDia / Fills[calcInt];
  float a = PI * insulatedDiameter;
  float b = (2.0 * L) + ((PI - 2.0) * W) + (PI * (insulatedDiameter));
  float c = -((lenInches * insulatedDiameter) / H);
  float disc = b * b - 4 * a * c;
  int sgnb = (b > 0 ? 1 : -1);
  float temp = -0.5 * (b + sgnb * sqrt(disc));
  float r1 = temp / a ;
  float r2 = c / temp ;

  layersFromLength = r1 < 0 ? r2 : r1;

}; //calcMath()

void calcPrint() {

/*
  float L = 1.315;
  float W = .1875;
  float H = .340;
  float flange = .975;
  int AWG = 42;
  int Ohms = 5250;
  int windings = 10000;
*/
  calcMath();

    Serial.println("-----------------------");
    Serial.println("-----Bobbin Specs------");
    Serial.print("Length: ");
    Serial.print(L);
    Serial.print(" | ");
    Serial.print("Width: ");
    Serial.print(W);
    Serial.print(" | ");
    Serial.print("Height: ");
    Serial.print(H);
    Serial.print(" | ");
    Serial.print("Flange: ");
    Serial.print(flange);
    Serial.println();
    Serial.println("-----------------------");
    
    Wire w = WireTable[AWG - 24];

    Serial.print("AWG:\t");
    Serial.println(w.AWG);

    Serial.print("Bare Wire Diameter:\t");
    Serial.println(w.bare, 5);

    Serial.print("Insulated Diameter:\t");
    Serial.println(w.singleInsulated, 5);

    Serial.print("Ohms:\t");
    Serial.println(Ohms);


    Serial.print("Ohms/1000ft:\t");
    Serial.println(R1000ft); //could be R1000ftB

      Serial.print("Wire feet:\t");
      Serial.println(wireFeet);
      
  for (int i = 0; i < 5; i++) {
  calcInt = i;
//    Serial.print("\n\nBobbin Name:\t");
//    Serial.println(b.name);

    Serial.println("-----------------------");

    Serial.print("Fill:\t");
    Serial.print(Fills[calcInt] * 100);
    Serial.println("%");
    

    Serial.print("Layers:\t");
    Serial.println(layersFromLength);
    
    float windingsPerLayer = ((H / w.singleInsulated) * Fills[calcInt]);
    Serial.print("Windings per Layer:\t");
    Serial.println(windingsPerLayer);

    float windings = layersFromLength * windingsPerLayer;
    Serial.print("Windings:\t");
    Serial.println(windings);

    Serial.print("MaxLayers:\t");
    Serial.println(maxLayers);

    if (layersFromLength > maxLayers)
    {
      Serial.println("Warning: Maximum layers exceeded.");
    }
    float maxWinds = maxLayers * windingsPerLayer;
    Serial.print("maxWinds:\t");
    Serial.println(maxWinds);

  }; //print loop
}; //calcMath()


void setup()
{
  Serial.begin(115200);
  delay(200);
  calcPrint();
};

void loop() {}

This isn't how I planned to leave it looking, but more of an attempt to just get the float arguments out and get the math working. Unfortunately my numbers didn't come up right...I could show you the serial printout results too if you want to see those, but that'd be more about tracking down the errors in the equations I cobbled together.

Software issues don't skirt easily. :slight_smile: What is the issue, anyway? The best we have so far is,
" issues when trying to stuff everything into a need little function to be added into the larger project file"

Please post the actual code and the actual error messages, or erroneous printout.

You clearly have mis-matched {} somewhere. The errors you posted:

  1. Did NOT come from the code you posted
  2. Have NOTHING to do with floats. They come from trying to define a function when WITHIN another function - a dead give-away that your curly braces are not properly matched.

I did post the actual error message that occurred when trying to stuff these floats with arguments into a void before, I'll quote it on the bottom of this post.

quote="jtbennett, post:3, topic:905159"]

/Users/jtb/Downloads/CG3-Nex-Backup/CG3-Nex-Backup.ino: In function 'void thing()':
CG3-Nex-Backup:68:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:73:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:79:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:84:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:90:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:97:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:110:1: error: a function-definition is not allowed here before '{' token
 {
 ^
/Users/jtb/Downloads/CG3-Nex-Backup/CG3-Nex-Backup.ino: In function 'void calcPrint()':
CG3-Nex-Backup:166:45: error: 'R1000ft' was not declared in this scope
     Serial.println(R1000ft(w.singleInsulated));
                                             ^
CG3-Nex-Backup:168:61: error: 'feetLengthFromResistance' was not declared in this scope
       float wireFeet = feetLengthFromResistance(Ohms, w.bare);
                                                             ^
CG3-Nex-Backup:191:65: error: 'layersFromLength' was not declared in this scope
     float layers = layersFromLength(wireFeet,  w.singleInsulated);
                                                                 ^
CG3-Nex-Backup:203:55: error: 'calcMaxLayers' was not declared in this scope
     float maxLayers = (calcMaxLayers(w.singleInsulated) * Fills[calcInt]);
                                                       ^
exit status 1
a function-definition is not allowed here before '{' token

[/quote]

Yes, but we can't see the code...
...as I already pointed out.

@RayLivingston - the error is when trying to wrap the floats with arguments in a void, like this:

void EXAMPLE(){
float calcMaxLayers(float wireDia)
{
  return ((flange - W) / 2.0) / wireDia;
}

float circularArea(float diameter)
{
  float radius = (diameter * Fills[calcInt]) / 2.0 ;
  return radius * radius * PI;
}

float Rin (float dia)
{
  return COPPER_RESISTIVITY / (12.0 * circularArea(dia));
}

float R1000ft(float dia)
{
  return (COPPER_RESISTIVITY * inchesPerMeter * INCHES_PER_1000FT) / circularArea(dia);
}


float feetLengthFromResistance(float totalResistance, float bareDiameter)
{

  float resistancePerThousandFeet = R1000ft(bareDiameter);
  return (totalResistance / resistancePerThousandFeet) * 1000.0;
}

float quadraticRoot(float a, float b, float c)
{
  float disc = b * b - 4 * a * c;

  int sgnb = (b > 0 ? 1 : -1);
  float temp = -0.5 * (b + sgnb * sqrt(disc));

  float r1 = temp / a ;
  float r2 = c / temp ;

  return r1 < 0 ? r2 : r1;
}

float layersFromLength(float wireLengthFeet, float insulatedDiameter)
{
  float lenInches = wireLengthFeet * 12.0;
  float insulatedDiameter2 = insulatedDiameter / Fills[calcInt];
  
  float a = PI * insulatedDiameter2;
  float b = (2.0 * L) + ((PI - 2.0) * W) + (PI * (insulatedDiameter2));
  float c = -((lenInches * insulatedDiameter2) / H);

  return quadraticRoot(a, b, c);
}
};

Error looks like this:

/Users/jtb/Downloads/CG3-Nex-Backup/CG3-Nex-Backup.ino: In function 'void EXAMPLE()':
CG3-Nex-Backup:66:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:71:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:77:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:82:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:88:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:95:1: error: a function-definition is not allowed here before '{' token
 {
 ^
CG3-Nex-Backup:107:70: error: a function-definition is not allowed here before '{' token
 float layersFromLength(float wireLengthFeet, float insulatedDiameter){
                                                                      ^
/Users/jtb/Downloads/CG3-Nex-Backup/CG3-Nex-Backup.ino: In function 'void calcPrint()':
CG3-Nex-Backup:163:45: error: 'R1000ft' was not declared in this scope
     Serial.println(R1000ft(w.singleInsulated));
                                             ^
CG3-Nex-Backup:165:61: error: 'feetLengthFromResistance' was not declared in this scope
       float wireFeet = feetLengthFromResistance(Ohms, w.bare);
                                                             ^
CG3-Nex-Backup:188:65: error: 'layersFromLength' was not declared in this scope
     float layers = layersFromLength(wireFeet,  w.singleInsulated);
                                                                 ^
CG3-Nex-Backup:200:55: error: 'calcMaxLayers' was not declared in this scope
     float maxLayers = (calcMaxLayers(w.singleInsulated) * Fills[calcInt]);
                                                       ^
exit status 1
a function-definition is not allowed here before '{' token

Oh, I see. Yeah, you are not allowed to define functions inside of functions. But I can't really understand what you are trying to accomplish. It makes no sense - for example, if you called EXAMPLE(), what would happen to all the return values from the nested functions? Assuming that for some strange reason, this was allowed by the language...

Your best bet is to explain in detail, what you want to happen. But I see that is not your "strong suit".

What you have there, is not only invalid in the language, but it's semantically ambiguous (has no definite meaning).

Well the issue arises when I'm trying to take into account that defined variables that all these functions are using, such as:

  float L = 1.315;
  float W = .1875;
  float H = .340;
  float flange = .975;
  int AWG = 42;
  int Ohms = 5250;
  int windings = 10000;

are going to be entered and changed manually by the user, so I need to run this stuff on command. It's hard to explain because there's stuff that's not in the code here that I'm taking into account, so I was hoping to distill it without piling on a massive amount of stuff for you to go through.

I'll come back to this tomorrow, maybe I just need to spend some more time with it.

I think you have a function definition and a function call mixed up.

Function calls can be nested - otherwise you couldn't have any functions in loop() or setup(). But not definitions.

1 Like

Multiple all of those numbers by 1000 and eliminate floats. Want more resolution increase by factors of 10. You do not need to use floats with an Arduino for the vast majority of cases and should try to avoid them as the operations can be hundreds of times slower.
Also @aarg is correct in that you have a function definition error in your code that must be corrected.

Fixed

Use those little "f" to make sure the number is a float.

I do not see any multi core programming or tasking code.

The ESP32 does floats real well.

An example of a multi-core task on an ESP32.

#include <WiFi.h>
#include <PubSubClient.h>
#include "certs.h"
#include "sdkconfig.h"
#include "esp32/ulp.h"
#include "driver/rtc_io.h"
#include "esp_system.h" //This inclusion configures the peripherals in the ESP system.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"
#include <Adafruit_NeoPixel.h>
#include "AudioAnalyzer.h"
////
/* define event group and event bits */
EventGroupHandle_t eg;
#define evtDo_AudioReadFreq       ( 1 << 0 ) // 1
////
QueueHandle_t xQ_LED_Info;
////
//const int LED_COUNT = 24; //total number of leds in the strip
const int LED_COUNT = 108; //total number of leds in the strip
////
// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
Adafruit_NeoPixel leds = Adafruit_NeoPixel( LED_COUNT, 26, NEO_GRB + NEO_KHZ800 );
//
WiFiClient wifiClient;
PubSubClient MQTTclient(mqtt_server, mqtt_port, wifiClient);
////
SemaphoreHandle_t sema_MQTT_KeepAlive;
////
void ULP_BLINK_RUN(uint32_t us);
////
void setup()
{
  ULP_BLINK_RUN(100000);
  eg = xEventGroupCreate();
  ////
  int FreqVal[7]; // used to set queue size
  xQ_LED_Info = xQueueCreate ( 1, sizeof(FreqVal) );
  ////
  sema_MQTT_KeepAlive = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_MQTT_KeepAlive );
  ////
  // setting must be set before a mqtt connection is made
  MQTTclient.setKeepAlive( 90 ); // setting keep alive to 90 seconds
  connectToWiFi();
  connectToMQTT();
  //////////////////////////////////////////////////////////////////////////////////////////////
  xTaskCreatePinnedToCore( MQTTkeepalive, "MQTTkeepalive", 7000, NULL, 5, NULL, 1 );
  xTaskCreatePinnedToCore( fDo_AudioReadFreq, "fDo_ AudioReadFreq", 30000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fDo_LEDs, "fDo_ LEDs", 30000, NULL, 3, NULL, 1 );

  xEventGroupSetBits( eg, evtDo_AudioReadFreq );
} // setup()
////
void loop() {} // void loop
////
void fMQTT_Disconnect(  )
{
  MQTTclient.disconnect();
}
////
void GetTheTime()
{
  char* ntpServer = "2.us.pool.ntp.org";
  int gmtOffset_sec = -(3600 * 7 );
  int daylightOffset_sec = 3600;
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  printLocalTime();
}
////
// http://www.cplusplus.com/reference/ctime/strftime/
////
void MQTTkeepalive( void *pvParameters )
{
  int keepCount = 0;
  for (;;)
  {
    log_i( " mqtt keep alive run." );

    if ( (wifiClient.connected()) && (WiFi.status() == WL_CONNECTED) )
    {
      //      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY ); //
      MQTTclient.loop();
      //      xSemaphoreGive( sema_MQTT_KeepAlive );
    }
    else {
      log_i( "MQTT keep alive found MQTT status %s WiFi status %s", String(wifiClient.connected()), String(WiFi.status()) );
      if ( !(WiFi.status() == WL_CONNECTED) )
      {
        connectToWiFi();
      }
      connectToMQTT();
    }
    vTaskDelay( 4900 );
    keepCount++;
    log_i( "%d", keepCount );
    if ( keepCount >= 5 )
    {
      fMQTT_Disconnect();
      keepCount = 0;
    }
  }
  vTaskDelete ( NULL );
}
////
int getHour()
{
  struct tm timeinfo;
  getLocalTime(&timeinfo);
  char _time[ 5 ];
  strftime( _time, 80, "%T", &timeinfo );
  return String(_time).toInt();
}
////
void printLocalTime() {
  struct tm timeinfo;
  getLocalTime(&timeinfo);
  char _time[ 80 ];
  strftime( _time, 80, "%T", &timeinfo );
  log_i( "%s", _time);
  //  }
}
////
void connectToMQTT()
{
  log_i( "connect to mqtt" );
  while ( !MQTTclient.connected() )
  {
    MQTTclient.connect( clientID, mqtt_username, mqtt_password );
    log_i( "connecting to MQTT" );
    vTaskDelay( 250 );
  }
  log_i("MQTT Connected");
  MQTTclient.setCallback( mqttCallback );
  MQTTclient.subscribe( mqtt_topic );
}
////
void connectToWiFi()
{
  log_i( " Begin Connect to wifi" );
  while ( WiFi.status() != WL_CONNECTED )
  {
    WiFi.disconnect();
    WiFi.begin( SSID, PASSWORD );
    log_i(" waiting on wifi connection" );
    vTaskDelay( 4000 );
  }
  log_i( "End connected to WiFi" );
  WiFi.onEvent( WiFiEvent );
  GetTheTime();
}
////
static void mqttCallback(char* topic, byte * payload, unsigned int length)
{

  //xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY);
  //str_eTopicPtr = topic + '\0';

  int i = 0;
  String temp = "";
  for ( i; i < length; i++) {
    temp += ((char)payload[i]);
  }
  log_i( " topic %s payload %s", topic, temp );
  //  strPayloadPtr[i] = '\0';
  // xSemaphoreGive ( sema_MQTT_KeepAlive );
  //
} // void mqttCallback(char* topic, byte* payload, unsigned int length)
////
void fDo_LEDs( void *pvParameters )
{
  const int Brightness = 200;
  const int SEG = 6; // how many parts you want to separate the led strip into
  const int ledCount = LED_COUNT; //total number of leds in the strip
  int iFreqVal[7];
  int j;
  leds.begin(); // Call this to start up the LED strip.
  clearLEDs( ledCount );  // This function, defined below, de-energizes all LEDs...
  leds.show();  // ...but the LEDs don't actually update until you call this.
  leds.setBrightness( Brightness ); //  1 = min brightness (off), 255 = max brightness.
  for (;;)
  {
    if (xQueueReceive( xQ_LED_Info, &iFreqVal,  portMAX_DELAY) == pdTRUE)
    {
      j = 0;
      //assign different values for different parts of the led strip
      for (j = 0; j < ledCount; j++)
      {
        if ( (0 <= j) && (j < (ledCount / SEG)) )
        {
          //log_i( "segment 0 %d", iFreqVal[0] );
          set( j, iFreqVal[0], ledCount, SEG ); // set the color of led
        }
        else if ( ((ledCount / SEG) <= j) && (j < (ledCount / SEG * 2)) )
        {
          set( j, iFreqVal[0], ledCount, SEG );
        }
        else if ( ((ledCount / SEG * 2) <= j) && (j < (ledCount / SEG * 3)) )
        {
          set( j, iFreqVal[0], ledCount, SEG );
        }
        else if ( ((ledCount / SEG * 3) <= j) && (j < (ledCount / SEG * 4)) )
        {
          set( j, iFreqVal[0], ledCount, SEG );
        }
        else if ( ((ledCount / SEG * 4) <= j) && (j < (ledCount / SEG * 5)) )
        {
          set( j, iFreqVal[0], ledCount, SEG );
        }
        else
        {
          set( j, iFreqVal[0], ledCount, SEG );
        }
      }
      leds.show();
    }
    xEventGroupSetBits( eg, evtDo_AudioReadFreq );
  }
  vTaskDelete( NULL );
} // void fDo_ LEDs( void *pvParameters )
////
void fDo_AudioReadFreq( void *pvParameters )
{
  int FreqVal[7];
  const int NOISE = 10; // noise that you want to chop off
  const int A_D_ConversionBits = 4096; // arduino use 1024, ESP32 use 4096
  Analyzer Audio = Analyzer( 5, 15, 36 );//Strobe pin ->15  RST pin ->2 Analog Pin ->36
  Audio.Init(); // start the audio analyzer
  int64_t EndTime = esp_timer_get_time();
  int64_t StartTime = esp_timer_get_time(); //gets time in uSeconds like Arduino Micros
  for (;;)
  {
    xEventGroupWaitBits (eg, evtDo_AudioReadFreq, pdTRUE, pdTRUE, portMAX_DELAY);
    EndTime = esp_timer_get_time() - StartTime;
    // log_i( "TimeSpentOnTasks: %d", EndTime );
    Audio.ReadFreq(FreqVal);
    for (int i = 0; i < 7; i++)
    {
      FreqVal[i] = constrain( FreqVal[i], NOISE, A_D_ConversionBits );
      FreqVal[i] = map( FreqVal[i], NOISE, A_D_ConversionBits, 0, 255 );
      // log_i( "Freq %d Value: %d", i, FreqVal[i]);//used for debugging and Freq choosing
    }
    xQueueSend( xQ_LED_Info, ( void * ) &FreqVal, 0 );
    StartTime = esp_timer_get_time();
  }
  vTaskDelete( NULL );
} // fDo_ AudioReadFreq( void *pvParameters )
////
//the following function set the led color based on its position and freq value
//
void set(byte position, int value, int ledCount, int segment)
{
  int valueLowLimit = 20;
  // segment 0, red
  if ( (0 <= position) && (position < ledCount / segment) ) // segment 0 (bottom to top)
  {
    if ( value <= valueLowLimit )
    {
      leds.setPixelColor( position, 0, 0, 0 );
    }
    else
    {
      leds.setPixelColor( position, leds.Color( value , 0, 0) );
    }
  }
  else if ( (ledCount / segment <= position) && (position < ledCount / segment * 2) ) // segment 1 yellow
  {
    if ( value <= valueLowLimit )
    {
      leds.setPixelColor(position, leds.Color(0, 0, 0));
    }
    else
    {
      leds.setPixelColor(position, leds.Color( value, value, 0)); // works better to make yellow
    }
  }
  else if ( (ledCount / segment * 2 <= position) && (position < ledCount / segment * 3) ) // segment 2 pink
  {
    if ( value <= valueLowLimit )
    {
      leds.setPixelColor(position, leds.Color(0, 0, 0));
    }
    else
    {
      leds.setPixelColor(position, leds.Color( value, 0, value * .91) ); // pink
    }
  }
  else if ( (ledCount / segment * 3 <= position) && (position < ledCount / segment * 4) ) // seg 3, green
  {
    if ( value <= valueLowLimit )
    {
      leds.setPixelColor(position, leds.Color( 0, 0, 0));
    }
    else //
    {
      leds.setPixelColor( position, leds.Color( 0, value, 0) ); //
    }
  }
  else if ( (ledCount / segment * 4 <= position) && (position < ledCount / segment * 5) ) // segment 4, leds.color( R, G, B ), blue
  {
    if ( value <= valueLowLimit )
    {
      leds.setPixelColor(position, leds.Color( 0, 0, 0));
    }
    else //
    {
      leds.setPixelColor(position, leds.Color( 0, 0, value) ); // blue
    }
  }
  else // segment 5
  {
    if ( value <= valueLowLimit )
    {
      leds.setPixelColor(position, leds.Color( 0, 0, 0));
    }
    else
    {
      leds.setPixelColor( position, leds.Color( value, value * .3, 0) ); // orange
    }
  }
} // void set(byte position, int value)
////
void clearLEDs( int ledCount)
{
  for (int i = 0; i < ledCount; i++)
  {
    leds.setPixelColor(i, 0);
  }
} // void clearLEDs()
////
void WiFiEvent(WiFiEvent_t event)
{
  // log_i( "[WiFi-event] event: %d\n", event );
  switch (event)
  {
    case SYSTEM_EVENT_STA_CONNECTED:
      log_i("Connected to access point");
      break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
      log_i("Disconnected from WiFi access point");
      break;
    case SYSTEM_EVENT_AP_STADISCONNECTED:
      log_i("WiFi client disconnected");
    default: break;
  }
}
//////////////////////////////////////////////
/*
  Each I_XXX preprocessor define translates into a single 32-bit instruction. So you can count instructions to learn which memory address are used and where the free mem space starts.

  To generate branch instructions, special M_ preprocessor defines are used. M_LABEL define can be used to define a branch target.
  Implementation note: these M_ preprocessor defines will be translated into two ulp_insn_t values: one is a token value which contains label number, and the other is the actual instruction.

*/
void ULP_BLINK_RUN(uint32_t us)
{
  size_t load_addr = 0;
  RTC_SLOW_MEM[12] = 0;
  ulp_set_wakeup_period(0, us);
  const ulp_insn_t  ulp_blink[] =
  {
    I_MOVI(R3, 12),                         // #12 -> R3
    I_LD(R0, R3, 0),                        // R0 = RTC_SLOW_MEM[R3(#12)]
    M_BL(1, 1),                             // GOTO M_LABEL(1) IF R0 < 1
    I_WR_REG(RTC_GPIO_OUT_REG, 26, 27, 1),  // RTC_GPIO2 = 1
    I_SUBI(R0, R0, 1),                      // R0 = R0 - 1, R0 = 1, R0 = 0
    I_ST(R0, R3, 0),                        // RTC_SLOW_MEM[R3(#12)] = R0
    M_BX(2),                                // GOTO M_LABEL(2)
    M_LABEL(1),                             // M_LABEL(1)
    I_WR_REG(RTC_GPIO_OUT_REG, 26, 27, 0),// RTC_GPIO2 = 0
    I_ADDI(R0, R0, 1),                    // R0 = R0 + 1, R0 = 0, R0 = 1
    I_ST(R0, R3, 0),                      // RTC_SLOW_MEM[R3(#12)] = R0
    M_LABEL(2),                             // M_LABEL(2)
    I_HALT()                                // HALT COPROCESSOR
  };
  const gpio_num_t led_gpios[] =
  {
    GPIO_NUM_2,
    // GPIO_NUM_0,
    // GPIO_NUM_4
  };
  for (size_t i = 0; i < sizeof(led_gpios) / sizeof(led_gpios[0]); ++i) {
    rtc_gpio_init(led_gpios[i]);
    rtc_gpio_set_direction(led_gpios[i], RTC_GPIO_MODE_OUTPUT_ONLY);
    rtc_gpio_set_level(led_gpios[i], 0);
  }
  size_t size = sizeof(ulp_blink) / sizeof(ulp_insn_t);
  ulp_process_macros_and_load( load_addr, ulp_blink, &size);
  ulp_run( load_addr );
} // void ULP_BLINK_RUN(uint32_t us)
//////////////////////////////////////////////