portable television 3d printer
Tags: webdev, career, html, java
Published: 2023-12-25 16:08:27

#define X_STEP_PIN      54

#define X_DIR_PIN       55

#define X_ENABLE_PIN       38

#define X_MIN_PIN           3

#define X_MAX_PIN           2

#define Y_STEP_PIN         60

#define Y_DIR_PIN          61

#define Y_ENABLE_PIN       56

#define Y_MIN_PIN          14

#define Y_MAX_PIN          15

#define Z_STEP_PIN         46

#define Z_DIR_PIN          48

#define Z_ENABLE_PIN       62

#define Z_MIN_PIN          18

#define Z_MAX_PIN          19

#define E_STEP_PIN         26

#define E_DIR_PIN          28

#define E_ENABLE_PIN       24

#define Q_STEP_PIN     36

#define Q_DIR_PIN          34

#define Q_ENABLE_PIN       30

#define SDPOWER            -1

#define SDSS               53

#define LED_PIN            13

#define FAN_PIN         9

#define PS_ON_PIN          12

#define KILL_PIN           -1

#define HEATER_0_PIN       10

#define HEATER_1_PIN    8

#define TEMP_0_PIN          13   // ANALOG NUMBERING

#define TEMP_1_PIN          14   // ANALOG NUMBERING

void setup() {

 pinMode(FAN_PIN , OUTPUT);

 pinMode(HEATER_0_PIN , OUTPUT);

 pinMode(HEATER_1_PIN , OUTPUT);

 pinMode(LED_PIN  , OUTPUT);

 pinMode(X_STEP_PIN  , OUTPUT);

 pinMode(X_DIR_PIN , OUTPUT);

 pinMode(X_ENABLE_PIN , OUTPUT);

 pinMode(Y_STEP_PIN  , OUTPUT);

 pinMode(Y_DIR_PIN , OUTPUT);

 pinMode(Y_ENABLE_PIN , OUTPUT);

 pinMode(Z_STEP_PIN  , OUTPUT);

 pinMode(Z_DIR_PIN , OUTPUT);

 pinMode(Z_ENABLE_PIN , OUTPUT);

 pinMode(E_STEP_PIN  , OUTPUT);

 pinMode(E_DIR_PIN , OUTPUT);

 pinMode(E_ENABLE_PIN , OUTPUT);

 pinMode(Q_STEP_PIN  , OUTPUT);

 pinMode(Q_DIR_PIN , OUTPUT);

 pinMode(Q_ENABLE_PIN , OUTPUT);

  digitalWrite(X_ENABLE_PIN , LOW);

   digitalWrite(Y_ENABLE_PIN , LOW);

   digitalWrite(Z_ENABLE_PIN , LOW);

   digitalWrite(E_ENABLE_PIN , LOW);

digitalWrite(Q_ENABLE_PIN , LOW);

}

void loop () {

 if (millis() %1000 <500)

digitalWrite(LED_PIN, HIGH);

 else

  digitalWrite(LED_PIN, LOW);

 if (millis() %1000 <300) {

   digitalWrite(HEATER_0_PIN, HIGH);

digitalWrite(HEATER_1_PIN, LOW);

digitalWrite(FAN_PIN, LOW);

 } else if (millis() %1000 <600) {

   digitalWrite(HEATER_0_PIN, LOW);

   digitalWrite(HEATER_1_PIN, HIGH);

digitalWrite(FAN_PIN, LOW);

 } else  {

   digitalWrite(HEATER_0_PIN, LOW);

   digitalWrite(HEATER_1_PIN, LOW);

digitalWrite(FAN_PIN, HIGH);

 }

 if (millis() %10000 <5000) {

   digitalWrite(X_DIR_PIN , HIGH);

   digitalWrite(Y_DIR_PIN , HIGH);

   digitalWrite(Z_DIR_PIN , HIGH);

   digitalWrite(E_DIR_PIN , HIGH);

   digitalWrite(Q_DIR_PIN , HIGH);

 }

 else {

   digitalWrite(X_DIR_PIN , LOW);

   digitalWrite(Y_DIR_PIN , LOW);

   digitalWrite(Z_DIR_PIN , LOW);

   digitalWrite(E_DIR_PIN , LOW);

   digitalWrite(Q_DIR_PIN , LOW);

 }

 

   digitalWrite(X_STEP_PIN , HIGH);

   digitalWrite(Y_STEP_PIN , HIGH);

   digitalWrite(Z_STEP_PIN , HIGH);

   digitalWrite(E_STEP_PIN , HIGH);

   digitalWrite(Q_STEP_PIN , HIGH);

 delay(1);

   digitalWrite(X_STEP_PIN , LOW);

   digitalWrite(Y_STEP_PIN , LOW);

   digitalWrite(Z_STEP_PIN , LOW);

   digitalWrite(E_STEP_PIN , LOW);

   digitalWrite(Q_STEP_PIN , LOW);

}



This code should activate all the steppers, fans, and heaters. The only problem with this code is that it does not activate both z-axis steppers; it only activates one. If you encounter any problems with any of the components, try isolating them and only running them in the code. If you would like to test the z-axis and thermistor, download the following codes:



<thermistortables.h>



#ifndef THERMISTORTABLES_H_
#define THERMISTORTABLES_H_

#define OVERSAMPLENR 16

#if (THERMISTORHEATER_0 == 1) || (THERMISTORHEATER_1 == 1)  || (THERMISTORHEATER_2 == 1) || (THERMISTORBED == 1) //100k bed thermistor

const short temptable_1[][2] PROGMEM = {
{       23*OVERSAMPLENR ,       300     },
{       25*OVERSAMPLENR ,       295     },
{       27*OVERSAMPLENR ,       290     },
{       28*OVERSAMPLENR ,       285     },
{       31*OVERSAMPLENR ,       280     },
{       33*OVERSAMPLENR ,       275     },
{       35*OVERSAMPLENR ,       270     },
{       38*OVERSAMPLENR ,       265     },
{       41*OVERSAMPLENR ,       260     },
{       44*OVERSAMPLENR ,       255     },
{       48*OVERSAMPLENR ,       250     },
{       52*OVERSAMPLENR ,       245     },
{       56*OVERSAMPLENR ,       240     },
{       61*OVERSAMPLENR ,       235     },
{       66*OVERSAMPLENR ,       230     },
{       71*OVERSAMPLENR ,       225     },
{       78*OVERSAMPLENR ,       220     },
{       84*OVERSAMPLENR ,       215     },
{       92*OVERSAMPLENR ,       210     },
{       100*OVERSAMPLENR        ,       205     },
{       109*OVERSAMPLENR        ,       200     },
{       120*OVERSAMPLENR        ,       195     },
{       131*OVERSAMPLENR        ,       190     },
{       143*OVERSAMPLENR        ,       185     },
{       156*OVERSAMPLENR        ,       180     },
{       171*OVERSAMPLENR        ,       175     },
{       187*OVERSAMPLENR        ,       170     },
{       205*OVERSAMPLENR        ,       165     },
{       224*OVERSAMPLENR        ,       160     },
{       245*OVERSAMPLENR        ,       155     },
{       268*OVERSAMPLENR        ,       150     },
{       293*OVERSAMPLENR        ,       145     },
{       320*OVERSAMPLENR        ,       140     },
{       348*OVERSAMPLENR        ,       135     },
{       379*OVERSAMPLENR        ,       130     },
{       411*OVERSAMPLENR        ,       125     },
{       445*OVERSAMPLENR        ,       120     },
{       480*OVERSAMPLENR        ,       115     },
{       516*OVERSAMPLENR        ,       110     },
{       553*OVERSAMPLENR        ,       105     },
{       591*OVERSAMPLENR        ,       100     },
{       628*OVERSAMPLENR        ,       95      },
{       665*OVERSAMPLENR        ,       90      },
{       702*OVERSAMPLENR        ,       85      },
{       737*OVERSAMPLENR        ,       80      },
{       770*OVERSAMPLENR        ,       75      },
{       801*OVERSAMPLENR        ,       70      },
{       830*OVERSAMPLENR        ,       65      },
{       857*OVERSAMPLENR        ,       60      },
{       881*OVERSAMPLENR        ,       55      },
{       903*OVERSAMPLENR        ,       50      },
{       922*OVERSAMPLENR        ,       45      },
{       939*OVERSAMPLENR        ,       40      },
{       954*OVERSAMPLENR        ,       35      },
{       966*OVERSAMPLENR        ,       30      },
{       977*OVERSAMPLENR        ,       25      },
{       985*OVERSAMPLENR        ,       20      },
{       993*OVERSAMPLENR        ,       15      },
{       999*OVERSAMPLENR        ,       10      },
{       1004*OVERSAMPLENR       ,       5       },
{       1008*OVERSAMPLENR       ,       0       } //safety
};
#endif
#if (THERMISTORHEATER_0 == 2) || (THERMISTORHEATER_1 == 2) || (THERMISTORHEATER_2 == 2) || (THERMISTORBED == 2) //200k bed thermistor
const short temptable_2[][2] PROGMEM = {
  {1*OVERSAMPLENR, 848},
  {54*OVERSAMPLENR, 275},
  {107*OVERSAMPLENR, 228},
  {160*OVERSAMPLENR, 202},
  {213*OVERSAMPLENR, 185},
  {266*OVERSAMPLENR, 171},
  {319*OVERSAMPLENR, 160},
  {372*OVERSAMPLENR, 150},
  {425*OVERSAMPLENR, 141},
  {478*OVERSAMPLENR, 133},
  {531*OVERSAMPLENR, 125},
  {584*OVERSAMPLENR, 118},
  {637*OVERSAMPLENR, 110},
  {690*OVERSAMPLENR, 103},
  {743*OVERSAMPLENR, 95},
  {796*OVERSAMPLENR, 86},
  {849*OVERSAMPLENR, 77},
  {902*OVERSAMPLENR, 65},
  {955*OVERSAMPLENR, 49},
  {1008*OVERSAMPLENR, 17},
  {1020*OVERSAMPLENR, 0} //safety
};

