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