first commit
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
||||||
10
.vscode/extensions.json
vendored
Normal file
10
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||||
|
// for the documentation about the extensions.json format
|
||||||
|
"recommendations": [
|
||||||
|
"platformio.platformio-ide"
|
||||||
|
],
|
||||||
|
"unwantedRecommendations": [
|
||||||
|
"ms-vscode.cpptools-extension-pack"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
CMakeLists.txt
Normal file
3
CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16.0)
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(KgeuTraining)
|
||||||
37
include/README
Normal file
37
include/README
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the convention is to give header files names that end with `.h'.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||||
46
lib/README
Normal file
46
lib/README
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into the executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a separate directory
|
||||||
|
("lib/your_library_name/[Code]").
|
||||||
|
|
||||||
|
For example, see the structure of the following example libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
Example contents of `src/main.c` using Foo and Bar:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
The PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries by scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||||
18
platformio.ini
Normal file
18
platformio.ini
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:wemos_d1_uno32]
|
||||||
|
platform = espressif32
|
||||||
|
board = wemos_d1_uno32
|
||||||
|
framework = espidf
|
||||||
|
monitor_speed = 115200
|
||||||
|
monitor_filters =
|
||||||
|
default
|
||||||
|
esp32_exception_decoder
|
||||||
2232
sdkconfig.wemos_d1_uno32
Normal file
2232
sdkconfig.wemos_d1_uno32
Normal file
File diff suppressed because it is too large
Load Diff
7
src/CMakeLists.txt
Normal file
7
src/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# This file was automatically generated for projects
|
||||||
|
# without default 'CMakeLists.txt' file.
|
||||||
|
|
||||||
|
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
|
||||||
|
|
||||||
|
idf_component_register(SRCS ${app_sources}
|
||||||
|
REQUIRES nvs_flash)
|
||||||
436
src/main.c
Normal file
436
src/main.c
Normal file
@@ -0,0 +1,436 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/event_groups.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_wifi.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "lwip/sockets.h"
|
||||||
|
#include "lwip/dns.h"
|
||||||
|
#include "lwip/netdb.h"
|
||||||
|
|
||||||
|
static const char *TAG = "KgeuTraining";
|
||||||
|
|
||||||
|
// WiFi AP Configuration
|
||||||
|
#define WIFI_SSID "KgeuTraining_AP"
|
||||||
|
#define WIFI_PASS "12345678"
|
||||||
|
#define WIFI_CHANNEL 1
|
||||||
|
#define MAX_STA_CONN 4
|
||||||
|
|
||||||
|
// Hall Sensor GPIO pins (KY-003)
|
||||||
|
#define HALL_SENSOR_TOP_PIN GPIO_NUM_4
|
||||||
|
#define HALL_SENSOR_BOTTOM_PIN GPIO_NUM_5
|
||||||
|
|
||||||
|
// TCP server configuration
|
||||||
|
#define TCP_PORT 8080
|
||||||
|
#define MAX_CLIENTS 4
|
||||||
|
#define TCP_BUFFER_SIZE 128
|
||||||
|
|
||||||
|
// Position states
|
||||||
|
typedef enum {
|
||||||
|
POSITION_TOP,
|
||||||
|
POSITION_BOTTOM,
|
||||||
|
POSITION_MIDDLE
|
||||||
|
} position_state_t;
|
||||||
|
|
||||||
|
static position_state_t current_position = POSITION_MIDDLE;
|
||||||
|
static position_state_t last_position = POSITION_MIDDLE;
|
||||||
|
|
||||||
|
// TCP client list
|
||||||
|
static int tcp_clients[MAX_CLIENTS] = {-1, -1, -1, -1};
|
||||||
|
static int tcp_client_count = 0;
|
||||||
|
static int tcp_server_socket = -1;
|
||||||
|
|
||||||
|
// Mutex for client list access
|
||||||
|
static SemaphoreHandle_t client_mutex = NULL;
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
static void send_position_to_client(int sock_fd, const char *position);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add TCP client to the list
|
||||||
|
*/
|
||||||
|
static void add_tcp_client(int sock_fd) {
|
||||||
|
xSemaphoreTake(client_mutex, portMAX_DELAY);
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (tcp_clients[i] == -1) {
|
||||||
|
tcp_clients[i] = sock_fd;
|
||||||
|
tcp_client_count++;
|
||||||
|
ESP_LOGI(TAG, "TCP client connected, total: %d", tcp_client_count);
|
||||||
|
|
||||||
|
// Send current position immediately
|
||||||
|
const char *current_pos_str;
|
||||||
|
switch (current_position) {
|
||||||
|
case POSITION_TOP:
|
||||||
|
current_pos_str = "top";
|
||||||
|
break;
|
||||||
|
case POSITION_BOTTOM:
|
||||||
|
current_pos_str = "bottom";
|
||||||
|
break;
|
||||||
|
case POSITION_MIDDLE:
|
||||||
|
current_pos_str = "middle";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
current_pos_str = "middle";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
send_position_to_client(sock_fd, current_pos_str);
|
||||||
|
|
||||||
|
xSemaphoreGive(client_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_LOGW(TAG, "Maximum clients reached");
|
||||||
|
xSemaphoreGive(client_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove TCP client from the list
|
||||||
|
*/
|
||||||
|
static void remove_tcp_client(int sock_fd) {
|
||||||
|
xSemaphoreTake(client_mutex, portMAX_DELAY);
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (tcp_clients[i] == sock_fd) {
|
||||||
|
tcp_clients[i] = -1;
|
||||||
|
tcp_client_count--;
|
||||||
|
ESP_LOGI(TAG, "TCP client disconnected, total: %d", tcp_client_count);
|
||||||
|
xSemaphoreGive(client_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xSemaphoreGive(client_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send position to a specific TCP client
|
||||||
|
*/
|
||||||
|
static void send_position_to_client(int sock_fd, const char *position) {
|
||||||
|
// Simple JSON formatting: {"position": "top"}
|
||||||
|
char json_string[64];
|
||||||
|
snprintf(json_string, sizeof(json_string), "{\"position\":\"%s\"}\n", position);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Sending to client %d: %s", sock_fd, json_string);
|
||||||
|
|
||||||
|
int len = strlen(json_string);
|
||||||
|
int sent = send(sock_fd, json_string, len, 0);
|
||||||
|
|
||||||
|
if (sent < 0) {
|
||||||
|
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
|
||||||
|
remove_tcp_client(sock_fd);
|
||||||
|
close(sock_fd);
|
||||||
|
} else if (sent != len) {
|
||||||
|
ESP_LOGW(TAG, "Sent %d bytes instead of %d", sent, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast JSON message to all connected TCP clients
|
||||||
|
*/
|
||||||
|
static void broadcast_position(const char *position) {
|
||||||
|
if (tcp_client_count == 0) {
|
||||||
|
return; // No clients connected
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple JSON formatting: {"position": "top"}
|
||||||
|
char json_string[64];
|
||||||
|
snprintf(json_string, sizeof(json_string), "{\"position\":\"%s\"}\n", position);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Broadcasting: %s", json_string);
|
||||||
|
|
||||||
|
xSemaphoreTake(client_mutex, portMAX_DELAY);
|
||||||
|
for (int i = 0; i < MAX_CLIENTS; i++) {
|
||||||
|
if (tcp_clients[i] != -1) {
|
||||||
|
int sock_fd = tcp_clients[i];
|
||||||
|
int len = strlen(json_string);
|
||||||
|
int sent = send(sock_fd, json_string, len, 0);
|
||||||
|
|
||||||
|
if (sent < 0) {
|
||||||
|
ESP_LOGE(TAG, "Error sending to client %d: errno %d", sock_fd, errno);
|
||||||
|
remove_tcp_client(sock_fd);
|
||||||
|
close(sock_fd);
|
||||||
|
} else if (sent != len) {
|
||||||
|
ESP_LOGW(TAG, "Sent %d bytes instead of %d to client %d", sent, len, sock_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xSemaphoreGive(client_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle individual TCP client connection
|
||||||
|
*/
|
||||||
|
static void tcp_client_task(void *pvParameters) {
|
||||||
|
int sock_fd = (int)(intptr_t)pvParameters;
|
||||||
|
char rx_buffer[TCP_BUFFER_SIZE];
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "New client task started for socket %d", sock_fd);
|
||||||
|
|
||||||
|
// Set socket timeout
|
||||||
|
struct timeval timeout;
|
||||||
|
timeout.tv_sec = 10;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int len = recv(sock_fd, rx_buffer, sizeof(rx_buffer) - 1, 0);
|
||||||
|
|
||||||
|
if (len < 0) {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
// Timeout - continue
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "recv failed: errno %d", errno);
|
||||||
|
break;
|
||||||
|
} else if (len == 0) {
|
||||||
|
ESP_LOGI(TAG, "Connection closed by client");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Null-terminate the received data
|
||||||
|
rx_buffer[len] = '\0';
|
||||||
|
ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);
|
||||||
|
|
||||||
|
// Echo back or process the received data if needed
|
||||||
|
// For now, we just log it
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Closing client socket %d", sock_fd);
|
||||||
|
remove_tcp_client(sock_fd);
|
||||||
|
close(sock_fd);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TCP server task - accepts new connections
|
||||||
|
*/
|
||||||
|
static void tcp_server_task(void *pvParameters) {
|
||||||
|
struct sockaddr_in server_addr;
|
||||||
|
struct sockaddr_in client_addr;
|
||||||
|
socklen_t client_addr_len = sizeof(client_addr);
|
||||||
|
|
||||||
|
// Create socket
|
||||||
|
tcp_server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (tcp_server_socket < 0) {
|
||||||
|
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set socket options
|
||||||
|
int opt = 1;
|
||||||
|
setsockopt(tcp_server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||||
|
|
||||||
|
// Bind socket
|
||||||
|
server_addr.sin_family = AF_INET;
|
||||||
|
server_addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
server_addr.sin_port = htons(TCP_PORT);
|
||||||
|
|
||||||
|
if (bind(tcp_server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
|
||||||
|
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
||||||
|
close(tcp_server_socket);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for connections
|
||||||
|
if (listen(tcp_server_socket, MAX_CLIENTS) < 0) {
|
||||||
|
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
|
||||||
|
close(tcp_server_socket);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "TCP server started on port %d", TCP_PORT);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// Accept new connection
|
||||||
|
int client_sock = accept(tcp_server_socket, (struct sockaddr *)&client_addr, &client_addr_len);
|
||||||
|
|
||||||
|
if (client_sock < 0) {
|
||||||
|
ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "New client connected from %s", inet_ntoa(client_addr.sin_addr));
|
||||||
|
|
||||||
|
// Add client to list
|
||||||
|
add_tcp_client(client_sock);
|
||||||
|
|
||||||
|
// Create task for this client
|
||||||
|
xTaskCreate(tcp_client_task, "tcp_client", 4096, (void*)(intptr_t)client_sock, 5, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(tcp_server_socket);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WiFi event handler
|
||||||
|
*/
|
||||||
|
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_AP_STACONNECTED) {
|
||||||
|
wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
|
||||||
|
ESP_LOGI(TAG, "Station joined, AID=%d", event->aid);
|
||||||
|
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STADISCONNECTED) {
|
||||||
|
wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
|
||||||
|
ESP_LOGI(TAG, "Station left, AID=%d", event->aid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize WiFi Access Point
|
||||||
|
*/
|
||||||
|
static void wifi_init_softap(void) {
|
||||||
|
ESP_ERROR_CHECK(esp_netif_init());
|
||||||
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
esp_netif_create_default_wifi_ap();
|
||||||
|
|
||||||
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
||||||
|
ESP_EVENT_ANY_ID,
|
||||||
|
&wifi_event_handler,
|
||||||
|
NULL,
|
||||||
|
NULL));
|
||||||
|
|
||||||
|
wifi_config_t wifi_config = {
|
||||||
|
.ap = {
|
||||||
|
.ssid = WIFI_SSID,
|
||||||
|
.ssid_len = strlen(WIFI_SSID),
|
||||||
|
.channel = WIFI_CHANNEL,
|
||||||
|
.password = WIFI_PASS,
|
||||||
|
.max_connection = MAX_STA_CONN,
|
||||||
|
.authmode = WIFI_AUTH_WPA2_PSK
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (strlen(WIFI_PASS) == 0) {
|
||||||
|
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_start());
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "WiFi AP initialized. SSID:%s password:%s channel:%d",
|
||||||
|
WIFI_SSID, WIFI_PASS, WIFI_CHANNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize GPIO for hall sensors
|
||||||
|
*/
|
||||||
|
static void init_hall_sensors(void) {
|
||||||
|
gpio_config_t io_conf = {
|
||||||
|
.intr_type = GPIO_INTR_DISABLE,
|
||||||
|
.mode = GPIO_MODE_INPUT,
|
||||||
|
.pin_bit_mask = (1ULL << HALL_SENSOR_TOP_PIN) | (1ULL << HALL_SENSOR_BOTTOM_PIN),
|
||||||
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||||
|
.pull_up_en = GPIO_PULLUP_ENABLE // KY-003 has pull-up resistor, enable internal pull-up
|
||||||
|
};
|
||||||
|
gpio_config(&io_conf);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Hall sensors initialized on GPIO %d (top) and %d (bottom)",
|
||||||
|
HALL_SENSOR_TOP_PIN, HALL_SENSOR_BOTTOM_PIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read position from hall sensors
|
||||||
|
*/
|
||||||
|
static position_state_t read_position(void) {
|
||||||
|
int top_sensor = gpio_get_level(HALL_SENSOR_TOP_PIN);
|
||||||
|
int bottom_sensor = gpio_get_level(HALL_SENSOR_BOTTOM_PIN);
|
||||||
|
|
||||||
|
// KY-003: LOW when magnet is near, HIGH when no magnet
|
||||||
|
// Top sensor LOW = weight at top
|
||||||
|
// Bottom sensor LOW = weight at bottom
|
||||||
|
// Both HIGH = weight in middle
|
||||||
|
|
||||||
|
if (top_sensor == 0) {
|
||||||
|
return POSITION_TOP;
|
||||||
|
} else if (bottom_sensor == 0) {
|
||||||
|
return POSITION_BOTTOM;
|
||||||
|
} else {
|
||||||
|
return POSITION_MIDDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task to monitor position changes and broadcast updates
|
||||||
|
*/
|
||||||
|
static void position_monitor_task(void *pvParameters) {
|
||||||
|
ESP_LOGI(TAG, "Position monitoring task started");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
current_position = read_position();
|
||||||
|
|
||||||
|
// Broadcast if position changed
|
||||||
|
if (current_position != last_position) {
|
||||||
|
const char *position_str;
|
||||||
|
switch (current_position) {
|
||||||
|
case POSITION_TOP:
|
||||||
|
position_str = "top";
|
||||||
|
break;
|
||||||
|
case POSITION_BOTTOM:
|
||||||
|
position_str = "bottom";
|
||||||
|
break;
|
||||||
|
case POSITION_MIDDLE:
|
||||||
|
position_str = "middle";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
position_str = "middle";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcast_position(position_str);
|
||||||
|
last_position = current_position;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check every 100ms
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main application entry point
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Initialize WiFi AP
|
||||||
|
wifi_init_softap();
|
||||||
|
|
||||||
|
// Wait for WiFi to be ready
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
|
||||||
|
// Initialize hall sensors
|
||||||
|
init_hall_sensors();
|
||||||
|
|
||||||
|
// Create mutex for client list
|
||||||
|
client_mutex = xSemaphoreCreateMutex();
|
||||||
|
if (client_mutex == NULL) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create mutex");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start TCP server task
|
||||||
|
xTaskCreate(tcp_server_task, "tcp_server", 4096, NULL, 5, NULL);
|
||||||
|
|
||||||
|
// Start position monitoring task
|
||||||
|
xTaskCreate(position_monitor_task, "position_monitor", 2048, NULL, 5, NULL);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "KgeuTraining application started");
|
||||||
|
ESP_LOGI(TAG, "Connect to TCP server at 192.168.4.1:%d", TCP_PORT);
|
||||||
|
}
|
||||||
11
test/README
Normal file
11
test/README
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
This directory is intended for PlatformIO Test Runner and project tests.
|
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of
|
||||||
|
source code, sets of one or more MCU program modules together with associated
|
||||||
|
control data, usage procedures, and operating procedures, are tested to
|
||||||
|
determine whether they are fit for use. Unit testing finds problems early
|
||||||
|
in the development cycle.
|
||||||
|
|
||||||
|
More information about PlatformIO Unit Testing:
|
||||||
|
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
||||||
Reference in New Issue
Block a user