#endif
#if (THERMISTORHEATER_0 == 3) || (THERMISTORHEATER_1 == 3) || (THERMISTORHEATER_2 == 3) || (THERMISTORBED == 3) //mendel-parts
const short temptable_3[][2] PROGMEM = {
               {1*OVERSAMPLENR,864},
               {21*OVERSAMPLENR,300},
               {25*OVERSAMPLENR,290},
               {29*OVERSAMPLENR,280},
               {33*OVERSAMPLENR,270},
               {39*OVERSAMPLENR,260},
               {46*OVERSAMPLENR,250},
               {54*OVERSAMPLENR,240},
               {64*OVERSAMPLENR,230},
               {75*OVERSAMPLENR,220},
               {90*OVERSAMPLENR,210},
               {107*OVERSAMPLENR,200},
               {128*OVERSAMPLENR,190},
               {154*OVERSAMPLENR,180},
               {184*OVERSAMPLENR,170},
               {221*OVERSAMPLENR,160},
               {265*OVERSAMPLENR,150},
               {316*OVERSAMPLENR,140},
               {375*OVERSAMPLENR,130},
               {441*OVERSAMPLENR,120},
               {513*OVERSAMPLENR,110},
               {588*OVERSAMPLENR,100},
               {734*OVERSAMPLENR,80},
               {856*OVERSAMPLENR,60},
               {938*OVERSAMPLENR,40},
               {986*OVERSAMPLENR,20},
               {1008*OVERSAMPLENR,0},
               {1018*OVERSAMPLENR,-20}
       };

#endif
#if (THERMISTORHEATER_0 == 4) || (THERMISTORHEATER_1 == 4) || (THERMISTORHEATER_2 == 4) || (THERMISTORBED == 4) //10k thermistor
const short temptable_4[][2] PROGMEM = {
  {1*OVERSAMPLENR, 430},
  {54*OVERSAMPLENR, 137},
  {107*OVERSAMPLENR, 107},
  {160*OVERSAMPLENR, 91},
  {213*OVERSAMPLENR, 80},
  {266*OVERSAMPLENR, 71},
  {319*OVERSAMPLENR, 64},
  {372*OVERSAMPLENR, 57},
  {425*OVERSAMPLENR, 51},
  {478*OVERSAMPLENR, 46},
  {531*OVERSAMPLENR, 41},
  {584*OVERSAMPLENR, 35},
  {637*OVERSAMPLENR, 30},
  {690*OVERSAMPLENR, 25},
  {743*OVERSAMPLENR, 20},
  {796*OVERSAMPLENR, 14},
  {849*OVERSAMPLENR, 7},
  {902*OVERSAMPLENR, 0},
  {955*OVERSAMPLENR, -11},
  {1008*OVERSAMPLENR, -35}
};
#endif

#if (THERMISTORHEATER_0 == 5) || (THERMISTORHEATER_1 == 5) || (THERMISTORHEATER_2 == 5) || (THERMISTORBED == 5) //100k ParCan thermistor (104GT-2)
const short temptable_5[][2] PROGMEM = {
{1*OVERSAMPLENR, 713},
{18*OVERSAMPLENR, 316},
{35*OVERSAMPLENR, 266},
{52*OVERSAMPLENR, 239},
{69*OVERSAMPLENR, 221},
{86*OVERSAMPLENR, 208},
{103*OVERSAMPLENR, 197},
{120*OVERSAMPLENR, 188},
{137*OVERSAMPLENR, 181},
{154*OVERSAMPLENR, 174},
{171*OVERSAMPLENR, 169},
{188*OVERSAMPLENR, 163},
{205*OVERSAMPLENR, 159},
{222*OVERSAMPLENR, 154},
{239*OVERSAMPLENR, 150},
{256*OVERSAMPLENR, 147},
{273*OVERSAMPLENR, 143},
{290*OVERSAMPLENR, 140},
{307*OVERSAMPLENR, 136},
{324*OVERSAMPLENR, 133},
{341*OVERSAMPLENR, 130},
{358*OVERSAMPLENR, 128},
{375*OVERSAMPLENR, 125},
{392*OVERSAMPLENR, 122},
{409*OVERSAMPLENR, 120},
{426*OVERSAMPLENR, 117},
{443*OVERSAMPLENR, 115},
{460*OVERSAMPLENR, 112},
{477*OVERSAMPLENR, 110},
{494*OVERSAMPLENR, 108},
{511*OVERSAMPLENR, 106},
{528*OVERSAMPLENR, 103},
{545*OVERSAMPLENR, 101},
{562*OVERSAMPLENR, 99},
{579*OVERSAMPLENR, 97},
{596*OVERSAMPLENR, 95},
{613*OVERSAMPLENR, 92},
{630*OVERSAMPLENR, 90},
{647*OVERSAMPLENR, 88},
{664*OVERSAMPLENR, 86},
{681*OVERSAMPLENR, 84},
{698*OVERSAMPLENR, 81},
{715*OVERSAMPLENR, 79},
{732*OVERSAMPLENR, 77},
{749*OVERSAMPLENR, 75},
{766*OVERSAMPLENR, 72},
{783*OVERSAMPLENR, 70},
{800*OVERSAMPLENR, 67},
{817*OVERSAMPLENR, 64},
{834*OVERSAMPLENR, 61},
{851*OVERSAMPLENR, 58},
{868*OVERSAMPLENR, 55},
{885*OVERSAMPLENR, 52},
{902*OVERSAMPLENR, 48},
{919*OVERSAMPLENR, 44},
{936*OVERSAMPLENR, 40},
{953*OVERSAMPLENR, 34},
{970*OVERSAMPLENR, 28},
{987*OVERSAMPLENR, 20},
{1004*OVERSAMPLENR, 8},
{1021*OVERSAMPLENR, 0}
};
#endif

