Funktionsgenerator mit dem ESP32, Display und Gehäuse - [Teil 3] - AZ-Delivery

After we added an amplifier in the second part with which you can set both the amplitude and the offset, we no longer want to carry out the operation via the serial monitor, but directly on the device.

For example, the LCD1602 display Keypad Shield, which has an LCD display with two lines A 16 characters and buttons for operation, is suitable for this. This Shield was designed for the Microcontroller Arduino. So that it can be used simply with the ESP32, we replace the ESP32 Devkit CV4 from the first part with the Board ESP32 D1 R32, which is the same as an ArduinoUno has created. So we can put the Shield directly on it. However, examples will not work because the ESP32 has different IO pins than the Arduino. In addition, it should be noted that the inputs of the ESP32 can only tolerate 3.3 V.

The illustrations show the pin assignment of the ESP32 D1 R32 board and the occupancy of the LCD-Kypad Shield. This only includes those pins that are also used.

Since all LCD pins are used as inputs of the LCD controller, we do not need to worry about the tension, since the entrances also work safely with 3.3 V.

It looks a little different with the button connection. Depending on the button, it delivers a value between 0 and 5V. On the Shield, the output is connected to the +5V connection via a 2 Kohm resistance. If we now solder a resistance of 3.9 KOHM from the button connection and the 0V connection, the maximum voltage at this connection is only 5V * 3900 / (2000 + 3900) = 3.3 V. This resistance can be seen on the illustration.

We have another problem with the GPIO12 connection. This must be on 0V during the boat process, otherwise the operating voltage for the flash memory will be switched to 1.8V. Here again a resistance of 10 kohm between GPIO12 and GND. We better solder this resistance to the ESP32 D1 R32 board, since this problem can also occur with other Arduino Shields.

Image 1: Pin assignment of the ESP32 D1 R32

Image 2: Pin assignment of the LCD Keypad Shield (only used connections)

Image 3: back ESP32 D1 R32 with 10 KOHM resistance between GPIO12 and GND

Required hardware

All parts, including those from the second part, are listed here.

Number Component annotation
1 ESP32 D1 R32 board
2 LCD Keypad Shield
1 Resistance 3.9 Kohm
1 Resistance 10 kohm
1 LM358 Dual operational amplifier From part2
2 Potentiometer 10 kohm with 4mm axis From part2
1 Resistance 1 KOHM R3 From part2
1 Resistance 1.5 Kohm R5 From part2
1 Resistance 2.2 Kohm R2 From part2
2 Resistance 100 Kohm R1 and R4 From part2
1 Pencil bar 7-pin
1 Park bar 6-pin
3 Park bar 3-pin From part2
1 Park bar 2-pin From part2
3 Jumper wire cable female/female 3-pin
2 Jumper wire cable female/female 2-pin
1 P ladder plate or hole grid plate 30 x 40 mm From part2
1 DC-DC-Boost-Buck-converter with a positive and negative voltage input 5V, output +/- 5V From part2
1 BNC planking
2 Buttons for potentiometers
1 Buttons from the 3D printer with TPU filament
4 Housing and spacers from the 3D printer with PLA filament


8 Tin screws 2.2 x 6.5 mm
4 Tin screws 2.2 x 9.5 mm


The software


/* 
 * Functional generator for sinus, triangle and rectangle signals
 * Adjustable frequency 20 Hz up to 20 kHz
 * For triangle and rectangle adjustable dipper ratio 0 to 100%
*/

// Libraries for direct access to the tax register of the ESP32
#include "Soc/rtc_cntl_reg.h"
#include "Soc/Sens_REG.H"
#include "Soc/RTC.H"

// Libraries for the use of the digitally to be analogous converter and for the I2S bus
#include "Driver/DAC.H"
#include "Driver/i2s.h"

// library for the LCD display
#include <Liquidcrystal.H>

#define Sinfact 127.0 // measured for step size = 1 and no advantage (8.3MHz)
#define Signalout 26 // PIN for the signal edition

