394 lines
19 KiB
C++
394 lines
19 KiB
C++
// BLOG Eletrogate
|
|
// ESP32 Frequency Meter
|
|
// ESP32 DevKit 38 pins + LCD
|
|
// https://blog.eletrogate.com/esp32-frequencimetro-de-precisao
|
|
// Rui Viana and Gustavo Murta august/2020
|
|
|
|
#include "stdio.h" // Library STDIO
|
|
#include "driver/ledc.h" // Library ESP32 LEDC
|
|
#include "driver/pcnt.h" // Library ESP32 PCNT
|
|
|
|
#define LCD_OFF // LCD_ON, if want use LCD 4 bits interface
|
|
#define LCD_I2C_OFF // LCD_I2C_ON, if want use I2C LCD (PCF8574)
|
|
#define JFS
|
|
#define TEST
|
|
#define WIFI
|
|
|
|
#ifdef LCD_I2C_ON // If I2C LCD enabled
|
|
#define I2C_SDA 21 // LCD I2C SDA - GPIO_21
|
|
#define I2C_SCL 22 // LCD I2C SCL - GPIO_22
|
|
#include <Wire.h> // I2C Libray
|
|
#include <LiquidCrystal_PCF8574.h> // Library LCD with PCF8574
|
|
LiquidCrystal_PCF8574 lcd(0x3F); // Instance LCD I2C - adress x3F
|
|
#endif // LCD I2C
|
|
|
|
#ifdef LCD_ON // LCD with 4 bits interface enabled
|
|
#include <LiquidCrystal.h> // Library LCD
|
|
LiquidCrystal lcd(4, 16, 17, 5, 18, 19); // Instance - ports RS,ENA,D4,D5,D6,D7
|
|
#endif
|
|
// LCD
|
|
#ifdef JFS
|
|
#include "SSD1306.h" // alias for `#include "SSD1306Wire.h" // Board Handling
|
|
// jfs Wemos lolin32
|
|
// jfs Heltec WiFi kit 32 (weisses Board)
|
|
// #define HELTEC
|
|
// Initialize the OLED display using Wire library
|
|
#ifdef HELTEC
|
|
SSD1306 display(0x3c, 4, 15);
|
|
#else
|
|
SSD1306 display(0x3c, 5, 4);
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef WIFI
|
|
#include <WiFi.h>
|
|
const int mxSize=4;
|
|
String ssids[mxSize] ={"GAST","pipanet","FRITZ!Box Gastzugang","WLAN-DE8245"};
|
|
String ssidp[mxSize] = {"passatvr6","passatvr6","praxis123","4955065570896956"};
|
|
boolean conok =false;
|
|
|
|
void netfound(int i){
|
|
display.clear();
|
|
display.setColor(BLACK);
|
|
display.fillRect(0, 0, 128, 10);
|
|
display.setColor(WHITE);
|
|
display.drawString(0,0,String(i));
|
|
display.drawString(20,0,"networks found");
|
|
display.display();
|
|
}
|
|
|
|
boolean init_wifi(){
|
|
boolean ok = false;
|
|
WiFi.mode(WIFI_STA);
|
|
WiFi.disconnect();
|
|
delay(100);
|
|
int n = WiFi.scanNetworks();
|
|
Serial.println("scan done");
|
|
if (n == 0) {
|
|
Serial.println("no networks found");
|
|
netfound(0);
|
|
} else {
|
|
Serial.print(n);
|
|
Serial.println(" networks found");
|
|
netfound(n);
|
|
for (int i = 0; i < n; ++i) {
|
|
for (int p=0;p<mxSize;p++){
|
|
if (WiFi.SSID(i).equals(ssids[p])){
|
|
String pp = ssidp[p];
|
|
String ss = WiFi.SSID(i);
|
|
WiFi.begin(ss.c_str(), pp.c_str());
|
|
i=n;
|
|
ok = true;
|
|
}
|
|
}
|
|
delay(10);
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define PCNT_COUNT_UNIT PCNT_UNIT_0 // Set Pulse Counter Unit - 0
|
|
#define PCNT_COUNT_CHANNEL PCNT_CHANNEL_0 // Set Pulse Counter channel - 0
|
|
|
|
#define PCNT_INPUT_SIG_IO GPIO_NUM_36 // Set Pulse Counter input - Freq Meter Input GPIO 34
|
|
#define LEDC_HS_CH0_GPIO GPIO_NUM_16 // Saida do LEDC - gerador de pulsos - GPIO_33
|
|
#define PCNT_INPUT_CTRL_IO GPIO_NUM_25 // Set Pulse Counter Control GPIO pin - HIGH = count up, LOW = count down
|
|
#define OUTPUT_CONTROL_GPIO GPIO_NUM_26 // Timer output control port - GPIO_32
|
|
#define PCNT_H_LIM_VAL overflow // Overflow of Pulse Counter
|
|
|
|
#define IN_BOARD_LED GPIO_NUM_2 // ESP32 native LED - GPIO 2
|
|
|
|
bool flag = true; // Flag to enable print frequency reading
|
|
uint32_t overflow = 20000; // Max Pulse Counter value
|
|
int16_t pulses = 0; // Pulse Counter value
|
|
uint32_t multPulses = 0; // Quantidade de overflows do contador PCNT
|
|
uint32_t sample_time = 1000000; // sample time of 1 second to count pulses 1000000
|
|
uint32_t osc_freq = 1254312; // Oscillator frequency - initial 12543 Hz (may be 1 Hz to 40 MHz)
|
|
uint32_t mDuty = 0; // Duty value
|
|
uint32_t resolution = 0; // Resolution value
|
|
float frequency = 0; // frequency value
|
|
char buf[32]; // Buffer
|
|
#ifdef JFS
|
|
uint32_t factor = 1; // factor in sample_time is different jfs
|
|
uint32_t frequenz = 0; // resulting frequency jfs
|
|
uint32_t save_osc = 0; // for output jfs
|
|
#endif
|
|
esp_timer_create_args_t create_args; // Create an esp_timer instance
|
|
esp_timer_handle_t timer_handle; // Create an single timer
|
|
|
|
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; // portMUX_TYPE to do synchronism
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
void setup()
|
|
{
|
|
#ifdef JFS
|
|
// put your setup code here, to run once:
|
|
#ifdef HELTEC
|
|
pinMode(16,OUTPUT); digitalWrite(16, LOW); delay(50); digitalWrite(16, HIGH);
|
|
#endif
|
|
#endif
|
|
|
|
Serial.begin(115200); // Serial Console Arduino 115200 Bps
|
|
Serial.println(" Input the Frequency - 1 to 40 MHz"); // Console print
|
|
|
|
#ifdef LCD_I2C_ON // If usinf I2C LCD
|
|
Wire.begin(I2C_SDA, I2C_SCL); // Begin I2C Interface
|
|
lcd.setBacklight(255); // Set I2C LCD Backlight ON
|
|
#endif
|
|
|
|
#if defined LCD_ON || defined LCD_I2C_ON // If using LCD or I2C LCD
|
|
lcd.begin(16, 2); // LCD init 16 x2
|
|
lcd.print(" Frequency:"); // LCD print
|
|
#endif
|
|
#ifdef JFS
|
|
#ifdef TEST
|
|
sample_time = 5000;
|
|
#endif
|
|
display.init();
|
|
display.flipScreenVertically();
|
|
display.clear();
|
|
display.drawString(0, 0, "Starting...");
|
|
display.display();
|
|
#ifdef WIFI
|
|
while (!init_wifi()){
|
|
delay(200);
|
|
}
|
|
display.drawString(0, 10, "Connecting to WiFi...");
|
|
display.display();
|
|
while (WiFi.status() != WL_CONNECTED) {
|
|
delay(500);
|
|
Serial.print(".");
|
|
}
|
|
display.drawString(0, 20, "\nWiFi connected, IP address: ");
|
|
display.drawString(0, 30, WiFi.localIP().toString());
|
|
//server.begin(); // server starten
|
|
display.display();
|
|
#endif
|
|
#endif
|
|
init_frequencyMeter (); // Initialize Frequency Meter
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void init_osc_freq () // Initialize Oscillator to test Freq Meter
|
|
{
|
|
#ifdef JFS
|
|
save_osc = osc_freq; // jfs for output
|
|
|
|
#endif
|
|
resolution = (log (80000000 / osc_freq) / log(2)) / 2 ; // Calc of resolution of Oscillator
|
|
if (resolution < 1) resolution = 1; // set min resolution
|
|
// Serial.println(resolution); // Print
|
|
mDuty = (pow(2, resolution)) / 2; // Calc of Duty Cycle 50% of the pulse
|
|
// Serial.println(mDuty); // Print
|
|
|
|
ledc_timer_config_t ledc_timer = {}; // LEDC timer config instance
|
|
|
|
ledc_timer.duty_resolution = ledc_timer_bit_t(resolution); // Set resolution
|
|
ledc_timer.freq_hz = osc_freq; // Set Oscillator frequency
|
|
ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE; // Set high speed mode
|
|
ledc_timer.timer_num = LEDC_TIMER_0; // Set LEDC timer index - 0
|
|
ledc_timer_config(&ledc_timer); // Set LEDC Timer config
|
|
|
|
ledc_channel_config_t ledc_channel = {}; // LEDC Channel config instance
|
|
|
|
ledc_channel.channel = LEDC_CHANNEL_0; // Set HS Channel - 0
|
|
ledc_channel.duty = mDuty; // Set Duty Cycle 50%
|
|
ledc_channel.gpio_num = LEDC_HS_CH0_GPIO; // LEDC Oscillator output GPIO 33
|
|
ledc_channel.intr_type = LEDC_INTR_DISABLE; // LEDC Fade interrupt disable
|
|
ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE; // Set LEDC high speed mode
|
|
ledc_channel.timer_sel = LEDC_TIMER_0; // Set timer source of channel - 0
|
|
ledc_channel_config(&ledc_channel); // Config LEDC channel
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
static void IRAM_ATTR pcnt_intr_handler(void *arg) // Counting overflow pulses
|
|
{
|
|
portENTER_CRITICAL_ISR(&timerMux); // disabling the interrupts
|
|
multPulses++; // increment Overflow counter
|
|
PCNT.int_clr.val = BIT(PCNT_COUNT_UNIT); // Clear Pulse Counter interrupt bit
|
|
portEXIT_CRITICAL_ISR(&timerMux); // enabling the interrupts
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
void init_PCNT(void) // Initialize and run PCNT unit
|
|
{
|
|
pcnt_config_t pcnt_config = { }; // PCNT unit instance
|
|
|
|
pcnt_config.pulse_gpio_num = PCNT_INPUT_SIG_IO; // Pulse input GPIO 34 - Freq Meter Input
|
|
pcnt_config.ctrl_gpio_num = PCNT_INPUT_CTRL_IO; // Control signal input GPIO 35
|
|
pcnt_config.unit = PCNT_COUNT_UNIT; // Unidade de contagem PCNT - 0
|
|
pcnt_config.channel = PCNT_COUNT_CHANNEL; // PCNT unit number - 0
|
|
pcnt_config.counter_h_lim = PCNT_H_LIM_VAL; // Maximum counter value - 20000
|
|
pcnt_config.pos_mode = PCNT_COUNT_INC; // PCNT positive edge count mode - inc
|
|
pcnt_config.neg_mode = PCNT_COUNT_INC; // PCNT negative edge count mode - inc
|
|
pcnt_config.lctrl_mode = PCNT_MODE_DISABLE; // PCNT low control mode - disable
|
|
pcnt_config.hctrl_mode = PCNT_MODE_KEEP; // PCNT high control mode - won't change counter mode
|
|
pcnt_unit_config(&pcnt_config); // Initialize PCNT unit
|
|
|
|
pcnt_counter_pause(PCNT_COUNT_UNIT); // Pause PCNT unit
|
|
pcnt_counter_clear(PCNT_COUNT_UNIT); // Clear PCNT unit
|
|
|
|
pcnt_event_enable(PCNT_COUNT_UNIT, PCNT_EVT_H_LIM); // Enable event to watch - max count
|
|
pcnt_isr_register(pcnt_intr_handler, NULL, 0, NULL); // Setup Register ISR handler
|
|
pcnt_intr_enable(PCNT_COUNT_UNIT); // Enable interrupts for PCNT unit
|
|
|
|
pcnt_counter_resume(PCNT_COUNT_UNIT); // Resume PCNT unit - starts count
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
void read_PCNT(void *p) // Read Pulse Counter
|
|
{
|
|
gpio_set_level(OUTPUT_CONTROL_GPIO, 0); // Stop counter - output control LOW
|
|
pcnt_get_counter_value(PCNT_COUNT_UNIT, &pulses); // Read Pulse Counter value
|
|
flag = true; // Change flag to enable print
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
void init_frequencyMeter ()
|
|
{
|
|
init_osc_freq(); // Initialize Oscillator
|
|
init_PCNT(); // Initialize and run PCNT unit
|
|
|
|
gpio_pad_select_gpio(OUTPUT_CONTROL_GPIO); // Set GPIO pad
|
|
gpio_set_direction(OUTPUT_CONTROL_GPIO, GPIO_MODE_OUTPUT); // Set GPIO 32 as output
|
|
|
|
create_args.callback = read_PCNT; // Set esp-timer argument
|
|
esp_timer_create(&create_args, &timer_handle); // Create esp-timer instance
|
|
|
|
gpio_set_direction(IN_BOARD_LED, GPIO_MODE_OUTPUT); // Set LED inboard as output
|
|
|
|
gpio_matrix_in(PCNT_INPUT_SIG_IO, SIG_IN_FUNC226_IDX, false); // Set GPIO matrin IN - Freq Meter input
|
|
gpio_matrix_out(IN_BOARD_LED, SIG_IN_FUNC226_IDX, false, false); // Set GPIO matrix OUT - to inboard LED
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
char *ultos_recursive(unsigned long val, char *s, unsigned radix, int pos) // Format an unsigned long (32 bits) into a string
|
|
{
|
|
int c;
|
|
if (val >= radix)
|
|
s = ultos_recursive(val / radix, s, radix, pos + 1);
|
|
c = val % radix;
|
|
c += (c < 10 ? '0' : 'a' - 10);
|
|
*s++ = c;
|
|
if (pos % 3 == 0) *s++ = ',';
|
|
return s;
|
|
}
|
|
//----------------------------------------------------------------------------------------
|
|
char *ltos(long val, char *s, int radix) // Format an long (32 bits) into a string
|
|
{
|
|
if (radix < 2 || radix > 36) {
|
|
s[0] = 0;
|
|
} else {
|
|
char *p = s;
|
|
if (radix == 10 && val < 0) {
|
|
val = -val;
|
|
*p++ = '-';
|
|
}
|
|
p = ultos_recursive(val, p, radix, 0) - 1;
|
|
*p = 0;
|
|
}
|
|
return s;
|
|
}
|
|
#ifdef JFS
|
|
uint32_t start = 100000;
|
|
void test(){
|
|
if (start < 100){
|
|
start = 100000;
|
|
} else {
|
|
osc_freq = start;
|
|
init_osc_freq ();
|
|
start -= 100;
|
|
}
|
|
}
|
|
#endif
|
|
//---------------------------------------------------------------------------------
|
|
void loop()
|
|
{
|
|
if (flag == true) // If count has ended
|
|
{
|
|
flag = false; // change flag to disable print
|
|
frequency = (pulses + (multPulses * overflow)) / 2 ; // Calculation of frequency
|
|
#ifdef JFS
|
|
factor = 1000000/sample_time;
|
|
frequenz = frequency*factor; // correct frequency if sample_time is smaller then 1sec
|
|
Serial.println(frequency);
|
|
#else
|
|
printf("Frequency : %s", (ltos(frequency, buf, 10))); // Print frequency with commas
|
|
printf(" Hz \n"); // Print unity Hz
|
|
#endif
|
|
#if defined LCD_ON || defined LCD_I2C_ON // If using LCD or I2C LCD
|
|
lcd.setCursor(2, 1); // Set cursor position - column and row
|
|
lcd.print((ltos(frequency, buf, 10))); // LCD print frequency
|
|
lcd.print(" Hz "); // LCD print unity Hz
|
|
#endif
|
|
#ifdef JFS
|
|
display.clear();
|
|
#ifdef WIFI
|
|
display.drawString(0,0,"IP : "+WiFi.localIP().toString());
|
|
#endif
|
|
display.drawString(0, 10,"sample time ms ");
|
|
display.drawString(80,10,String(sample_time/1000));
|
|
display.drawString(0, 20,"Pulses SVP");
|
|
display.drawString(60, 20, (ltos(frequency, buf, 10)));
|
|
display.drawString(0, 30,"Freq");
|
|
display.drawString(60, 30, (ltos(frequenz, buf, 10)));
|
|
display.drawString(0, 40,"Oscila 16");
|
|
display.drawString(60, 40, (ltos(save_osc, buf, 10)));
|
|
display.display();
|
|
#endif
|
|
multPulses = 0; // Clear overflow counter
|
|
// Put your function here, if you want
|
|
#ifdef JFS
|
|
#ifdef TEST
|
|
test();
|
|
#endif
|
|
delay (5); // Delay 100 ms 100
|
|
#else
|
|
delay(100);
|
|
#endif
|
|
// Put your function here, if you want
|
|
|
|
pcnt_counter_clear(PCNT_COUNT_UNIT); // Clear Pulse Counter
|
|
esp_timer_start_once(timer_handle, sample_time); // Initialize High resolution timer (1 sec)
|
|
gpio_set_level(OUTPUT_CONTROL_GPIO, 1); // Set enable PCNT count
|
|
}
|
|
|
|
String inputString = ""; // clear temporary string
|
|
osc_freq = 0; // Clear oscillator frequency
|
|
while (Serial.available())
|
|
{
|
|
char inChar = (char)Serial.read(); // Reads a byte on the console
|
|
inputString += inChar; // Add char to string
|
|
if (inChar == '\n') // If new line (enter)
|
|
{
|
|
Serial.println(inputString);
|
|
#ifdef JFS
|
|
if (inputString.startsWith("s")){
|
|
inputString.remove(0,1);
|
|
Serial.println(inputString);
|
|
sample_time = inputString.toInt();
|
|
Serial.println(sample_time);
|
|
}
|
|
if (inputString.startsWith("o")){
|
|
inputString.remove(0,1);
|
|
Serial.println(inputString);
|
|
osc_freq = inputString.toInt();
|
|
Serial.println(sample_time);
|
|
}
|
|
#else
|
|
osc_freq = inputString.toInt();
|
|
#endif
|
|
// Converts String into integer value
|
|
inputString = ""; // Clear string
|
|
}
|
|
}
|
|
if (osc_freq != 0) // If some value inputted to oscillator frequency
|
|
{
|
|
init_osc_freq (); // reconfigure ledc function - oscillator
|
|
}
|
|
}
|