Compare commits
3 Commits
80478c7400
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
64bb06f292
|
|||
|
537e7bfe10
|
|||
|
fe6cc2ebb9
|
67
.vscode/settings.json
vendored
67
.vscode/settings.json
vendored
@@ -42,6 +42,69 @@
|
|||||||
"atomic": "c",
|
"atomic": "c",
|
||||||
"__bit_reference": "c",
|
"__bit_reference": "c",
|
||||||
"err.h": "c",
|
"err.h": "c",
|
||||||
"httpd.h": "c"
|
"httpd.h": "c",
|
||||||
}
|
"random": "c",
|
||||||
|
"limits": "c",
|
||||||
|
"tuple": "c",
|
||||||
|
"init.h": "c",
|
||||||
|
"hw_i2c.h": "c",
|
||||||
|
"chrono": "c",
|
||||||
|
"stop_token": "c",
|
||||||
|
"__locale": "c",
|
||||||
|
"stdint.h": "c",
|
||||||
|
"ch32v208_eth.h": "c",
|
||||||
|
"bit": "c",
|
||||||
|
"any": "c",
|
||||||
|
"array": "c",
|
||||||
|
"hash_map": "c",
|
||||||
|
"strstream": "c",
|
||||||
|
"charconv": "c",
|
||||||
|
"cmath": "c",
|
||||||
|
"codecvt": "c",
|
||||||
|
"complex": "c",
|
||||||
|
"concepts": "c",
|
||||||
|
"condition_variable": "c",
|
||||||
|
"coroutine": "c",
|
||||||
|
"cstddef": "c",
|
||||||
|
"unordered_map": "c",
|
||||||
|
"unordered_set": "c",
|
||||||
|
"exception": "c",
|
||||||
|
"memory": "c",
|
||||||
|
"numeric": "c",
|
||||||
|
"optional": "c",
|
||||||
|
"ratio": "c",
|
||||||
|
"string_view": "c",
|
||||||
|
"system_error": "c",
|
||||||
|
"type_traits": "c",
|
||||||
|
"algorithm": "c",
|
||||||
|
"iomanip": "c",
|
||||||
|
"mutex": "c",
|
||||||
|
"ostream": "c",
|
||||||
|
"semaphore": "c",
|
||||||
|
"shared_mutex": "c",
|
||||||
|
"span": "c",
|
||||||
|
"stacktrace": "c",
|
||||||
|
"text_encoding": "c",
|
||||||
|
"thread": "c",
|
||||||
|
"typeindex": "c",
|
||||||
|
"typeinfo": "c",
|
||||||
|
"utility": "c",
|
||||||
|
"valarray": "c",
|
||||||
|
"__assert": "c",
|
||||||
|
"__split_buffer": "c",
|
||||||
|
"ios": "c",
|
||||||
|
"map": "c",
|
||||||
|
"new": "c",
|
||||||
|
"queue": "c",
|
||||||
|
"set": "c",
|
||||||
|
"stack": "c",
|
||||||
|
"stdexcept": "c",
|
||||||
|
"__node_handle": "c",
|
||||||
|
"execution": "c",
|
||||||
|
"numbers": "c",
|
||||||
|
"print": "c",
|
||||||
|
"ha_mqtt.h": "c",
|
||||||
|
"ethernetif.h": "c"
|
||||||
|
},
|
||||||
|
"cmake.sourceDirectory": "/home/mira/src/embedded/ch32v208_sens/lwip"
|
||||||
}
|
}
|
||||||
1
Makefile
1
Makefile
@@ -21,6 +21,7 @@ LWIP_C_FILES += $(COREFILES)
|
|||||||
LWIP_C_FILES += $(CORE4FILES)
|
LWIP_C_FILES += $(CORE4FILES)
|
||||||
LWIP_C_FILES += $(NETIFFILES)
|
LWIP_C_FILES += $(NETIFFILES)
|
||||||
LWIP_C_FILES += $(HTTPFILES)
|
LWIP_C_FILES += $(HTTPFILES)
|
||||||
|
LWIP_C_FILES += $(MQTTFILES)
|
||||||
|
|
||||||
LWIP_C_FILES_WITH_PATH := $(LWIP_C_FILES)
|
LWIP_C_FILES_WITH_PATH := $(LWIP_C_FILES)
|
||||||
LWIP_PORT_FILES := $(wildcard $(PORT_DIR)/*.c $(PORT_DIR)/arch/*.c)
|
LWIP_PORT_FILES := $(wildcard $(PORT_DIR)/*.c $(PORT_DIR)/arch/*.c)
|
||||||
|
|||||||
2
ch32fun
2
ch32fun
Submodule ch32fun updated: 08885a5ea4...b0764004f5
199
ha_mqtt.c
Normal file
199
ha_mqtt.c
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
// ha_mqtt.c
|
||||||
|
|
||||||
|
#include "ha_mqtt.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "lwip/apps/mqtt.h"
|
||||||
|
#include "lwip/ip4_addr.h"
|
||||||
|
#include "lwip/netif.h"
|
||||||
|
#include "systick.h"
|
||||||
|
|
||||||
|
// config
|
||||||
|
#define MQTT_BROKER_IP_A 192
|
||||||
|
#define MQTT_BROKER_IP_B 168
|
||||||
|
#define MQTT_BROKER_IP_C 102
|
||||||
|
#define MQTT_BROKER_IP_D 1
|
||||||
|
#define MQTT_BROKER_PORT 1883
|
||||||
|
#define MQTT_CLIENT_ID "gxht30_sensor_node"
|
||||||
|
#define MQTT_RECONNECT_INTERVAL_MS 5000
|
||||||
|
|
||||||
|
#define DEVICE_UNIQUE_ID "gxht30_board_01"
|
||||||
|
#define DEVICE_NAME "Office Climate Sensor"
|
||||||
|
#define DEVICE_MANUFACTURER "me"
|
||||||
|
#define DEVICE_MODEL "GXHT30-ETH-v1"
|
||||||
|
|
||||||
|
// home assistant topics
|
||||||
|
#define HA_DISCOVERY_PREFIX "homeassistant"
|
||||||
|
|
||||||
|
#define TEMP_UNIQUE_ID DEVICE_UNIQUE_ID "_temperature"
|
||||||
|
#define TEMP_CONFIG_TOPIC \
|
||||||
|
HA_DISCOVERY_PREFIX "/sensor/" TEMP_UNIQUE_ID "/config"
|
||||||
|
#define TEMP_STATE_TOPIC "devices/" DEVICE_UNIQUE_ID "/temperature/state"
|
||||||
|
#define TEMP_NAME "Temperature"
|
||||||
|
|
||||||
|
#define HUM_UNIQUE_ID DEVICE_UNIQUE_ID "_humidity"
|
||||||
|
#define HUM_CONFIG_TOPIC HA_DISCOVERY_PREFIX "/sensor/" HUM_UNIQUE_ID "/config"
|
||||||
|
#define HUM_STATE_TOPIC "devices/" DEVICE_UNIQUE_ID "/humidity/state"
|
||||||
|
#define HUM_NAME "Humidity"
|
||||||
|
|
||||||
|
#define MQTT_RECONNECT_INTERVAL_MS 5000
|
||||||
|
|
||||||
|
static mqtt_client_t* s_mqtt_client;
|
||||||
|
static ip_addr_t s_mqtt_broker_ip;
|
||||||
|
static uint8_t s_mqtt_connected = 0;
|
||||||
|
static uint32_t s_last_mqtt_retry_time = 0;
|
||||||
|
|
||||||
|
extern struct netif g_netif;
|
||||||
|
|
||||||
|
static void publish_ha_discovery_configs(mqtt_client_t* client);
|
||||||
|
static void mqtt_pub_request_cb(void* arg, err_t err);
|
||||||
|
static void mqtt_connection_cb(mqtt_client_t* client, void* arg,
|
||||||
|
mqtt_connection_status_t status);
|
||||||
|
static void publish_sensor_value(const char* topic, int32_t value_x100);
|
||||||
|
|
||||||
|
// public fns
|
||||||
|
|
||||||
|
void ha_mqtt_init(void) {
|
||||||
|
s_mqtt_client = mqtt_client_new();
|
||||||
|
if (s_mqtt_client == NULL) {
|
||||||
|
printf("error: failed to create mqtt client\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IP4_ADDR(&s_mqtt_broker_ip, MQTT_BROKER_IP_A, MQTT_BROKER_IP_B,
|
||||||
|
MQTT_BROKER_IP_C, MQTT_BROKER_IP_D);
|
||||||
|
|
||||||
|
ha_mqtt_check_and_reconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ha_mqtt_publish_sensor_data(int32_t temp_x100, int32_t hum_x100) {
|
||||||
|
if (!s_mqtt_connected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
publish_sensor_value(TEMP_STATE_TOPIC, temp_x100);
|
||||||
|
publish_sensor_value(HUM_STATE_TOPIC, hum_x100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ha_mqtt_check_and_reconnect(void) {
|
||||||
|
if (s_mqtt_connected || !netif_is_up(&g_netif) ||
|
||||||
|
!netif_is_link_up(&g_netif) || netif_ip4_addr(&g_netif)->addr == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (millis() - s_last_mqtt_retry_time > MQTT_RECONNECT_INTERVAL_MS) {
|
||||||
|
printf("attempting to connect to mqtt broker\n");
|
||||||
|
|
||||||
|
const struct mqtt_connect_client_info_t client_info = {
|
||||||
|
.client_id = MQTT_CLIENT_ID,
|
||||||
|
.keep_alive = 60,
|
||||||
|
// .client_user = "user", // authentication
|
||||||
|
// .client_pass = "pass",
|
||||||
|
};
|
||||||
|
|
||||||
|
err_t err =
|
||||||
|
mqtt_client_connect(s_mqtt_client, &s_mqtt_broker_ip, MQTT_BROKER_PORT,
|
||||||
|
mqtt_connection_cb, NULL, &client_info);
|
||||||
|
|
||||||
|
if (err != ERR_OK) {
|
||||||
|
printf("mqtt connect failed with immediate error: %d\n", err);
|
||||||
|
}
|
||||||
|
s_last_mqtt_retry_time = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ha_mqtt_is_connected(void) { return s_mqtt_connected; }
|
||||||
|
|
||||||
|
// static fns
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes a sensor value after formatting it from a fixed-point
|
||||||
|
* int
|
||||||
|
* @param topic The MQTT topic to publish to.
|
||||||
|
* @param value_x100 The sensor value, scaled by 100.
|
||||||
|
*/
|
||||||
|
static void publish_sensor_value(const char* topic, int32_t value_x100) {
|
||||||
|
char payload_buf[16];
|
||||||
|
|
||||||
|
// format the fixed-point value (e.g., 2345) into a decimal string ("23.45")
|
||||||
|
int32_t val_int = value_x100 / 100;
|
||||||
|
int32_t val_frac = value_x100 % 100;
|
||||||
|
if (val_frac < 0) {
|
||||||
|
val_frac = -val_frac;
|
||||||
|
}
|
||||||
|
snprintf(payload_buf, sizeof(payload_buf), "%ld.%02ld", val_int, val_frac);
|
||||||
|
|
||||||
|
err_t err = mqtt_publish(s_mqtt_client, topic, payload_buf,
|
||||||
|
strlen(payload_buf), 1, 0, NULL, NULL);
|
||||||
|
|
||||||
|
if (err != ERR_OK) {
|
||||||
|
printf("failed to publish to topic %s: %d\n", topic, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void publish_ha_discovery_configs(mqtt_client_t* client) {
|
||||||
|
char payload[512];
|
||||||
|
err_t err;
|
||||||
|
|
||||||
|
// publish temperature sensor config
|
||||||
|
snprintf(
|
||||||
|
payload, sizeof(payload),
|
||||||
|
"{"
|
||||||
|
"\"name\":\"%s\",\"unique_id\":\"%s\",\"stat_t\":\"%s\","
|
||||||
|
"\"dev_cla\":\"temperature\",\"unit_of_meas\":\"°C\","
|
||||||
|
"\"val_tpl\":\"{{ value|float(2) }}\","
|
||||||
|
"\"dev\":{\"ids\":[\"%s\"],\"name\":\"%s\",\"mf\":\"%s\",\"mdl\":\"%s\"}"
|
||||||
|
"}",
|
||||||
|
TEMP_NAME, TEMP_UNIQUE_ID, TEMP_STATE_TOPIC, DEVICE_UNIQUE_ID,
|
||||||
|
DEVICE_NAME, DEVICE_MANUFACTURER, DEVICE_MODEL);
|
||||||
|
|
||||||
|
printf("publishing ha discovery for temperature...\n");
|
||||||
|
err = mqtt_publish(client, TEMP_CONFIG_TOPIC, payload, strlen(payload), 1, 1,
|
||||||
|
mqtt_pub_request_cb, NULL);
|
||||||
|
if (err != ERR_OK) {
|
||||||
|
printf("failed to publish temp config: %d\n", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// publish humidity sensor config
|
||||||
|
snprintf(
|
||||||
|
payload, sizeof(payload),
|
||||||
|
"{"
|
||||||
|
"\"name\":\"%s\",\"unique_id\":\"%s\",\"stat_t\":\"%s\","
|
||||||
|
"\"dev_cla\":\"humidity\",\"unit_of_meas\":\"%%\","
|
||||||
|
"\"val_tpl\":\"{{ value|float(2) }}\","
|
||||||
|
"\"dev\":{\"ids\":[\"%s\"],\"name\":\"%s\",\"mf\":\"%s\",\"mdl\":\"%s\"}"
|
||||||
|
"}",
|
||||||
|
HUM_NAME, HUM_UNIQUE_ID, HUM_STATE_TOPIC, DEVICE_UNIQUE_ID, DEVICE_NAME,
|
||||||
|
DEVICE_MANUFACTURER, DEVICE_MODEL);
|
||||||
|
|
||||||
|
printf("publishing ha discovery for humidity...\n");
|
||||||
|
err = mqtt_publish(client, HUM_CONFIG_TOPIC, payload, strlen(payload), 1, 1,
|
||||||
|
mqtt_pub_request_cb, NULL);
|
||||||
|
if (err != ERR_OK) {
|
||||||
|
printf("failed to publish hum config: %d\n", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mqtt_pub_request_cb(void* arg, err_t err) {
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
if (err != ERR_OK) {
|
||||||
|
printf("mqtt publish failed with error: %d\n", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mqtt_connection_cb(mqtt_client_t* client, void* arg,
|
||||||
|
mqtt_connection_status_t status) {
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
if (status == MQTT_CONNECT_ACCEPTED) {
|
||||||
|
printf("mqtt connection successful\n");
|
||||||
|
s_mqtt_connected = 1;
|
||||||
|
// on connect, publish the discovery configuration messages
|
||||||
|
publish_ha_discovery_configs(client);
|
||||||
|
} else {
|
||||||
|
printf("mqtt connection failed, status: %d. will retry.\n", status);
|
||||||
|
s_mqtt_connected = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +1,16 @@
|
|||||||
#ifndef _GXHT30_CH32_HW_I2C_H
|
#ifndef _GXHT30_HW_I2C_H
|
||||||
#define _GXHT30_CH32_HW_I2C_H
|
#define _GXHT30_HW_I2C_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "ch32fun.h"
|
#include "hw_i2c.h"
|
||||||
#include "ch32v20xhw.h"
|
|
||||||
|
|
||||||
// I2C Configuration
|
// GXHT30 I2C addresses
|
||||||
#define GXHT30_I2C_CLKRATE 400000
|
|
||||||
#define GXHT30_I2C_PRERATE 2000000
|
|
||||||
#define GXHT30_I2C_TIMEOUT_MAX 250000
|
|
||||||
|
|
||||||
// GXHT30 I2C Addresses
|
|
||||||
#define GXHT30_I2C_ADDR_DEFAULT 0x44
|
#define GXHT30_I2C_ADDR_DEFAULT 0x44
|
||||||
#define GXHT30_I2C_ADDR_ALT 0x45
|
#define GXHT30_I2C_ADDR_ALT 0x45
|
||||||
|
|
||||||
// Commands
|
// GXHT30 cmds
|
||||||
#define GXHT30_CMD_MEAS_MSB 0x2C
|
#define GXHT30_CMD_MEAS_MSB 0x2C
|
||||||
#define GXHT30_CMD_MEAS_LSB 0x06
|
#define GXHT30_CMD_MEAS_LSB 0x06
|
||||||
#define GXHT30_CMD_SOFT_RESET_MSB 0x30
|
#define GXHT30_CMD_SOFT_RESET_MSB 0x30
|
||||||
@@ -30,26 +24,18 @@
|
|||||||
#define GXHT30_CMD_CLEAR_STATUS_MSB 0x30
|
#define GXHT30_CMD_CLEAR_STATUS_MSB 0x30
|
||||||
#define GXHT30_CMD_CLEAR_STATUS_LSB 0x41
|
#define GXHT30_CMD_CLEAR_STATUS_LSB 0x41
|
||||||
|
|
||||||
// I2C Event Masks
|
// conversion constants
|
||||||
#define GXHT30_I2C_EVT_MASTER_MODE_SELECT \
|
#define GXHT30_TEMP_MULTIPLIER 2670
|
||||||
((uint32_t)0x00030001) // BUSY, MSL, SB
|
#define GXHT30_TEMP_OFFSET 45000000L
|
||||||
#define GXHT30_I2C_EVT_MASTER_TRANSMITTER_MODE \
|
#define GXHT30_HUM_MULTIPLIER 1526
|
||||||
((uint32_t)0x00070082) // BUSY, MSL, ADDR, TXE, TRA
|
#define GXHT30_SCALE_DIVISOR 10000
|
||||||
#define GXHT30_I2C_EVT_MASTER_RECEIVER_MODE \
|
|
||||||
((uint32_t)0x00030002) // BUSY, MSL, ADDR
|
|
||||||
#define GXHT30_I2C_EVT_MASTER_BYTE_TRANSMITTED \
|
|
||||||
((uint32_t)0x00070084) // TRA, BUSY, MSL, TXE, BTF
|
|
||||||
#define GXHT30_I2C_EVT_MASTER_BYTE_RECEIVED \
|
|
||||||
((uint32_t)0x00030040) // BUSY, MSL, RXNE
|
|
||||||
|
|
||||||
// Sensor Data Structure
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
float temperature; // Temperature in Celsius
|
int32_t temperature_x100; // Temperature in hundredths of a degree C
|
||||||
float humidity; // Relative humidity in %
|
int32_t humidity_x100; // Humidity in hundredths of a percent RH
|
||||||
uint8_t error; // Last error code
|
uint8_t error; // Last error code
|
||||||
} GXHT30_Data;
|
} GXHT30_Data;
|
||||||
|
|
||||||
// Status Register Structure
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t alert_pending; // Bit 15: Alert status
|
uint8_t alert_pending; // Bit 15: Alert status
|
||||||
uint8_t heater_on; // Bit 13: Heater status
|
uint8_t heater_on; // Bit 13: Heater status
|
||||||
@@ -61,76 +47,14 @@ typedef struct {
|
|||||||
uint16_t raw_status; // Raw 16-bit status value
|
uint16_t raw_status; // Raw 16-bit status value
|
||||||
} GXHT30_Status;
|
} GXHT30_Status;
|
||||||
|
|
||||||
// Error Codes
|
|
||||||
enum GXHT30_Error {
|
enum GXHT30_Error {
|
||||||
GXHT30_OK = 0,
|
GXHT30_OK = 0,
|
||||||
GXHT30_ERR_TIMEOUT,
|
GXHT30_ERR_TIMEOUT,
|
||||||
GXHT30_ERR_CRC,
|
GXHT30_ERR_CRC,
|
||||||
GXHT30_ERR_I2C,
|
GXHT30_ERR_I2C
|
||||||
GXHT30_ERR_BUSY
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline uint8_t _gxht30_i2c_check_event(uint32_t event_mask) {
|
static inline bool gxht30_crc8_check(uint8_t msb, uint8_t lsb, uint8_t crc) {
|
||||||
uint32_t status = I2C1->STAR1 | (I2C1->STAR2 << 16);
|
|
||||||
return (status & event_mask) == event_mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint8_t _gxht30_wait_event(uint32_t event_mask) {
|
|
||||||
int32_t timeout = GXHT30_I2C_TIMEOUT_MAX;
|
|
||||||
while (!_gxht30_i2c_check_event(event_mask) && (timeout-- > 0));
|
|
||||||
return timeout > 0 ? GXHT30_OK : GXHT30_ERR_TIMEOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint8_t _gxht30_wait_flag(uint32_t flag) {
|
|
||||||
int32_t timeout = GXHT30_I2C_TIMEOUT_MAX;
|
|
||||||
while (!(I2C1->STAR1 & flag) && (timeout-- > 0));
|
|
||||||
return timeout > 0 ? GXHT30_OK : GXHT30_ERR_TIMEOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint8_t _gxht30_i2c_start(uint8_t addr, uint8_t direction) {
|
|
||||||
// wait until bus is not busy
|
|
||||||
int32_t timeout = GXHT30_I2C_TIMEOUT_MAX;
|
|
||||||
while ((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout-- > 0));
|
|
||||||
if (timeout <= 0) return GXHT30_ERR_TIMEOUT;
|
|
||||||
|
|
||||||
// gen START
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_START;
|
|
||||||
if (_gxht30_wait_event(GXHT30_I2C_EVT_MASTER_MODE_SELECT) != GXHT30_OK)
|
|
||||||
return GXHT30_ERR_TIMEOUT;
|
|
||||||
|
|
||||||
// send addr
|
|
||||||
I2C1->DATAR = (addr << 1) | direction;
|
|
||||||
|
|
||||||
uint32_t event = (direction == 0) ? GXHT30_I2C_EVT_MASTER_TRANSMITTER_MODE
|
|
||||||
: GXHT30_I2C_EVT_MASTER_RECEIVER_MODE;
|
|
||||||
|
|
||||||
return _gxht30_wait_event(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint8_t _gxht30_i2c_write_byte(uint8_t data) {
|
|
||||||
I2C1->DATAR = data;
|
|
||||||
return _gxht30_wait_flag(I2C_STAR1_TXE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint8_t _gxht30_i2c_read(uint8_t* buffer, uint8_t length) {
|
|
||||||
for (uint8_t i = 0; i < length; i++) {
|
|
||||||
if (i == length - 1) {
|
|
||||||
I2C1->CTLR1 &= ~I2C_CTLR1_ACK; // NACK last byte
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_gxht30_wait_flag(I2C_STAR1_RXNE) != GXHT30_OK) {
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_ACK;
|
|
||||||
return GXHT30_ERR_TIMEOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer[i] = I2C1->DATAR;
|
|
||||||
}
|
|
||||||
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_ACK; // re-enable ACK
|
|
||||||
return GXHT30_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool _gxht30_crc8_check(uint8_t msb, uint8_t lsb, uint8_t crc) {
|
|
||||||
uint8_t calc_crc = 0xFF;
|
uint8_t calc_crc = 0xFF;
|
||||||
uint8_t data[2] = {msb, lsb};
|
uint8_t data[2] = {msb, lsb};
|
||||||
|
|
||||||
@@ -144,121 +68,61 @@ static inline bool _gxht30_crc8_check(uint8_t msb, uint8_t lsb, uint8_t crc) {
|
|||||||
return calc_crc == crc;
|
return calc_crc == crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// init I2C hw
|
|
||||||
static inline void gxht30_i2c_init(void) {
|
|
||||||
uint16_t tempreg;
|
|
||||||
|
|
||||||
RCC->APB2PCENR |= RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO;
|
|
||||||
RCC->APB1PCENR |= RCC_APB1Periph_I2C1;
|
|
||||||
|
|
||||||
// PB6 (SCL) and PB7 (SDA)
|
|
||||||
GPIOB->CFGLR &= ~(0xff << (4 * 6));
|
|
||||||
GPIOB->CFGLR |= ((GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF) << (4 * 6)) |
|
|
||||||
((GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF) << (4 * 7));
|
|
||||||
|
|
||||||
// rst I2C1
|
|
||||||
RCC->APB1PRSTR |= RCC_APB1Periph_I2C1;
|
|
||||||
RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1;
|
|
||||||
|
|
||||||
// i2c frequency
|
|
||||||
tempreg = I2C1->CTLR2;
|
|
||||||
tempreg &= ~I2C_CTLR2_FREQ;
|
|
||||||
tempreg |= (FUNCONF_SYSTEM_CORE_CLOCK / GXHT30_I2C_PRERATE) & I2C_CTLR2_FREQ;
|
|
||||||
I2C1->CTLR2 = tempreg;
|
|
||||||
|
|
||||||
// Fast Mode 400kHz
|
|
||||||
tempreg =
|
|
||||||
(FUNCONF_SYSTEM_CORE_CLOCK / (3 * GXHT30_I2C_CLKRATE)) & I2C_CKCFGR_CCR;
|
|
||||||
tempreg |= I2C_CKCFGR_FS;
|
|
||||||
I2C1->CKCFGR = tempreg;
|
|
||||||
|
|
||||||
// en I2C and ACK
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_PE | I2C_CTLR1_ACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint8_t gxht30_send_command(uint8_t addr, uint8_t cmd_msb,
|
static inline uint8_t gxht30_send_command(uint8_t addr, uint8_t cmd_msb,
|
||||||
uint8_t cmd_lsb) {
|
uint8_t cmd_lsb) {
|
||||||
uint8_t err;
|
uint8_t cmd[2] = {cmd_msb, cmd_lsb};
|
||||||
|
uint8_t err = i2c_write(addr, cmd, 2);
|
||||||
|
|
||||||
if ((err = _gxht30_i2c_start(addr, 0)) != GXHT30_OK) return err;
|
return (err == I2C_OK) ? GXHT30_OK : GXHT30_ERR_I2C;
|
||||||
if ((err = _gxht30_i2c_write_byte(cmd_msb)) != GXHT30_OK) return err;
|
|
||||||
if ((err = _gxht30_i2c_write_byte(cmd_lsb)) != GXHT30_OK) return err;
|
|
||||||
if (_gxht30_wait_event(GXHT30_I2C_EVT_MASTER_BYTE_TRANSMITTED) != GXHT30_OK)
|
|
||||||
return GXHT30_ERR_TIMEOUT;
|
|
||||||
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_STOP;
|
|
||||||
return GXHT30_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// read temp and humidity
|
|
||||||
static inline uint8_t gxht30_read_data(uint8_t addr, GXHT30_Data* data) {
|
static inline uint8_t gxht30_read_data(uint8_t addr, GXHT30_Data* data) {
|
||||||
|
uint8_t cmd[2] = {GXHT30_CMD_MEAS_MSB, GXHT30_CMD_MEAS_LSB};
|
||||||
uint8_t rx_data[6];
|
uint8_t rx_data[6];
|
||||||
uint8_t err;
|
uint8_t err;
|
||||||
|
|
||||||
if ((err = gxht30_send_command(addr, GXHT30_CMD_MEAS_MSB,
|
// send measurement command and read
|
||||||
GXHT30_CMD_MEAS_LSB)) != GXHT30_OK) {
|
if ((err = i2c_write_read(addr, cmd, 2, rx_data, 6)) != I2C_OK) {
|
||||||
data->error = err;
|
data->error = GXHT30_ERR_I2C;
|
||||||
return err;
|
return GXHT30_ERR_I2C;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read data
|
// verify CRC for both temp and humidity
|
||||||
if ((err = _gxht30_i2c_start(addr, 1)) != GXHT30_OK) {
|
if (!gxht30_crc8_check(rx_data[0], rx_data[1], rx_data[2]) ||
|
||||||
data->error = err;
|
!gxht30_crc8_check(rx_data[3], rx_data[4], rx_data[5])) {
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = _gxht30_i2c_read(rx_data, 6)) != GXHT30_OK) {
|
|
||||||
data->error = err;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_STOP;
|
|
||||||
|
|
||||||
// verify crc
|
|
||||||
if (!_gxht30_crc8_check(rx_data[0], rx_data[1], rx_data[2]) ||
|
|
||||||
!_gxht30_crc8_check(rx_data[3], rx_data[4], rx_data[5])) {
|
|
||||||
data->error = GXHT30_ERR_CRC;
|
data->error = GXHT30_ERR_CRC;
|
||||||
return GXHT30_ERR_CRC;
|
return GXHT30_ERR_CRC;
|
||||||
}
|
}
|
||||||
|
|
||||||
// calc values
|
// calc the values
|
||||||
uint16_t temp_raw = (rx_data[0] << 8) | rx_data[1];
|
uint16_t temp_raw = (rx_data[0] << 8) | rx_data[1];
|
||||||
uint16_t hum_raw = (rx_data[3] << 8) | rx_data[4];
|
uint16_t hum_raw = (rx_data[3] << 8) | rx_data[4];
|
||||||
|
|
||||||
data->temperature = (float)temp_raw * 0.00267033f - 45.0f;
|
data->temperature_x100 =
|
||||||
data->humidity = (float)hum_raw * 0.0015259f;
|
(((int64_t)temp_raw * GXHT30_TEMP_MULTIPLIER) - GXHT30_TEMP_OFFSET) /
|
||||||
|
GXHT30_SCALE_DIVISOR;
|
||||||
|
data->humidity_x100 =
|
||||||
|
((int64_t)hum_raw * GXHT30_HUM_MULTIPLIER) / GXHT30_SCALE_DIVISOR;
|
||||||
data->error = GXHT30_OK;
|
data->error = GXHT30_OK;
|
||||||
|
|
||||||
return GXHT30_OK;
|
return GXHT30_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint8_t gxht30_read_status(uint8_t addr, GXHT30_Status* status) {
|
static inline uint8_t gxht30_read_status(uint8_t addr, GXHT30_Status* status) {
|
||||||
|
uint8_t cmd[2] = {GXHT30_CMD_STATUS_MSB, GXHT30_CMD_STATUS_LSB};
|
||||||
uint8_t rx_data[3];
|
uint8_t rx_data[3];
|
||||||
uint8_t err;
|
uint8_t err;
|
||||||
|
|
||||||
if ((err = _gxht30_i2c_start(addr, 0)) != GXHT30_OK) return err;
|
if ((err = i2c_write_read(addr, cmd, 2, rx_data, 3)) != I2C_OK) {
|
||||||
if ((err = _gxht30_i2c_write_byte(GXHT30_CMD_STATUS_MSB)) != GXHT30_OK)
|
return GXHT30_ERR_I2C;
|
||||||
return err;
|
}
|
||||||
if ((err = _gxht30_i2c_write_byte(GXHT30_CMD_STATUS_LSB)) != GXHT30_OK)
|
|
||||||
return err;
|
|
||||||
if (_gxht30_wait_event(GXHT30_I2C_EVT_MASTER_BYTE_TRANSMITTED) != GXHT30_OK)
|
|
||||||
return GXHT30_ERR_TIMEOUT;
|
|
||||||
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_START;
|
// verify CRC
|
||||||
if (_gxht30_wait_event(GXHT30_I2C_EVT_MASTER_MODE_SELECT) != GXHT30_OK)
|
if (!gxht30_crc8_check(rx_data[0], rx_data[1], rx_data[2])) {
|
||||||
return GXHT30_ERR_TIMEOUT;
|
|
||||||
|
|
||||||
I2C1->DATAR = (addr << 1) | 0x01;
|
|
||||||
if (_gxht30_wait_event(GXHT30_I2C_EVT_MASTER_RECEIVER_MODE) != GXHT30_OK)
|
|
||||||
return GXHT30_ERR_TIMEOUT;
|
|
||||||
|
|
||||||
if ((err = _gxht30_i2c_read(rx_data, 3)) != GXHT30_OK) return err;
|
|
||||||
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_STOP;
|
|
||||||
|
|
||||||
if (!_gxht30_crc8_check(rx_data[0], rx_data[1], rx_data[2]))
|
|
||||||
return GXHT30_ERR_CRC;
|
return GXHT30_ERR_CRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse status register
|
||||||
uint16_t raw = (rx_data[0] << 8) | rx_data[1];
|
uint16_t raw = (rx_data[0] << 8) | rx_data[1];
|
||||||
status->raw_status = raw;
|
status->raw_status = raw;
|
||||||
status->alert_pending = (raw >> 15) & 0x01;
|
status->alert_pending = (raw >> 15) & 0x01;
|
||||||
@@ -292,4 +156,4 @@ static inline uint8_t gxht30_clear_status(uint8_t addr) {
|
|||||||
GXHT30_CMD_CLEAR_STATUS_LSB);
|
GXHT30_CMD_CLEAR_STATUS_LSB);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // _GXHT30_CH32_HW_I2C_H
|
#endif // _GXHT30_HW_I2C_H
|
||||||
|
|||||||
13
inc/ha_mqtt.h
Normal file
13
inc/ha_mqtt.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// ha_mqtt.h
|
||||||
|
|
||||||
|
#ifndef HA_MQTT_H
|
||||||
|
#define HA_MQTT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void ha_mqtt_init(void);
|
||||||
|
void ha_mqtt_publish_sensor_data(int32_t temp_x100, int32_t hum_x100);
|
||||||
|
void ha_mqtt_check_and_reconnect(void);
|
||||||
|
uint8_t ha_mqtt_is_connected(void);
|
||||||
|
|
||||||
|
#endif // HA_MQTT_H
|
||||||
181
inc/hw_i2c.h
Normal file
181
inc/hw_i2c.h
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
#ifndef _HW_I2C_H
|
||||||
|
#define _HW_I2C_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "ch32fun.h"
|
||||||
|
#include "ch32v20xhw.h"
|
||||||
|
|
||||||
|
// config
|
||||||
|
#define I2C_CLKRATE 400000
|
||||||
|
#define I2C_PRERATE 2000000
|
||||||
|
#define I2C_TIMEOUT_MAX 250000
|
||||||
|
|
||||||
|
#define I2C_DIRECTION_TX 0
|
||||||
|
#define I2C_DIRECTION_RX 1
|
||||||
|
|
||||||
|
enum I2C_Error { I2C_OK = 0, I2C_ERR_TIMEOUT, I2C_ERR_BUSY, I2C_ERR_NACK };
|
||||||
|
|
||||||
|
static inline uint8_t i2c_check_event(uint32_t event_mask) {
|
||||||
|
uint32_t status = I2C1->STAR1 | (I2C1->STAR2 << 16);
|
||||||
|
return (status & event_mask) == event_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t i2c_wait_event(uint32_t event_mask) {
|
||||||
|
int32_t timeout = I2C_TIMEOUT_MAX;
|
||||||
|
while (!i2c_check_event(event_mask) && (timeout-- > 0));
|
||||||
|
return timeout > 0 ? I2C_OK : I2C_ERR_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t i2c_wait_flag(uint32_t flag) {
|
||||||
|
int32_t timeout = I2C_TIMEOUT_MAX;
|
||||||
|
while (!(I2C1->STAR1 & flag) && (timeout-- > 0));
|
||||||
|
return timeout > 0 ? I2C_OK : I2C_ERR_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t i2c_start(uint8_t addr, uint8_t direction) {
|
||||||
|
// wait until bus is not busy
|
||||||
|
int32_t timeout = I2C_TIMEOUT_MAX;
|
||||||
|
while ((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout-- > 0));
|
||||||
|
if (timeout <= 0) return I2C_ERR_TIMEOUT;
|
||||||
|
|
||||||
|
// gen START
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_START;
|
||||||
|
if (i2c_wait_event(I2C_EVENT_MASTER_MODE_SELECT) != I2C_OK)
|
||||||
|
return I2C_ERR_TIMEOUT;
|
||||||
|
|
||||||
|
// send address
|
||||||
|
I2C1->DATAR = (addr << 1) | direction;
|
||||||
|
|
||||||
|
uint32_t event = (direction == I2C_DIRECTION_TX)
|
||||||
|
? I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED
|
||||||
|
: I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED;
|
||||||
|
|
||||||
|
return i2c_wait_event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void i2c_stop(void) { I2C1->CTLR1 |= I2C_CTLR1_STOP; }
|
||||||
|
|
||||||
|
static inline uint8_t i2c_write_byte(uint8_t data) {
|
||||||
|
I2C1->DATAR = data;
|
||||||
|
return i2c_wait_flag(I2C_STAR1_TXE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t i2c_write(uint8_t addr, const uint8_t* data,
|
||||||
|
uint8_t length) {
|
||||||
|
uint8_t err;
|
||||||
|
|
||||||
|
if ((err = i2c_start(addr, I2C_DIRECTION_TX)) != I2C_OK) return err;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < length; i++) {
|
||||||
|
if ((err = i2c_write_byte(data[i])) != I2C_OK) return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2c_wait_event(I2C_EVENT_MASTER_BYTE_TRANSMITTED) != I2C_OK)
|
||||||
|
return I2C_ERR_TIMEOUT;
|
||||||
|
|
||||||
|
i2c_stop();
|
||||||
|
return I2C_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t i2c_read(uint8_t addr, uint8_t* buffer, uint8_t length) {
|
||||||
|
uint8_t err;
|
||||||
|
|
||||||
|
if ((err = i2c_start(addr, I2C_DIRECTION_RX)) != I2C_OK) return err;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < length; i++) {
|
||||||
|
if (i == length - 1) {
|
||||||
|
I2C1->CTLR1 &= ~I2C_CTLR1_ACK; // NACK last byte
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2c_wait_flag(I2C_STAR1_RXNE) != I2C_OK) {
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_ACK;
|
||||||
|
return I2C_ERR_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[i] = I2C1->DATAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_ACK; // re-enable ACK
|
||||||
|
i2c_stop();
|
||||||
|
return I2C_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t i2c_write_read(uint8_t addr, const uint8_t* tx_data,
|
||||||
|
uint8_t tx_len, uint8_t* rx_data,
|
||||||
|
uint8_t rx_len) {
|
||||||
|
uint8_t err;
|
||||||
|
|
||||||
|
// Write phase
|
||||||
|
if ((err = i2c_start(addr, I2C_DIRECTION_TX)) != I2C_OK) return err;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < tx_len; i++) {
|
||||||
|
if ((err = i2c_write_byte(tx_data[i])) != I2C_OK) return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2c_wait_event(I2C_EVENT_MASTER_BYTE_TRANSMITTED) != I2C_OK)
|
||||||
|
return I2C_ERR_TIMEOUT;
|
||||||
|
|
||||||
|
// repeated START for read phase
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_START;
|
||||||
|
if (i2c_wait_event(I2C_EVENT_MASTER_MODE_SELECT) != I2C_OK)
|
||||||
|
return I2C_ERR_TIMEOUT;
|
||||||
|
|
||||||
|
I2C1->DATAR = (addr << 1) | I2C_DIRECTION_RX;
|
||||||
|
if (i2c_wait_event(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != I2C_OK)
|
||||||
|
return I2C_ERR_TIMEOUT;
|
||||||
|
|
||||||
|
// read phase
|
||||||
|
for (uint8_t i = 0; i < rx_len; i++) {
|
||||||
|
if (i == rx_len - 1) {
|
||||||
|
I2C1->CTLR1 &= ~I2C_CTLR1_ACK; // NACK last byte
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2c_wait_flag(I2C_STAR1_RXNE) != I2C_OK) {
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_ACK;
|
||||||
|
return I2C_ERR_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
rx_data[i] = I2C1->DATAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_ACK; // re-enable ACK
|
||||||
|
i2c_stop();
|
||||||
|
return I2C_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void i2c_init(void) {
|
||||||
|
uint16_t tempreg;
|
||||||
|
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO;
|
||||||
|
RCC->APB1PCENR |= RCC_APB1Periph_I2C1;
|
||||||
|
|
||||||
|
AFIO->PCFR1 |= AFIO_PCFR1_I2C1_REMAP; // I2C1_RM: 1
|
||||||
|
|
||||||
|
// PB8 (SCL) and PB9 (SDA)
|
||||||
|
funPinMode(PB8, GPIO_CFGLR_OUT_10Mhz_AF_OD);
|
||||||
|
funPinMode(PB9, GPIO_CFGLR_OUT_10Mhz_AF_OD);
|
||||||
|
|
||||||
|
// Reset I2C1 to init all regs
|
||||||
|
RCC->APB1PRSTR |= RCC_APB1Periph_I2C1;
|
||||||
|
RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1;
|
||||||
|
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_SWRST;
|
||||||
|
I2C1->CTLR1 &= ~I2C_CTLR1_SWRST;
|
||||||
|
|
||||||
|
// set I2C freq
|
||||||
|
tempreg = I2C1->CTLR2;
|
||||||
|
tempreg &= ~I2C_CTLR2_FREQ;
|
||||||
|
tempreg |= (FUNCONF_SYSTEM_CORE_CLOCK / I2C_PRERATE) & I2C_CTLR2_FREQ;
|
||||||
|
I2C1->CTLR2 = tempreg;
|
||||||
|
|
||||||
|
// Fast Mode 400kHz
|
||||||
|
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK / (3 * I2C_CLKRATE)) & I2C_CKCFGR_CCR;
|
||||||
|
tempreg |= I2C_CKCFGR_FS;
|
||||||
|
I2C1->CKCFGR = tempreg;
|
||||||
|
|
||||||
|
// en I2C and ACK
|
||||||
|
I2C1->CTLR1 |= I2C_CTLR1_PE | I2C_CTLR1_ACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _HW_I2C_H
|
||||||
72
main.c
72
main.c
@@ -4,6 +4,8 @@
|
|||||||
#include "ch32v20xhw.h"
|
#include "ch32v20xhw.h"
|
||||||
#include "ethernetif.h"
|
#include "ethernetif.h"
|
||||||
#include "gxht30_hw_i2c.h"
|
#include "gxht30_hw_i2c.h"
|
||||||
|
#include "ha_mqtt.h"
|
||||||
|
#include "hw_i2c.h"
|
||||||
#include "lwip/apps/httpd.h"
|
#include "lwip/apps/httpd.h"
|
||||||
#include "lwip/dhcp.h"
|
#include "lwip/dhcp.h"
|
||||||
#include "lwip/init.h"
|
#include "lwip/init.h"
|
||||||
@@ -18,7 +20,7 @@
|
|||||||
#define HSE_STARTUP_TIMEOUT 10000
|
#define HSE_STARTUP_TIMEOUT 10000
|
||||||
#define PLL_LOCK_TIMEOUT 10000
|
#define PLL_LOCK_TIMEOUT 10000
|
||||||
#define LED_TOGGLE_INTERVAL_MS 500
|
#define LED_TOGGLE_INTERVAL_MS 500
|
||||||
#define LINK_POLL_INTERVAL_MS 500
|
#define LINK_POLL_INTERVAL_MS 100
|
||||||
|
|
||||||
#define RCC_PREDIV1_OFFSET 0
|
#define RCC_PREDIV1_OFFSET 0
|
||||||
#define HSE_CLOCK_MHZ 32
|
#define HSE_CLOCK_MHZ 32
|
||||||
@@ -26,8 +28,7 @@
|
|||||||
#define PLL_MULTIPLIER 15
|
#define PLL_MULTIPLIER 15
|
||||||
|
|
||||||
#define STATS_PRINT_INTERVAL_MS 10000
|
#define STATS_PRINT_INTERVAL_MS 10000
|
||||||
#define SENSOR_READ_INTERVAL_MS 5000
|
#define SENSOR_READ_INTERVAL_MS 60000
|
||||||
#define STATUS_READ_INTERVAL_MS 30000
|
|
||||||
|
|
||||||
struct netif g_netif;
|
struct netif g_netif;
|
||||||
static volatile int g_httpd_is_initialized = 0;
|
static volatile int g_httpd_is_initialized = 0;
|
||||||
@@ -181,17 +182,17 @@ void ethernetif_print_stats(void) {
|
|||||||
int main() {
|
int main() {
|
||||||
SystemInit();
|
SystemInit();
|
||||||
set_sysclk_to_120mhz_from_hse();
|
set_sysclk_to_120mhz_from_hse();
|
||||||
print_clock_registers();
|
|
||||||
|
|
||||||
systick_init();
|
systick_init();
|
||||||
led_init();
|
led_init();
|
||||||
lwip_stack_init();
|
lwip_stack_init();
|
||||||
|
i2c_init();
|
||||||
gxht30_i2c_init();
|
|
||||||
|
|
||||||
gxht30_soft_reset(GXHT30_I2C_ADDR_DEFAULT);
|
gxht30_soft_reset(GXHT30_I2C_ADDR_DEFAULT);
|
||||||
Delay_Ms(10);
|
Delay_Ms(10);
|
||||||
|
|
||||||
|
ha_mqtt_init();
|
||||||
|
|
||||||
uint32_t last_led_toggle_time = 0;
|
uint32_t last_led_toggle_time = 0;
|
||||||
uint32_t last_link_poll_time = 0;
|
uint32_t last_link_poll_time = 0;
|
||||||
#if LWIP_STATS
|
#if LWIP_STATS
|
||||||
@@ -203,9 +204,8 @@ int main() {
|
|||||||
uint32_t last_status_read_time = 0;
|
uint32_t last_status_read_time = 0;
|
||||||
|
|
||||||
GXHT30_Data sensor_data = {0};
|
GXHT30_Data sensor_data = {0};
|
||||||
GXHT30_Status sensor_status = {0};
|
|
||||||
|
|
||||||
printf("GXHT30 Sensor initialized\n");
|
printf("System initialized. Main loop starting.\n");
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
ethernetif_input(&g_netif);
|
ethernetif_input(&g_netif);
|
||||||
@@ -223,60 +223,30 @@ int main() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Read sensor data periodically
|
ha_mqtt_check_and_reconnect();
|
||||||
|
|
||||||
if (millis() - last_sensor_read_time > SENSOR_READ_INTERVAL_MS) {
|
if (millis() - last_sensor_read_time > SENSOR_READ_INTERVAL_MS) {
|
||||||
if (gxht30_read_data(GXHT30_I2C_ADDR_DEFAULT, &sensor_data) ==
|
if (gxht30_read_data(GXHT30_I2C_ADDR_DEFAULT, &sensor_data) ==
|
||||||
GXHT30_OK) {
|
GXHT30_OK) {
|
||||||
int16_t temp_int = (int16_t)(sensor_data.temperature * 100);
|
// int32_t temp_int = sensor_data.temperature_x100 / 100;
|
||||||
int16_t hum_int = (int16_t)(sensor_data.humidity * 100);
|
// int32_t temp_frac = sensor_data.temperature_x100 % 100;
|
||||||
|
// if (temp_frac < 0) temp_frac = -temp_frac;
|
||||||
|
|
||||||
int16_t temp_whole = temp_int / 100;
|
// int32_t hum_int = sensor_data.humidity_x100 / 100;
|
||||||
int16_t temp_decimal = temp_int % 100;
|
// int32_t hum_frac = sensor_data.humidity_x100 % 100;
|
||||||
if (temp_decimal < 0) temp_decimal = -temp_decimal;
|
|
||||||
|
|
||||||
int16_t hum_whole = hum_int / 100;
|
// printf("Read Sensor -> Temp: %ld.%02ld C, Hum: %ld.%02ld %%\n",
|
||||||
int16_t hum_decimal = hum_int % 100;
|
// temp_int, temp_frac, hum_int, hum_frac);
|
||||||
|
ha_mqtt_publish_sensor_data(sensor_data.temperature_x100,
|
||||||
|
sensor_data.humidity_x100);
|
||||||
|
|
||||||
printf("Temperature: %d.%02d C | Humidity: %d.%02d %%\n", temp_whole,
|
|
||||||
temp_decimal, hum_whole, hum_decimal);
|
|
||||||
} else {
|
} else {
|
||||||
printf("Sensor error: %d\n", sensor_data.error);
|
printf("Failed to read sensor. Error: %d\n", sensor_data.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
last_sensor_read_time = millis();
|
last_sensor_read_time = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read status register periodically
|
|
||||||
if (millis() - last_status_read_time > STATUS_READ_INTERVAL_MS) {
|
|
||||||
if (gxht30_read_status(GXHT30_I2C_ADDR_DEFAULT, &sensor_status) ==
|
|
||||||
GXHT30_OK) {
|
|
||||||
printf("Status: ");
|
|
||||||
|
|
||||||
if (sensor_status.alert_pending) printf("ALERT ");
|
|
||||||
if (sensor_status.heater_on) printf("HEATER_ON ");
|
|
||||||
if (sensor_status.humidity_alert) printf("HUM_ALERT ");
|
|
||||||
if (sensor_status.temperature_alert) printf("TEMP_ALERT ");
|
|
||||||
if (sensor_status.reset_detected) printf("RESET ");
|
|
||||||
if (sensor_status.command_status) printf("CMD_ERR ");
|
|
||||||
if (sensor_status.crc_status) printf("CRC_ERR ");
|
|
||||||
|
|
||||||
if (sensor_status.raw_status == 0x0010) {
|
|
||||||
printf("OK (reset detected on startup)");
|
|
||||||
} else if (sensor_status.raw_status == 0x0000) {
|
|
||||||
printf("OK");
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
// Clear reset flag after first read if you want
|
|
||||||
// if (sensor_status.reset_detected) {
|
|
||||||
// gxht30_clear_status(GXHT30_I2C_ADDR_DEFAULT);
|
|
||||||
// }
|
|
||||||
} else {
|
|
||||||
printf("Status read error\n");
|
|
||||||
}
|
|
||||||
last_status_read_time = millis();
|
|
||||||
}
|
|
||||||
|
|
||||||
// uint32_t now = millis();
|
// uint32_t now = millis();
|
||||||
// if (now - last_led_toggle_time > LED_TOGGLE_INTERVAL_MS) {
|
// if (now - last_led_toggle_time > LED_TOGGLE_INTERVAL_MS) {
|
||||||
// if (led_state) {
|
// if (led_state) {
|
||||||
|
|||||||
@@ -8,66 +8,24 @@
|
|||||||
#include "ch32v20xhw.h"
|
#include "ch32v20xhw.h"
|
||||||
#include "lwip/etharp.h"
|
#include "lwip/etharp.h"
|
||||||
#include "lwip/snmp.h"
|
#include "lwip/snmp.h"
|
||||||
#include "systick.h"
|
|
||||||
|
#define CH32V208_ETH_IMPLEMENTATION
|
||||||
|
#define ETH_RX_BUF_COUNT 4
|
||||||
|
#define ETH_TX_BUF_COUNT 2
|
||||||
|
|
||||||
|
#include "ch32v208_eth.h"
|
||||||
|
|
||||||
#define IFNAME0 'e'
|
#define IFNAME0 'e'
|
||||||
#define IFNAME1 'n'
|
#define IFNAME1 'n'
|
||||||
|
|
||||||
typedef struct {
|
static volatile bool g_link_changed = false;
|
||||||
volatile uint32_t head; // producer idx: next free slot to write to
|
|
||||||
volatile uint32_t tail; // consumer idx: next slot to be txed
|
|
||||||
volatile bool is_full; // for N=1 size
|
|
||||||
} tx_queue_t;
|
|
||||||
|
|
||||||
struct ethernetif {
|
static void eth_link_callback(bool link_up);
|
||||||
ETH_DMADESCTypeDef* rx_desc_head; // next desc to be filled by DMA
|
|
||||||
ETH_DMADESCTypeDef* rx_desc_tail; // next desc to be read by CPU
|
|
||||||
tx_queue_t tx_q;
|
|
||||||
};
|
|
||||||
|
|
||||||
__attribute__((aligned(4))) ETH_DMADESCTypeDef g_dma_rx_descs[ETH_RX_BUF_COUNT];
|
|
||||||
__attribute__((aligned(4))) ETH_DMADESCTypeDef g_dma_tx_descs[ETH_TX_BUF_COUNT];
|
|
||||||
__attribute__((
|
|
||||||
aligned(4))) uint8_t g_mac_rx_bufs[ETH_RX_BUF_COUNT * ETH_RX_BUF_SIZE];
|
|
||||||
__attribute__((
|
|
||||||
aligned(4))) uint8_t g_mac_tx_bufs[ETH_TX_BUF_COUNT * ETH_TX_BUF_SIZE];
|
|
||||||
|
|
||||||
static struct ethernetif g_eth_state;
|
|
||||||
static volatile bool g_link_irq_flag = false;
|
|
||||||
|
|
||||||
static inline void tx_queue_init(tx_queue_t* q) {
|
|
||||||
q->head = 0;
|
|
||||||
q->tail = 0;
|
|
||||||
q->is_full = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool tx_queue_is_empty(const tx_queue_t* q) {
|
|
||||||
return !q->is_full && (q->head == q->tail);
|
|
||||||
}
|
|
||||||
static inline bool tx_queue_is_full(const tx_queue_t* q) { return q->is_full; }
|
|
||||||
static inline void tx_queue_produce(tx_queue_t* q) {
|
|
||||||
q->head = (q->head + 1) % ETH_TX_BUF_COUNT;
|
|
||||||
if (q->head == q->tail) {
|
|
||||||
q->is_full = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static inline void tx_queue_consume(tx_queue_t* q) {
|
|
||||||
q->tail = (q->tail + 1) % ETH_TX_BUF_COUNT;
|
|
||||||
q->is_full = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void low_level_init(struct netif* netif);
|
|
||||||
static err_t low_level_output(struct netif* netif, struct pbuf* p);
|
static err_t low_level_output(struct netif* netif, struct pbuf* p);
|
||||||
static struct pbuf* low_level_input(struct netif* netif);
|
|
||||||
void phy_write_reg(uint8_t reg_add, uint16_t reg_val);
|
|
||||||
uint16_t phy_read_reg(uint8_t reg_add);
|
|
||||||
|
|
||||||
static void eth_get_mac_addr(uint8_t* mac) {
|
static void eth_link_callback(bool link_up) {
|
||||||
// Mac is backwards.
|
(void)link_up;
|
||||||
const uint8_t* macaddr_src = (const uint8_t*)(ROM_CFG_USERADR_ID + 5);
|
g_link_changed = true;
|
||||||
for (int i = 0; i < 6; i++) {
|
|
||||||
mac[i] = *(macaddr_src--);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err_t ethernetif_init(struct netif* netif) {
|
err_t ethernetif_init(struct netif* netif) {
|
||||||
@@ -83,274 +41,106 @@ err_t ethernetif_init(struct netif* netif) {
|
|||||||
netif->linkoutput = low_level_output;
|
netif->linkoutput = low_level_output;
|
||||||
|
|
||||||
MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 10000000); // 10Mbps
|
MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 10000000); // 10Mbps
|
||||||
|
netif->mtu = 1500;
|
||||||
|
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
|
||||||
|
|
||||||
|
eth_config_t eth_cfg = {.mac_addr = NULL, // we'll use part uuid MAC
|
||||||
|
.rx_callback = NULL, // no cb, polling API
|
||||||
|
.link_callback = eth_link_callback,
|
||||||
|
.promiscuous_mode = false,
|
||||||
|
.broadcast_filter = true,
|
||||||
|
.multicast_filter = true};
|
||||||
|
|
||||||
|
if (eth_init(ð_cfg) != 0) {
|
||||||
|
printf("ERROR: Ethernet initialization failed\n");
|
||||||
|
return ERR_IF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get MAC from driver
|
||||||
netif->hwaddr_len = ETH_HWADDR_LEN;
|
netif->hwaddr_len = ETH_HWADDR_LEN;
|
||||||
eth_get_mac_addr(netif->hwaddr);
|
eth_get_mac_address(netif->hwaddr);
|
||||||
|
|
||||||
printf("MAC Address: %02X:%02X:%02X:%02X:%02X:%02X\n", netif->hwaddr[0],
|
printf("MAC Address: %02X:%02X:%02X:%02X:%02X:%02X\n", netif->hwaddr[0],
|
||||||
netif->hwaddr[1], netif->hwaddr[2], netif->hwaddr[3], netif->hwaddr[4],
|
netif->hwaddr[1], netif->hwaddr[2], netif->hwaddr[3], netif->hwaddr[4],
|
||||||
netif->hwaddr[5]);
|
netif->hwaddr[5]);
|
||||||
|
|
||||||
netif->mtu = 1500;
|
|
||||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
|
|
||||||
|
|
||||||
low_level_init(netif);
|
|
||||||
|
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void low_level_init(struct netif* netif) {
|
|
||||||
struct ethernetif* ethernetif = netif->state;
|
|
||||||
|
|
||||||
// clocks
|
|
||||||
RCC->APB2PCENR |= RCC_APB2Periph_AFIO;
|
|
||||||
RCC->CFGR0 |= RCC_ETHPRE; // div 2
|
|
||||||
EXTEN->EXTEN_CTR |= EXTEN_ETH_10M_EN;
|
|
||||||
|
|
||||||
// reset mac rx and tx
|
|
||||||
ETH10M->ECON1 = RB_ETH_ECON1_TXRST | RB_ETH_ECON1_RXRST;
|
|
||||||
ETH10M->ECON1 = 0;
|
|
||||||
|
|
||||||
// mac regs
|
|
||||||
ETH10M->ERXFCON = RB_ETH_ERXFCON_BCEN | RB_ETH_ERXFCON_MCEN;
|
|
||||||
ETH10M->MACON1 = RB_ETH_MACON1_MARXEN;
|
|
||||||
ETH10M->MACON2 = PADCFG_AUTO_3 | RB_ETH_MACON2_TXCRCEN;
|
|
||||||
ETH10M->MAMXFL = ETH_MAX_PACKET_SIZE;
|
|
||||||
|
|
||||||
R8_ETH_MAADRL1 = netif->hwaddr[5];
|
|
||||||
R8_ETH_MAADRL2 = netif->hwaddr[4];
|
|
||||||
R8_ETH_MAADRL3 = netif->hwaddr[3];
|
|
||||||
R8_ETH_MAADRL4 = netif->hwaddr[2];
|
|
||||||
R8_ETH_MAADRL5 = netif->hwaddr[1];
|
|
||||||
R8_ETH_MAADRL6 = netif->hwaddr[0];
|
|
||||||
|
|
||||||
// PHY analog block
|
|
||||||
ETH10M->ECON2 = RB_ETH_ECON2_DEFAULT;
|
|
||||||
|
|
||||||
// init TX descriptors
|
|
||||||
tx_queue_init(ðernetif->tx_q);
|
|
||||||
for (int i = 0; i < ETH_TX_BUF_COUNT; i++) {
|
|
||||||
g_dma_tx_descs[i].Status = 0;
|
|
||||||
g_dma_tx_descs[i].Buffer1Addr =
|
|
||||||
(uint32_t)&g_mac_tx_bufs[i * ETH_TX_BUF_SIZE];
|
|
||||||
g_dma_tx_descs[i].Buffer2NextDescAddr =
|
|
||||||
(uint32_t)&g_dma_tx_descs[(i + 1) % ETH_TX_BUF_COUNT];
|
|
||||||
}
|
|
||||||
|
|
||||||
// init RX descriptors
|
|
||||||
ethernetif->rx_desc_head = g_dma_rx_descs;
|
|
||||||
ethernetif->rx_desc_tail = g_dma_rx_descs;
|
|
||||||
for (int i = 0; i < ETH_RX_BUF_COUNT; i++) {
|
|
||||||
g_dma_rx_descs[i].Status = ETH_DMARxDesc_OWN;
|
|
||||||
g_dma_rx_descs[i].Buffer1Addr =
|
|
||||||
(uint32_t)&g_mac_rx_bufs[i * ETH_RX_BUF_SIZE];
|
|
||||||
g_dma_rx_descs[i].Buffer2NextDescAddr =
|
|
||||||
(uint32_t)&g_dma_rx_descs[(i + 1) % ETH_RX_BUF_COUNT];
|
|
||||||
}
|
|
||||||
|
|
||||||
// set RX buffer start and enable receiver
|
|
||||||
ETH10M->ERXST = ethernetif->rx_desc_head->Buffer1Addr;
|
|
||||||
ETH10M->ECON1 = RB_ETH_ECON1_RXEN;
|
|
||||||
|
|
||||||
phy_write_reg(PHY_BMCR, PHY_BMCR_RESET);
|
|
||||||
Delay_Ms(200);
|
|
||||||
|
|
||||||
phy_write_reg(PHY_BMCR, PHY_BMCR_FULL_DUPLEX);
|
|
||||||
|
|
||||||
ETH10M->EIR = 0xFF; // clear all interrupt flags
|
|
||||||
ETH10M->EIE = RB_ETH_EIE_INTIE | RB_ETH_EIE_RXIE | RB_ETH_EIE_TXIE |
|
|
||||||
RB_ETH_EIE_LINKIE | RB_ETH_EIE_TXERIE | RB_ETH_EIE_RXERIE |
|
|
||||||
RB_ETH_EIE_R_EN50;
|
|
||||||
|
|
||||||
NVIC_EnableIRQ(ETH_IRQn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tx_start_if_possible(void) {
|
|
||||||
// if TXRTS bit is set, MAC is busy sending a packet
|
|
||||||
if (ETH10M->ECON1 & RB_ETH_ECON1_TXRTS) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ethernetif* ethernetif = &g_eth_state;
|
|
||||||
|
|
||||||
if (tx_queue_is_empty(ðernetif->tx_q)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get descriptor for the next packet to send
|
|
||||||
uint32_t idx = ethernetif->tx_q.tail;
|
|
||||||
ETH_DMADESCTypeDef* dma_desc = &g_dma_tx_descs[idx];
|
|
||||||
|
|
||||||
uint16_t len = dma_desc->Status;
|
|
||||||
|
|
||||||
// tell MAC which buffer to send
|
|
||||||
ETH10M->ETXLN = len;
|
|
||||||
ETH10M->ETXST = dma_desc->Buffer1Addr;
|
|
||||||
// start tx
|
|
||||||
ETH10M->ECON1 |= RB_ETH_ECON1_TXRTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static err_t low_level_output(struct netif* netif, struct pbuf* p) {
|
static err_t low_level_output(struct netif* netif, struct pbuf* p) {
|
||||||
struct ethernetif* ethernetif = netif->state;
|
(void)netif;
|
||||||
err_t errval = ERR_OK;
|
|
||||||
|
|
||||||
if (tx_queue_is_full(ðernetif->tx_q)) {
|
// single-segment pbuf
|
||||||
// should this be ERR_BUF or ERR_MEM? does ERR_MEM re-queue the packet?
|
if (p->next == NULL && p->len <= ETH_TX_BUF_SIZE) {
|
||||||
// queue full, drop pkt
|
if (eth_send_packet(p->payload, p->len) == 0) {
|
||||||
errval = ERR_BUF;
|
LINK_STATS_INC(link.xmit);
|
||||||
// errval = ERR_MEM;
|
return ERR_OK;
|
||||||
} else {
|
}
|
||||||
uint32_t current_idx = ethernetif->tx_q.head;
|
}
|
||||||
uint8_t* tx_buf_ptr = (uint8_t*)g_dma_tx_descs[current_idx].Buffer1Addr;
|
|
||||||
uint32_t len = 0;
|
// chain of pbufs
|
||||||
|
static uint8_t tx_buffer[ETH_TX_BUF_SIZE];
|
||||||
|
uint32_t total_len = 0;
|
||||||
|
|
||||||
for (struct pbuf* q = p; q != NULL; q = q->next) {
|
for (struct pbuf* q = p; q != NULL; q = q->next) {
|
||||||
memcpy(&tx_buf_ptr[len], q->payload, q->len);
|
if (total_len + q->len > ETH_TX_BUF_SIZE) {
|
||||||
len += q->len;
|
LINK_STATS_INC(link.err);
|
||||||
|
return ERR_BUF;
|
||||||
|
}
|
||||||
|
memcpy(&tx_buffer[total_len], q->payload, q->len);
|
||||||
|
total_len += q->len;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_dma_tx_descs[current_idx].Status = len;
|
if (eth_send_packet(tx_buffer, total_len) == 0) {
|
||||||
tx_queue_produce(ðernetif->tx_q);
|
LINK_STATS_INC(link.xmit);
|
||||||
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
tx_start_if_possible();
|
LINK_STATS_INC(link.drop);
|
||||||
return errval;
|
return ERR_BUF;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct pbuf* low_level_input(struct netif* netif) {
|
void ethernetif_input(struct netif* netif) {
|
||||||
struct ethernetif* ethernetif = netif->state;
|
uint16_t length;
|
||||||
struct pbuf* p = NULL;
|
const uint8_t* packet;
|
||||||
|
|
||||||
// if OWN bit is set, it's still owned by DMA and no packet rdy
|
// process all pending packets using polling API
|
||||||
if (ethernetif->rx_desc_tail->Status & ETH_DMARxDesc_OWN) {
|
while ((packet = eth_get_rx_packet(&length)) != NULL) {
|
||||||
return NULL;
|
struct pbuf* p = pbuf_alloc(PBUF_RAW, length, PBUF_POOL);
|
||||||
}
|
|
||||||
|
|
||||||
// packet ready
|
|
||||||
uint32_t len = (ethernetif->rx_desc_tail->Status & ETH_DMARxDesc_FL) >> 16;
|
|
||||||
|
|
||||||
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
|
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
uint8_t* buffer = (uint8_t*)ethernetif->rx_desc_tail->Buffer1Addr;
|
// usually contiguous in PBUF_POOL
|
||||||
uint32_t offset = 0;
|
pbuf_take(p, packet, length);
|
||||||
for (struct pbuf* q = p; q != NULL; q = q->next) {
|
|
||||||
memcpy(q->payload, buffer + offset, q->len);
|
|
||||||
offset += q->len;
|
|
||||||
}
|
|
||||||
LINK_STATS_INC(link.recv);
|
LINK_STATS_INC(link.recv);
|
||||||
|
|
||||||
|
// pass to lwIP
|
||||||
|
if (netif->input(p, netif) != ERR_OK) {
|
||||||
|
pbuf_free(p);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// oom
|
||||||
LINK_STATS_INC(link.memerr);
|
LINK_STATS_INC(link.memerr);
|
||||||
LINK_STATS_INC(link.drop);
|
LINK_STATS_INC(link.drop);
|
||||||
}
|
}
|
||||||
|
|
||||||
// give buffer back to DMA
|
// release packet back to driver
|
||||||
ethernetif->rx_desc_tail->Status = ETH_DMARxDesc_OWN;
|
eth_release_rx_packet();
|
||||||
// advance read pointer to the next descriptor in the ring
|
|
||||||
ethernetif->rx_desc_tail =
|
|
||||||
(ETH_DMADESCTypeDef*)ethernetif->rx_desc_tail->Buffer2NextDescAddr;
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ethernetif_input(struct netif* netif) {
|
|
||||||
struct pbuf* p;
|
|
||||||
while ((p = low_level_input(netif)) != NULL) {
|
|
||||||
if (netif->input(p, netif) != ERR_OK) {
|
|
||||||
pbuf_free(p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ethernetif_link_poll(struct netif* netif) {
|
void ethernetif_link_poll(struct netif* netif) {
|
||||||
if (!g_link_irq_flag) return;
|
// driver does PHY polling and autoneg
|
||||||
g_link_irq_flag = false;
|
eth_poll_link();
|
||||||
|
|
||||||
// supposedly, first read latches link status 2nd get cur val
|
if (g_link_changed) {
|
||||||
(void)phy_read_reg(PHY_BMSR);
|
g_link_changed = false;
|
||||||
uint16_t bmsr = phy_read_reg(PHY_BMSR);
|
|
||||||
|
|
||||||
if (bmsr & PHY_BMSR_LINK_STATUS) {
|
bool link_up = eth_is_link_up();
|
||||||
if (!netif_is_link_up(netif)) {
|
|
||||||
ETH10M->MACON2 |= RB_ETH_MACON2_FULDPX;
|
if (link_up && !netif_is_link_up(netif)) {
|
||||||
netif_set_link_up(netif);
|
netif_set_link_up(netif);
|
||||||
}
|
} else if (!link_up && netif_is_link_up(netif)) {
|
||||||
} else {
|
|
||||||
if (netif_is_link_up(netif)) {
|
|
||||||
netif_set_link_down(netif);
|
netif_set_link_down(netif);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ETH_IRQHandler(void) __attribute__((interrupt)) __attribute__((used));
|
|
||||||
void ETH_IRQHandler(void) {
|
|
||||||
uint32_t flags = ETH10M->EIR;
|
|
||||||
struct ethernetif* ethernetif = &g_eth_state;
|
|
||||||
|
|
||||||
if (flags & RB_ETH_EIR_RXIF) {
|
|
||||||
ETH10M->EIR = RB_ETH_EIR_RXIF;
|
|
||||||
|
|
||||||
// descriptor should be owned by DMA
|
|
||||||
if (ethernetif->rx_desc_head->Status & ETH_DMARxDesc_OWN) {
|
|
||||||
ETH_DMADESCTypeDef* next_desc =
|
|
||||||
(ETH_DMADESCTypeDef*)ethernetif->rx_desc_head->Buffer2NextDescAddr;
|
|
||||||
|
|
||||||
// if next descriptor OWN bit is 0, ring is full and we must drop
|
|
||||||
if (!(next_desc->Status & ETH_DMARxDesc_OWN)) {
|
|
||||||
LINK_STATS_INC(link.drop);
|
|
||||||
} else {
|
|
||||||
// process and re-arm
|
|
||||||
ethernetif->rx_desc_head->Status &= ~ETH_DMARxDesc_OWN;
|
|
||||||
// write packet len into status field for CPU
|
|
||||||
ethernetif->rx_desc_head->Status |=
|
|
||||||
(ETH_DMARxDesc_FS | ETH_DMARxDesc_LS |
|
|
||||||
(ETH10M->ERXLN << ETH_DMARxDesc_FrameLengthShift));
|
|
||||||
// advance descripotor ptr
|
|
||||||
ethernetif->rx_desc_head = next_desc;
|
|
||||||
// re-arm receiver with new emtpy buf
|
|
||||||
ETH10M->ERXST = (uint32_t)ethernetif->rx_desc_head->Buffer1Addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & RB_ETH_EIR_TXIF) {
|
|
||||||
ETH10M->EIR = RB_ETH_EIR_TXIF;
|
|
||||||
|
|
||||||
if (!tx_queue_is_empty(ðernetif->tx_q)) {
|
|
||||||
LINK_STATS_INC(link.xmit);
|
|
||||||
tx_queue_consume(ðernetif->tx_q);
|
|
||||||
}
|
|
||||||
|
|
||||||
tx_start_if_possible();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & RB_ETH_EIR_TXERIF) {
|
|
||||||
ETH10M->EIR = RB_ETH_EIR_TXERIF;
|
|
||||||
LINK_STATS_INC(link.err);
|
|
||||||
|
|
||||||
if (!tx_queue_is_empty(ðernetif->tx_q)) {
|
|
||||||
tx_queue_consume(ðernetif->tx_q);
|
|
||||||
}
|
|
||||||
tx_start_if_possible();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & RB_ETH_EIR_RXERIF) {
|
|
||||||
ETH10M->EIR = RB_ETH_EIR_RXERIF;
|
|
||||||
ETH10M->ECON1 |= RB_ETH_ECON1_RXEN; // re-enable receiver
|
|
||||||
LINK_STATS_INC(link.err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & RB_ETH_EIR_LINKIF) {
|
|
||||||
g_link_irq_flag = true;
|
|
||||||
ETH10M->EIR = RB_ETH_EIR_LINKIF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void phy_write_reg(uint8_t reg_add, uint16_t reg_val) {
|
|
||||||
R32_ETH_MIWR = (reg_add & RB_ETH_MIREGADR_MASK) | RB_ETH_MIWR_MIIWR |
|
|
||||||
(reg_val << RB_ETH_MIWR_DATA_SHIFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t phy_read_reg(uint8_t reg_add) {
|
|
||||||
ETH10M->MIERGADR = reg_add;
|
|
||||||
return ETH10M->MIRD;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,38 +4,9 @@
|
|||||||
#include "lwip/err.h"
|
#include "lwip/err.h"
|
||||||
#include "lwip/netif.h"
|
#include "lwip/netif.h"
|
||||||
|
|
||||||
/* Unique device ID */
|
#ifdef __cplusplus
|
||||||
#define ROM_CFG_USERADR_ID 0x1FFFF7E8
|
extern "C" {
|
||||||
|
#endif
|
||||||
/* Ethernet Frame Size Definitions */
|
|
||||||
#define ETH_HEADER \
|
|
||||||
14 /* 6 byte Dest addr, 6 byte Src addr, 2 byte length/type */
|
|
||||||
#define ETH_CRC 4 /* Ethernet CRC */
|
|
||||||
#define ETH_EXTRA 2 /* Extra bytes in some cases */
|
|
||||||
#define VLAN_TAG 4 /* optional 802.1q VLAN Tag */
|
|
||||||
|
|
||||||
#define MIN_ETH_PAYLOAD 46 /* Minimum Ethernet payload size */
|
|
||||||
#define MAX_ETH_PAYLOAD 1500 /* Maximum Ethernet payload size */
|
|
||||||
|
|
||||||
#define ETH_MAX_PACKET_SIZE \
|
|
||||||
1536 /* ETH_HEADER + VLAN_TAG + MAX_ETH_PAYLOAD + ETH_CRC */
|
|
||||||
#define MIN_ETH_FRAME_SIZE (ETH_HEADER + MIN_ETH_PAYLOAD) /* 60 bytes */
|
|
||||||
|
|
||||||
/* Buffer Configuration */
|
|
||||||
#define ETH_RX_BUF_COUNT 4
|
|
||||||
#define ETH_TX_BUF_COUNT 2
|
|
||||||
#define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE
|
|
||||||
#define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE
|
|
||||||
|
|
||||||
/* DMA descriptor stuff */
|
|
||||||
#define ETH_DMARxDesc_FrameLengthShift 16
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t volatile Status; /* Status */
|
|
||||||
uint32_t ControlBufferSize; /* Control and Buffer1, Buffer2 lengths */
|
|
||||||
uint32_t Buffer1Addr; /* Buffer1 address pointer */
|
|
||||||
uint32_t Buffer2NextDescAddr; /* Buffer2 or next descriptor address pointer */
|
|
||||||
} ETH_DMADESCTypeDef;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should be called at the beginning of the program to set up the
|
* Should be called at the beginning of the program to set up the
|
||||||
@@ -70,20 +41,8 @@ void ethernetif_input(struct netif* netif);
|
|||||||
*/
|
*/
|
||||||
void ethernetif_link_poll(struct netif* netif);
|
void ethernetif_link_poll(struct netif* netif);
|
||||||
|
|
||||||
/**
|
#ifdef __cplusplus
|
||||||
* Write a value to PHY register.
|
}
|
||||||
*
|
#endif
|
||||||
* @param reg_add PHY register address.
|
|
||||||
* @param reg_val Value to write.
|
|
||||||
*/
|
|
||||||
void phy_write_reg(uint8_t reg_add, uint16_t reg_val);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a value from PHY register.
|
|
||||||
*
|
|
||||||
* @param reg_add PHY register address.
|
|
||||||
* @return Register value.
|
|
||||||
*/
|
|
||||||
uint16_t phy_read_reg(uint8_t reg_add);
|
|
||||||
|
|
||||||
#endif /* __ETHERNETIF_H */
|
#endif /* __ETHERNETIF_H */
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
// #define ETHARP_DEBUG LWIP_DBG_ON
|
// #define ETHARP_DEBUG LWIP_DBG_ON
|
||||||
|
|
||||||
#define NO_SYS 1
|
#define NO_SYS 1
|
||||||
#define SYS_LIGHTWEIGHT_PROT 1
|
#define SYS_LIGHTWEIGHT_PROT 0
|
||||||
#define LWIP_NETCONN 0
|
#define LWIP_NETCONN 0
|
||||||
#define LWIP_SOCKET 0
|
#define LWIP_SOCKET 0
|
||||||
|
|
||||||
@@ -20,11 +20,9 @@
|
|||||||
#define MEM_SIZE (4 * 1024)
|
#define MEM_SIZE (4 * 1024)
|
||||||
|
|
||||||
// Pbuf options
|
// Pbuf options
|
||||||
#define PBUF_POOL_SIZE 6
|
#define PBUF_POOL_SIZE 16
|
||||||
#define PBUF_POOL_BUFSIZE 590
|
#define PBUF_POOL_BUFSIZE 590
|
||||||
|
|
||||||
#define MEMP_NUM_PBUF 6 // default 16
|
|
||||||
|
|
||||||
// TCP options
|
// TCP options
|
||||||
#define LWIP_TCP 1
|
#define LWIP_TCP 1
|
||||||
#define TCP_MSS 536
|
#define TCP_MSS 536
|
||||||
@@ -34,6 +32,7 @@
|
|||||||
|
|
||||||
#define LWIP_UDP 1
|
#define LWIP_UDP 1
|
||||||
#define MEMP_NUM_UDP_PCB 3 // # of concurrent UDP "connections"
|
#define MEMP_NUM_UDP_PCB 3 // # of concurrent UDP "connections"
|
||||||
|
#define MEMP_NUM_SYS_TIMEOUT 8
|
||||||
#define LWIP_ICMP 1
|
#define LWIP_ICMP 1
|
||||||
|
|
||||||
#define LWIP_DHCP 1
|
#define LWIP_DHCP 1
|
||||||
@@ -41,6 +40,10 @@
|
|||||||
#define LWIP_HTTPD_FS_SUPPORT 1
|
#define LWIP_HTTPD_FS_SUPPORT 1
|
||||||
#define HTTPD_USE_CUSTOM_FSDATA 1
|
#define HTTPD_USE_CUSTOM_FSDATA 1
|
||||||
|
|
||||||
|
// MQTT
|
||||||
|
#define LWIP_MQTT_CLIENT 1
|
||||||
|
#define MQTT_OUTPUT_RINGBUF_SIZE 512
|
||||||
|
|
||||||
#define LWIP_NETIF_HOSTNAME 1
|
#define LWIP_NETIF_HOSTNAME 1
|
||||||
#define LWIP_NETIF_LINK_CALLBACK 1
|
#define LWIP_NETIF_LINK_CALLBACK 1
|
||||||
#define LWIP_NETIF_STATUS_CALLBACK 1
|
#define LWIP_NETIF_STATUS_CALLBACK 1
|
||||||
|
|||||||
Reference in New Issue
Block a user