// LCD pins
#define Pin_rs 12  // tab 0 = commands 1 = data
#define Pin_en 13  // enable clock to write
#define PIN_D4 17  // databit 
#define PIN_D5 16  // databit
#define PIN_D6 27  // databit
#define PIN_D7 14  // databit
#define Pin_bl 5   // backlight 0 = from

// analog pin for buttons
#define Keys A12

// voltage divider for buttons
#define RV 2000  // pre -resistance
#define R3 3900  // protective resistance for a maximum of 3.3 V
#define RP 1000  // Resistance GPIO2 against mass
#define RR 0     // voltage divider with the Right key pressed
#define Ru 330   // voltage divider with pressed up button
#define RD 950   // voltage divider with the Down button pressed
#define RL 1950  // voltage divider with pressed LEFT button
#define RS 5250  // voltage divider with pressed Select button

// keys codes
#define None    0
#define Leaf    1
#define Right   2
#define Up      3
#define Down    4
#define Select  5

// operating modes
#define Msinus 0
#define Mrectangle 1
#define Mtriangle 2

// change types
#define Emode 0
#define Efrequency 1
#define Eratio 2

// Init i2c LCD
Liquidcrystal LCD(Pin_rs, Pin_en, PIN_D4, PIN_D5, PIN_D6, PIN_D7);

// variables for saving the threshold values ​​for buttons
uint16_t Original, Uu, UD, UL, US;

// buffer for creating the triangular function
uint32_t buf[128];

// Setting values ​​for corner shape, frequency and dicer
int8_t Fashion = Msinus; // 0 = sinus, 1 = rectangle, 2 = triangle
float frequency = 1000; // 20 to 200,000 Hz
int8_t ratio = 50; // Test ratio 0 to 100%

int8_t edit = Emode; // What is changed 0 = fashion 1 = frequency 2 = duty ratio

uint32_t tic; // for waiting time

int8_t Lastkey = 0; // last key or 0 if none
uint16_t step = 0;  // Step width for frequency increase
float FTMP;         // variable for saving the frequency during setting
int16_t RTMP;       // Variable for saving the duty ratio during setting
int8_t MTMP;        // variable for saving the operating mode during setting

// flag is true if the initialization has already taken place
Bool initdone = false;


// configuration for the I2S bus
I2S_Config_t I2S_Config = {
     .Fashion = (i2s_mode_t)(I2S_Mode_Master | I2s_mode_tx | I2s_mode_dac_built_in), // operating mode
     .sample_rate = 100000, //Sampling rate
     .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // The DAC only uses 8 bit of the MSB
     .Channel_Format = I2S_CANNEL_FMT_Right_Left, // Channel format ESP32 only supports stereo
     .Communication_Format = (I2S_Comm_Format_t)I2S_Comm_Format_i2S_MSB, // Standard format for I2S
     .intr_alloc_flags = 0, // standard interrupt 
     .dma_buf_count = 2, // Number of FIFO Buffer
     .dma_buf_len = 32, // size of the FIFO buffer
     .use_apll = 0 // clock source
    };


// fill buffer for triangle waveform
// parameter up is the duration for the increase in percent
// Parameter SZ specifies the buffer size for a period
// The values ​​for a period are written in the buffer
void fill buffer(uint8_t up, uint8_t SZ) {
  uint8_t down;  // time for the falling flank in %
  uint32_t sample; // 32bit data word (I2S needs two channels with 16 bit each
  float you,DD,Val; // auxiliary variables
  down=100-up;
  // calculate the number of steps for increase and waste
  uint16_t interrogation = round(1.0*SZ/100 * up);
  uint16_t stdwn = round(1.0*SZ/100*down);
  uint16_t I;
  IF ((interrogation + stdwn) < SZ) interrogation++;// compensation of possible rounding errors
  // amplitude change per step for increase and waste 
  you = 256.0/interrogation;
  DD = 256.0/stdwn;
  // fill the buffer
  Val = 0; // climb begins with 0
  for (I=0; I<interrogation; I++) {
    sample = Val; 
    sample = sample << 8; // move byte to the higher -quality byte 
    buf[I]=sample;
    Val = Val+you; // increase value
  }
  Val=255; // slide flank begins with maximum value
  // rest as with the rising flank
  for (I=0; I<stdwn; I++) {
    sample = Val;
    sample = sample << 8;
    buf[I+interrogation]=sample;
    Val = Val-DD;
  }
}