#if (THERMISTORHEATER_0 == 6) || (THERMISTORHEATER_1 == 6) || (THERMISTORHEATER_2 == 6) || (THERMISTORBED == 6) // 100k Epcos thermistor
const short temptable_6[][2] PROGMEM = {
  {28*OVERSAMPLENR, 250},
  {31*OVERSAMPLENR, 245},
  {35*OVERSAMPLENR, 240},
  {39*OVERSAMPLENR, 235},
  {42*OVERSAMPLENR, 230},
  {44*OVERSAMPLENR, 225},
  {49*OVERSAMPLENR, 220},
  {53*OVERSAMPLENR, 215},
  {62*OVERSAMPLENR, 210},
  {73*OVERSAMPLENR, 205},
  {72*OVERSAMPLENR, 200},
  {94*OVERSAMPLENR, 190},
  {102*OVERSAMPLENR, 185},
  {116*OVERSAMPLENR, 170},
  {143*OVERSAMPLENR, 160},
  {183*OVERSAMPLENR, 150},
  {223*OVERSAMPLENR, 140},
  {270*OVERSAMPLENR, 130},
  {318*OVERSAMPLENR, 120},
  {383*OVERSAMPLENR, 110},
  {413*OVERSAMPLENR, 105},
  {439*OVERSAMPLENR, 100},
  {484*OVERSAMPLENR, 95},
  {513*OVERSAMPLENR, 90},
  {607*OVERSAMPLENR, 80},
  {664*OVERSAMPLENR, 70},
  {781*OVERSAMPLENR, 60},
  {810*OVERSAMPLENR, 55},
  {849*OVERSAMPLENR, 50},
  {914*OVERSAMPLENR, 45},
  {914*OVERSAMPLENR, 40},
  {935*OVERSAMPLENR, 35},
  {954*OVERSAMPLENR, 30},
  {970*OVERSAMPLENR, 25},
  {978*OVERSAMPLENR, 22},
  {1008*OVERSAMPLENR, 3}
};
#endif

#if (THERMISTORHEATER_0 == 7) || (THERMISTORHEATER_1 == 7) || (THERMISTORHEATER_2 == 7) || (THERMISTORBED == 7) // 100k Honeywell 135-104LAG-J01
const short temptable_7[][2] PROGMEM = {
  {46*OVERSAMPLENR, 270},
  {50*OVERSAMPLENR, 265},
  {54*OVERSAMPLENR, 260},
  {58*OVERSAMPLENR, 255},
  {62*OVERSAMPLENR, 250},
  {67*OVERSAMPLENR, 245},
  {72*OVERSAMPLENR, 240},
  {79*OVERSAMPLENR, 235},
  {85*OVERSAMPLENR, 230},
  {91*OVERSAMPLENR, 225},
  {99*OVERSAMPLENR, 220},
  {107*OVERSAMPLENR, 215},
  {116*OVERSAMPLENR, 210},
  {126*OVERSAMPLENR, 205},
  {136*OVERSAMPLENR, 200},
  {149*OVERSAMPLENR, 195},
  {160*OVERSAMPLENR, 190},
  {175*OVERSAMPLENR, 185},
  {191*OVERSAMPLENR, 180},
  {209*OVERSAMPLENR, 175},
  {224*OVERSAMPLENR, 170},
  {246*OVERSAMPLENR, 165},
  {267*OVERSAMPLENR, 160},
  {293*OVERSAMPLENR, 155},
  {316*OVERSAMPLENR, 150},
  {340*OVERSAMPLENR, 145},
  {364*OVERSAMPLENR, 140},
  {396*OVERSAMPLENR, 135},
  {425*OVERSAMPLENR, 130},
  {460*OVERSAMPLENR, 125},
  {489*OVERSAMPLENR, 120},
  {526*OVERSAMPLENR, 115},
  {558*OVERSAMPLENR, 110},
  {591*OVERSAMPLENR, 105},
  {628*OVERSAMPLENR, 100},
  {660*OVERSAMPLENR, 95},
  {696*OVERSAMPLENR, 90},
  {733*OVERSAMPLENR, 85},
  {761*OVERSAMPLENR, 80},
  {794*OVERSAMPLENR, 75},
  {819*OVERSAMPLENR, 70},
  {847*OVERSAMPLENR, 65},
  {870*OVERSAMPLENR, 60},
  {892*OVERSAMPLENR, 55},
  {911*OVERSAMPLENR, 50},
  {929*OVERSAMPLENR, 45},
  {944*OVERSAMPLENR, 40},
  {959*OVERSAMPLENR, 35},
  {971*OVERSAMPLENR, 30},
  {981*OVERSAMPLENR, 25},
  {989*OVERSAMPLENR, 20},
  {994*OVERSAMPLENR, 15},
  {1001*OVERSAMPLENR, 10},
  {1005*OVERSAMPLENR, 5}
};
#endif

#define _TT_NAME(_N) temptable_ ## _N
#define TT_NAME(_N) _TT_NAME(_N)

#ifdef THERMISTORHEATER_0
 #define heater_0_temptable TT_NAME(THERMISTORHEATER_0)
 #define heater_0_temptable_len (sizeof(heater_0_temptable)/sizeof(*heater_0_temptable))
#else
#ifdef HEATER_0_USES_THERMISTOR
 #error No heater 0 thermistor table specified
#else  // HEATER_0_USES_THERMISTOR
 #define heater_0_temptable 0
 #define heater_0_temptable_len 0
#endif // HEATER_0_USES_THERMISTOR
#endif

#ifdef THERMISTORHEATER_1
 #define heater_1_temptable TT_NAME(THERMISTORHEATER_1)
 #define heater_1_temptable_len (sizeof(heater_1_temptable)/sizeof(*heater_1_temptable))
#else
#ifdef HEATER_1_USES_THERMISTOR
 #error No heater 1 thermistor table specified
#else  // HEATER_1_USES_THERMISTOR
 #define heater_1_temptable 0
 #define heater_1_temptable_len 0
#endif // HEATER_1_USES_THERMISTOR
#endif

#ifdef THERMISTORHEATER_2
 #define heater_2_temptable TT_NAME(THERMISTORHEATER_2)
 #define heater_2_temptable_len (sizeof(heater_2_temptable)/sizeof(*heater_2_temptable))
#else
#ifdef HEATER_2_USES_THERMISTOR
 #error No heater 2 thermistor table specified
#else  // HEATER_2_USES_THERMISTOR
 #define heater_2_temptable 0
 #define heater_2_temptable_len 0
#endif // HEATER_2_USES_THERMISTOR
#endif

#ifdef THERMISTORBED
 #define bedtemptable TT_NAME(THERMISTORBED)
 #define bedtemptable_len (sizeof(bedtemptable)/sizeof(*bedtemptable))
#else
#ifdef BED_USES_THERMISTOR
 #error No bed thermistor table specified
#endif // BED_USES_THERMISTOR
#endif

#endif //THERMISTORTABLES_H_







<ramps14test.ino>



#include "thermistortables.h"



#define X_STEP_PIN         54

#define X_DIR_PIN          55

#define X_ENABLE_PIN       38

#define X_MIN_PIN           3

#define X_MAX_PIN           2



#define Y_STEP_PIN         60

#define Y_DIR_PIN          61

#define Y_ENABLE_PIN       56

#define Y_MIN_PIN          14

#define Y_MAX_PIN          15



#define Z_STEP_PIN         46

#define Z_DIR_PIN          48

#define Z_ENABLE_PIN       62

#define Z_MIN_PIN          18

#define Z_MAX_PIN          19



#define E_STEP_PIN         26

#define E_DIR_PIN          28

#define E_ENABLE_PIN       24



#define Q_STEP_PIN         36

#define Q_DIR_PIN          34

#define Q_ENABLE_PIN       30



#define SDPOWER            -1



#define EXTRUDERS 3



#define TEMP_SENSOR_AD595_OFFSET 0.0

#define TEMP_SENSOR_AD595_GAIN   1.0



#define THERMISTORHEATER_0 1

#define THERMISTORHEATER_1 1

#define THERMISTORHEATER_2 1



#define HEATER_0_USES_THERMISTOR 1

#define HEATER_1_USES_THERMISTOR 1

#define HEATER_2_USES_THERMISTOR 1



 static void *heater_ttbl_map[EXTRUDERS] = { (void *)heater_0_temptable

#if EXTRUDERS > 1

                                           , (void *)heater_1_temptable

#endif

#if EXTRUDERS > 2

                                           , (void *)heater_2_temptable

#endif

#if EXTRUDERS > 3

 #error Unsupported number of extruders

#endif

 };

 

   static int heater_ttbllen_map[EXTRUDERS] = { heater_0_temptable_len

#if EXTRUDERS > 1

                                            , heater_1_temptable_len

#endif

#if EXTRUDERS > 2

                                            , heater_2_temptable_len

#endif

#if EXTRUDERS > 3

 #error Unsupported number of extruders

#endif

 };

 

 #define PGM_RD_W(x)   (short)pgm_read_word(&x)



