ESP32 variable scope problem

wesS2020
Posts: 7
Joined: Thu Mar 25, 2021 5:31 am

ESP32 variable scope problem

Postby wesS2020 » Thu Mar 25, 2021 6:15 am

I am working on converting a project from Microchip PIc32 to ESP32. Because I am using different peripherals - WiFi, SD Card, UART, I am using different "C" source files.
And here I came across the problem with variables not retaining values between different source files.
Here is an example: I have header file def.h and two source files sdcard.c and hub_main.c.
I will only show part of relevant code:
In def.h

Code: Select all

#define		MAX_FCMMSG1			17
#define		MAX_FCMMSG2			17
char FcmMsg1[MAX_FCMMSG1][20];
char FcmMsg2[MAX_FCMMSG2][10];
In hub_main.c

Code: Select all

#include "sdcard.h"
#include "def.h"

void app_main(void){
    //Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
    initData();
    board_init();
    initSD_Card();
    SD_Read();
    ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
    wifi_init_softap();
    uart_init();
    xTaskCreate(uart_event_task, "uart_event_task", 2048, NULL, 12, NULL);
	//added this to check the value in FcmMsg1[2]
    ESP_LOGI(TAG, "*** msg1 2 %s", FcmMsg1[2]);
And in sdcard.c

Code: Select all

#include "sdcard.h"
#include "def.h"


void SD_Read(void){
	struct stat st;
	FILE* f;
	int size;

	stat(MOUNT_POINT"/msg1.hex", &st);
	size = st.st_size;
    ESP_LOGI(TAG, "file msg1.hex size %d", size);
    if(size > 0){
    	f = fopen(MOUNT_POINT"/msg1.hex", "r");
        if (f == NULL) {
            ESP_LOGE(TAG, "Failed to open file msg1.hex for reading");
            return;
        }
    	memset(&FcmMsg1, 0, sizeof(FcmMsg1)-1);
    	fread((void*) FcmMsg1, 1, size, f);
		startupState.insteon = 1;
		fclose(f);
    }
    ESP_LOGI(TAG, "msg1 2 %s", FcmMsg1[2]);

 	f = fopen(MOUNT_POINT"/msg2.hex", "r");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file msg2.hex for reading");
        return;
    }
	memset(&FcmMsg1, 0, sizeof(FcmMsg2)-1);
	stat(MOUNT_POINT"/msg2.hex", &st);
	size = st.st_size;
    ESP_LOGI(TAG, "file msg2.hex size %d", size);
    if(size > 0){
    	fread((void*) FcmMsg2, 1, size, f);
		startupState.insteon = 1;
    	fclose(f);
    }
    ESP_LOGI(TAG, "msg2 2 %s", FcmMsg2[3]);
   // All done, unmount partition and disable SDMMC or SPI peripheral
    esp_vfs_fat_sdcard_unmount(mount_point, card);
    ESP_LOGI(TAG, "Card unmounted");
     //deinitialize the bus after all devices are removed
     spi_bus_free(host.slot);
}
I have noticed that the value read from SD Card in FcmMsg1 is not retained.
That's why I log it both in (sdcard.c) SD_Read() and in (hub_main.c) app_main()
Here is part of the log:
Name: SL64G
Type: SDHC/SDXC
Speed: 20 MHz
Size: 60906MB
.[0;32mI (762) SDCARD: file msg1.hex size 340.[0m
.[0;32mI (762) SDCARD: msg1 2 Kitchen Alarm .[0m
.[0;32mI (772) SDCARD: file msg2.hex size 170.[0m
.[0;32mI (772) SDCARD: msg2 2 CLOSED .[0m
.[0;32mI (822) SDCARD: Card unmounted.[0m
.[0;32mI (822) Hub1: ESP_WIFI_MODE_AP.[0m
...
...

.[0;32mI (1052) Hub1: wifi_init_softap finished.
.[0;32mI (1062) uart: queue free spaces: 24.[0m
.[0;32mI (1062) Hub1: *** msg1 2 .[0m
I have been working with microchip PIC for many years and I have never seen a problem like this.
Another thing that I noticed is that if I create a task in one source file like this:
xTaskCreate(udp_server_task, "udp_server", 4096, (void*)AF_INET, 5, NULL);
And then define function:
static void udp_server_task(void *pvParameters)
in another file, it doesn't compile, with the message that it can't find this function. Even though I define a prototype for this function.

I am working using Eclipse with IDF plugin.

What am I doing wrong? What is so different with ESP32 programming?
Any suggestions?

Wes

dastoned
Posts: 50
Joined: Fri May 29, 2020 2:52 pm

Re: ESP32 variable scope problem

Postby dastoned » Thu Mar 25, 2021 10:54 am

Hi Wes, on ESP32 your code behaves as it should according to C specification (I've no idea what the PIC toolchain does, but it doesn't seem to conform to C spec). If you define a variable in a header and include this in two different .c files, the compiler will instantiate two different (and independent) copies of the variable.

What you want to do is declare the variable in the header file using the keyword extern. This will tell the compiler to leave only a reference to the variable which gets resolved while linking the object files. Additionally you need to define the variable in some .c file - this preferably happens separately from the .c files where you're referencing it.

Declaration in def.h:

Code: Select all

#define		MAX_FCMMSG1			17
#define		MAX_FCMMSG2			17
extern char FcmMsg1[MAX_FCMMSG1][20];
extern char FcmMsg2[MAX_FCMMSG2][10];
Definition in def.c:

Code: Select all

#include "def.h"
char FcmMsg1[MAX_FCMMSG1][20] = {'\0'};
char FcmMsg2[MAX_FCMMSG2][10] = {'\0'};
Now you can include the def.h in any other .c file and it'll behave as you expect it to.

See https://www.geeksforgeeks.org/understan ... word-in-c/ for a detailed explanation.

As for defining a function with "static void udp_server_task(void *pvParameters)" - here you're using the keyword static which explicitly hides the function from other .c files. That's precisely contrary to what you want. Again, you need to have a .h file which declares your function prototype, then define it in the relevant .c file.

Declaration in func.h:

Code: Select all

void udp_server_task(void *pvParameters);
Definition in func.c:

Code: Select all

#include "func.h"
void udp_server_task(void *pvParameters) {
    // Do something
}

wesS2020
Posts: 7
Joined: Thu Mar 25, 2021 5:31 am

Re: ESP32 variable scope problem

Postby wesS2020 » Fri Mar 26, 2021 12:46 am

Thanks for your reply but this didn't fix the problem.

So, I started with a new project based on the "Hello_World" in idf example directory.
I created a def.h file:

Code: Select all

#ifndef _DEF_H
#define _DEF_H
//#pragma once
int result;

#endif
And another source calc.c:

Code: Select all

#include <stdio.h>
#include "def.h"

//extern int result;

void calculate(int v){
	result += v;
	printf("result in calc.c = %d\n", result);
}

hello_world_main.c

Code: Select all

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "nvs_flash.h"
#include "def.h"

extern void calculate(int v);

int var1 = 1;
//int result = 0;

void app_main(void)
{
	result = 0;
    while(1){
    	var1 += 1;
    	printf("var1 = %d\n", var1);
    	calculate(var1);
    	printf("result = %d\n", result);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
And the outcome is as expected - the value of variable result is not reset
I have also tried an option without a def.h header file, but declaring variable "result" in one source file and as extern variable in another. And it also worked as expected.

The next step was to add to the second file (calc.c) the code for initializing and reading SD Card from my previous project.
I have also added declaration of char FcmMsg1[MAX_FCMMSG1][20]; in def.h file.
And the outcome again was as it supposed to be - the string in FcmMsg1 that was read in calc.c file was retained in the hello_world_main.c

So now, I am not sure what is going on in my first project, and how to debug it?
Unless I will start adding the code from my project into the hello_world project and see if/when it fails again.

Thanks for your help.

Wes

ESP_Sprite
Posts: 9723
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP32 variable scope problem

Postby ESP_Sprite » Fri Mar 26, 2021 2:16 am

Don't put definitions like that in header files, period. As dastoned said, it'll cause every C file that includes the header to have it's own 'private' version of the variable. Instead, *declare* the existence of the variable using 'extern char* ...' and then *define* (without 'extern') the variable in one (and just one) of your C files.

The logic is that defining the variable tells the C compiler to go and allocate memory for it, while declaring a variable only tells the C compiler the variable exists somewhere. You want to have all your C files to do the latter, not the former.

In general, any definition of a variable in any header is a big honking red flag, which is normally only OK'ish in special circumstances (but it'll not be a normal variable then but e.g. a static const). You'd always expect an 'extern' in any line having anything to do with a variable in a header.

wesS2020
Posts: 7
Joined: Thu Mar 25, 2021 5:31 am

Re: ESP32 variable scope problem

Postby wesS2020 » Fri Mar 26, 2021 5:11 am

Don't put definitions like that in header files, period. As dastoned said, it'll cause every C file that includes the header to have it's own 'private' version of the variable. Instead, *declare* the existence of the variable using 'extern char* ...' and then *define* (without 'extern') the variable in one (and just one) of your C files.
Unfortunately this is not an issue in this case. Because I found out that something is wrong with the variables not retaining values was when in the project I had definition like this:
In hub_main.c -> char rx_buffer[500];
in udp_task.c -> extern char rx_buffer[500];
And I noticed that in udp_task.c this variable is reset to 0.
That's when I decided to check other variables - i.e. variables defined in def.h. And they didn't work as well.

As I wrote before, I followed dastoned advice and declared them in .c source file and use extern in the def.h - but this didn't work either. That's why decided to start a new project and test it.

Anyway, the issue is partially solved.
As I wrote in my previous post, I started a new project based on the template Hello_World.
I copied parts of the code from my first project - SDcard (init and read), Uart (init and uart_event_task), Wifi (wifi_init_softap and wifi_event_handler) and UDP server (init, udp_server_task, and data processing).

I tested after adding each part and everything works as it should.
So, I have no idea why the original project still doesn't work!
The only reason I could see is, in the configuration of the IDF template used for creating project - for original project I used "softAP" template?

Anyway I will follow dastoned and your advice, because it makes sense - although I have never had any problems before.
Mind you, I am not a programmer, just a hobbyist, doing what I need for myself.
If I find out why it didn't work I will post it here, but I won't spend a lot of time trying to do it.

Thanks for your help
Wes

Who is online

Users browsing this forum: No registered users and 333 guests