// stop all outputs
void stopall(){
    ledcdetachpin(Signalout); 
    I2S_Driver_uninstall((i2S_port_t)0); 
    DAC_OUTPUT_DISABLE(Dac_channel_2);
    dac_i2s_disable();
    initdone=false;
}

// start curve form rectangle
// assign a pin for signal output
void startrectangle(){
    ledcattachpine(Signalout,1 );
    initdone=true;
}

// Set frequency for rectangle with a corresponding dip ratio
void Rectanglesis frequency(double frequency,uint8_t ratio)
{
    ledcsetup(1,frequency,7); // We use the LEDC function with 7 bit resolution
    ledcwrite(1,127.0*ratio/100);  // Calculation of the step number for condition = 1
}


// Start the triangle signal
void triangle(){
  i2s_set_pin((i2S_port_t)0, ZERO); // i2s is used with the DAC
    initdone=true;
}

// Set frequency for triangle with a corresponding dip ratio
double Trianglesnet frequency(double frequency,uint8_t ratio)
{
  intimately size=64;
  // First the suitable buffer size is determined
  // In this way the output functioning, the I2S soda between
  // 5200 and 650000 lie
  IF (frequency<5000) {
    size = 64;
  } Else IF (frequency<10000) {
    size = 32;
  } Else IF (frequency<20000) {
    size = 16;
  } Else {
    size = 8;
  }
  // Subscribe must spend both buffers in one period
  uint32_t rate = frequency * 2 * size;
  // The sampling rate may only be within the limit values
  IF (rate < 5200) rate = 5200;
  IF (rate > 650000) rate = 650000;
  // set real frequency value
  frequency = rate / 2 / size;

  // Remove I2S driver 
  I2S_Driver_uninstall((i2S_port_t)0);
  // adjust configuration 
  I2S_Config.sample_rate = rate;
  I2S_Config.dma_buf_len = size;
  // install with the new configuration
  I2S_Driver_Intall((i2S_port_t)0, &I2S_Config, 0, ZERO);
  // Set the sampling rate
  i2S_set_sample_rates((i2S_port_t)0, rate); 
  // fill buffer
  fill buffer(ratio,size*2);
  // and output once
  i2S_write_bytes((i2S_port_t)0, (const char *)&buf, size*8, 100);  
  return frequency;
}

// prepare sinus output
void start -in(){
    // Release output for signal output
    dac_outPut_enable(Dac_channel_2);
    // Activate the generator of the sinus
    Set_peri_reg_mask(Sens_sar_dac_ctrl1_reg, Sens_sw_tone_en);
    // start output on channel 1
    Set_peri_reg_mask(Sens_sar_dac_ctrl2_reg, Sens_dac_cw_en2_m);
    // reverse the sign bit
    Set_peri_reg_bits(Sens_sar_dac_ctrl2_reg, Sens_dac_inv2, 2, Sens_dac_inv2_s);
    initdone=true;
}