#define SDSS               53

#define LED_PIN            13



#define FAN_PIN            9



#define PS_ON_PIN          12

#define KILL_PIN           -1



#define HEATER_0_PIN       10



#define HEATER_1_PIN       8

#define TEMP_0_PIN         15   // ANALOG NUMBERING

#define TEMP_1_PIN         14   // ANALOG NUMBERING

#define TEMP_2_PIN         13   // ANALOG NUMBERING



void setup() {

 

 pinMode(TEMP_0_PIN  , INPUT);

 pinMode(TEMP_1_PIN  , INPUT);

 pinMode(TEMP_2_PIN  , INPUT);

 

 pinMode(FAN_PIN , OUTPUT);

 pinMode(HEATER_0_PIN , OUTPUT);

 pinMode(HEATER_1_PIN , OUTPUT);

 pinMode(LED_PIN  , OUTPUT);

 

 pinMode(X_STEP_PIN  , OUTPUT);

 pinMode(X_DIR_PIN    , OUTPUT);

 pinMode(X_ENABLE_PIN    , OUTPUT);

 

 pinMode(Y_STEP_PIN  , OUTPUT);

 pinMode(Y_DIR_PIN    , OUTPUT);

 pinMode(Y_ENABLE_PIN    , OUTPUT);

 

 pinMode(Z_STEP_PIN  , OUTPUT);

 pinMode(Z_DIR_PIN    , OUTPUT);

 pinMode(Z_ENABLE_PIN    , OUTPUT);

 

 pinMode(E_STEP_PIN  , OUTPUT);

 pinMode(E_DIR_PIN    , OUTPUT);

 pinMode(E_ENABLE_PIN    , OUTPUT);

 

 pinMode(Q_STEP_PIN  , OUTPUT);

 pinMode(Q_DIR_PIN    , OUTPUT);

 pinMode(Q_ENABLE_PIN    , OUTPUT);

 

  digitalWrite(X_ENABLE_PIN    , LOW);

   digitalWrite(Y_ENABLE_PIN    , LOW);

   digitalWrite(Z_ENABLE_PIN    , LOW);

   digitalWrite(E_ENABLE_PIN    , LOW);

   digitalWrite(Q_ENABLE_PIN    , LOW);

   Serial.begin(115200);

}



float analog2temp(int raw, uint8_t e) {



 #ifdef HEATER_0_USES_MAX6675

   if (e == 0)

   {

     return 0.25 * raw;

   }

 #endif



 if(heater_ttbl_map[e] != 0)

 {

   float celsius = 0;

   byte i;  

   short (*tt)[][2] = (short (*)[][2])(heater_ttbl_map[e]);



   raw = (1023 * OVERSAMPLENR) - raw;

   for (i=1; i<heater_ttbllen_map[e]; i++)

   {

     if ((PGM_RD_W((*tt)[i][0]) > raw) && ((float)(PGM_RD_W((*tt)[i][0]) - PGM_RD_W((*tt)[i-1][0])) >0))

     {

       celsius = PGM_RD_W((*tt)[i-1][1]) +

         (raw - PGM_RD_W((*tt)[i-1][0])) *

         (float)(PGM_RD_W((*tt)[i][1]) - PGM_RD_W((*tt)[i-1][1])) /

         (float)(PGM_RD_W((*tt)[i][0]) - PGM_RD_W((*tt)[i-1][0]));

       break;

     }

   }



   // Overflow: Set to last value in the table

   if (i == heater_ttbllen_map[e]) celsius = PGM_RD_W((*tt)[i-1][1]);



   return celsius;

 }

 return ((raw * ((5.0 * 100.0) / 1024.0) / OVERSAMPLENR) * TEMP_SENSOR_AD595_GAIN) + TEMP_SENSOR_AD595_OFFSET;

}



unsigned long prevMillis;



void loop () {

 

 if (millis() %1000 <500)

   digitalWrite(LED_PIN, HIGH);

 else

  digitalWrite(LED_PIN, LOW);

 

 if (millis() %1000 <300) {

   digitalWrite(HEATER_0_PIN, HIGH);

   digitalWrite(HEATER_1_PIN, LOW);

   digitalWrite(FAN_PIN, LOW);

 } else if (millis() %1000 <600) {

   digitalWrite(HEATER_0_PIN, LOW);

   digitalWrite(HEATER_1_PIN, HIGH);

   digitalWrite(FAN_PIN, LOW);

 } else  {

   digitalWrite(HEATER_0_PIN, LOW);

   digitalWrite(HEATER_1_PIN, LOW);

   digitalWrite(FAN_PIN, HIGH);

 }

 

 if (millis() %2000 <1000) {

   digitalWrite(X_DIR_PIN    , HIGH);

   digitalWrite(Y_DIR_PIN    , HIGH);

   digitalWrite(Z_DIR_PIN    , HIGH);

   digitalWrite(E_DIR_PIN    , HIGH);

   digitalWrite(Q_DIR_PIN    , HIGH);

 }

 else {

   digitalWrite(X_DIR_PIN    , LOW);

   digitalWrite(Y_DIR_PIN    , LOW);

   digitalWrite(Z_DIR_PIN    , LOW);

   digitalWrite(E_DIR_PIN    , LOW);

   digitalWrite(Q_DIR_PIN    , LOW);

 }

 

 

   digitalWrite(X_STEP_PIN    , HIGH);

   digitalWrite(Y_STEP_PIN    , HIGH);

   digitalWrite(Z_STEP_PIN    , HIGH);

   digitalWrite(E_STEP_PIN    , HIGH);

   digitalWrite(Q_STEP_PIN    , HIGH);

 delay(1);

 

 

 

   

   digitalWrite(X_STEP_PIN    , LOW);

   digitalWrite(Y_STEP_PIN    , LOW);

   digitalWrite(Z_STEP_PIN    , LOW);

   digitalWrite(E_STEP_PIN    , LOW);

   digitalWrite(Q_STEP_PIN    , LOW);

   

   if (millis() -prevMillis >500){

   prevMillis=millis();

   int t = analogRead( TEMP_0_PIN);

   Serial.print("T0 ");

   Serial.print(t);

   Serial.print("/");

   Serial.print(analog2temp(1024 - t,0),0);

   

   Serial.print(" T1 ");

   t = analogRead( TEMP_1_PIN);

   Serial.print(t);

   Serial.print("/");

   Serial.print(analog2temp(1024 - t,1),0);

   

   Serial.print(" T2 ");

   t = analogRead( TEMP_2_PIN);

   Serial.print(t);

   Serial.print("/");

   Serial.println(analog2temp(1024 - t,2),0);

   

 }

   

}
/***
 * Required libraries:
 * Arduino_GFX: https://github.com/moononournation/Arduino_GFX.git
 * libhelix: https://github.com/pschatzmann/arduino-libhelix.git
 * JPEGDEC: https://github.com/bitbank2/JPEGDEC.git
 */

// auto fall back to MP3 if AAC file not available
#define AAC_FILENAME "/44100.aac"
#define MP3_FILENAME "/44100.mp3"
// #define MJPEG_FILENAME "/288_30fps.mjpeg"
// #define MJPEG_FILENAME "/320_30fps.mjpeg"
#define MJPEG_FILENAME "/480_15fps.mjpeg"
// #define MJPEG_FILENAME "/480_30fps.mjpeg"
#define FPS 15
// #define FPS 30
// #define MJPEG_BUFFER_SIZE (288 * 240 * 2 / 8)
// #define MJPEG_BUFFER_SIZE (320 * 240 * 2 / 8)
#define MJPEG_BUFFER_SIZE (480 * 270 * 2 / 8)

#define AUDIOASSIGNCORE 1
#define DECODEASSIGNCORE 0
#define DRAWASSIGNCORE 1

