add mqtt, decouple i2c and sensor

This commit is contained in:
2025-11-10 22:40:21 +06:00
parent 80478c7400
commit fe6cc2ebb9
8 changed files with 466 additions and 231 deletions

View File

@@ -42,6 +42,11 @@
"atomic": "c",
"__bit_reference": "c",
"err.h": "c",
"httpd.h": "c"
"httpd.h": "c",
"random": "c",
"limits": "c",
"tuple": "c",
"init.h": "c",
"hw_i2c.h": "c"
}
}

View File

@@ -21,6 +21,7 @@ LWIP_C_FILES += $(COREFILES)
LWIP_C_FILES += $(CORE4FILES)
LWIP_C_FILES += $(NETIFFILES)
LWIP_C_FILES += $(HTTPFILES)
LWIP_C_FILES += $(MQTTFILES)
LWIP_C_FILES_WITH_PATH := $(LWIP_C_FILES)
LWIP_PORT_FILES := $(wildcard $(PORT_DIR)/*.c $(PORT_DIR)/arch/*.c)

199
ha_mqtt.c Normal file
View 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;
}
}

View File

@@ -1,22 +1,16 @@
#ifndef _GXHT30_CH32_HW_I2C_H
#define _GXHT30_CH32_HW_I2C_H
#ifndef _GXHT30_HW_I2C_H
#define _GXHT30_HW_I2C_H
#include <stdbool.h>
#include <stdint.h>
#include "ch32fun.h"
#include "ch32v20xhw.h"
#include "hw_i2c.h"
// I2C Configuration
#define GXHT30_I2C_CLKRATE 400000
#define GXHT30_I2C_PRERATE 2000000
#define GXHT30_I2C_TIMEOUT_MAX 250000
// GXHT30 I2C Addresses
// GXHT30 I2C addresses
#define GXHT30_I2C_ADDR_DEFAULT 0x44
#define GXHT30_I2C_ADDR_ALT 0x45
// Commands
// GXHT30 cmds
#define GXHT30_CMD_MEAS_MSB 0x2C
#define GXHT30_CMD_MEAS_LSB 0x06
#define GXHT30_CMD_SOFT_RESET_MSB 0x30
@@ -30,26 +24,18 @@
#define GXHT30_CMD_CLEAR_STATUS_MSB 0x30
#define GXHT30_CMD_CLEAR_STATUS_LSB 0x41
// I2C Event Masks
#define GXHT30_I2C_EVT_MASTER_MODE_SELECT \
((uint32_t)0x00030001) // BUSY, MSL, SB
#define GXHT30_I2C_EVT_MASTER_TRANSMITTER_MODE \
((uint32_t)0x00070082) // BUSY, MSL, ADDR, TXE, TRA
#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
// conversion constants
#define GXHT30_TEMP_MULTIPLIER 2670
#define GXHT30_TEMP_OFFSET 45000000L
#define GXHT30_HUM_MULTIPLIER 1526
#define GXHT30_SCALE_DIVISOR 10000
// Sensor Data Structure
typedef struct {
float temperature; // Temperature in Celsius
float humidity; // Relative humidity in %
uint8_t error; // Last error code
int32_t temperature_x100; // Temperature in hundredths of a degree C
int32_t humidity_x100; // Humidity in hundredths of a percent RH
uint8_t error; // Last error code
} GXHT30_Data;
// Status Register Structure
typedef struct {
uint8_t alert_pending; // Bit 15: Alert status
uint8_t heater_on; // Bit 13: Heater status
@@ -61,76 +47,14 @@ typedef struct {
uint16_t raw_status; // Raw 16-bit status value
} GXHT30_Status;
// Error Codes
enum GXHT30_Error {
GXHT30_OK = 0,
GXHT30_ERR_TIMEOUT,
GXHT30_ERR_CRC,
GXHT30_ERR_I2C,
GXHT30_ERR_BUSY
GXHT30_ERR_I2C
};
static inline uint8_t _gxht30_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 _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) {
static inline bool gxht30_crc8_check(uint8_t msb, uint8_t lsb, uint8_t crc) {
uint8_t calc_crc = 0xFF;
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;
}
// 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,
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;
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;
return (err == I2C_OK) ? GXHT30_OK : GXHT30_ERR_I2C;
}
// read temp and humidity
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 err;
if ((err = gxht30_send_command(addr, GXHT30_CMD_MEAS_MSB,
GXHT30_CMD_MEAS_LSB)) != GXHT30_OK) {
data->error = err;
return err;
// send measurement command and read
if ((err = i2c_write_read(addr, cmd, 2, rx_data, 6)) != I2C_OK) {
data->error = GXHT30_ERR_I2C;
return GXHT30_ERR_I2C;
}
// read data
if ((err = _gxht30_i2c_start(addr, 1)) != GXHT30_OK) {
data->error = err;
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])) {
// verify CRC for both temp and humidity
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;
return GXHT30_ERR_CRC;
}
// calc values
// calc the values
uint16_t temp_raw = (rx_data[0] << 8) | rx_data[1];
uint16_t hum_raw = (rx_data[3] << 8) | rx_data[4];
data->temperature = (float)temp_raw * 0.00267033f - 45.0f;
data->humidity = (float)hum_raw * 0.0015259f;
data->temperature_x100 =
(((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;
return GXHT30_OK;
}
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 err;
if ((err = _gxht30_i2c_start(addr, 0)) != GXHT30_OK) return err;
if ((err = _gxht30_i2c_write_byte(GXHT30_CMD_STATUS_MSB)) != GXHT30_OK)
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;
if ((err = i2c_write_read(addr, cmd, 2, rx_data, 3)) != I2C_OK) {
return GXHT30_ERR_I2C;
}
I2C1->CTLR1 |= I2C_CTLR1_START;
if (_gxht30_wait_event(GXHT30_I2C_EVT_MASTER_MODE_SELECT) != GXHT30_OK)
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]))
// verify CRC
if (!gxht30_crc8_check(rx_data[0], rx_data[1], rx_data[2])) {
return GXHT30_ERR_CRC;
}
// parse status register
uint16_t raw = (rx_data[0] << 8) | rx_data[1];
status->raw_status = raw;
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);
}
#endif // _GXHT30_CH32_HW_I2C_H
#endif // _GXHT30_HW_I2C_H

13
inc/ha_mqtt.h Normal file
View 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

180
inc/hw_i2c.h Normal file
View File

@@ -0,0 +1,180 @@
#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;
// 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));
// 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

70
main.c
View File

@@ -4,6 +4,8 @@
#include "ch32v20xhw.h"
#include "ethernetif.h"
#include "gxht30_hw_i2c.h"
#include "ha_mqtt.h"
#include "hw_i2c.h"
#include "lwip/apps/httpd.h"
#include "lwip/dhcp.h"
#include "lwip/init.h"
@@ -26,8 +28,7 @@
#define PLL_MULTIPLIER 15
#define STATS_PRINT_INTERVAL_MS 10000
#define SENSOR_READ_INTERVAL_MS 5000
#define STATUS_READ_INTERVAL_MS 30000
#define SENSOR_READ_INTERVAL_MS 60000
struct netif g_netif;
static volatile int g_httpd_is_initialized = 0;
@@ -181,17 +182,17 @@ void ethernetif_print_stats(void) {
int main() {
SystemInit();
set_sysclk_to_120mhz_from_hse();
print_clock_registers();
systick_init();
led_init();
lwip_stack_init();
gxht30_i2c_init();
i2c_init();
gxht30_soft_reset(GXHT30_I2C_ADDR_DEFAULT);
Delay_Ms(10);
ha_mqtt_init();
uint32_t last_led_toggle_time = 0;
uint32_t last_link_poll_time = 0;
#if LWIP_STATS
@@ -203,9 +204,8 @@ int main() {
uint32_t last_status_read_time = 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) {
ethernetif_input(&g_netif);
@@ -223,60 +223,30 @@ int main() {
}
#endif
// Read sensor data periodically
ha_mqtt_check_and_reconnect();
if (millis() - last_sensor_read_time > SENSOR_READ_INTERVAL_MS) {
if (gxht30_read_data(GXHT30_I2C_ADDR_DEFAULT, &sensor_data) ==
GXHT30_OK) {
int16_t temp_int = (int16_t)(sensor_data.temperature * 100);
int16_t hum_int = (int16_t)(sensor_data.humidity * 100);
// int32_t temp_int = sensor_data.temperature_x100 / 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;
int16_t temp_decimal = temp_int % 100;
if (temp_decimal < 0) temp_decimal = -temp_decimal;
// int32_t hum_int = sensor_data.humidity_x100 / 100;
// int32_t hum_frac = sensor_data.humidity_x100 % 100;
int16_t hum_whole = hum_int / 100;
int16_t hum_decimal = hum_int % 100;
// printf("Read Sensor -> Temp: %ld.%02ld C, Hum: %ld.%02ld %%\n",
// 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 {
printf("Sensor error: %d\n", sensor_data.error);
printf("Failed to read sensor. Error: %d\n", sensor_data.error);
}
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();
// if (now - last_led_toggle_time > LED_TOGGLE_INTERVAL_MS) {
// if (led_state) {

View File

@@ -20,11 +20,9 @@
#define MEM_SIZE (4 * 1024)
// Pbuf options
#define PBUF_POOL_SIZE 6
#define PBUF_POOL_SIZE 16
#define PBUF_POOL_BUFSIZE 590
#define MEMP_NUM_PBUF 6 // default 16
// TCP options
#define LWIP_TCP 1
#define TCP_MSS 536
@@ -34,6 +32,7 @@
#define LWIP_UDP 1
#define MEMP_NUM_UDP_PCB 3 // # of concurrent UDP "connections"
#define MEMP_NUM_SYS_TIMEOUT 8
#define LWIP_ICMP 1
#define LWIP_DHCP 1
@@ -41,6 +40,10 @@
#define LWIP_HTTPD_FS_SUPPORT 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_LINK_CALLBACK 1
#define LWIP_NETIF_STATUS_CALLBACK 1