// set frequency for sinus
double Sinus set frequency(double frequency)
{
  // Formula f = s * Sinfakt /V
  // s are the steps per tactic pulse
  // V is the advantage for the 8MHz clock
  // There are 8 advantage of 1 to 1/8 for the combination of advantageous and
  // to find the number of steps, we test all eight advantage variants
  // The combination with the slightest frequency deviation is chosen
  
    double F,delta,delta_min = 999999999.0;
    uint16_t divi=0, step=1, S;
    uint8_t clk_8m_div = 0;// 0 to 7
    for (uint8_t div = 1; div<9; div++){
      S=round(frequency * div/Sinfact);
      IF ((S>0) && ((div == 1) || (S<1024))) {
        F= Sinfact*S/div;
        /*
 Serial.print (f); Serial.print ("");
 Serial.print (Div); Serial.print ("");
 Serial.println (s);
        */
        delta = Section(F-frequency);
        IF (delta < delta_min) { // Deviation less -> Remember current values
          step = S; divi = div-1; delta_min = delta;
        }
      }
    }
    // set real frequency value
    frequency = Sinfact * step / (divi+1);
    // adjust the advantage
    Reg_set_field(Rtc_cntl_clk_conf_reg, Rtc_cntl_ck8m_div_sel, divi);
    // Set steps per clock pulse
    Set_peri_reg_bits(Sens_sar_dac_ctrl1_reg, Sens_sw_fstep, step, Sens_sw_fstep_s);
    return frequency;
}

// Conduct changes
void controller() {
  switch (Fashion) {
    case Msinus: IF (!initdone) start -in();
        frequency = Sinus set frequency(frequency);
        break;
    case Mrectangle : IF (!initdone) startrectangle();
        Rectanglesis frequency(frequency,ratio);
        break;
    case Mtriangle : IF (!initdone) triangle();
        frequency = Trianglesnet frequency(frequency,ratio);
        break;
  }
}