#define SDMMC_D3 10  // SDMMC Data3 / SPI CS
#define SDMMC_CMD 11 // SDMMC CMD   / SPI MOSI
#define SDMMC_CLK 12 // SDMMC CLK   / SPI SCK
#define SDMMC_D0 13  // SDMMC Data0 / SPI MISO

#include <WiFi.h>
#include <FS.h>

#include <FFat.h>
#include <LittleFS.h>
#include <SD.h>
#include <SD_MMC.h>
#include <SPIFFS.h>

/*******************************************************************************
 * Start of Arduino_GFX setting
 ******************************************************************************/
#include <Arduino_GFX_Library.h>
#define GFX_BL 2
Arduino_DataBus *bus = new Arduino_ESP32LCD16(
    48 /* DC */, 45 /* CS */, 47 /* WR */, 21 /* RD */,
    5 /* D0 */, 6 /* D1 */, 7 /* D2 */, 15 /* D3 */, 16 /* D4 */, 4 /* D5 */, 8 /* D6 */, 3 /* D7 */,
    46 /* D8 */, 9 /* D9 */, 1 /* D10 */, 42 /* D11 */, 39 /* D12 */, 41 /* D13 */, 40 /* D14 */, 14 /* D15 */);
Arduino_GFX *gfx = new Arduino_NV3041A(bus, 17 /* RST */, 0 /* rotation */, true /* IPS */);
/*******************************************************************************
 * End of Arduino_GFX setting
 ******************************************************************************/

/* variables */
static int next_frame = 0;
static int skipped_frames = 0;
static unsigned long start_ms, curr_ms, next_frame_ms;

/* audio */
#include "esp32_audio_task.h"

/* MJPEG Video */
#include "mjpeg_decode_draw_task.h"

// pixel drawing callback
static int drawMCU(JPEGDRAW *pDraw)
{
  // Serial.printf("Draw pos = (%d, %d), size = %d x %d\n", pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);
  unsigned long s = millis();
  gfx->draw16bitRGBBitmap(pDraw->x, pDraw->y, pDraw->pPixels, pDraw->iWidth, pDraw->iHeight);
  total_show_video_ms += millis() - s;
  return 1;
} /* drawMCU() */

