more C notes
This commit is contained in:
parent
7fc0829cc9
commit
2ba5bfb2f3
5 changed files with 309 additions and 0 deletions
48
zk/Create_C_header_file.md
Normal file
48
zk/Create_C_header_file.md
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- C
|
||||||
|
---
|
||||||
|
|
||||||
|
In order to create a file that you can import into other C files you need to
|
||||||
|
create a header file.
|
||||||
|
|
||||||
|
> Remember that this isn't really a module since `.h` files and `#includes` are
|
||||||
|
> just text substition
|
||||||
|
|
||||||
|
In fact you create a `.h` header file which links to a `.c` file which actually
|
||||||
|
contains the code you want to import.
|
||||||
|
|
||||||
|
Let's say we want to create a "module" that handles connecting to WiFi:
|
||||||
|
|
||||||
|
```c
|
||||||
|
// wifi.h
|
||||||
|
|
||||||
|
#ifndef WIFI_H
|
||||||
|
#define WIFI_H
|
||||||
|
|
||||||
|
void wifi_connect(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
In the `.h` file we just define the function signature and put checks (so it
|
||||||
|
doesn't get redefined).
|
||||||
|
|
||||||
|
Then the implementation file:
|
||||||
|
|
||||||
|
```c
|
||||||
|
// main/wifi.c
|
||||||
|
|
||||||
|
// Add any header files or auxiliary functions that the main (wifi_connect)
|
||||||
|
// function will require here
|
||||||
|
|
||||||
|
void wifi_connect(void) {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in your main entrypoint:
|
||||||
|
|
||||||
|
```c
|
||||||
|
// main/main.c
|
||||||
|
#include "wifi.h"
|
||||||
|
wifi_connect()
|
||||||
|
```
|
||||||
187
zk/RTOS_event_loop_and_ESP32_program_architecture.md
Normal file
187
zk/RTOS_event_loop_and_ESP32_program_architecture.md
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- C
|
||||||
|
- ESP32
|
||||||
|
---
|
||||||
|
|
||||||
|
"RTOS" stands for "real time operating system". It is category of OSs designed
|
||||||
|
for embedded systems that need predicatable, time-sensitive responses.
|
||||||
|
|
||||||
|
"Free RTOS" is the particular implementation of RTOS on the ESP32.
|
||||||
|
|
||||||
|
It is **event-driven** because it runs multiple tasks at once rather than
|
||||||
|
running a single program from top to bottom. (In fact, as with all OSs, it is
|
||||||
|
running through task scheduling very rapidly.)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Event handlers
|
||||||
|
|
||||||
|
Typically, certain events are created by the ESP32 device which you then hook
|
||||||
|
into and run functions against.
|
||||||
|
|
||||||
|
You then register **handlers** for events that you care about.
|
||||||
|
|
||||||
|
## Event groups
|
||||||
|
|
||||||
|
You use **event groups** to efficiently manage the different events and prevent
|
||||||
|
other events being blocked.
|
||||||
|
|
||||||
|
An event group is just a number with bits that you can set and clear basically
|
||||||
|
like a switch.
|
||||||
|
|
||||||
|
These are predefined as `BIT0, BIT1, ...` in
|
||||||
|
[ESP-IDF](./Programming_ESP32_with_Expressif_IoT_Development_Framework.md) as
|
||||||
|
[macros](/zk/Macros_in_C.md) for [bitwise operators](/zk/Bitwise_operators.md).
|
||||||
|
|
||||||
|
Your event handler function can update an event group when the thing it is
|
||||||
|
concerned with happens. Like toggling a switch.
|
||||||
|
|
||||||
|
This allows other processes to act in response and further looped logic to
|
||||||
|
appply.
|
||||||
|
|
||||||
|
## Walkthrough example: connecting to WiFi
|
||||||
|
|
||||||
|
> Below is a simplified example of using the event loop to connect to WiFi
|
||||||
|
|
||||||
|
### Event group
|
||||||
|
|
||||||
|
First we define our event group bits in order to sequence and track the state:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define WIFI_CONNECTED_BIT BIT0
|
||||||
|
#define WIFI_FAIL_BIT BIT1
|
||||||
|
```
|
||||||
|
|
||||||
|
These macros rename the `BIT0` and `BIT1` event group bits that ESP-IDF provides
|
||||||
|
to more meaningful names focused on what we are trying to achieve:
|
||||||
|
|
||||||
|
- connect to WiFi
|
||||||
|
- track when/if this connection fails
|
||||||
|
|
||||||
|
### Events to target
|
||||||
|
|
||||||
|
The ESP32 WiFi driver publishes a `WIFI_EVENT` when WiFi-related processes
|
||||||
|
occur. `WIFI_EVENT` is the broad category (called the "event base"), which
|
||||||
|
includes the following more specific events:
|
||||||
|
|
||||||
|
- `WIFI_EVENT_STA_START`
|
||||||
|
- WiFi on device started in station mode (ready to connect)
|
||||||
|
- `WIFI-EVENT_STA_CONNECTED`
|
||||||
|
- Device has connected to router
|
||||||
|
- `WIFI_EVENT_STA_DISCONNECTED`
|
||||||
|
- Device has disconnected from router
|
||||||
|
|
||||||
|
Another event base is `IP_EVENT` with the specific events:
|
||||||
|
|
||||||
|
- `IP_EVENT_STA_GOT_IP`
|
||||||
|
- Device has been assigned an IP by [DHCP](./DHCP.md) server
|
||||||
|
- `IP_EVENT_STA_LOST_IP`
|
||||||
|
|
||||||
|
### Register event handler
|
||||||
|
|
||||||
|
The following code registers a handler for the `WIFI_EVENT` event base, in the
|
||||||
|
form of a custom function `wifi_event_handler`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler)
|
||||||
|
```
|
||||||
|
|
||||||
|
The second parameter captures all events in the `WIFI_EVENT` category. The third
|
||||||
|
parameter is a pointer to the function that runs when the event fires
|
||||||
|
(`wifi_event_handler`):
|
||||||
|
|
||||||
|
```c
|
||||||
|
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void *event_data) {
|
||||||
|
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||||
|
esp_wifi_connect();
|
||||||
|
} else if (event_base == WIFI_EVENT &&
|
||||||
|
event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||||
|
if (s_retry_num < MAX_RETRY) {
|
||||||
|
esp_wifi_connect();
|
||||||
|
s_retry_num++;
|
||||||
|
ESP_LOGI(TAG, "Retrying connection...");
|
||||||
|
} else {
|
||||||
|
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||||
|
}
|
||||||
|
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||||
|
ESP_LOGI(TAG, "Connected! Got IP");
|
||||||
|
s_retry_num = 0;
|
||||||
|
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In essence:
|
||||||
|
|
||||||
|
- If WiFi connection event is triggered attempt to connect
|
||||||
|
- If WiFi disconnection event is triggered try to reconnect within retry params,
|
||||||
|
if no luck, set wifi connection event group to error
|
||||||
|
- If IP is assigned set wifi connection event group to success
|
||||||
|
|
||||||
|
> `esp_wifi_connect` is the built in WiFi connection logic from ESP-IDF from
|
||||||
|
> `esp-wifi.h` header file
|
||||||
|
|
||||||
|
### Main program logic
|
||||||
|
|
||||||
|
We have a main wrapper function `wifi_connect` that manages calls to the event
|
||||||
|
handler function and which established the event group. This would run once at
|
||||||
|
setup:
|
||||||
|
|
||||||
|
```c
|
||||||
|
void wifi_connect(void) {
|
||||||
|
s_wifi_event_group = xEventGroupCreate();
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_netif_init());
|
||||||
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
esp_netif_create_default_wifi_sta();
|
||||||
|
|
||||||
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||||
|
|
||||||
|
esp_event_handler_instance_t instance_any_id;
|
||||||
|
esp_event_handler_instance_t instance_got_ip;
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_instance_register(
|
||||||
|
WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL,
|
||||||
|
&instance_any_id));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_instance_register(
|
||||||
|
IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL,
|
||||||
|
&instance_got_ip));
|
||||||
|
|
||||||
|
wifi_config_t wifi_config = {
|
||||||
|
.sta =
|
||||||
|
{
|
||||||
|
.ssid = WIFI_SSID,
|
||||||
|
.password = WIFI_PASS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_start());
|
||||||
|
|
||||||
|
// Wait for connection
|
||||||
|
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
||||||
|
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||||
|
pdFALSE, pdFALSE, portMAX_DELAY);
|
||||||
|
|
||||||
|
if (bits & WIFI_CONNECTED_BIT) {
|
||||||
|
ESP_LOGI(TAG, "WiFi connected successfully");
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "WiFi connection failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Lots going on but basically:
|
||||||
|
|
||||||
|
- Register the WiFi event group
|
||||||
|
- Register the event handler and pass in the events we are interested in
|
||||||
|
- Store the `ssid` and `password` in the internal state of the device (these
|
||||||
|
don't need to be handed to the actual `esp_wifi_connect()` function - they are
|
||||||
|
remembered once set)
|
||||||
|
- Log success based on the event group `WIFI_CONNECTED_BIT`
|
||||||
|
|
||||||
|
> Note that we register two event handlers but pass the same function
|
||||||
|
> (`wifi_event_handler`) because it handles both the `WIFI` and `IP` event
|
||||||
|
> bases.
|
||||||
12
zk/Static_in_C.md
Normal file
12
zk/Static_in_C.md
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- C
|
||||||
|
---
|
||||||
|
|
||||||
|
`static` signals that the variable is private to the file it appears in. It
|
||||||
|
cannot be accessed from outside of it.
|
||||||
|
|
||||||
|
The opposite to a global variable.
|
||||||
|
|
||||||
|
Using `static` prevents naming conflicts if you have another file with the same
|
||||||
|
variable name.
|
||||||
40
zk/Structs_in_C.md
Normal file
40
zk/Structs_in_C.md
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- C
|
||||||
|
---
|
||||||
|
|
||||||
|
A `struct` is a custom, user-defined type, in constrast to the primitive types
|
||||||
|
(`int`, `char`, `float` etc). They are known as **compound types**.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Define the custom type:
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct Person {
|
||||||
|
int age;
|
||||||
|
float height;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then to apply the type to a variable:
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct Person thomas
|
||||||
|
```
|
||||||
|
|
||||||
|
And define the sub-properties:
|
||||||
|
|
||||||
|
```c
|
||||||
|
thomas.age = 37
|
||||||
|
thomas.height = 5.8
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively do all together:
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct Person thomas = {
|
||||||
|
.age = 37,
|
||||||
|
.height = 5.8
|
||||||
|
}
|
||||||
|
```
|
||||||
22
zk/Typedefs_in_C.md
Normal file
22
zk/Typedefs_in_C.md
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- C
|
||||||
|
---
|
||||||
|
|
||||||
|
A `typedef` is an alias for an existing type. It can make complex types easier
|
||||||
|
to write and provides abstraction.
|
||||||
|
|
||||||
|
The syntax:
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef existing_type new_name;
|
||||||
|
```
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef unsigned int uint_t
|
||||||
|
```
|
||||||
|
|
||||||
|
> The convention is to append `_t` to the variable name to make it easier to
|
||||||
|
> understand that it is a typedef later in the execution.
|
||||||
Loading…
Add table
Reference in a new issue