// update display
// If the monitor is true, the output takes place 
// also on the serial interface
void display values(Boolean monitor) {
  char buf[15];
  // output current values
  String BA;
  switch (Fashion) {
    case Msinus: BA="Sine     "; break;
    case Mrectangle: BA="Rectangle"; break;
    case Mtriangle: BA="Triangle"; break;
  }
  // output operating mode
  LCD.setcursor(0,0);
  LCD.print(" ");
  LCD.print(BA);
  IF (monitor) {
    Serial.print("*************);
    Serial.print("Operating mode ="); Serial.print(BA);
  }
  // frequency depending on the value as Hz or KHz
  IF (frequency < 1000){
    sprint(buf,"%6.2f Hz",frequency);
  } Else {
    sprint(buf,"%6.2fkHz",frequency/1000);
  }
  // output frequency
  LCD.setcursor(0,1);
  LCD.print("F"); LCD.print(buf);
  IF (monitor) {
    Serial.print("Frequency ="); Serial.print(buf);
  }
  sprint(buf,"%2i %%",ratio);
  // output a tactning ratio
  LCD.setcursor(11,1);
  LCD.print("T"); LCD.print(buf);
  IF (monitor) {
    Serial.print("Test ratio ="); Serial.print(buf);
    Serial.print();
  }
  // output arrow signs depending on the edit fashion
  switch (edit) {
    case Emode: LCD.setcursor(0,0); break;
    case Efrequency: LCD.setcursor(0,1); break;
    case Eratio: LCD.setcursor(11,1); break;
  }
  LCD.print(char(126));
}

// Change edit mode with up and down button
void change(Boolean up) {
  // Depending on the direction, positive or negative
  intimately S = up?1:-1;
  edit += S;
  // jump back at the end at the beginning
  IF (edit < 0) edit = 2;
  IF (edit > 2) edit = 0;
  // The current values ​​in the temporary values
  // copy for the change
  FTMP = frequency;
  RTMP = ratio;
  MTMP = Fashion;
  // output changed edit fashion
  Serial.print("Mode =");Serial.print(Fashion);
  // update display without output on serial
  //Interface
  display values(false);
}

// Change operating mode with Right and LEFT button
void changemod(Boolean up) {
  // Depending on the direction, positive or negative
  intimately S = up?1:-1;
  // change temporary operating mode
  MTMP += S;
  // When the end is reached jump back at the beginning
  IF (MTMP < 0) MTMP = 2;
  IF (MTMP > 2) MTMP = 0;
  // Show changed operating mode on the display
  LCD.setcursor(1,0);
  switch (MTMP) {
    case 0: LCD.print("Sine     "); break;
    case 1: LCD.print("Rectangle"); break;
    case 2: LCD.print("Triangle"); break;
  }
}

// Change frequency with Right and Left key
void Changefrequency(Boolean up) {
  // If the button was not pressed before, the step is set to 1
  // While the button is pressed, the step size becomes continuous
  // doubles until a maximum step width was reached
  step = (Lastkey == None)?1:step*2;
  IF (step > 1024) step = 1024;
  // determine direction factor
  int16_t S = up?1:-1;
  // change temporary frequency
  FTMP = FTMP+S*step;
  // check for minimal and maximum values
  IF (FTMP < 20) FTMP=20;
  IF (FTMP > 20000) FTMP = 20000;
  char buf[15];
  // for the ad HZ or KHz
  IF (FTMP > 999) {
    sprint(buf,"%6.2fkHz",FTMP/1000.0);
  } Else {
    sprint(buf,"%6.2f Hz",FTMP*1.0);
  }
  // Show changed frequency on the display
  LCD.setcursor(2,1);
  LCD.print(buf);
}

// Change the pattern ratio with Right and Left button
void changeratio(Boolean up) {
  // Set direction
  int8_t JX = up?1:-1;
  // Change temporary tactning ratio
  RTMP = RTMP+JX;
  // check for minimal and maximum values
  IF (RTMP < 0) RTMP=0;
  IF (RTMP > 100) RTMP = 100;
  char buf[15];
  // Show changed diconing ratio on the display
  sprint(buf,"%2i %%",RTMP);
  LCD.setcursor(13,1);
  LCD.print(buf);  
}

// The function generator is released on the changed setting
// The temporary values ​​are adopted in the current values
void setValues() {
  Serial.print("Set values edit = "); Serial.println(edit);
  switch (edit) {
    case EMODE: stopAll(); mode = mtmp; break;
    case EFREQUENCY: frequency = ftmp; break;
    case ERATIO: ratio = rtmp; break;
  }
  //Funktionsgenerator selber ändern
  controlGenerator();
  displayValues(true);
}

//Tastaturspannung einlesen und auswerten
void handleKeys() {
  //Tastaturspannung einlesen
  int x=analogRead(KEYS);
  uint8_t key = NONE;
  if (x < Ur) { key = RIGHT; }
  else if (x < Uu) { key = UP; }
  else if (x < Ud){ key = DOWN; }
  else if (x < Ul){ key = LEFT; }
  else if (x < Us){ key = SELECT; }
  else {key = NONE;}
  if (((key == UP) || (key == DOWN)) && (lastKey == NONE)) changeEdit(key == DOWN);
  if ((key == LEFT) || (key == RIGHT)) {
    switch (edit) {
      case EMODE: if (lastKey == NONE) changeMode(key == RIGHT); 
        break;
      case EFREQUENCY: changeFrequency(key == RIGHT); 
        break;
      case ERATIO: changeRatio(key == RIGHT); 
        break;
    }
  }
  if ((key == SELECT) && (lastKey == NONE)) setValues();
  lastKey = key; 
  tic = millis();
}
//Serielle Schnittstelle aktivieren und 
//Defaulteinstellungen 1kHz Sinus setzen
//Schwellwerte für Tastatur festlegen
void setup()
{
    Serial.begin(115200);
    controlGenerator();
    lcd.begin(16,2);                           // initialisiere LCD I2C Anzeige
    lcd.clear();
    displayValues(true);
    tic = millis();
    Serial.print("Kommando M,F,A,T,O : ");
    pinMode(2,INPUT);
    //Schwellwerte für Taster berechnen
    //Diese Berechnung ist notwendig, da die Toleranzen sehr
    //gering sind, und die Schwellwerte von der Betriebsspannung
    //abhängen.
    //Versorgungsspannung ohne Taste ermitteln
    int x=analogRead(A12);
    float Rin = R3 * Rp / (R3 + Rp);
    float Ub = x / Rin * (Rin + Rv);
    //Schwellspannungen ermitteln
    float Uup,Udn,Ulf,Usl,Rtmp;
    //Mittelwert für UP Taste
    Rtmp = Rin * Ru / (Rin + Ru);
    Uup = Ub * Rtmp / (Rtmp + Rv); 
    //Mittelwert für DOWN Taste
    Rtmp = Rin * Rd / (Rin + Rd);
    Udn = Ub * Rtmp / (Rtmp + Rv); 
    //Mittelwert für LEFT Taste
    Rtmp = Rin * Rl / (Rin + Rl);
    Ulf = Ub * Rtmp / (Rtmp + Rv); 
    //Mittelwert für Select Taste
    Rtmp = Rin * Rs / (Rin + Rs);
    Usl = Ub * Rtmp / (Rtmp + Rv); 
    //eigentliche Schwellwerte berechnen
    //immer in die Mitte zwischen zwei Mittelwerten
    Ur = Uup/2;
    Uu = Uup + (Udn - Uup) / 2;
    Ud = Udn + (Ulf - Udn) / 2;
    Ul = Ulf + (Usl - Ulf) / 2;
    Us = Usl + (x-Usl) /2;
    //Schwellwerte auf die serielle Schnittstelle ausgeben
    Serial.printf("Schwellwerte: right %i, up %i, down %i, left %i, select %i\n",Ur,Uu,Ud,Ul,Us);
    
}


void loop(){
  if ((millis()-tic) > 200) handleKeys();
  //Serielle Schnittstelle abfragen
  if (Serial.available() > 0) {
    //Befehl von der Schnittstelle einlesen
    String inp = Serial.readStringUntil('\n');
    //und zur Kontrolle ausgeben
    Serial.println(inp);
    char cmd = inp[0]; //erstes Zeichen ist das Kommando 
    if ((cmd == 'M') || (cmd == 'm')) { //war das Zeichen 'M' wird die Betriebsart eingestellt
      char newMode = inp[1]; //zweites Zeichen ist die Betriebsart
      uint8_t nm=0;
      switch (newMode) {
        case 's':
        case 'S': nm=0; break;
        case 'r':
        case 'R': nm=1; break;
        case 't':
        case 'T': nm=2; break;
      }
      if (nm != mode) { //Nur wenn eine Änderung vorliegt, muss was getan werden
        stopAll(); 
        mode=nm;
        controlGenerator();
      }
    } else {
      //bei den anderen Befehlen folgt ein Zahlenwert
      String dat = inp.substring(1);
      //je nach Befehl, werden die Daten geändert
      switch (cmd) {
        case 'F' :
        case 'f' :frequency = dat.toDouble(); break; //Frequenz
        case 'T' :
        case 't' :ratio = dat.toInt(); break;  //Tastverhältnis
      }
      //Grenzwerte werden überprüft
      if (ratio > 100) ratio = 100;
      if (frequency < 20) frequency = 20;
      if (frequency > 20000) frequency = 20000;
      controlGenerator();
    }
    //aktuelle Werte ausgeben
    display values(true);
    Serial.print("Command m, f, t:");
  }
}

Sketch to download:

The part for the function generator and for operation via the serial interface is identical to the sketch from part 1. What's new is the operation via the button on the display Keypad Shield. In particular, the evaluation of the buttons requires special attention.

Image 4: Tension divider for the keys on the LCD Keypad Shield and on the left the ESP32 D1 R32 board with the parallel resistance

The voltage divider is designed in such a way that at 5V the difference between the individual voltages is about 1V. However, since we have installed the 3900-ohm protective resistance so that the outcome never reaches more than 3.3 V, this difference is reduced. But the problem is getting bigger. The ESP32 D1 R32 Board has a 1kOhm resistance in parallel to the GPIO2 input so only the GPIO0 has to be placed on GND to flash. The tension difference is reduced by this resistance. The table shows the tensions that you get from the buttons (without parallel resistance, with 3900 ohms in parallel, and with 3900 ohms and 1000 ohms in parallel.

Button Without parallel resistance 3900 Ohm 3900 ohms and 1000 ohms
No button 5.00 V 3.30 V 2.20 V
Select 3.60 V 2.60 V 2.04 V
Leaf 2.50 V 1.97 V 1.10 V
Down 1.60 V 1.4 V 0.89 V
Up 0.70 V 0.66 V 0.52 V
Right 0.00 V 0.00 V 0.00 V


In tests, it has been shown that the 5V voltage is not very precise. With an external power supply, the voltage was 4.4V, with the supply via USB between 5.0V on a computer connection and 5.2V on a USB charger.

If you were now working with fixed threshold values ​​for each button, would the device work or not? In order to ensure a secure function, the operating voltage is therefore determined without pressing the button when switched on. The medium voltages per button can then be calculated from this. Since the query is smaller when evaluating the key, you get the best result if you add half of the distance to the next higher average to get the threshold value for the medium voltage.

The housing

In order to increase usability, we have designed housing that can be made with a 3D printer. The housing is structured so that all parts can be accommodated in it.

Image 5: housing from the 3D printer

On the ground, you can see the rectangular recess for the display and the openings for the buttons. On the right next to it the two holes for the potentiometers. On the right wall, there is a hole for the BNC socket and on the left wall, there is a rectangular recess for the USB connector and for the power supply connector.

There are four distance cylinders with holes to attach the circuit boards.

The right lid has no recesses and can be easily attached.

The buttons are arranged on a common base plate for easier assembly. So that they can be pressed individually, they must be printed with an elastic filament, for example, TPU. If this is not possible, the individual buttons must be separated from the base plate after printing so that they can be operated individually.

Image 6: Buttons from the 3D printer

Finally, there are two spacers that are to be inserted between Shield and the display to prevent the display from tensioning when screwing.

Figure 7: LCD Keypad Shield, installation of the spacers


Links to download the 3D print files:

Housing lower part, Lid, Buttons, Spacer piece 1, Spacer piece 2

assembly

The LCD Keypad Shield also has free soldering points that are connected to the pen strips in addition to the pin strips that are used for the connection to the ESP32 D1 R32 board. We use these to connect the external modules to the board. To do this, we also have to stock them with pen strips.

Image 8: Additional pen strips on the LCD Keypad Shield

Likewise, the 3.9 KOHM should not be forgotten.

We start the assembly with the buttons. You are inserted into the corresponding recesses in the housing. The two potentiometers with a connection cable follow. For the connection cables, we halve a 3-pin jumper wire cable and solder it with the potentiometers. A strain relief with a cable tie is an advantage.

Figure 9: Installation of the buttons in the housing

Figure 10: Installation of the potentiometers and the BNC socket (signal output)

A 2-pin jumper wire cable is also halved and soldered with the BNC socket.

If the two potentiometers and the BNC socket are installed, the LCD keypad shield can be attached in the housing with four screws. Don't forget to insert the two distant pieces between the display and the board. The voltage converter module and the amplifier module can also be attached with four screws.

Figure 11: Housing with LCD Keypad Shield, DC-DC converter, surgical amplifier and wiring


The cables can now be inserted according to the wiring plan.

Figure 12: Wiring plan


Finally, the ESP32 D1 R32 board is plugged into the LCD keypad shield.

Figure 13: Shared housing with the complete structure

Now on top of the lid, and assemble buttons for the potentiometers, then our functional generator is ready for use. We can use a USB charger or a power supply with 6 to 12 V for the power supply.

Figure 14: Finished function generator on the oscilloscope

Good luck in the replica.

Link to the blog post as PDF

DisplaysEsp-32Projekte für fortgeschrittene

8 commenti

Andreas Wolter

Andreas Wolter

@lorenz: ich vermute, dass es eine Änderung im ESP Core gab. Die Methode ledcDetachPin() gehört zur LED Control (LEDC) Library und ist für PWM Steuerung gedacht. Es scheint, als hätte man an der Stelle Änderungen vorgenommen. Ich würde zuerst versuchen, sie umzubenennen in ‘ledcDetach’
Infos dazu hier: https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/ledc.html

Es könnte auch helfen, auf eine ältere Version der Bibliotheken zurückzugehen. Das ist leider ein sehr häufig auftretendes Problem bei der Verwendung der ESP Bibliotheken.

Grüße,
Andreas Wolter
AZ-Delivery Blog

lorenz

lorenz

Hallo,
ich habe den Fehler.
‘ledcDetachPin’ was not declared in this scope
Wie kann ich das ändern?
Gruß Lorenz

Andreas Wolter

Andreas Wolter

@Bastlersiggi: wahrscheinlich wurde die Bibliothek verändert. Es gibt die Funktion i2s_write(). Damit könnte es wieder funktionieren. Oder zur älteren Version zurückkehren.

https://github.com/earlephilhower/ESP8266Audio/issues/273

Mit freundlichen Grüßen,
Andreas Wolter
AZ-Delivery Blog

Bastlersiggi

Bastlersiggi

Der obige Sketch ergibt bei mir folgende Fehlermeldung:
FunktionsgeneratorESP32.ino:
In function ‘double triangleSetFrequency(double, uint8_t)’:
146:2: error: ‘i2s_write_bytes’ was not declared in this scope
i2s_write_bytes((i2s_port_t)0, (const char )&buf, size8, 100);
^~~~~~~~~~~~~~~
i2s_write_expand

exit status 1
Compilation error: ‘i2s_write_bytes’ was not declared in this scope

Verwendete IDE: 2.3.2 Board: ESP32 WROOM

Welchen Code kann man stattdessen verwenden bzw. den Sketch aktualisieren?

Gerald Lechner

Gerald Lechner

Hallo Claus, GPIO12 ist schon richtig. Es ist nicht GPIO02 gemeint. Der Pin GPIO12 dient beim ESP32 dazu, die Versorgungsspannung für den Flash-Speicher zu steuern. Ist GPIO12 beim Bootvorgang auf High, beträgt die Versorgungsspannung 1.8 V, bei LOW 3.3V. Daher muss GPIO12 beim Booten auf LOW sein.
Das mit dem Widerstand ist richtig. Beim ESP32 Board könnte er weggelassen werden. Ich habe ihn auf das Display gelötet, da ich dieses auch mit der ESP8266 Version genutzt habe. Bei diesem Board ist dieser Anschluss mit A0 verbunden. A0 ist aber ein hochohmiger Eingang, der dann mit 5V beschädigt würde. Mit dem 3.9 kOhm Widerstand ist man auf der sicheren Seite und es funktionieren die Tasten trotzdem problemlos. Der Funktionsgenerator funktioniert natürlich nur mit dem ESP32!

Claus Teubner

Claus Teubner

Hallo,
wie immer, sehr schöner Artikel.
Heute habe ich den versteckten Tipfehler :-) gefunden.
‘…Ein weiteres Problem haben wir mit dem GPIO12 Anschluss. Der muss während des Bootvorgangs auf 0V liegen…’
Sollte doch GPIO2 statt GPIO12 Anschluss sein.
Außerdem sollte doch der 3,9kOhm Widerstand direkt am ESP32 reichen, dann bleibt das Display unverändert und wenn beides zusammengesteckt ist, ist sichergestellt, dass nur max. 3,3V am ESP anliegen. Falls die 3,9kOhm zum booten nicht reicht, tut es auch der 1kOhm alleine, dann sind die Taster-Spannungen etwas größer.

Bernd Albrecht

Bernd Albrecht

@Sven: Der D1 R32 ist “baulich” am Uno angelehnt. Die Uno Shields können also aufgesteckt werden. Aber es gibt deutliche Unterschiede bei der Pinbelegung, der gravierendste ist die zulässige Spannung von 3,3V beim D1 R32. Um das LCD Keypad Shield zu verwenden, müssen deshalb wie beschrieben die zusätzlichen Widerstände angelötet werden.

Sven Waibel

Sven Waibel

Hallo,
wieso steht beim LCD, den ihr verlinkt habt, dass dieser nicht mit dem D1 R32 kompatibel ist?
Grüße
Sven

Lascia un commento

Tutti i commenti vengono moderati prima della pubblicazione