void setup()
{
  WiFi.mode(WIFI_OFF);

  Serial.begin(115200);
  // Serial.setDebugOutput(true);
  // while(!Serial);
  Serial.println("ESP32_4827A043");

#ifdef GFX_EXTRA_PRE_INIT
  GFX_EXTRA_PRE_INIT();
#endif

  Serial.println("Init display");
  if (!gfx->begin())
  {
    Serial.println("Init display failed!");
  }
  gfx->fillScreen(BLACK);

#ifdef GFX_BL
  pinMode(GFX_BL, OUTPUT);
  digitalWrite(GFX_BL, HIGH);
#endif

  Serial.println("Init I2S");
  gfx->println("Init I2S");
  esp_err_t ret_val = i2s_init(I2S_NUM_0, 44100, -1 /* MCLK */, 19 /* SCLK */, 18 /* LRCK */, 17 /* DOUT */, -1 /* DIN */);
  if (ret_val != ESP_OK)
  {
    Serial.printf("i2s_init failed: %d\n", ret_val);
  }
  i2s_zero_dma_buffer(I2S_NUM_0);

  Serial.println("Init FS");
  gfx->println("Init FS");
  // if (!LittleFS.begin(false, "/root"))
  // if (!SPIFFS.begin(false, "/root"))
  if (!FFat.begin(false, "/root"))

  // SPIClass spi = SPIClass(HSPI);
  // spi.begin(SDMMC_CLK, SDMMC_D0 /* MISO */, SDMMC_CMD /* MOSI */, SDMMC_D3 /* SS */);
  // if (!SD.begin(SDMMC_D3 /* SS */, spi, 80000000))

  // pinMode(SDMMC_D3 /* CS */, OUTPUT);
  // digitalWrite(SDMMC_D3 /* CS */, HIGH);
  // SD_MMC.setPins(SDMMC_CLK, SDMMC_CMD, SDMMC_D0);
  // if (!SD_MMC.begin("/root", true)) /* 1-bit SD bus mode */
  {
    Serial.println("ERROR: File system mount failed!");
    gfx->println("ERROR: File system mount failed!");
  }
  else
  {
    bool aac_file_available = false;
    Serial.println("Open AAC file: " AAC_FILENAME);
    gfx->println("Open AAC file: " AAC_FILENAME);
    // File aFile = LittleFS.open(AAC_FILENAME);
    // File aFile = SPIFFS.open(AAC_FILENAME);
    File aFile = FFat.open(AAC_FILENAME);
    // File aFile = SD.open(AAC_FILENAME);
    // File aFile = SD_MMC.open(AAC_FILENAME);
    if (aFile)
    {
      aac_file_available = true;
    }
    else
    {
      Serial.println("Open MP3 file: " MP3_FILENAME);
      gfx->println("Open MP3 file: " MP3_FILENAME);
      // aFile = LittleFS.open(MP3_FILENAME);
      // aFile = SPIFFS.open(MP3_FILENAME);
      aFile = FFat.open(MP3_FILENAME);
      // aFile = SD.open(MP3_FILENAME);
      // aFile = SD_MMC.open(MP3_FILENAME);
    }

    if (!aFile || aFile.isDirectory())
    {
      Serial.println("ERROR: Failed to open " AAC_FILENAME " or " MP3_FILENAME " file for reading");
      gfx->println("ERROR: Failed to open " AAC_FILENAME " or " MP3_FILENAME " file for reading");
    }
    else
    {
      Serial.println("Open MJPEG file: " MJPEG_FILENAME);
      gfx->println("Open MJPEG file: " MJPEG_FILENAME);
      // File vFile = LittleFS.open(MJPEG_FILENAME);
      // File vFile = SPIFFS.open(MJPEG_FILENAME);
      File vFile = FFat.open(MJPEG_FILENAME);
      // File vFile = SD.open(MJPEG_FILENAME);
      // File vFile = SD_MMC.open(MJPEG_FILENAME);
      if (!vFile || vFile.isDirectory())
      {
        Serial.println("ERROR: Failed to open " MJPEG_FILENAME " file for reading");
        gfx->println("ERROR: Failed to open " MJPEG_FILENAME " file for reading");
      }
      else
      {
        Serial.println("Init video");
        gfx->println("Init video");
        mjpeg_setup(&vFile, MJPEG_BUFFER_SIZE, drawMCU,
                    false /* useBigEndian */, DECODEASSIGNCORE, DRAWASSIGNCORE);

        Serial.println("Start play audio task");
        gfx->println("Start play audio task");
        BaseType_t ret_val;
        if (aac_file_available)
        {
          ret_val = aac_player_task_start(&aFile, AUDIOASSIGNCORE);
        }
        else
        {
          ret_val = mp3_player_task_start(&aFile, AUDIOASSIGNCORE);
        }
        if (ret_val != pdPASS)
        {
          Serial.printf("Audio player task start failed: %d\n", ret_val);
          gfx->printf("Audio player task start failed: %d\n", ret_val);
        }

        Serial.println("Start play video");
        gfx->println("Start play video");
        start_ms = millis();
        curr_ms = millis();
        next_frame_ms = start_ms + (++next_frame * 1000 / FPS / 2);
        while (vFile.available() && mjpeg_read_frame()) // Read video
        {
          total_read_video_ms += millis() - curr_ms;
          curr_ms = millis();

          if (millis() < next_frame_ms) // check show frame or skip frame
          {
            // Play video
            mjpeg_draw_frame();
            total_decode_video_ms += millis() - curr_ms;
            curr_ms = millis();
          }
          else
          {
            ++skipped_frames;
            Serial.println("Skip frame");
          }

          while (millis() < next_frame_ms)
          {
            vTaskDelay(pdMS_TO_TICKS(1));
          }

          curr_ms = millis();
          next_frame_ms = start_ms + (++next_frame * 1000 / FPS);
        }
        int time_used = millis() - start_ms;
        int total_frames = next_frame - 1;
        Serial.println("AV end");
        vFile.close();
        aFile.close();

        delay(200);

        int played_frames = total_frames - skipped_frames;
        float fps = 1000.0 * played_frames / time_used;
        total_decode_audio_ms -= total_play_audio_ms;
        // total_decode_video_ms -= total_show_video_ms;
        Serial.printf("Played frames: %d\n", played_frames);
        Serial.printf("Skipped frames: %d (%0.1f %%)\n", skipped_frames, 100.0 * skipped_frames / total_frames);
        Serial.printf("Time used: %d ms\n", time_used);
        Serial.printf("Expected FPS: %d\n", FPS);
        Serial.printf("Actual FPS: %0.1f\n", fps);
        Serial.printf("Read audio: %lu ms (%0.1f %%)\n", total_read_audio_ms, 100.0 * total_read_audio_ms / time_used);
        Serial.printf("Decode audio: %lu ms (%0.1f %%)\n", total_decode_audio_ms, 100.0 * total_decode_audio_ms / time_used);
        Serial.printf("Play audio: %lu ms (%0.1f %%)\n", total_play_audio_ms, 100.0 * total_play_audio_ms / time_used);
        Serial.printf("Read video: %lu ms (%0.1f %%)\n", total_read_video_ms, 100.0 * total_read_video_ms / time_used);
        Serial.printf("Decode video: %lu ms (%0.1f %%)\n", total_decode_video_ms, 100.0 * total_decode_video_ms / time_used);
        Serial.printf("Show video: %lu ms (%0.1f %%)\n", total_show_video_ms, 100.0 * total_show_video_ms / time_used);

#define CHART_MARGIN 64
#define LEGEND_A_COLOR 0x1BB6
#define LEGEND_B_COLOR 0xFBE1
#define LEGEND_C_COLOR 0x2D05
#define LEGEND_D_COLOR 0xD125
#define LEGEND_E_COLOR 0x9337
#define LEGEND_F_COLOR 0x8AA9
#define LEGEND_G_COLOR 0xE3B8
#define LEGEND_H_COLOR 0x7BEF
#define LEGEND_I_COLOR 0xBDE4
#define LEGEND_J_COLOR 0x15F9
        // gfx->setCursor(0, 0);
        gfx->setTextColor(WHITE);
        gfx->printf("Played frames: %d\n", played_frames);
        gfx->printf("Skipped frames: %d (%0.1f %%)\n", skipped_frames, 100.0 * skipped_frames / total_frames);
        gfx->printf("Time used: %d ms\n", time_used);
        gfx->printf("Expected FPS: %d\n", FPS);
        gfx->printf("Actual FPS: %0.1f\n\n", fps);

        int16_t r1 = ((gfx->height() - CHART_MARGIN - CHART_MARGIN) / 2);
        int16_t r2 = r1 / 2;
        int16_t cx = gfx->width() - r1 - 10;
        int16_t cy = r1 + CHART_MARGIN;

        float arc_start1 = 0;
        float arc_end1 = arc_start1 + max(2.0, 360.0 * total_read_audio_ms / time_used);
        for (int i = arc_start1 + 1; i < arc_end1; i += 2)
        {
          gfx->fillArc(cx, cy, r1, r2, arc_start1 - 90.0, i - 90.0, LEGEND_A_COLOR);
        }
        gfx->fillArc(cx, cy, r1, r2, arc_start1 - 90.0, arc_end1 - 90.0, LEGEND_A_COLOR);
        gfx->setTextColor(LEGEND_A_COLOR);
        gfx->printf("Read audio: %lu ms (%0.1f %%)\n", total_read_audio_ms, 100.0 * total_read_audio_ms / time_used);

        float arc_start2 = arc_end1;
        float arc_end2 = arc_start2 + max(2.0, 360.0 * total_decode_audio_ms / time_used);
        for (int i = arc_start2 + 1; i < arc_end2; i += 2)
        {
          gfx->fillArc(cx, cy, r1, r2, arc_start2 - 90.0, i - 90.0, LEGEND_B_COLOR);
        }
        gfx->fillArc(cx, cy, r1, r2, arc_start2 - 90.0, arc_end2 - 90.0, LEGEND_B_COLOR);
        gfx->setTextColor(LEGEND_B_COLOR);
        gfx->printf("Decode audio: %lu ms (%0.1f %%)\n", total_decode_audio_ms, 100.0 * total_decode_audio_ms / time_used);
        gfx->setTextColor(LEGEND_J_COLOR);
        gfx->printf("Play audio: %lu ms (%0.1f %%)\n", total_play_audio_ms, 100.0 * total_play_audio_ms / time_used);

        float arc_start3 = arc_end2;
        float arc_end3 = arc_start3 + max(2.0, 360.0 * total_read_video_ms / time_used);
        for (int i = arc_start3 + 1; i < arc_end3; i += 2)
        {
          gfx->fillArc(cx, cy, r1, r2, arc_start3 - 90.0, i - 90.0, LEGEND_C_COLOR);
        }
        gfx->fillArc(cx, cy, r1, r2, arc_start3 - 90.0, arc_end3 - 90.0, LEGEND_C_COLOR);
        gfx->setTextColor(LEGEND_C_COLOR);
        gfx->printf("Read video: %lu ms (%0.1f %%)\n", total_read_video_ms, 100.0 * total_read_video_ms / time_used);

        float arc_start4 = arc_end3;
        float arc_end4 = arc_start4 + max(2.0, 360.0 * total_show_video_ms / time_used);
        for (int i = arc_start4 + 1; i < arc_end4; i += 2)
        {
          gfx->fillArc(cx, cy, r1, r2, arc_start4 - 90.0, i - 90.0, LEGEND_D_COLOR);
        }
        gfx->fillArc(cx, cy, r1, r2, arc_start4 - 90.0, arc_end4 - 90.0, LEGEND_D_COLOR);
        gfx->setTextColor(LEGEND_D_COLOR);
        gfx->printf("Show video: %lu ms (%0.1f %%)\n", total_show_video_ms, 100.0 * total_show_video_ms / time_used);

        float arc_start5 = 0;
        float arc_end5 = arc_start5 + max(2.0, 360.0 * total_decode_video_ms / time_used);
        for (int i = arc_start5 + 1; i < arc_end5; i += 2)
        {
          gfx->fillArc(cx, cy, r2, 0, arc_start5 - 90.0, i - 90.0, LEGEND_E_COLOR);
        }
        gfx->fillArc(cx, cy, r2, 0, arc_start5 - 90.0, arc_end5 - 90.0, LEGEND_E_COLOR);
        gfx->setTextColor(LEGEND_E_COLOR);
        gfx->printf("Decode video: %lu ms (%0.1f %%)\n", total_decode_video_ms, 100.0 * total_decode_video_ms / time_used);
      }

      // delay(60000);
#ifdef GFX_BL
      // digitalWrite(GFX_BL, LOW);
#endif
      // gfx->displayOff();
      // esp_deep_sleep_start();
    }
  }
}

void loop()
{
}
#include "driver/i2s.h"

#include "AACDecoderHelix.h"
#include "MP3DecoderHelix.h"

#define GAIN_LEVEL 0.8

static unsigned long total_read_audio_ms = 0;
static unsigned long total_decode_audio_ms = 0;
static unsigned long total_play_audio_ms = 0;

static i2s_port_t _i2s_num;
static esp_err_t i2s_init(i2s_port_t i2s_num, uint32_t sample_rate,
                          int mck_io_num,   /*!< MCK in out pin. Note that ESP32 supports setting MCK on GPIO0/GPIO1/GPIO3 only*/
                          int bck_io_num,   /*!< BCK in out pin*/
                          int ws_io_num,    /*!< WS in out pin*/
                          int data_out_num, /*!< DATA out pin*/
                          int data_in_num   /*!< DATA in pin*/
)
{
  _i2s_num = i2s_num;

  esp_err_t ret_val = ESP_OK;

  i2s_config_t i2s_config;
  i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX);
  i2s_config.sample_rate = sample_rate;
  i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT;
  i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
  i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S;
  i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1;
  i2s_config.dma_buf_count = 8;
  i2s_config.dma_buf_len = 160;
  i2s_config.use_apll = false;
  i2s_config.tx_desc_auto_clear = true;
  i2s_config.fixed_mclk = 0;
  i2s_config.mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT;
  i2s_config.bits_per_chan = I2S_BITS_PER_CHAN_16BIT;

  i2s_pin_config_t pin_config;
  pin_config.mck_io_num = mck_io_num;
  pin_config.bck_io_num = bck_io_num;
  pin_config.ws_io_num = ws_io_num;
  pin_config.data_out_num = data_out_num;
  pin_config.data_in_num = data_in_num;

  ret_val |= i2s_driver_install(i2s_num, &i2s_config, 0, NULL);
  ret_val |= i2s_set_pin(i2s_num, &pin_config);

  return ret_val;
}

static int _samprate = 0;
static void aacAudioDataCallback(AACFrameInfo &info, int16_t *pwm_buffer, size_t len, void*)
{
  unsigned long s = millis();
  if (_samprate != info.sampRateOut)
  {
    // log_i("bitRate: %d, nChans: %d, sampRateCore: %d, sampRateOut: %d, bitsPerSample: %d, outputSamps: %d, profile: %d, tnsUsed: %d, pnsUsed: %d",
    //       info.bitRate, info.nChans, info.sampRateCore, info.sampRateOut, info.bitsPerSample, info.outputSamps, info.profile, info.tnsUsed, info.pnsUsed);
    i2s_set_clk(_i2s_num, info.sampRateOut /* sample_rate */, info.bitsPerSample /* bits_cfg */, (info.nChans == 2) ? I2S_CHANNEL_STEREO : I2S_CHANNEL_MONO /* channel */);
    _samprate = info.sampRateOut;
  }
  size_t i2s_bytes_written = 0;
  for (int i = 0; i < len; i++)
  {
    pwm_buffer[i] = pwm_buffer[i] * GAIN_LEVEL;
  }
  i2s_write(_i2s_num, pwm_buffer, len * 2, &i2s_bytes_written, portMAX_DELAY);
  // log_i("len: %d, i2s_bytes_written: %d", len, i2s_bytes_written);
  total_play_audio_ms += millis() - s;
}
static void mp3AudioDataCallback(MP3FrameInfo &info, int16_t *pwm_buffer, size_t len, void*)
{
  unsigned long s = millis();
  if (_samprate != info.samprate)
  {
    log_i("bitrate: %d, nChans: %d, samprate: %d, bitsPerSample: %d, outputSamps: %d, layer: %d, version: %d",
          info.bitrate, info.nChans, info.samprate, info.bitsPerSample, info.outputSamps, info.layer, info.version);
    i2s_set_clk(_i2s_num, info.samprate /* sample_rate */, info.bitsPerSample /* bits_cfg */, (info.nChans == 2) ? I2S_CHANNEL_STEREO : I2S_CHANNEL_MONO /* channel */);
    _samprate = info.samprate;
  }
  size_t i2s_bytes_written = 0;
  for (int i = 0; i < len; i++)
  {
    pwm_buffer[i] = pwm_buffer[i] * GAIN_LEVEL;
  }
  i2s_write(_i2s_num, pwm_buffer, len * 2, &i2s_bytes_written, portMAX_DELAY);
  // log_i("len: %d, i2s_bytes_written: %d", len, i2s_bytes_written);
  total_play_audio_ms += millis() - s;
}

static uint8_t _frame[MP3_MAX_FRAME_SIZE]; // MP3_MAX_FRAME_SIZE is smaller, so always use MP3_MAX_FRAME_SIZE

static libhelix::AACDecoderHelix _aac(aacAudioDataCallback);
static void aac_player_task(void *pvParam)
{
  Stream *input = (Stream *)pvParam;

  int r, w;
  unsigned long ms = millis();
  while (r = input->readBytes(_frame, MP3_MAX_FRAME_SIZE))
  {
    total_read_audio_ms += millis() - ms;
    ms = millis();

    while (r > 0)
    {
      w = _aac.write(_frame, r);
      // log_i("r: %d, w: %d\n", r, w);
      r -= w;
    }
    total_decode_audio_ms += millis() - ms;
    ms = millis();
  }
  log_i("AAC stop.");

  vTaskDelete(NULL);
}

static libhelix::MP3DecoderHelix _mp3(mp3AudioDataCallback);
static void mp3_player_task(void *pvParam)
{
  Stream *input = (Stream *)pvParam;

  int r, w;
  unsigned long ms = millis();
  while (r = input->readBytes(_frame, MP3_MAX_FRAME_SIZE))
  {
    total_read_audio_ms += millis() - ms;
    ms = millis();

    while (r > 0)
    {
      w = _mp3.write(_frame, r);
      // log_i("r: %d, w: %d\n", r, w);
      r -= w;
    }
    total_decode_audio_ms += millis() - ms;
    ms = millis();
  }
  log_i("MP3 stop.");

  vTaskDelete(NULL);
}

static BaseType_t aac_player_task_start(Stream *input, BaseType_t audioAssignCore)
{
  _aac.begin();

  return xTaskCreatePinnedToCore(
      (TaskFunction_t)aac_player_task,
      (const char *const)"AAC Player Task",
      (const uint32_t)2000,
      (void *const)input,
      (UBaseType_t)configMAX_PRIORITIES - 1,
      (TaskHandle_t *const)NULL,
      (const BaseType_t)audioAssignCore);
}

static BaseType_t mp3_player_task_start(Stream *input, BaseType_t audioAssignCore)
{
  _mp3.begin();

  return xTaskCreatePinnedToCore(
      (TaskFunction_t)mp3_player_task,
      (const char *const)"MP3 Player Task",
      (const uint32_t)2000,
      (void *const)input,
      (UBaseType_t)configMAX_PRIORITIES - 1,
      (TaskHandle_t *const)NULL,
      (const BaseType_t)audioAssignCore);
}
#define READ_BUFFER_SIZE 1024
#define MAXOUTPUTSIZE (MAX_BUFFERED_PIXELS / 16 / 16)
// #define MAXOUTPUTSIZE (480 / 4 / 16)
#define NUMBER_OF_DECODE_BUFFER 3
#define NUMBER_OF_DRAW_BUFFER 9

#include <FS.h>
#include <JPEGDEC.h>

typedef struct
{
  int32_t size;
  uint8_t *buf;
} mjpegBuf;

typedef struct
{
  xQueueHandle xqh;
  JPEG_DRAW_CALLBACK *drawFunc;
} paramDrawTask;

typedef struct
{
  xQueueHandle xqh;
  mjpegBuf *mBuf;
  JPEG_DRAW_CALLBACK *drawFunc;
} paramDecodeTask;

static JPEGDRAW jpegdraws[NUMBER_OF_DRAW_BUFFER];
static int _draw_queue_cnt = 0;
static JPEGDEC _jpegDec;
static xQueueHandle _xqh;
static bool _useBigEndian;

static unsigned long total_read_video_ms = 0;
static unsigned long total_decode_video_ms = 0;
static unsigned long total_show_video_ms = 0;

Stream *_input;

int32_t _mjpegBufSize;

uint8_t *_read_buf;
int32_t _mjpeg_buf_offset = 0;

TaskHandle_t _decodeTask;
TaskHandle_t _draw_task;
paramDecodeTask _pDecodeTask;
paramDrawTask _pDrawTask;
uint8_t *_mjpeg_buf;
uint8_t _mBufIdx = 0;

int32_t _inputindex = 0;
int32_t _buf_read;
int32_t _remain = 0;
mjpegBuf _mjpegBufs[NUMBER_OF_DECODE_BUFFER];

static int queueDrawMCU(JPEGDRAW *pDraw)
{
  int len = pDraw->iWidth * pDraw->iHeight * 2;
  JPEGDRAW *j = &jpegdraws[_draw_queue_cnt % NUMBER_OF_DRAW_BUFFER];
  j->x = pDraw->x;
  j->y = pDraw->y;
  j->iWidth = pDraw->iWidth;
  j->iHeight = pDraw->iHeight;
  memcpy(j->pPixels, pDraw->pPixels, len);

  // log_i("queueDrawMCU start.");
  ++_draw_queue_cnt;
  xQueueSend(_xqh, &j, portMAX_DELAY);
  // log_i("queueDrawMCU end.");

  return 1;
}

static void decode_task(void *arg)
{
  paramDecodeTask *p = (paramDecodeTask *)arg;
  mjpegBuf *mBuf;
  log_i("decode_task start.");
  while (xQueueReceive(p->xqh, &mBuf, portMAX_DELAY))
  {
    // log_i("mBuf->size: %d", mBuf->size);
    // log_i("mBuf->buf start: %X %X, end: %X, %X.", mBuf->buf[0], mBuf->buf[1], mBuf->buf[mBuf->size - 2], mBuf->buf[mBuf->size - 1]);
    unsigned long s = millis();

    _jpegDec.openRAM(mBuf->buf, mBuf->size, p->drawFunc);

    // _jpegDec.setMaxOutputSize(MAXOUTPUTSIZE);
    if (_useBigEndian)
    {
      _jpegDec.setPixelType(RGB565_BIG_ENDIAN);
    }
    _jpegDec.setMaxOutputSize(MAXOUTPUTSIZE);
    _jpegDec.decode(0, 0, 0);
    _jpegDec.close();

    total_decode_video_ms += millis() - s;
  }
  vQueueDelete(p->xqh);
  log_i("decode_task end.");
  vTaskDelete(NULL);
}

static void draw_task(void *arg)
{
  paramDrawTask *p = (paramDrawTask *)arg;
  JPEGDRAW *pDraw;
  log_i("draw_task start.");
  while (xQueueReceive(p->xqh, &pDraw, portMAX_DELAY))
  {
    // log_i("draw_task work start: x: %d, y: %d, iWidth: %d, iHeight: %d.", pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight);
    p->drawFunc(pDraw);
    // log_i("draw_task work end.");
  }
  vQueueDelete(p->xqh);
  log_i("draw_task end.");
  vTaskDelete(NULL);
}

bool mjpeg_setup(Stream *input, int32_t mjpegBufSize, JPEG_DRAW_CALLBACK *pfnDraw,
                 bool useBigEndian, BaseType_t decodeAssignCore, BaseType_t drawAssignCore)
{
  _input = input;
  _mjpegBufSize = mjpegBufSize;
  _useBigEndian = useBigEndian;

  for (int i = 0; i < NUMBER_OF_DECODE_BUFFER; ++i)
  {
    _mjpegBufs[i].buf = (uint8_t *)malloc(mjpegBufSize);
    if (_mjpegBufs[i].buf)
    {
      log_i("#%d decode buffer allocated.", i);
    }
    else
    {
      log_e("#%d decode buffer allocat failed.", i);
    }
  }
  _mjpeg_buf = _mjpegBufs[_mBufIdx].buf;

  if (!_read_buf)
  {
    _read_buf = (uint8_t *)malloc(READ_BUFFER_SIZE);
  }
  if (_read_buf)
  {
    log_i("Read buffer allocated.");
  }

  _xqh = xQueueCreate(NUMBER_OF_DRAW_BUFFER, sizeof(JPEGDRAW));
  _pDrawTask.xqh = _xqh;
  _pDrawTask.drawFunc = pfnDraw;
  _pDecodeTask.xqh = xQueueCreate(NUMBER_OF_DECODE_BUFFER, sizeof(mjpegBuf));
  _pDecodeTask.drawFunc = queueDrawMCU;

  xTaskCreatePinnedToCore(
      (TaskFunction_t)decode_task,
      (const char *const)"MJPEG decode Task",
      (const uint32_t)2000,
      (void *const)&_pDecodeTask,
      (UBaseType_t)configMAX_PRIORITIES - 1,
      (TaskHandle_t *const)&_decodeTask,
      (const BaseType_t)decodeAssignCore);
  xTaskCreatePinnedToCore(
      (TaskFunction_t)draw_task,
      (const char *const)"MJPEG Draw Task",
      (const uint32_t)2000,
      (void *const)&_pDrawTask,
      (UBaseType_t)configMAX_PRIORITIES - 1,
      (TaskHandle_t *const)&_draw_task,
      (const BaseType_t)drawAssignCore);

  for (int i = 0; i < NUMBER_OF_DRAW_BUFFER; i++)
  {
    if (!jpegdraws[i].pPixels)
    {
      jpegdraws[i].pPixels = (uint16_t *)heap_caps_malloc(MAXOUTPUTSIZE * 16 * 16 * 2, MALLOC_CAP_DMA);
    }
    if (jpegdraws[i].pPixels)
    {
      log_i("#%d draw buffer allocated.", i);
    }
    else
    {
      log_e("#%d draw buffer allocat failed.", i);
    }
  }

  return true;
}

bool mjpeg_read_frame()
{
  if (_inputindex == 0)
  {
    _buf_read = _input->readBytes(_read_buf, READ_BUFFER_SIZE);
    _inputindex += _buf_read;
  }
  _mjpeg_buf_offset = 0;
  int i = 0;
  bool found_FFD8 = false;
  while ((_buf_read > 0) && (!found_FFD8))
  {
    i = 0;
    while ((i < _buf_read) && (!found_FFD8))
    {
      if ((_read_buf[i] == 0xFF) && (_read_buf[i + 1] == 0xD8)) // JPEG header
      {
        // log_i("Found FFD8 at: %d.", i);
        found_FFD8 = true;
      }
      ++i;
    }
    if (found_FFD8)
    {
      --i;
    }
    else
    {
      _buf_read = _input->readBytes(_read_buf, READ_BUFFER_SIZE);
    }
  }
  uint8_t *_p = _read_buf + i;
  _buf_read -= i;
  bool found_FFD9 = false;
  if (_buf_read > 0)
  {
    i = 3;
    while ((_buf_read > 0) && (!found_FFD9))
    {
      if ((_mjpeg_buf_offset > 0) && (_mjpeg_buf[_mjpeg_buf_offset - 1] == 0xFF) && (_p[0] == 0xD9)) // JPEG trailer
      {
        found_FFD9 = true;
      }
      else
      {
        while ((i < _buf_read) && (!found_FFD9))
        {
          if ((_p[i] == 0xFF) && (_p[i + 1] == 0xD9)) // JPEG trailer
          {
            found_FFD9 = true;
            ++i;
          }
          ++i;
        }
      }

      // log_i("i: %d", i);
      memcpy(_mjpeg_buf + _mjpeg_buf_offset, _p, i);
      _mjpeg_buf_offset += i;
      int32_t o = _buf_read - i;
      if (o > 0)
      {
        // log_i("o: %d", o);
        memcpy(_read_buf, _p + i, o);
        _buf_read = _input->readBytes(_read_buf + o, READ_BUFFER_SIZE - o);
        _p = _read_buf;
        _inputindex += _buf_read;
        _buf_read += o;
        // log_i("_buf_read: %d", _buf_read);
      }
      else
      {
        _buf_read = _input->readBytes(_read_buf, READ_BUFFER_SIZE);
        _p = _read_buf;
        _inputindex += _buf_read;
      }
      i = 0;
    }
    if (found_FFD9)
    {
      // log_i("Found FFD9 at: %d.", _mjpeg_buf_offset);
      if (_mjpeg_buf_offset > _mjpegBufSize)
      {
        log_e("_mjpeg_buf_offset(%d) > _mjpegBufSize (%d)", _mjpeg_buf_offset, _mjpegBufSize);
      }
      return true;
    }
  }

  return false;
}

bool mjpeg_draw_frame()
{
  mjpegBuf *mBuf = &_mjpegBufs[_mBufIdx];
  mBuf->size = _mjpeg_buf_offset;
  // log_i("_mjpegBufs[%d].size: %d.", _mBufIdx, _mjpegBufs[_mBufIdx].size);
  // log_i("_mjpegBufs[%d].buf start: %X %X, end: %X, %X.", _mjpegBufs, _mjpegBufs[_mBufId].buf[0], _mjpegBufs[_mBufIdx].buf[1], _mjpegBufs[_mBufIdx].buf[_mjpeg_buf_offset - 2], _mjpegBufs[_mBufIdx].buf[_mjpeg_buf_offset - 1]);
  xQueueSend(_pDecodeTask.xqh, &mBuf, portMAX_DELAY);
  ++_mBufIdx;
  if (_mBufIdx >= NUMBER_OF_DECODE_BUFFER)
  {
    _mBufIdx = 0;
  }
  _mjpeg_buf = _mjpegBufs[_mBufIdx].buf;
  // log_i("queue decode_task end");

  return true;
}