Compare commits

...

12 Commits

35 changed files with 2087 additions and 487 deletions

15
.vscode/settings.json vendored
View File

@@ -29,6 +29,19 @@
"esp_mac.h": "c",
"gpio.h": "c",
"chrono": "c",
"cstdint": "c"
"cstdint": "c",
"task.h": "c",
"compare": "c",
"uart_struct.h": "c",
"uart.h": "c",
"gpio_num.h": "c",
"mcp3550.h": "c",
"esp_timer.h": "c",
"math.h": "c",
"esp_check.h": "c",
"radio.h": "c",
"sx1262.h": "c",
"gps.h": "c",
"servocontroller.h": "c"
}
}

View File

@@ -1,5 +0,0 @@
set(component_srcs "ra01s.c")
idf_component_register(SRCS "${component_srcs}"
PRIV_REQUIRES driver
INCLUDE_DIRS ".")

View File

@@ -1,163 +0,0 @@
menu "SX126X Configuration"
config GPIO_RANGE_MAX
int
default 33 if IDF_TARGET_ESP32
default 46 if IDF_TARGET_ESP32S2
default 48 if IDF_TARGET_ESP32S3
default 18 if IDF_TARGET_ESP32C2
default 19 if IDF_TARGET_ESP32C3
default 30 if IDF_TARGET_ESP32C6
choice FREQUENCY
prompt "Frequency to use"
default 433MHZ
help
Select Frequency to use.
config 433MHZ
bool "433MHz"
help
Frequency is 433MHz.
config 866MHZ
bool "866MHz"
help
Frequency is 866MHz.
config 915MHZ
bool "915MHz"
help
Frequency is 915MHz.
config OTHER
bool "Other"
help
Other Frequency.
endchoice
config OTHER_FREQUENCY
depends on OTHER
int "Frequency to use[MHz]"
range 1 999
default 433
help
Frequency to use[MHz].
config ADVANCED
bool "Enable Advanced settings"
default false
help
Enable Advanced settings.
config CODING_RATE
depends on ADVANCED
int "Error coding rate"
range 1 4
default 1
help
Error coding rate.
config BANDWIDTH
depends on ADVANCED
int "Signal Bandwidth"
range 0 6
default 4
help
Signal Bandwidth.
config SF_RATE
depends on ADVANCED
int "Spreading Factor"
range 5 12
default 7
help
Spreading Factor.
config USE_TCXO
bool "Enable TCXO"
default false
help
Enable Temperature-Compensated Crystal Oscillator.
config MISO_GPIO
int "SX126X MISO GPIO"
range 0 GPIO_RANGE_MAX
default 19 if IDF_TARGET_ESP32
default 37 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 4 # C3 and others
help
Pin Number to be used as the MISO SPI signal.
config SCLK_GPIO
int "SX126X SCLK GPIO"
range 0 GPIO_RANGE_MAX
default 18 if IDF_TARGET_ESP32
default 36 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 5 # C3 and others
help
Pin Number to be used as the SCLK SPI signal.
config MOSI_GPIO
int "SX126X MOSI GPIO"
range 0 GPIO_RANGE_MAX
default 23 if IDF_TARGET_ESP32
default 35 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 6 # C3 and others
help
Pin Number to be used as the MOSI SPI signal.
config NSS_GPIO
int "SX126X NSS GPIO"
range 0 GPIO_RANGE_MAX
default 15 if IDF_TARGET_ESP32
default 34 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 7 # C3 and others
help
Pin Number to be used as the NSS SPI signal.
config RST_GPIO
int "SX126X RST GPIO"
range 0 GPIO_RANGE_MAX
default 16 if IDF_TARGET_ESP32
default 38 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 2 # C3 and others
help
Pin Number to be used as the RST signal.
config BUSY_GPIO
int "SX126X BUSY GPIO"
range 0 GPIO_RANGE_MAX
default 17 if IDF_TARGET_ESP32
default 39 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 3 # C3 and others
help
Pin Number to be used as the BUSY signal.
config TXEN_GPIO
int "SX126X TXEN GPIO"
range -1 GPIO_RANGE_MAX
default -1
help
Pin Number to be used as the TXEN signal.
config RXEN_GPIO
int "SX126X RXEN GPIO"
range -1 GPIO_RANGE_MAX
default -1
help
Pin Number to be used as the RXEN signal.
choice SPI_HOST
prompt "SPI peripheral that controls this bus"
default SPI2_HOST
help
Select SPI peripheral that controls this bus.
config SPI2_HOST
bool "SPI2_HOST"
help
Use SPI2_HOST. This is also called HSPI_HOST.
config SPI3_HOST
depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
bool "SPI3_HOST"
help
USE SPI3_HOST. This is also called VSPI_HOST
endchoice
endmenu

View File

@@ -13,8 +13,20 @@ idf_component_register(SRCS
"hw/mcp23018.h"
"hw/mpu9250.c"
"hw/mpu9250.h"
"hw/gps.c"
"hw/gps.h"
"hw/mcp3550.c"
"hw/mcp3550.h"
"hw/sx1262.c"
"hw/sx1262.h"
"components/sensors.c"
"components/sensors.h"
"components/servocontroller.c"
"components/servocontroller.h"
"components/radio.c"
"components/radio.h"
"components/sdcard.c"
"components/sdcard.h"
"main.c"
INCLUDE_DIRS ".")

127
main/components/packets.h Normal file
View File

@@ -0,0 +1,127 @@
#ifndef PACKETS_STRUCTS
#define PACKETS_STRUCTS
#include "stdint.h"
#define UplinkSync "PlechHore"
#define DownlinkSync "PlechDole"
#define UplinkPacketType_SystemControl 0
#define UplinkPacketType_Ping 1
#define UplinkPacketType_ACK 255
#define DownlinkPacketType_Telemetry 0
#define DownlinkPacketType_Ping 1
#define DownlinkPacketType_ACK 255
typedef struct __attribute__((packed))
{
char syncPhrase[10]; //10
uint32_t packetIndex; //14
uint8_t packetType; //15
uint32_t missionTimer; //19
uint32_t CRCCheck;
} DownBoundPacket;
typedef struct __attribute__((packed))
{
// MPU data
int16_t accelerationX;//21
int16_t accelerationY; //23
int16_t accelerationZ;//25
int16_t gyroX;//27
int16_t gyroY;//29
int16_t gyroZ;//31
int16_t magnetX;//33
int16_t magnetY;//35
int16_t magnetZ;//37
int16_t accelerometer_temperature;//39
// CCS data
uint16_t eCO2;//41
uint16_t tvoc;//43
uint8_t currentCCS;//44
uint16_t rawCCSData;//46
// INA data
uint16_t volts;//48
uint16_t current;//50
uint16_t power;//52
// BME DATA
uint32_t temperature;//56
uint16_t humidity;//58
uint32_t pressure;//62
uint16_t gas;//64
bool gas_valid;//later
bool heater_stable;//later
uint8_t gas_range;//65
uint8_t gas_index;//66
float air_temperature; /*!< air temperature in degrees celsius */ //70
float relative_humidity; /*!< relative humidity in percent */ //74
float barometric_pressure; /*!< barometric pressure in hecto-pascal */ //78
float gas_resistance; /*!< gas resistance in ohms */ //82
uint16_t iaq_score; /*!< air quality index (0..500) */ //84
float temperature_score; //88
float humidity_score; //92
float gas_score; //96
// GPS DATA
uint32_t time_seconds; // Seconds since start of day //100
int32_t latitude_centi_degrees; // Latitude * 10,000 //104
int32_t longitude_centi_degrees; // Longitude * 10,000 //108
int16_t altitude_centi_meters; // Altitude * 100 //110
uint8_t fix_quality; //111
uint8_t num_satellites; //112
uint16_t date_yyddmm; // YYDDMM (from GPRMC) //114
uint16_t speed_centi_knots; // Speed * 100 (from GPRMC) //116
int32_t predicted_latitude_centi_degrees; // Latitude * 10,000 //120
int32_t predicted_longitude_centi_degrees; // Longitude * 10,000 //124
int16_t predicted_altitude_centi_meters; // Altitude * 100 //126
// ADC DATA
int32_t NH3; //130
int32_t CO; //134
int32_t NO2; //138
int32_t UVC; //142
int16_t currentServoA; //144
int16_t targetServoA; //146
int16_t currentServoB; //148
int16_t targetServoB; //150
uint8_t presentDevices;
uint8_t telemetryIndex; //151
} TelemetryPacket;
typedef struct __attribute__((packed))
{
char syncPhrase[10];
uint32_t packetIndex;
uint8_t packetType;
uint32_t CRCCheck;
} UplinkPacket;
typedef struct __attribute__((packed))
{
uint8_t powerMode;
uint8_t controlMode;
uint16_t servoA;
uint16_t servoB;
} SystemControlPacket;
typedef struct __attribute__((packed))
{
uint8_t PingData[20];
} PingPacket;
typedef struct __attribute__((packed))
{
uint32_t packetIndex;
uint32_t crc32Checksum;
} ACKPacket;
#endif

396
main/components/radio.c Normal file
View File

@@ -0,0 +1,396 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "../hw/sx1262.h"
#include "radio.h"
#include "esp_log.h"
#include "string.h"
#include "esp_timer.h"
#include "esp_rom_crc.h"
#include <hw/buscfg.h>
#include "sensors.h"
#include "sdcard.h"
#define TAG "LoRa"
#define ACK_TIMEOUT_MS 250 // Wait 300ms for ACK
#define MAX_RETRIES 1
uint32_t packetIndexTX = 0;
uint32_t packetIndexRX = 0;
TelemetryPacket telemetryPacket;
uint8_t packetReadiness = 0;
volatile bool ackReceived = false;
uint32_t lastAckIndex = 0;
SemaphoreHandle_t loraRadioMutex;
void setup_lora(void)
{
LoRaInit();
int8_t txPowerInDbm = 20;
uint32_t frequencyInHz = 869525000;
float tcxoVoltage = 2.2;
bool useRegulatorLDO = true;
ESP_LOGW(TAG, "Enable TCXO");
LoRaDebugPrint(false);
if (LoRaBegin(frequencyInHz, txPowerInDbm, tcxoVoltage, useRegulatorLDO) != 0)
{
ESP_LOGE(TAG, "LoRa module not recognized. Halting.");
while (1)
{
vTaskDelay(1);
}
}
uint8_t spreadingFactor = 8;
uint8_t bandwidth = SX126X_LORA_BW_250_0;
uint8_t codingRate = SX126X_LORA_CR_4_8;
uint16_t preambleLength = 4;
bool crcOn = true;
bool invertIrq = false;
LoRaConfig(spreadingFactor, bandwidth, codingRate, preambleLength, 0, crcOn, invertIrq);
}
static void prepare_downbound_packet(DownBoundPacket *packet, uint8_t type, uint64_t missionTimer, uint32_t crc)
{
memset(packet, 0, sizeof(DownBoundPacket));
packet->missionTimer = missionTimer;
packet->packetIndex = packetIndexTX++;
ESP_LOGI(TAG_RADIO, "Sending downbound packet with index %ld", packetIndexTX - 1);
packet->packetType = type;
packet->CRCCheck = crc;
strcpy(packet->syncPhrase, "PlechDole");
}
static void send_packet_without_retries(uint8_t *data, uint16_t size)
{
if (xSemaphoreTake(loraRadioMutex, portMAX_DELAY) == pdTRUE)
{
writeFile(sensFile, data, size);
if (!LoRaSend(data, size, SX126x_TXMODE_SYNC))
{
ESP_LOGW(TAG, "LoRaSend failed");
xSemaphoreGive(loraRadioMutex);
}
else
{
ESP_LOGI(TAG, "%d byte packet sent", size);
xSemaphoreGive(loraRadioMutex);
}
}
}
static void send_packet_with_retries(uint8_t *data, uint16_t size)
{
if (xSemaphoreTake(loraRadioMutex, portMAX_DELAY) == pdTRUE)
{
writeFile(sensFile, data, size);
for (int retry = 0; retry <= MAX_RETRIES; retry++)
{
if (!LoRaSend(data, size, SX126x_TXMODE_SYNC))
{
ESP_LOGW(TAG, "LoRaSend failed, retry %d", retry);
xSemaphoreGive(loraRadioMutex);
}
else
{
ESP_LOGI(TAG, "%d byte packet sent (attempt %d)", size, retry + 1);
xSemaphoreGive(loraRadioMutex);
// Wait for ACK
ackReceived = false;
uint64_t start_wait = esp_timer_get_time();
while ((esp_timer_get_time() - start_wait) < (ACK_TIMEOUT_MS * 1000))
{
if (ackReceived)
{
ESP_LOGI(TAG, "ACK received for packet.");
return;
}
vTaskDelay(pdMS_TO_TICKS(10));
}
ESP_LOGW(TAG, "ACK timeout, retrying...");
}
}
}
ESP_LOGE(TAG, "Failed to send packet after %d retries", MAX_RETRIES);
}
void prepare_and_send_telemetry(uint64_t missionTimer)
{
uint8_t bufOut[256] = {0};
DownBoundPacket downboundPacket;
uint32_t crc = esp_rom_crc32_le(0, (uint8_t *)&telemetryPacket, sizeof(telemetryPacket));
prepare_downbound_packet(&downboundPacket, DownlinkPacketType_Telemetry, missionTimer, crc);
uint16_t offset = 0;
memcpy(bufOut + offset, &downboundPacket, sizeof(downboundPacket));
offset += sizeof(downboundPacket);
memcpy(bufOut + offset, &telemetryPacket, sizeof(telemetryPacket));
offset += sizeof(telemetryPacket);
if (csvFile != NULL) {
fprintf(csvFile,
"%llu,%lu,%u,%u," // missionTimer, packetIndex, telemetryIndex, packetType
"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d," // MPU
"%u,%u,%d,%u," // CCS
"%u,%u,%u,%lu,%u,%lu,%u," // INA, BME basic
"%s,%s,%u,%u," // bools and gas index/range
"%.2f,%.2f,%.2f,%.2f," // Bosch env data
"%u,%.2f,%.2f,%.2f," // IAQ + scores
"%lu,%ld,%ld,%d,%u,%u,%u,%u," // GPS data
"%ld,%ld,%d," // Predicted GPS
"%ld,%ld,%ld,%ld," // ADC
"%d,%d,%d,%d," // Servo
"%u\n", // presentDevices
missionTimer,
downboundPacket.packetIndex,
telemetryPacket.telemetryIndex,
downboundPacket.packetType,
telemetryPacket.accelerationX, telemetryPacket.accelerationY, telemetryPacket.accelerationZ,
telemetryPacket.gyroX, telemetryPacket.gyroY, telemetryPacket.gyroZ,
telemetryPacket.magnetX, telemetryPacket.magnetY, telemetryPacket.magnetZ,
telemetryPacket.accelerometer_temperature,
telemetryPacket.eCO2, telemetryPacket.tvoc, telemetryPacket.currentCCS, telemetryPacket.rawCCSData,
telemetryPacket.volts, telemetryPacket.current, telemetryPacket.power,
telemetryPacket.temperature, telemetryPacket.humidity, telemetryPacket.pressure, telemetryPacket.gas,
telemetryPacket.gas_valid ? "Yes" : "No",
telemetryPacket.heater_stable ? "Yes" : "No",
telemetryPacket.gas_range, telemetryPacket.gas_index,
telemetryPacket.air_temperature, telemetryPacket.relative_humidity,
telemetryPacket.barometric_pressure, telemetryPacket.gas_resistance,
telemetryPacket.iaq_score,
telemetryPacket.temperature_score, telemetryPacket.humidity_score, telemetryPacket.gas_score,
telemetryPacket.time_seconds,
telemetryPacket.latitude_centi_degrees,
telemetryPacket.longitude_centi_degrees,
telemetryPacket.altitude_centi_meters,
telemetryPacket.fix_quality,
telemetryPacket.num_satellites,
telemetryPacket.date_yyddmm,
telemetryPacket.speed_centi_knots,
telemetryPacket.predicted_latitude_centi_degrees,
telemetryPacket.predicted_longitude_centi_degrees,
telemetryPacket.predicted_altitude_centi_meters,
telemetryPacket.NH3, telemetryPacket.CO, telemetryPacket.NO2, telemetryPacket.UVC,
telemetryPacket.currentServoA, telemetryPacket.targetServoA,
telemetryPacket.currentServoB, telemetryPacket.targetServoB,
telemetryPacket.presentDevices);
fflush(csvFile);
fsync(fileno(csvFile)); // Critical: this ensures actual write to disk
}
printf("Sending %d byte packet hehe\n", offset);
send_packet_without_retries(bufOut, offset);
packetReadiness = 0;
}
static void build_and_send_ack(uint32_t ackIndex, uint32_t crc32Checksum, uint64_t missionTimer)
{
uint8_t bufOut[256] = {0};
ACKPacket ackPacket = {
.packetIndex = ackIndex,
.crc32Checksum = crc32Checksum,
};
uint32_t crc = esp_rom_crc32_le(0, (uint8_t *)&ackPacket, sizeof(ackPacket));
DownBoundPacket downboundPacket;
prepare_downbound_packet(&downboundPacket, DownlinkPacketType_ACK, missionTimer, crc);
uint16_t offset = 0;
memcpy(bufOut + offset, &downboundPacket, sizeof(downboundPacket));
offset += sizeof(downboundPacket);
memcpy(bufOut + offset, &ackPacket, sizeof(ackPacket));
offset += sizeof(ackPacket);
if (xSemaphoreTake(loraRadioMutex, portMAX_DELAY) == pdTRUE)
{
if (!LoRaSend(bufOut, offset, SX126x_TXMODE_SYNC))
{
ESP_LOGE(TAG, "Failed to send ACK");
}
else
{
ESP_LOGI(TAG, "%d byte ACK sent", offset);
}
xSemaphoreGive(loraRadioMutex);
}
}
void process_uplink_packet(uint8_t *data, uint8_t len, uint64_t missionTimer)
{
if (len < sizeof(UplinkPacket))
{
ESP_LOGW(TAG, "Uplink packet too small: %d bytes", len);
return;
}
UplinkPacket uplinkPacket;
memcpy(&uplinkPacket, data, sizeof(UplinkPacket));
if (strcmp(UplinkSync, uplinkPacket.syncPhrase) != 0)
{
ESP_LOGW(TAG, "Invalid sync phrase");
return;
}
ESP_LOGI(TAG, "Got uplink packet of type %d, index %ld", uplinkPacket.packetType, uplinkPacket.packetIndex);
uint8_t *payload = data + sizeof(UplinkPacket);
uint8_t payloadRXLen = len - sizeof(UplinkPacket);
uint32_t crc = esp_rom_crc32_le(0, payload, payloadRXLen);
if (crc != uplinkPacket.CRCCheck)
{
ESP_LOGE(TAG, "Received BAD CRC for packet %ld, crc is %ld, should be %ld", uplinkPacket.packetIndex, crc, uplinkPacket.CRCCheck);
return;
}
if (uplinkPacket.packetType == UplinkPacketType_ACK)
{
ESP_LOGI(TAG, "Received ACK for packet %ld", uplinkPacket.packetIndex);
ackReceived = true;
lastAckIndex = uplinkPacket.packetIndex;
return;
}
if (uplinkPacket.packetIndex == packetIndexRX + 1)
{
ESP_LOGI(TAG, "Packet arrived in correct order");
packetIndexRX = uplinkPacket.packetIndex;
uint32_t crc = esp_rom_crc32_le(0, data + sizeof(UplinkPacket), payloadRXLen);
build_and_send_ack(packetIndexRX, crc, missionTimer);
switch (uplinkPacket.packetType)
{
case UplinkPacketType_SystemControl:
if (payloadRXLen == sizeof(SystemControlPacket))
{
SystemControlPacket sysCtrl;
memcpy(&sysCtrl, data + sizeof(UplinkPacket), sizeof(SystemControlPacket));
setPowerMode(sysCtrl.powerMode);
// TODO: Process sysCtrl
}
else
{
ESP_LOGW(TAG, "SystemControlPacket size mismatch");
}
break;
case UplinkPacketType_Ping:
// TODO: handle Ping
break;
default:
ESP_LOGW(TAG, "Unknown uplink packet type %d", uplinkPacket.packetType);
break;
}
}
else if (uplinkPacket.packetIndex > packetIndexRX + 1)
{
ESP_LOGW(TAG, "Skipped %ld packets", uplinkPacket.packetIndex - (packetIndexRX + 1));
packetIndexRX = uplinkPacket.packetIndex;
}
else
{
ESP_LOGW(TAG, "Duplicate packet: %ld", (packetIndexRX + 1) - uplinkPacket.packetIndex);
}
}
void lora_receive_task(void *pvParameters)
{
ESP_LOGI(TAG, "lora_receive_task started");
uint8_t bufIn[256];
while (1)
{
// Wait to take the semaphore before accessing LoRa
if (xSemaphoreTake(loraRadioMutex, 0) == pdTRUE)
{
uint8_t rxLen = LoRaReceive(bufIn, sizeof(bufIn));
if (rxLen > 0)
{
ESP_LOGI(TAG, "%d byte packet received", rxLen);
process_uplink_packet(bufIn, rxLen, esp_timer_get_time());
int8_t rssi, snr;
GetPacketStatus(&rssi, &snr);
ESP_LOGI(TAG, "rssi=%d[dBm], snr=%d[dB]", rssi, snr);
}
// Release the semaphore when done with LoRa RX
xSemaphoreGive(loraRadioMutex);
}
vTaskDelay(pdMS_TO_TICKS(10)); // Delay to prevent busy-waiting
}
}
void lora_comms_task(void *pvParameters)
{
// while (foundDevices[0] != 2) {
// vTaskDelay(10);
// }
// Initialize the semaphore for radio access (binary semaphore, 1 = available)
loraRadioMutex = xSemaphoreCreateMutex();
xSemaphoreGive(loraRadioMutex); // Set semaphore as available
ESP_LOGI(TAG, "lora_comms_task started");
setup_lora();
ESP_LOGI(TAG, "lora_comms_task continuing");
xTaskCreate(
lora_receive_task,
"LoraReceiveTask",
8192,
NULL,
(tskIDLE_PRIORITY + 2),
NULL);
ESP_LOGI(TAG, "loraInit");
while (1)
{
int64_t start_time = esp_timer_get_time();
if (packetReadiness)
{
ESP_LOGI(TAG, "Preparing telemetry");
prepare_and_send_telemetry(start_time);
}
const int64_t interval_us = ((powerMode == HIGH_POWER_MODE) ? 10000 : 10000000); // 10 ms or 10 000 ms
int64_t end_time = esp_timer_get_time();
int64_t elapsed = end_time - start_time;
if (elapsed < interval_us)
{
vTaskDelay(pdMS_TO_TICKS((interval_us - elapsed) / 1000));
}
}
}

21
main/components/radio.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef RADIO_COMPONENT
#define RADIO_COMPONENT
#define LORA_POWER_LOW 2
#define LORA_POWER_HIGH 20
#define LORA_POWER_MAX_MAYBE_UNSAFE 22
#define LORA_TX_POWER LORA_POWER_LOW
#define TAG_RADIO "RADIO"
#include "packets.h"
void lora_comms_task(void *pvParameters);
void lora_receive_task(void *pvParameters);
extern TelemetryPacket telemetryPacket;
extern uint8_t packetReadiness;
extern SemaphoreHandle_t loraRadioMutex;
#endif

128
main/components/sdcard.c Normal file
View File

@@ -0,0 +1,128 @@
#include "sdcard.h"
void writeFile(FILE *f, const void *data, size_t size)
{
if (f != NULL)
{
fwrite(data, size, 1, f);
fflush(f);
fsync(fileno(f)); // Critical: this ensures actual write to disk
}
}
FILE *csvFile = NULL;
FILE *sensFile = NULL;
void initSD(void)
{
esp_err_t ret;
// Options for mounting the filesystem.
// If format_if_mount_failed is set to true, SD card will be partitioned and
// formatted in case when mounting fails.
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 100,
.allocation_unit_size = 16 * 1024};
sdmmc_card_t *card;
const char mount_point[] = MOUNT_POINT;
ESP_LOGI(TAGSD, "Initializing SD card");
// Use settings defined above to initialize SD card and mount FAT filesystem.
// Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
// Please check its source code and implement error recovery when developing
// production applications.
ESP_LOGI(TAGSD, "Using SPI peripheral");
// By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz)
// For setting a specific frequency, use host.max_freq_khz (range 400kHz - 20MHz for SDSPI)
// Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000;
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
host.slot = SPI3_HOST;
// This initializes the slot without card detect (CD) and write protect (WP) signals.
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
slot_config.gpio_cs = HSPI_SD_CS;
slot_config.host_id = host.slot;
ESP_LOGI(TAGSD, "Mounting filesystem");
ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);
if (ret != ESP_OK)
{
if (ret == ESP_FAIL)
{
ESP_LOGE(TAGSD, "Failed to mount filesystem. "
"If you want the card to be formatted, set the CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
}
else
{
ESP_LOGE(TAGSD, "Failed to initialize the card (%s). "
"Make sure SD card lines have pull-up resistors in place.",
esp_err_to_name(ret));
}
return;
}
ESP_LOGI(TAGSD, "Filesystem mounted");
// Card has been initialized, print its properties
sdmmc_card_print_info(stdout, card);
int index = 0;
FILE *f = fopen(COUNTER_FILE, "r");
if (f)
{
fscanf(f, "%d", &index);
fclose(f);
}
// === 2. Increment and write back ===
f = fopen(COUNTER_FILE, "w");
if (!f)
{
ESP_LOGE(TAGSD, "Failed to open counter file");
return;
}
index++;
if (f != NULL)
{
fprintf(f, "%d", index);
fflush(f);
fsync(fileno(f)); // Critical: this ensures actual write to disk
fclose(f);
ESP_LOGI(TAGSD, "Index is now %d", index);
}
// === 3. Make folder ===
char folder_path[MAX_PATH - 20];
snprintf(folder_path, MAX_PATH, MOUNT_POINT "/%d", index);
mkdir(folder_path, 0777); // safe if exists already
// === 4. Create CSV and BIN ===
char csv_path[MAX_PATH], bin_path[MAX_PATH];
snprintf(csv_path, MAX_PATH, "%s/data.csv", folder_path);
snprintf(bin_path, MAX_PATH, "%s/data.bin", folder_path);
csvFile = fopen(csv_path, "w");
sensFile = fopen(bin_path, "wb");
if (csvFile != NULL)
{
fprintf(csvFile,
"missionTimer,packetIndex,telemetryIndex,packetType,"
"accX,accY,accZ,gyroX,gyroY,gyroZ,magX,magY,magZ,accTemp,"
"eCO2,tVOC,currentCCS,rawCCS,"
"volts,current,power,rawtemperature,rawhumidity,rawpressure,rawgas,"
"gasValid,heaterStable,gasRange,gasIndex,"
"airTemp,relHumidity,baroPressure,gasResistance,"
"iaqScore,tempScore,humidityScore,gasScore,"
"gpsTime,latitude,longitude,altitude,fixQuality,numSatellites,date,speed,"
"predLatitude,predLongitude,predAltitude,"
"NH3,CO,NO2,UVC,"
"currentServoA,targetServoA,currentServoB,targetServoB,"
"presentDevices\n");
fflush(csvFile);
fsync(fileno(csvFile)); // Critical: this ensures actual write to disk
}
}

26
main/components/sdcard.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef SDCARD_COMPONENT
#define SDCARD_COMPONENT
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#include "../hw/buscfg.h"
#define TAGSD "SDSubsys"
#define COUNTER_FILE "cnt.txt"
#define MOUNT_POINT "/sdcard"
void writeFile(FILE *f, const void *data, size_t size);
extern FILE *csvFile;
extern FILE *sensFile;
#define MAX_PATH 80
void initSD(void);
#endif

View File

@@ -1,76 +1,237 @@
#include "sensors.h"
#include "esp_timer.h"
#include "radio.h"
#include "../hw/gps.h"
#include "servocontroller.h"
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#define MOUNT_POINT "/sdcard"
#define MAX_LINE_LENGTH 64
#define BLINK_GPIO 2
// uint8_t powerMode = LOW_POWER_MODE;
uint8_t powerMode = HIGH_POWER_MODE;
static uint8_t s_led_state = 0;
uint8_t telemetryIndex = 1;
const uint8_t expectedAdressesCount = 5;
const uint8_t expectedAdresses[] = {MCP23018_ADDRESS, BME680_ADDRESS, CCS811_ADDRESS, MPU9250_ADDRESS, INA260_ADDRESS};
uint8_t foundDevices[5];
uint8_t prevDevices[5];
static void configure_led(void)
{
gpio_reset_pin(BLINK_GPIO);
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
}
void update_devices()
{
memcpy(prevDevices, foundDevices, sizeof(prevDevices));
for (uint8_t i = 0; i < expectedAdressesCount; i++)
{
fflush(stdout);
esp_err_t ret = i2c_master_probe(i2c0_bus_hdl, expectedAdresses[i], 20);
if (ret == ESP_OK)
{
if (foundDevices[i] == 0)
{
foundDevices[i] = 1;
}
// printf("Found device at 0x%02X\n", i);
}
else
{
foundDevices[i] = 0;
if (i == 1 && powerMode != HIGH_POWER_MODE)
{
continue;
}
printf("Not found device at 0x%02X\n", expectedAdresses[i]);
}
}
}
void setPowerMode(uint8_t powerModeIn)
{
powerMode = powerModeIn;
if (foundDevices[0] == 2)
{
if (powerMode == HIGH_POWER_MODE)
{
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_CCS811_WAKE, 0);
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_CCS811_POWER, 1);
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_MICS_POWER, 1);
}
else
{
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_CCS811_WAKE, 1);
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_CCS811_POWER, 0);
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_MICS_POWER, 0);
}
}
}
void init_connected()
{
for (uint8_t i = 0; i < expectedAdressesCount; i++)
{
if (foundDevices[i] != prevDevices[i])
{
if (foundDevices[i])
{
esp_err_t ret = ESP_FAIL;
bool foundUsed = true;
switch (i)
{
case MCP23018_ADDRESS:
/* code */
ret = mcp23018_init();
setPowerMode(powerMode);
mcp3550_spi_init();
break;
case INA260_ADDRESS:
ret = ina260_init();
/* code */
break;
case CCS811_ADDRESS:
ret = ccs811_init();
/* code */
break;
case MPU9250_ADDRESS:
ret = mpu9250_init();
/* code */
break;
case BME680_ADDRESS:
ret = bme680b_init();
/* code */
break;
default:
foundUsed = false;
break;
}
if (foundUsed)
{
if (ret == ESP_OK)
{
foundDevices[i] = 2;
}
else
{
printf("Device init error at 0x%02X - %s\n", expectedAdresses[i], esp_err_to_name(ret));
}
}
}
}
}
}
void i2c_sensors_task(void *pvParameters)
{
// initialize the xLastWakeTime variable with the current time.
TickType_t last_wake_time = xTaskGetTickCount();
const TickType_t I2C0_TASK_SAMPLING_RATE = 5;
//
// initialize i2c device configuration
memset(foundDevices, 0, sizeof(foundDevices));
memset(prevDevices, 0, sizeof(prevDevices));
bme680b_init();
mpu9250_init();
mcp23018_init();
ina260_init();
ccs811_init();
ina260_init();
// update_devices();
// init_connected();
// initialize the xLastWakeTime variable with the current time.
const int64_t interval_us = 50000; // 50 ms
int64_t start_time, end_time, elapsed;
//
// initialize i2c device configuration
configure_led();
int16_t accel[3], gyro[3], temp;
float accel_f[3], gyro_f[3], temp_f;
int16_t accel[3], gyro[3], temp, magnet[3];
float accel_f[3], gyro_f[3], temp_f, magnet_f[3];
uint16_t eCO2;
uint16_t tvoc;
uint8_t currentCCS;
uint16_t rawData;
uint16_t volts;
uint16_t current;
uint16_t power;
bme680_data_t bmeData;
mics_adc_data_t ADCData;
// task loop entry point
for (;;)
{
start_time = esp_timer_get_time(); // µs since boot
uint8_t presentDevices = 0xFF;
// update_devices();
// init_connected();
//
// handle sensor
presentDevices |= BME680_PRESENT_BIT;
if (BME680_DEV_HANDLE)
{
bme680_data_t data;
esp_err_t result = bme680_get_data(BME680_DEV_HANDLE, &data);
esp_err_t result = bme680_get_data(BME680_DEV_HANDLE, &bmeData);
if (result != ESP_OK)
{
ESP_LOGE(TAG_BME, "bme680 device read failed (%s)", esp_err_to_name(result));
}
else
{
data.barometric_pressure = data.barometric_pressure / 100;
// ESP_LOGI(TAG, "dewpoint temperature:%.2f °C", data.dewpoint_temperature);
ESP_LOGI(TAG_BME, "air temperature: %.2f °C", data.air_temperature);
ESP_LOGI(TAG_BME, "relative humidity: %.2f %%", data.relative_humidity);
ESP_LOGI(TAG_BME, "barometric pressure: %.2f hPa", data.barometric_pressure);
// ESP_LOGI(TAG, "gas resistance: %.2f kOhms", data.gas_resistance / 1000);
// ESP_LOGI(TAG, "iaq score: %u (%s)", data.iaq_score, bme680_air_quality_to_string(data.iaq_score));
bmeData.barometric_pressure = bmeData.barometric_pressure / 100;
ESP_LOGI(TAG_BME, "dewpoint temperature:%.2f °C", bmeData.dewpoint_temperature);
ESP_LOGI(TAG_BME, "air temperature: %.2f °C", bmeData.air_temperature);
ESP_LOGI(TAG_BME, "relative humidity: %.2f %%", bmeData.relative_humidity);
ESP_LOGI(TAG_BME, "barometric pressure: %.2f hPa", bmeData.barometric_pressure);
ccs811_set_env_data(bmeData.air_temperature, bmeData.relative_humidity);
ESP_LOGI(TAG_BME, "gas resistance: %.2f kOhms", bmeData.gas_resistance / 1000);
ESP_LOGI(TAG_BME, "iaq score: %u (%s)", bmeData.iaq_score, bme680_air_quality_to_string(bmeData.iaq_score));
}
} else {
}
else
{
bme680b_init();
}
ccs811_get_data(&eCO2, &tvoc);
presentDevices |= CCS811_PRESENT_BIT;
ccs811_get_data(&eCO2, &tvoc, &currentCCS, &rawData);
ESP_LOGI(TAG_CCS, "eCO₂: %d ppm, TVOC: %d ppb", eCO2, tvoc);
ESP_LOGI(TAG_CCS, "Current: %d μA, Raw voltage: %d V", currentCCS, rawData);
if (mpu9250_read_sensor_data(MPU9250_DEV_HANDLE, accel, gyro, &temp) == ESP_OK)
presentDevices |= MPU9250_PRESENT_BIT;
if (mpu9250_read_sensor_data(MPU9250_DEV_HANDLE, accel, gyro, &temp, magnet) == ESP_OK)
{
mpu9250_convert_data(accel, gyro, temp, accel_f, gyro_f, &temp_f);
mpu9250_convert_data(accel, gyro, temp, magnet, accel_f, gyro_f, &temp_f, magnet_f);
ESP_LOGI(TAG_MPU, "Accel: X=%.2f g, Y=%.2f g, Z=%.2f g", accel_f[0], accel_f[1], accel_f[2]);
ESP_LOGI(TAG_MPU, "Gyro: X=%.2f°/s, Y=%.2f°/s, Z=%.2f°/s", gyro_f[0], gyro_f[1], gyro_f[2]);
ESP_LOGI(TAG_MPU, "Magnet: X=%.2fμT, Y=%.2fμT, Z=%.2fμT", magnet_f[0], magnet_f[1], magnet_f[2]);
ESP_LOGI(TAG_MPU, "Temperature: %.2f °C", temp_f);
}
else
@@ -78,13 +239,97 @@ void i2c_sensors_task(void *pvParameters)
ESP_LOGE(TAG_MPU, "Failed to read sensor data");
}
presentDevices |= INA260_PRESENT_BIT;
ina260_readParams(&volts, &current, &power);
ina260_printParams(volts, current, power);
gpio_set_level(BLINK_GPIO, s_led_state);
/* Toggle the LED state */
presentDevices |= MCP23018_PRESENT_BIT;
float VREFVoltage = 2.5;
ADCData = mcp3550_read_all(VREFVoltage);
log_mics_adc_values(&ADCData);
if (packetReadiness == 0)
{
memset(&telemetryPacket, 0, sizeof(telemetryPacket));
telemetryPacket.accelerationX = accel[0];
telemetryPacket.accelerationY = accel[1];
telemetryPacket.accelerationZ = accel[2];
telemetryPacket.gyroX = gyro[0];
telemetryPacket.gyroX = gyro[1];
telemetryPacket.gyroX = gyro[2];
telemetryPacket.magnetX = gyro[0];
telemetryPacket.magnetX = gyro[1];
telemetryPacket.magnetX = gyro[2];
telemetryPacket.accelerometer_temperature = temp;
telemetryPacket.eCO2 = eCO2;
telemetryPacket.tvoc = tvoc;
telemetryPacket.currentCCS = currentCCS;
telemetryPacket.rawCCSData = rawData;
telemetryPacket.volts = volts;
telemetryPacket.current = current;
telemetryPacket.power = power;
telemetryPacket.temperature = bmeData.raw_data.temperature;
telemetryPacket.humidity = bmeData.raw_data.humidity;
telemetryPacket.pressure = bmeData.raw_data.pressure;
telemetryPacket.gas = bmeData.raw_data.gas;
telemetryPacket.gas_range = bmeData.raw_data.gas_range;
telemetryPacket.heater_stable = bmeData.raw_data.heater_stable;
telemetryPacket.gas_valid = bmeData.raw_data.gas_valid;
telemetryPacket.air_temperature = bmeData.air_temperature;
telemetryPacket.relative_humidity = bmeData.relative_humidity;
telemetryPacket.barometric_pressure = bmeData.barometric_pressure;
telemetryPacket.gas_resistance = bmeData.gas_resistance;
telemetryPacket.iaq_score = bmeData.iaq_score;
telemetryPacket.temperature_score = bmeData.temperature_score;
telemetryPacket.humidity_score = bmeData.humidity_score;
telemetryPacket.gas_score = bmeData.gas_score;
telemetryPacket.NH3 = ADCData.raw_nh3;
telemetryPacket.CO = ADCData.raw_co;
telemetryPacket.NO2 = ADCData.raw_no2;
telemetryPacket.UVC = ADCData.raw_uvc;
// TODO MOVE THIS TO A BETTER PLACE FOR SYNC
telemetryPacket.time_seconds = gpsDataOut.time_seconds;
telemetryPacket.latitude_centi_degrees = gpsDataOut.latitude_centi_degrees;
telemetryPacket.longitude_centi_degrees = gpsDataOut.longitude_centi_degrees;
telemetryPacket.fix_quality = gpsDataOut.fix_quality;
telemetryPacket.num_satellites = gpsDataOut.num_satellites;
telemetryPacket.altitude_centi_meters = gpsDataOut.altitude_centi_meters;
telemetryPacket.date_yyddmm = gpsDataOut.date_yyddmm;
telemetryPacket.speed_centi_knots = gpsDataOut.speed_centi_knots;
telemetryPacket.predicted_altitude_centi_meters = predictedPosition.altitude_centi_meters;
telemetryPacket.predicted_latitude_centi_degrees = predictedPosition.latitude_centi_degrees;
telemetryPacket.predicted_longitude_centi_degrees = predictedPosition.longitude_centi_degrees;
telemetryPacket.targetServoA = servoState.targetServoA;
telemetryPacket.targetServoB = servoState.targetServoB;
telemetryPacket.currentServoA = servoState.currentServoA;
telemetryPacket.currentServoB = servoState.currentServoB;
telemetryPacket.presentDevices = presentDevices;
telemetryPacket.telemetryIndex = telemetryIndex++;
}
packetReadiness = 1;
s_led_state = !s_led_state;
vTaskDelaySecUntil(&last_wake_time, I2C0_TASK_SAMPLING_RATE);
end_time = esp_timer_get_time();
elapsed = end_time - start_time;
if (elapsed < interval_us)
{
vTaskDelay(pdMS_TO_TICKS((interval_us - elapsed) / 1000));
}
}
//
// free resources

View File

@@ -7,6 +7,8 @@
#include <bme680.h>
#include "esp_mac.h"
#include "../hw/mcp3550.h"
#include "../hw/bme680b.h"
#include "../hw/ccs811.h"
#include "../hw/i2cbrn.h"
@@ -16,8 +18,18 @@
#include "../hw/mpu9250.h"
#include "esp_log.h"
#define LOW_POWER_MODE 0
#define HIGH_POWER_MODE 1
extern uint8_t powerMode;
extern uint8_t foundDevices[5];
extern uint8_t prevDevices[5];
void init_connected();
void update_devices();
void setPowerMode(uint8_t powerModeIn);
void i2c_sensors_task(void *pvParameters);
#endif

View File

@@ -0,0 +1,10 @@
#include "servocontroller.h"
#include "string.h"
ServoState servoState;
//TODO add a task and implement
void servoControllerInit() {
memset(&servoState, 0, sizeof(servoState));
}

View File

@@ -0,0 +1,19 @@
#ifndef SERVO_CONTROLLER_COMPONENT
#define SERVO_CONTROLLER_COMPONENT
#include "stdint.h"
typedef struct
{
int16_t currentServoA;
int16_t targetServoA;
int16_t currentServoB;
int16_t targetServoB;
} ServoState;
extern ServoState servoState;
void servoControllerInit();
#endif

View File

@@ -38,14 +38,15 @@ void bme680_print_registers(bme680_handle_t handle)
ESP_LOGI(TAG_BME, "Control Gas 1 (0x%02x): %s", ctrl_gas1_reg.reg, uint8_to_binary(ctrl_gas1_reg.reg));
}
void bme680b_init() {
esp_err_t bme680b_init() {
// init device
bme680_init(i2c0_bus_hdl, &BME680_DEV_CFG, &BME680_DEV_HANDLE);
esp_err_t ret = bme680_init(i2c0_bus_hdl, &BME680_DEV_CFG, &BME680_DEV_HANDLE);
if (BME680_DEV_HANDLE == NULL)
{
ESP_LOGE(TAG_BME, "bme680 handle init failed");
} else {
bme680_print_registers(BME680_DEV_HANDLE);
}
return ret;
}

View File

@@ -8,6 +8,6 @@ extern bme680_config_t BME680_DEV_CFG;
extern bme680_handle_t BME680_DEV_HANDLE;
void bme680_print_registers(bme680_handle_t handle);
void bme680b_init();
esp_err_t bme680b_init();
#endif

80
main/hw/buscfg.h Normal file
View File

@@ -0,0 +1,80 @@
#ifndef BUSCFG_FILE
#define BUSCFG_FILE
#include "soc/gpio_num.h"
#define ESP_USB_DP GPIO_NUM_20
#define ESP_USB_DM GPIO_NUM_19
#define ESP_RXD0 GPIO_NUM_44
#define ESP_TXD0 GPIO_NUM_43
#define BME680_ADDRESS 0x76
#define CCS811_ADDRESS 0x5A
#define MPU9250_ADDRESS 0x68
#define INA260_ADDRESS 0x40
#define MCP23018_ADDRESS 0x20
#define BME680_PRESENT_BIT (1 << 0)
#define CCS811_PRESENT_BIT (1 << 1)
#define MPU9250_PRESENT_BIT (1 << 2)
#define INA260_PRESENT_BIT (1 << 3)
#define MCP23018_PRESENT_BIT (1 << 4)
#define ESP_CONNECTOR_P1 MCP3550_MISO_GPIO
#define ESP_CONNECTOR_P2 MCP3550_MOSI_GPIO
#define ESP_CONNECTOR_P3 MCP3550_SCK_GPIO
#define ESP_CONNECTOR_P4 GPIO_NUM_39
#define ESP_CONNECTOR_P5 GPIO_NUM_38
#define ESP_CONNECTOR_P6 HSPI_UNKNOWN_CS
#define ESP_CONNECTOR_P9 HSPI_MISO_GPIO
#define ESP_CONNECTOR_P10 MCP3550_MOSI_GPIO
#define ESP_CONNECTOR_P11 MCP3550_SCK_GPIO
#define ESP_CONNECTOR_P12 HSPI_SD_CS
#define ESP_CONNECTOR_P13 HSPI_LORA_CS
#define ESP_CONNECTOR_P17 HWI2C_SDA
#define ESP_CONNECTOR_P18 HWI2C_SCL
#define ESP_CONNECTOR_P19 I2C2_SDA
#define ESP_CONNECTOR_P20 I2C2_SCL
#define ESP_CONNECTOR_P25 LORA_DIO1
#define ESP_CONNECTOR_P26 LORA_BUSY
#define ESP_CONNECTOR_P27 LORA_RXEN_MANUAL
#define ESP_CONNECTOR_P28 GPIO_NUM_6
#define ESP_CONNECTOR_P29 GPIO_NUM_5
#define ESP_CONNECTOR_P30 GPIO_NUM_4
#define ESP_CONNECTOR_P47 GPS_RXD
#define ESP_CONNECTOR_P48 GPS_TXD
#define ESP_CONNECTOR_P51 ESP_RXD0
#define ESP_CONNECTOR_P52 ESP_TXD0
#define ESP_CONNECTOR_P57 ESP_USB_DP
#define ESP_CONNECTOR_P58 ESP_USB_DM
#define HSPI_UNKNOWN_CS GPIO_NUM_42
#define LORA_RXEN_MANUAL GPIO_NUM_7
#define HSPI_MISO_GPIO GPIO_NUM_13
#define HSPI_MOSI_GPIO GPIO_NUM_11
#define HSPI_SCK_GPIO GPIO_NUM_12
#define HSPI_LORA_CS GPIO_NUM_48
#define LORA_DIO1 GPIO_NUM_16
#define LORA_BUSY GPIO_NUM_15
#define HSPI_SD_CS GPIO_NUM_10
#define MCP3550_MOSI_GPIO GPIO_NUM_35
#define MCP3550_SCK_GPIO GPIO_NUM_36
#define MCP3550_MISO_GPIO GPIO_NUM_37
#define GPS_TXD GPIO_NUM_17
#define GPS_RXD GPIO_NUM_18
#define GPS_RTS UART_PIN_NO_CHANGE
#define GPS_CTS UART_PIN_NO_CHANGE
#define GPS_UART_NUM UART_NUM_2
#define HWI2C_SDA GPIO_NUM_8
#define HWI2C_SCL GPIO_NUM_9
#define I2C2_SDA GPIO_NUM_40
#define I2C2_SCL GPIO_NUM_41
#endif

View File

@@ -1,46 +1,86 @@
#include "ccs811.h"
#define CONFIG_FREERTOS_HZ 100
i2c_device_config_t CCS811_DEV_CFG = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = 0x5A,
.device_address = CCS811_ADDRESS,
.scl_speed_hz = 100000,
};
i2c_master_dev_handle_t CCS811_DEV_HANDLE;
void ccs811_init()
esp_err_t ccs811_getStatus()
{
ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c0_bus_hdl, &CCS811_DEV_CFG, &CCS811_DEV_HANDLE));
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_CCS811_WAKE, 0);
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_CCS811_POWER, 1);
vTaskDelay(10 / portTICK_PERIOD_MS);
uint8_t reset_seq[4] = {0x11, 0xE5, 0x72, 0x8A};
i2c_write_register(CCS811_DEV_HANDLE, 0xFF, reset_seq, sizeof(reset_seq)); //Reset
vTaskDelay(10 / portTICK_PERIOD_MS);
uint8_t errorID;
uint8_t status;
uint8_t hardwareVersion;
uint16_t version;
i2c_read_register_8(CCS811_DEV_HANDLE, 0x00, &status);
i2c_read_register_16(CCS811_DEV_HANDLE, 0x24, &version);
ESP_LOGW(TAG_CCS, "CCS811 status: %d, version: %d", status, version);
i2c_write_register(CCS811_DEV_HANDLE, 0xF4, NULL, 0); //start
vTaskDelay(10 / portTICK_PERIOD_MS);
i2c_write_register_8(CCS811_DEV_HANDLE, 0x10, 0x0001); // MODE 1 interrupts vypnuté
i2c_read_register_8(CCS811_DEV_HANDLE, 0x00, &status);
i2c_read_register_16(CCS811_DEV_HANDLE, 0x24, &version);
ESP_LOGW(TAG_CCS, "CCS811 status: %d, version: %d", status, version);
}
uint16_t bootVersion;
esp_err_t ccs811_get_data(uint16_t * eCO2, uint16_t * tvoc)
{
uint8_t ccsResult[8];
esp_err_t ret = i2c_read_register(CCS811_DEV_HANDLE, 0x05, ccsResult, 8);
if (ret == ESP_OK)
{
*eCO2 = (((uint16_t)(ccsResult[0] & 0xFF)) << 8) | (ccsResult[1] & 0xFF);
*tvoc = (((uint16_t)(ccsResult[2] & 0xFF)) << 8) | (ccsResult[3] & 0xFF);
ESP_LOGI(TAG_CCS, "CCS Status: %d, Error %d", ccsResult[4], ccsResult[5]);
}
esp_err_t ret = i2c_read_register_8(CCS811_DEV_HANDLE, CCS811_REG_STATUS, &status);
if (ret != ESP_OK) {return ret;}
ret = i2c_read_register_16(CCS811_DEV_HANDLE, CCS811_REG_FW_APP_VERSION, &version);
if (ret != ESP_OK) {return ret;}
ret = i2c_read_register_16(CCS811_DEV_HANDLE, CCS811_REG_FW_BOOT_VERSION, &bootVersion);
if (ret != ESP_OK) {return ret;}
ret = i2c_read_register_8(CCS811_DEV_HANDLE, CCS811_REG_HW_VERSION, &hardwareVersion);
if (ret != ESP_OK) {return ret;}
ret = i2c_read_register_8(CCS811_DEV_HANDLE, CCS811_REG_ERROR_ID, &errorID);
ESP_LOGW(TAG_CCS, "CCS811 status: %d, version: %d, boot version: %d, hardware version: %d, error ID: %d", status, version, bootVersion, hardwareVersion, errorID);
return ret;
}
esp_err_t ccs811_init()
{
esp_err_t ret =i2c_master_bus_add_device(i2c0_bus_hdl, &CCS811_DEV_CFG, &CCS811_DEV_HANDLE);
if (ret != ESP_OK) {return ret;}
vTaskDelay(10 / portTICK_PERIOD_MS);
uint8_t reset_seq[4] = {0x11, 0xE5, 0x72, 0x8A};
ret = i2c_write_register(CCS811_DEV_HANDLE, CCS811_REG_SW_RESET, reset_seq, sizeof(reset_seq)); // Reset
if (ret != ESP_OK) {return ret;}
vTaskDelay(10 / portTICK_PERIOD_MS);
ret = ccs811_getStatus();
if (ret != ESP_OK) {return ret;}
ret = i2c_write_register(CCS811_DEV_HANDLE, CCS811_REG_APP_START, NULL, 0); // start
if (ret != ESP_OK) {return ret;}
vTaskDelay(10 / portTICK_PERIOD_MS);
ret = i2c_write_register_8(CCS811_DEV_HANDLE, CCS811_REG_MEAS_MODE, 0x40); // MODE 1 interrupts vypnuté
if (ret != ESP_OK) {return ret;}
ret = ccs811_getStatus();
return ret;
}
esp_err_t ccs811_get_data(uint16_t *eCO2, uint16_t *tvoc, uint8_t *current, uint16_t *rawData)
{
uint8_t algResultData[8];
esp_err_t ret = i2c_read_register(CCS811_DEV_HANDLE, CCS811_REG_ALG_RESULT_DATA, algResultData, 8);
if (ret == ESP_OK)
{
*eCO2 = ((uint16_t)algResultData[0] << 8) | algResultData[1];
*tvoc = ((uint16_t)algResultData[2] << 8) | algResultData[3];
*current = algResultData[6] >> 2;
*rawData = ((uint16_t)(algResultData[6] & 0x03) << 8) | algResultData[7];
ESP_LOGI(TAG_CCS, "CCS Status: %d, Error: %d", algResultData[4], algResultData[5]);
return ESP_OK;
}
return ret;
}
esp_err_t ccs811_set_env_data(float temperature, float humidity)
{
uint8_t envData[4];
// Convert humidity: %RH × 512 (rounding)
uint16_t hum_conv = (uint16_t)(humidity * 512.0f + 0.5f);
envData[0] = (hum_conv >> 8) & 0xFF;
envData[1] = hum_conv & 0xFF;
// Convert temperature: (°C + 25) × 512
uint16_t temp_conv = (uint16_t)((temperature + 25.0f) * 512.0f + 0.5f);
envData[2] = (temp_conv >> 8) & 0xFF;
envData[3] = temp_conv & 0xFF;
return i2c_write_register(CCS811_DEV_HANDLE, CCS811_REG_ENV_DATA, envData, 4);
}

View File

@@ -5,11 +5,39 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define TAG_CCS "CCS811"
// CCS811 Registers
#define CCS811_REG_STATUS 0x00
#define CCS811_REG_MEAS_MODE 0x01
#define CCS811_REG_ALG_RESULT_DATA 0x02
#define CCS811_REG_RAW_DATA 0x03
#define CCS811_REG_ENV_DATA 0x05
#define CCS811_REG_THRESHOLDS 0x10
#define CCS811_REG_BASELINE 0x11
#define CCS811_REG_HW_ID 0x20
#define CCS811_REG_HW_VERSION 0x21
#define CCS811_REG_FW_BOOT_VERSION 0x23
#define CCS811_REG_FW_APP_VERSION 0x24
#define CCS811_REG_INTERNAL_STATE 0xA0
#define CCS811_REG_ERROR_ID 0xE0
#define CCS811_REG_APP_ERASE 0xF1
#define CCS811_REG_APP_DATA 0xF2
#define CCS811_REG_APP_VERIFY 0xF3
#define CCS811_REG_APP_START 0xF4
#define CCS811_REG_SW_RESET 0xFF
// Measurement Modes
#define CCS811_MODE_IDLE 0x00
#define CCS811_MODE_CONSTANT_1S 0x10
#define CCS811_MODE_CONSTANT_10S 0x20
#define CCS811_MODE_CONSTANT_60S 0x30
#define CCS811_MODE_WAKEUP 0x40
extern i2c_device_config_t CCS811_DEV_CFG;
extern i2c_master_dev_handle_t CCS811_DEV_HANDLE;
void ccs811_init();
esp_err_t ccs811_get_data(uint16_t * eCO2, uint16_t * tvoc);
esp_err_t ccs811_init();
esp_err_t ccs811_get_data(uint16_t *eCO2, uint16_t *tvoc, uint8_t *current, uint16_t *rawData);
esp_err_t ccs811_set_env_data(float temperature, float humidity);
#endif

245
main/hw/gps.c Normal file
View File

@@ -0,0 +1,245 @@
#include "driver/uart.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "string.h"
#include "gps.h"
#define TAG "GPS"
#define BUF_SIZE 1024
#define GPS_LINE_MAX_LEN 128
gps_binary_struct_t gpsDataOut;
static QueueHandle_t uart_queue;
predicted_binary_position_struct_t predictedPosition;
// Initializes UART for GPS
void gps_init()
{
memset(&gpsDataOut, 0, sizeof(gpsDataOut));
uart_config_t uart_config = {
.baud_rate = 9600,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS,
.rx_flow_ctrl_thresh = 122,
};
ESP_ERROR_CHECK_WITHOUT_ABORT(uart_param_config(GPS_UART_NUM, &uart_config));
ESP_ERROR_CHECK_WITHOUT_ABORT(uart_set_pin(GPS_UART_NUM, GPS_TXD, GPS_RXD, GPS_RTS, GPS_CTS));
const int uart_buffer_size = (1024 * 2);
ESP_ERROR_CHECK_WITHOUT_ABORT(uart_driver_install(GPS_UART_NUM, uart_buffer_size, uart_buffer_size, 10, &uart_queue, 0));
}
void gps_task(void *arg)
{
uint8_t byte;
char line[GPS_LINE_MAX_LEN];
size_t line_pos = 0;
gps_init();
while (1) {
int len = uart_read_bytes(GPS_UART_NUM, &byte, 1, pdMS_TO_TICKS(1000));
if (len > 0) {
if (byte == '\n') {
line[line_pos] = '\0';
if (line[0] == '$') {
ESP_LOGI(TAG, "Received NMEA: %s", line);
if (strstr(line, "$GNGGA") == line) {
parse_gpgga(line);
parse_gpgga_to_struct(line, &gpsDataOut);
} else if (strstr(line, "$GPRMC") == line) {
parse_gprmc(line);
parse_gprmc_to_struct(line, &gpsDataOut);
}
}
line_pos = 0;
} else if (byte != '\r' && line_pos < (GPS_LINE_MAX_LEN - 1)) {
line[line_pos++] = byte;
} else if (line_pos >= GPS_LINE_MAX_LEN - 1) {
ESP_LOGW(TAG, "Line too long, discarded");
line_pos = 0;
}
}
}
}
void parse_gpgga(const char *nmea) {
char *fields[15];
char temp[GPS_LINE_MAX_LEN];
strncpy(temp, nmea, GPS_LINE_MAX_LEN);
temp[GPS_LINE_MAX_LEN - 1] = '\0';
int i = 0;
char *token = strtok(temp, ",");
while (token != NULL && i < 15) {
fields[i++] = token;
token = strtok(NULL, ",");
}
if (i < 10) return; // Not enough fields
// Time (hhmmss.sss)
const char *utc_time = fields[1];
// Latitude (ddmm.mmmm)
const char *lat = fields[2];
const char *lat_dir = fields[3];
// Longitude (dddmm.mmmm)
const char *lon = fields[4];
const char *lon_dir = fields[5];
// Fix quality (0 = invalid, 1 = GPS fix, 2 = DGPS fix)
const char *fix_quality = fields[6];
// Number of satellites
const char *num_satellites = fields[7];
// Altitude
const char *altitude = fields[9];
printf("[GPGGA] Time: %s, Lat: %s %s, Lon: %s %s, Fix: %s, Sats: %s, Alt: %sm\n",
utc_time, lat, lat_dir, lon, lon_dir, fix_quality, num_satellites, altitude);
}
void parse_gprmc(const char *nmea) {
char *fields[13];
char temp[GPS_LINE_MAX_LEN];
strncpy(temp, nmea, GPS_LINE_MAX_LEN);
temp[GPS_LINE_MAX_LEN - 1] = '\0';
int i = 0;
char *token = strtok(temp, ",");
while (token != NULL && i < 13) {
fields[i++] = token;
token = strtok(NULL, ",");
}
if (i < 12) return;
const char *utc_time = fields[1];
const char *status = fields[2]; // A = active, V = void
const char *lat = fields[3];
const char *lat_dir = fields[4];
const char *lon = fields[5];
const char *lon_dir = fields[6];
const char *speed_knots = fields[7];
const char *date = fields[9];
printf("[GPRMC] Date: %s, Time: %s, Lat: %s %s, Lon: %s %s, Speed: %s knots, Status: %s\n",
date, utc_time, lat, lat_dir, lon, lon_dir, speed_knots, status);
}
// Function to convert time string (hhmmss.sss) to seconds since start of day
static uint32_t time_to_seconds_struct(const char *time_str) {
if (time_str == NULL || strlen(time_str) < 6) return 0;
uint32_t hours = (time_str[0] - '0') * 10 + (time_str[1] - '0');
uint32_t minutes = (time_str[2] - '0') * 10 + (time_str[3] - '0');
uint32_t seconds = (time_str[4] - '0') * 10 + (time_str[5] - '0');
return hours * 3600 + minutes * 60 + seconds;
}
// Function to convert DMS (degrees minutes.decimalminutes) to centi-degrees
static int32_t dms_to_centi_degrees_struct(const char *dms_str, const char *direction) {
if (dms_str == NULL || direction == NULL || strlen(dms_str) < 7) return 0;
char data_str[20] = {0};
char *dataStr = data_str;
for (int i = 0; i < 20; i++) {
if (dms_str[i] == 0) {
break;
}
if (dms_str[i] == '.') {
continue;
}
*(dataStr++) = dms_str[i];
}
int32_t degrees = atoi(data_str);
if (direction[0] == 'S' || direction[0] == 'W') {
degrees *= -1;
}
return degrees;
}
// Function to convert altitude string to centi-meters
static int16_t altitude_to_centi_meters_struct(const char *alt_str) {
if (alt_str == NULL) return 0;
return (int16_t) (atof(alt_str) * 100);
}
// Function to convert speed from knots to centi-knots
static uint16_t speed_to_centi_knots_struct(const char *speed_str) {
if (speed_str == NULL) return 0;
return (uint16_t) (atof(speed_str) * 100);
}
// Function to convert date string (ddmmyy) to yymmdd integer
static uint16_t date_to_yyddmm_struct(const char *date_str) {
if (date_str == NULL || strlen(date_str) != 6) return 0;
uint16_t day = (date_str[0] - '0') * 10 + (date_str[1] - '0');
uint16_t month = (date_str[2] - '0') * 10 + (date_str[3] - '0');
uint16_t year_short = (date_str[4] - '0') * 10 + (date_str[5] - '0');
// Assuming year is in the 21st century for simplicity
return (2000 + year_short) * 10000 + month * 100 + day;
}
// Function to parse GPGGA NMEA string and return the struct
void parse_gpgga_to_struct(const char *nmea, gps_binary_struct_t *data) {
char *fields[15];
char temp[GPS_LINE_MAX_LEN];
strncpy(temp, nmea, GPS_LINE_MAX_LEN);
temp[GPS_LINE_MAX_LEN - 1] = '\0';
int i = 0;
char *token = strtok(temp, ",");
while (token != NULL && i < 15) {
fields[i++] = token;
token = strtok(NULL, ",");
}
if (i >= 10) {
data->time_seconds = time_to_seconds_struct(fields[1]);
data->latitude_centi_degrees = dms_to_centi_degrees_struct(fields[2], fields[3]);
data->longitude_centi_degrees = dms_to_centi_degrees_struct(fields[4], fields[5]);
data->fix_quality = atoi(fields[6]);
data->num_satellites = atoi(fields[7]);
data->altitude_centi_meters = altitude_to_centi_meters_struct(fields[9]);
} else {
printf("GPGGA: Not enough fields to parse struct");
}
}
// Function to parse GPRMC NMEA string and return the struct
void parse_gprmc_to_struct(const char *nmea, gps_binary_struct_t *data) {
char *fields[13];
char temp[GPS_LINE_MAX_LEN];
strncpy(temp, nmea, GPS_LINE_MAX_LEN);
temp[GPS_LINE_MAX_LEN - 1] = '\0';
int i = 0;
char *token = strtok(temp, ",");
while (token != NULL && i < 13) {
fields[i++] = token;
token = strtok(NULL, ",");
}
if (i >= 12) {
data->time_seconds = time_to_seconds_struct(fields[1]);
data->latitude_centi_degrees = dms_to_centi_degrees_struct(fields[3], fields[4]);
data->longitude_centi_degrees = dms_to_centi_degrees_struct(fields[5], fields[6]);
data->date_yyddmm = date_to_yyddmm_struct(fields[9]);
data->speed_centi_knots = speed_to_centi_knots_struct(fields[7]);
// Fix quality and num_satellites are typically in GPGGA, so they might be 0 here.
} else {
printf("GPRMC: Not enough fields to parse struct");
}
}

39
main/hw/gps.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef GPS_COMPONENT
#define GPS_COMPONENT
#include "driver/uart.h"
#include "soc/uart_struct.h"
#include "buscfg.h"
// Structure to hold parsed GPS data in a compact binary format
typedef struct {
uint32_t time_seconds; // Seconds since start of day
int32_t latitude_centi_degrees; // Latitude * 10,000
int32_t longitude_centi_degrees; // Longitude * 10,000
uint8_t fix_quality;
uint8_t num_satellites;
int16_t altitude_centi_meters; // Altitude * 100
uint16_t date_yyddmm; // YYDDMM (from GPRMC)
uint16_t speed_centi_knots; // Speed * 100 (from GPRMC)
} gps_binary_struct_t;
typedef struct {
int32_t latitude_centi_degrees; // Latitude * 10,000
int32_t longitude_centi_degrees; // Longitude * 10,000
int16_t altitude_centi_meters; // Altitude * 100
} predicted_binary_position_struct_t;
extern gps_binary_struct_t gpsDataOut;
extern predicted_binary_position_struct_t predictedPosition;
void gps_task(void *arg);
void parse_gpgga(const char *nmea);
void parse_gprmc(const char *nmea);
void parse_gprmc_to_struct(const char *nmea, gps_binary_struct_t *data);
void parse_gpgga_to_struct(const char *nmea, gps_binary_struct_t *data);
#endif

View File

@@ -3,46 +3,80 @@
i2c_master_bus_config_t i2c0_bus_cfg = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_NUM_0,
.scl_io_num = GPIO_NUM_9,
.sda_io_num = GPIO_NUM_8,
.glitch_ignore_cnt = 7,
.scl_io_num = HWI2C_SCL,
.sda_io_num = HWI2C_SDA,
.glitch_ignore_cnt = 1,
.flags.enable_internal_pullup = true,
};
i2c_master_bus_handle_t i2c0_bus_hdl;
// esp_err_t i2c_master_bus_detect_devices(i2c_master_bus_handle_t handle)
// {
// const uint16_t probe_timeout_ms = 20; // timeout in milliseconds
// uint8_t address;
// printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
// for (int i = 0; i < 128; i += 16)
// {
// printf("%02x: ", i);
// for (int j = 0; j < 16; j++)
// {
// fflush(stdout);
// address = i + j;
// esp_err_t ret = i2c_master_probe(handle, address, probe_timeout_ms);
// if (ret == ESP_OK)
// {
// printf("%02x ", address);
// }
// else if (ret == ESP_ERR_TIMEOUT)
// {
// printf("UU ");
// }
// else
// {
// printf("-- ");
// }
// }
// printf("\r\n");
// }
// return ESP_OK;
// }
/**
* i2c master initialization
*/
esp_err_t i2c_master_bus_detect_devices(i2c_master_bus_handle_t handle)
{
const uint16_t probe_timeout_ms = 50; // timeout in milliseconds
uint8_t address;
bool found_any = false;
printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
printf("Scanning I2C bus...\n");
for (int i = 0; i < 128; i += 16)
{
printf("%02x: ", i);
for (int j = 0; j < 16; j++)
for (int i = 0; i < 128; i++)
{
fflush(stdout);
address = i;
address = i + j;
esp_err_t ret = i2c_master_probe(handle, address, probe_timeout_ms);
esp_err_t ret = i2c_master_probe(handle, address, 20);
if (ret == ESP_OK)
{
printf("%02x ", address);
printf("Found device at 0x%02X\n", address);
found_any = true;
}
else if (ret == ESP_ERR_TIMEOUT)
}
if (!found_any)
{
printf("UU ");
}
else
{
printf("-- ");
}
}
printf("\r\n");
printf("No I2C devices found.\n");
}
return ESP_OK;

View File

@@ -3,6 +3,7 @@
#define I2C_TIMEOUT_MS_VALUE 20
#include <string.h>
#include "esp_log.h"
#include "buscfg.h"
#define TAG_I2C "cani2c"

View File

@@ -2,25 +2,32 @@
i2c_device_config_t INA260_DEV_CFG = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = 0x40,
.device_address = INA260_ADDRESS,
.scl_speed_hz = 100000,
};
i2c_master_dev_handle_t INA260_DEV_HANDLE;
void ina260_reset() {
void ina260_reset()
{
i2c_write_register_16(INA260_DEV_HANDLE, 0x00, 0x0FFF); // set ina max averaging and max time
}
void ina260_init()
esp_err_t ina260_init()
{
ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c0_bus_hdl, &INA260_DEV_CFG, &INA260_DEV_HANDLE));
i2c_write_register_16(INA260_DEV_HANDLE, INA260_CONFIG_REGISTER, 0x0FFF); // set ina max averaging and max time
esp_err_t ret = i2c_master_bus_add_device(i2c0_bus_hdl, &INA260_DEV_CFG, &INA260_DEV_HANDLE);
if (ret != ESP_OK)
{
ESP_LOGE(TAG_INA, "%s", esp_err_to_name(ret));
return ret;
}
ESP_LOGI(TAG_INA, "hehedstesfsewscdsfsrgerpodsvhdrsivhriuvjdreiv");
return i2c_write_register_16(INA260_DEV_HANDLE, INA260_CONFIG_REGISTER,
CONFIG_AVG_1024 | CONFIG_VBUSCT_8_244MS | CONFIG_ISHCT_8_244MS | CONFIG_MODE_CURRENT_VOLTAGE_CONTINOUS); // set ina max averaging and max time
}
esp_err_t i2c_master_read_register_transmit_receive(i2c_master_dev_handle_t device_handle, uint8_t reg_addr, uint8_t *data, size_t data_len) {
esp_err_t i2c_master_read_register_transmit_receive(i2c_master_dev_handle_t device_handle, uint8_t reg_addr, uint8_t *data, size_t data_len)
{
esp_err_t ret;
// The register address is what we want to send first (the "transmit" part)
@@ -37,7 +44,8 @@ esp_err_t i2c_master_read_register_transmit_receive(i2c_master_dev_handle_t devi
read_buffer, read_size,
I2C_TIMEOUT_MS_VALUE);
if (ret == ESP_OK) {
if (ret == ESP_OK)
{
// Copy the data from the temporary read buffer to the output buffer
memcpy(data, read_buffer, read_size);
}
@@ -46,6 +54,8 @@ esp_err_t i2c_master_read_register_transmit_receive(i2c_master_dev_handle_t devi
}
void ina260_readParams(uint16_t *volt, uint16_t *cur, uint16_t *pow)
{
if (INA260_DEV_HANDLE)
{
*volt = 0;
*cur = 0;
@@ -54,7 +64,7 @@ void ina260_readParams(uint16_t *volt, uint16_t *cur, uint16_t *pow)
{
uint8_t reg_value[2] = {0}; // Buffer for storing register data
ESP_ERROR_CHECK(i2c_master_read_register_transmit_receive(INA260_DEV_HANDLE, reg_addr, reg_value, sizeof(reg_value)));
ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_read_register_transmit_receive(INA260_DEV_HANDLE, reg_addr, reg_value, sizeof(reg_value)));
// Perform the register read
switch (reg_addr)
{
@@ -73,15 +83,14 @@ void ina260_readParams(uint16_t *volt, uint16_t *cur, uint16_t *pow)
}
}
}
}
void ina260_printParams(uint16_t volt, uint16_t cur, uint16_t pow)
{
float miliVolts = volt * 1.25;
float miliAmps = cur * 1.25;
float power = pow * 10;
cur *= 125;
ESP_LOGI(TAG_INA, "Current: %.3f mA (raw %d)", miliAmps, volt);
cur *= 125;
ESP_LOGI(TAG_INA, "Voltage: %.3f mV (raw %d)", miliVolts, cur);
ESP_LOGI(TAG_INA, "Current: %.3f mA (raw %d)", miliAmps, cur);
ESP_LOGI(TAG_INA, "Voltage: %.3f mV (raw %d)", miliVolts, volt);
ESP_LOGI(TAG_INA, "Power: %.3f mW (raw %d)", power, pow);
}

View File

@@ -56,20 +56,17 @@ extern i2c_master_dev_handle_t INA260_DEV_HANDLE;
#define CONFIG_ISHCT_8_244MS (0x7 << 3)
// Operating mode (MODE)
#define CONFIG_MODE_POWERDOWN (0x0 << 0)
#define CONFIG_MODE_CURRENT_TRIGGER (0x1 << 0)
#define CONFIG_MODE_VOLTAGE_TRIGGER (0x2 << 0)
#define CONFIG_MODE_CURRENT_VOLTAGE_TRIGGER (0x3 << 0)
#define CONFIG_MODE_POWERDOWN2 (0x4 << 0)
#define CONFIG_MODE_POWERDOWN (0x5 << 0)
#define CONFIG_MODE_POWERDOWN (0x6 << 0)
#define CONFIG_MODE_POWERDOWN (0x7 << 0)
#define CONFIG_MODE_POWERDOWN 0x00
#define CONFIG_MODE_CURRENT_TRIGGER 0x01
#define CONFIG_MODE_VOLTAGE_TRIGGER 0x02
#define CONFIG_MODE_CURRENT_VOLTAGE_TRIGGER 0x03
#define CONFIG_MODE_POWERDOWN2 0x04
#define CONFIG_MODE_CURRENT_CONTINOUS 0x05
#define CONFIG_MODE_VOLTAGE_CONTINOUS 0x06
#define CONFIG_MODE_CURRENT_VOLTAGE_CONTINOUS 0x07
void ina260_init();
esp_err_t ina260_init();
void ina260_readParams(uint16_t *volt, uint16_t *cur, uint16_t *pow);
void ina260_printParams(uint16_t volt, uint16_t cur, uint16_t pow);
#endif

View File

@@ -1,9 +1,12 @@
#include "mcp23018.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "../components/sensors.h"
// Local buffer for tracking GPIO state
i2c_device_config_t MCP23018_DEV_CFG = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = 0x20,
.device_address = MCP23018_ADDRESS,
.scl_speed_hz = 100000,
};
@@ -11,8 +14,10 @@ i2c_master_dev_handle_t MCP23018_DEV_HANDLE;
uint8_t gpioa_state = 0x00; // All LOW initially
uint8_t gpiob_state = 0x00; // All LOW initially
void mcp23018_set_pin(i2c_master_dev_handle_t dev_handle, uint8_t pin, uint8_t value)
esp_err_t mcp23018_set_pin(i2c_master_dev_handle_t dev_handle, uint8_t pin, uint8_t value)
{
ESP_LOGI(TAG_MCP, "Setting %d to %d", pin, value);
esp_err_t ret = ESP_FAIL;
if (pin < 8)
{
// GPIOA (Pins 0-7)
@@ -22,7 +27,7 @@ void mcp23018_set_pin(i2c_master_dev_handle_t dev_handle, uint8_t pin, uint8_t v
gpioa_state &= ~(1 << pin); // Clear bit
// Write updated buffer to MCP23018
i2c_write_register_8(dev_handle, MCP23018_GPIOA, gpioa_state);
ret = i2c_write_register_8(dev_handle, MCP23018_GPIOA, gpioa_state);
}
else if (pin < 16)
{
@@ -34,18 +39,28 @@ void mcp23018_set_pin(i2c_master_dev_handle_t dev_handle, uint8_t pin, uint8_t v
gpiob_state &= ~(1 << pinB); // Clear bit
// Write updated buffer to MCP23018
i2c_write_register_8(dev_handle, MCP23018_GPIOB, gpiob_state);
ret = i2c_write_register_8(dev_handle, MCP23018_GPIOB, gpiob_state);
}
vTaskDelay(1 / portTICK_PERIOD_MS);
return ret;
}
void mcp23018_init()
esp_err_t mcp23018_init()
{
ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c0_bus_hdl, &MCP23018_DEV_CFG, &MCP23018_DEV_HANDLE));
esp_err_t ret = i2c_master_bus_add_device(i2c0_bus_hdl, &MCP23018_DEV_CFG, &MCP23018_DEV_HANDLE);
if (ret != ESP_OK) {return ret;}
i2c_write_register_8(MCP23018_DEV_HANDLE, MCP23018_IODIRA, gpioa_state);
if (ret != ESP_OK) {return ret;}
i2c_write_register_8(MCP23018_DEV_HANDLE, MCP23018_IODIRB, gpiob_state);
if (ret != ESP_OK) {return ret;}
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_CS_ADC_CO, 1);
if (ret != ESP_OK) {return ret;}
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_CS_ADC_NH3, 1);
if (ret != ESP_OK) {return ret;}
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_CS_ADC_NO2, 1);
if (ret != ESP_OK) {return ret;}
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_CS_ADC_UVC, 1);
if (ret != ESP_OK) {return ret;}
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_LORA_RST, 1);
return ret;
}

View File

@@ -7,6 +7,7 @@
#define MCP23018_GPIOA 0x12 // GPIO Output A
#define MCP23018_GPIOB 0x13 // GPIO Output B
#define MCP_LORA_RST 4
#define MCP_MICS_POWER 5
#define MCP_CCS811_WAKE 6
@@ -25,7 +26,7 @@ extern i2c_master_dev_handle_t MCP23018_DEV_HANDLE;
extern uint8_t gpioa_state;
extern uint8_t gpiob_state;
void mcp23018_set_pin(i2c_master_dev_handle_t dev_handle, uint8_t pin, uint8_t value);
void mcp23018_init();
esp_err_t mcp23018_set_pin(i2c_master_dev_handle_t dev_handle, uint8_t pin, uint8_t value);
esp_err_t mcp23018_init();
#endif

212
main/hw/mcp3550.c Normal file
View File

@@ -0,0 +1,212 @@
#include "mcp3550.h"
#include "mcp23018.h"
#include "driver/gpio.h"
#include <esp_timer.h>
#define TAG_MICS "MICS_ADC"
#define MCP3550_SPS 3.75f
#define MCP3550_CONVERSION_MS ((int)(1000.0f / MCP3550_SPS)) // ~267ms
#define MCP3550_TIMEOUT_MS 400
#define ADC_COUNT 4
const uint8_t adc_cs_pins[ADC_COUNT] = {
MCP_CS_ADC_NH3, MCP_CS_ADC_CO, MCP_CS_ADC_NO2, MCP_CS_ADC_UVC
};
spi_device_handle_t mcp3550_handle;
void mcp3550_spi_init()
{
// spi_device_interface_config_t devcfg = {
// .clock_speed_hz = 100000,
// .mode = 0,
// .spics_io_num = -1, // We handle CS manually
// .queue_size = 1,
// };
// ESP_ERROR_CHECK_WITHOUT_ABORT(spi_bus_add_device(SPI2_HOST, &devcfg, &mcp3550_handle));
// Set MISO pin for input (needed for polling)
gpio_set_direction(MCP3550_MISO_GPIO, GPIO_MODE_INPUT);
}
// int32_t mcp3550_read(uint8_t cs_pin)
// {
// uint8_t rx_buf[4] = {0}; // 25 bits fits in 4 bytes
// int64_t start = esp_timer_get_time(); // in microseconds
// while (true) {
// mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 0); // CS LOW
// vTaskDelay(pdMS_TO_TICKS(30)); // Wait before retrying
// mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH
// vTaskDelay(pdMS_TO_TICKS(200)); // Wait before retrying
// mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 0); // CS LOW
// esp_rom_delay_us(1000); // Wait before retrying
// spi_transaction_t trans = {
// .length = 25,
// .rx_buffer = rx_buf,
// };
// esp_err_t err = spi_device_transmit(mcp3550_handle, &trans);
// mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH
// if (err != ESP_OK) {
// ESP_LOGE(TAG_MICS, "SPI transmit failed on CS pin %u", cs_pin);
// return INT32_MIN;
// }
// bool dr = (rx_buf[0] & 0x80) != 0;
// if (!dr) break;
// if ((esp_timer_get_time() - start) > (MCP3550_TIMEOUT_MS * 1000)) {
// ESP_LOGW(TAG_MICS, "Timeout waiting for DR=0 on CS pin %u", cs_pin);
// return INT32_MIN;
// }
// vTaskDelay(pdMS_TO_TICKS(10)); // Wait before retrying
// }
// // Combine 22-bit result (drop DR + status bits)
// uint32_t raw = ((rx_buf[0] & 0x3F) << 16) | (rx_buf[1] << 8) | rx_buf[2];
// // Sign-extend 22-bit value
// int32_t value = raw;
// if (value & (1 << 21)) value |= 0xFFC00000;
// return value;
// }
// int32_t mcp3550_read(uint8_t cs_pin)
// {
// uint8_t rx_buf[4] = {0};
// uint32_t timeout_us = MCP3550_TIMEOUT_MS * 1000;
// int64_t start = esp_timer_get_time();
// // Start conversion
// mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 0); // CS LOW
// // Wait until MISO/SDO goes LOW = DR ready
// while (gpio_get_level(MCP3550_MISO_GPIO)) {
// if ((esp_timer_get_time() - start) > timeout_us) {
// ESP_LOGW(TAG_MICS, "Timeout waiting for MISO=0 on CS %u", cs_pin);
// mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH
// return INT32_MIN;
// }
// esp_rom_delay_us(10); // micro delay
// }
// // Data is ready, do full SPI read
// spi_transaction_t trans = {
// .length = 25, // 25 bits
// .rx_buffer = rx_buf,
// };
// esp_err_t err = spi_device_transmit(mcp3550_handle, &trans);
// mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH to start next conversion
// if (err != ESP_OK) {
// ESP_LOGE(TAG_MICS, "SPI transmit failed");
// return INT32_MIN;
// }
// // Extract 22-bit result
// uint32_t raw = ((rx_buf[0] & 0x3F) << 16) | (rx_buf[1] << 8) | rx_buf[2];
// // Sign-extend 22-bit value
// int32_t value = raw;
// if (value & (1 << 21)) value |= 0xFFC00000;
// return value;
// }
int32_t mcp3550_read(uint8_t cs_pin)
{
uint32_t data = 0;
int64_t start = esp_timer_get_time();
uint32_t timeout_us = MCP3550_TIMEOUT_MS * 1000;
ESP_LOGI(TAG_MCP, "Starting read from ADC CS %d", cs_pin);
// CS LOW
mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 0);
vTaskDelay(pdMS_TO_TICKS(10));
// Wait for DR (MISO LOW)
while (gpio_get_level(MCP3550_MISO_GPIO)) {
if ((esp_timer_get_time() - start) > timeout_us) {
ESP_LOGW(TAG_MICS, "Timeout waiting for MISO=0 on CS %u", cs_pin);
mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH
return INT32_MIN;
}
vTaskDelay(pdMS_TO_TICKS(10)); // Wait 1 tick (e.g., 1ms)
}
// Clock out 25 bits
for (int i = 0; i < 25; i++) {
gpio_set_level(MCP3550_SCK_GPIO, 1);
esp_rom_delay_us(5); // small delay to simulate clock high
data = (data << 1) | gpio_get_level(MCP3550_MISO_GPIO);
gpio_set_level(MCP3550_SCK_GPIO, 0);
esp_rom_delay_us(5); // small delay to simulate clock low
}
mcp23018_set_pin(MCP23018_DEV_HANDLE, cs_pin, 1); // CS HIGH
// Extract 22-bit value (bits [23:2])
uint32_t raw = (data >> 2) & 0x3FFFFF;
// Sign-extend 22-bit value
int32_t value = raw;
if (value & (1 << 21)) value |= 0xFFC00000;
return value;
}
float mcp3550_to_voltage(int32_t value, float vref)
{
return ((float)value / (1 << 21)) * vref;
}
mics_adc_data_t mcp3550_read_all(float vref)
{
mics_adc_data_t data;
int32_t raw[ADC_COUNT];
float volts[ADC_COUNT];
for (int i = 0; i < ADC_COUNT; i++) {
raw[i] = mcp3550_read(adc_cs_pins[i]);
vTaskDelay(pdMS_TO_TICKS(10)); // Wait before retrying
volts[i] = mcp3550_to_voltage(raw[i], vref);
}
data.raw_nh3 = raw[0]; data.volts_nh3 = volts[0];
data.raw_co = raw[1]; data.volts_co = volts[1];
data.raw_no2 = raw[2]; data.volts_no2 = volts[2];
data.raw_uvc = raw[3]; data.volts_uvc = volts[3];
return data;
}
void log_mics_adc_values(mics_adc_data_t *data)
{
ESP_LOGI(TAG_MICS,
"ADC Readings:\n"
" NH3 -> Raw: %ld, Voltage: %f V\n"
" CO -> Raw: %ld, Voltage: %f V\n"
" NO2 -> Raw: %ld, Voltage: %f V\n"
" UVC -> Raw: %ld, Voltage: %f V",
data->raw_nh3, data->volts_nh3,
data->raw_co, data->volts_co,
data->raw_no2, data->volts_no2,
data->raw_uvc, data->volts_uvc);
}

36
main/hw/mcp3550.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef MPC3550_COMPONENT
#define MPC3550_COMPONENT
#include "driver/spi_master.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "buscfg.h"
#define MCP3550_SPS 3.75f
#define MCP3550_CONVERSION_MS ((int)(1000.0f / MCP3550_SPS)) // ~267ms
extern spi_device_handle_t mcp3550_handle;
void mcp3550_spi_init();
int32_t mcp3550_read(uint8_t cs_pin);
float mcp3550_to_voltage(int32_t value, float vref);
typedef struct {
int32_t raw_nh3;
int32_t raw_co;
int32_t raw_no2;
int32_t raw_uvc;
float volts_nh3;
float volts_co;
float volts_no2;
float volts_uvc;
} mics_adc_data_t;
extern spi_device_handle_t mcp3550_handle;
void log_mics_adc_values(mics_adc_data_t *data);
mics_adc_data_t mcp3550_read_all(float vref);
#endif

View File

@@ -1,14 +1,17 @@
#include "mpu9250.h"
#include "esp_timer.h"
i2c_device_config_t MPU9250_DEV_CFG = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = 0x68,
.device_address = MPU9250_ADDRESS,
.scl_speed_hz = 100000,
};
#define MAG_SCALE (4912.0f / 32760.0f)
i2c_master_dev_handle_t MPU9250_DEV_HANDLE;
esp_err_t mpu9250_read_sensor_data(i2c_master_dev_handle_t dev_handle, int16_t *accel, int16_t *gyro, int16_t *temp)
esp_err_t mpu9250_read_sensor_data(i2c_master_dev_handle_t dev_handle, int16_t *accel, int16_t *gyro, int16_t *temp, int16_t *magnet)
{
uint8_t buffer[14]; // 6 (Accel) + 2 (Temp) + 6 (Gyro)
esp_err_t ret = i2c_read_register(dev_handle, 0x3B, buffer, 14);
@@ -20,14 +23,24 @@ esp_err_t mpu9250_read_sensor_data(i2c_master_dev_handle_t dev_handle, int16_t *
accel[1] = (((uint16_t)(buffer[2] & 0xFF)) << 8) | (buffer[3] & 0xFF); // Accel Y
accel[2] = (((uint16_t)(buffer[4] & 0xFF)) << 8) | (buffer[5] & 0xFF); // Accel Z
*temp = (((uint16_t)(buffer[6] & 0xFF)) << 8) | (buffer[7] & 0xFF); // Temperature
gyro[0] = (((uint16_t)(buffer[8] & 0xFF)) << 8) | (buffer[9] & 0xFF);; // Gyro X
gyro[1] = (((uint16_t)(buffer[10] & 0xFF)) << 8) | (buffer[11] & 0xFF);; // Gyro Y
gyro[2] = (((uint16_t)(buffer[12] & 0xFF)) << 8) | (buffer[13] & 0xFF);; // Gyro Z
gyro[0] = (((uint16_t)(buffer[8] & 0xFF)) << 8) | (buffer[9] & 0xFF);
; // Gyro X
gyro[1] = (((uint16_t)(buffer[10] & 0xFF)) << 8) | (buffer[11] & 0xFF);
; // Gyro Y
gyro[2] = (((uint16_t)(buffer[12] & 0xFF)) << 8) | (buffer[13] & 0xFF);
; // Gyro Z
ret = i2c_read_register(dev_handle, 0x03, buffer, 6);
if (ret != ESP_OK)
return ret;
magnet[0] = (((uint16_t)(buffer[0] & 0xFF)) << 8) | (buffer[1] & 0xFF); // Magnet X
magnet[1] = (((uint16_t)(buffer[2] & 0xFF)) << 8) | (buffer[3] & 0xFF); // Magnet Y
magnet[2] = (((uint16_t)(buffer[4] & 0xFF)) << 8) | (buffer[5] & 0xFF); // Magnet Z
return ESP_OK;
}
void mpu9250_convert_data(int16_t *accel, int16_t *gyro, int16_t temp, float *accel_out, float *gyro_out, float *temp_out)
void mpu9250_convert_data(int16_t *accel, int16_t *gyro, int16_t temp, int16_t *magnet, float *accel_out, float *gyro_out, float *temp_out, float *magnet_out)
{
accel_out[0] = accel[0] / 16384.0; // Accel X in g
accel_out[1] = accel[1] / 16384.0; // Accel Y in g
@@ -37,10 +50,19 @@ void mpu9250_convert_data(int16_t *accel, int16_t *gyro, int16_t temp, float *ac
gyro_out[1] = gyro[1] / 131.0; // Gyro Y in deg/s
gyro_out[2] = gyro[2] / 131.0; // Gyro Z in deg/s
magnet_out[0] = magnet[0] * MAG_SCALE; // Gyro X in deg/s
magnet_out[1] = magnet[1] * MAG_SCALE; // Gyro Y in deg/s
magnet_out[2] = magnet[2] * MAG_SCALE; // Gyro Z in deg/s
*temp_out = (temp / 333.87) + 21.0; // Temperature in °C
}
void mpu9250_init() {
ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c0_bus_hdl, &MPU9250_DEV_CFG, &MPU9250_DEV_HANDLE));
i2c_write_register_16(MPU9250_DEV_HANDLE, 0x6B, 0x0001); // zapni uz tu hovadinu
esp_err_t mpu9250_init()
{
esp_err_t ret = i2c_master_bus_add_device(i2c0_bus_hdl, &MPU9250_DEV_CFG, &MPU9250_DEV_HANDLE);
if (ret != ESP_OK) {return ret;}
ret = i2c_write_register_16(MPU9250_DEV_HANDLE, 0x6B, 0x0001); // zapni uz tu hovadinu
if (ret != ESP_OK) {return ret;}
ret = i2c_write_register_16(MPU9250_DEV_HANDLE, 0x0A, 0x0012); // zapni uz tu hovadinu
return ret;
}

View File

@@ -8,8 +8,8 @@ extern i2c_device_config_t MPU9250_DEV_CFG;
extern i2c_master_dev_handle_t MPU9250_DEV_HANDLE;
esp_err_t mpu9250_read_sensor_data(i2c_master_dev_handle_t dev_handle, int16_t *accel, int16_t *gyro, int16_t *temp);
void mpu9250_convert_data(int16_t *accel, int16_t *gyro, int16_t temp, float *accel_out, float *gyro_out, float *temp_out);
void mpu9250_init();
esp_err_t mpu9250_read_sensor_data(i2c_master_dev_handle_t dev_handle, int16_t *accel, int16_t *gyro, int16_t *temp, int16_t *magnet);
void mpu9250_convert_data(int16_t *accel, int16_t *gyro, int16_t temp, int16_t *magnet, float *accel_out, float *gyro_out, float *temp_out, float *magnet_out);
esp_err_t mpu9250_init();
#endif

93
components/ra01s/ra01s.c → main/hw/sx1262.c Executable file → Normal file
View File

@@ -4,23 +4,21 @@
#include <math.h>
#include <stdlib.h>
#include "../components/sensors.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <driver/spi_master.h>
#include <driver/gpio.h>
#include "esp_log.h"
#include "buscfg.h"
#include "ra01s.h"
#include "sx1262.h"
#define TAG "RA01S"
#define TAG "SX1262"
// SPI Stuff
#if CONFIG_SPI2_HOST
#define HOST_ID SPI2_HOST
#elif CONFIG_SPI3_HOST
#define HOST_ID SPI3_HOST
#endif
static spi_device_handle_t SpiHandle;
@@ -30,10 +28,7 @@ static bool txActive;
static int txLost = 0;
static bool debugPrint;
static int SX126x_SPI_SELECT;
static int SX126x_RESET;
static int SX126x_BUSY;
static int SX126x_TXEN;
static int SX126x_RXEN;
// Arduino compatible macros
#define delayMicroseconds(us) esp_rom_delay_us(us)
@@ -55,20 +50,14 @@ __attribute__ ((weak, alias ("LoRaErrorDefault"))) void LoRaError(int error);
void LoRaInit(void)
{
ESP_LOGI(TAG, "CONFIG_MISO_GPIO=%d", CONFIG_MISO_GPIO);
ESP_LOGI(TAG, "CONFIG_MOSI_GPIO=%d", CONFIG_MOSI_GPIO);
ESP_LOGI(TAG, "CONFIG_SCLK_GPIO=%d", CONFIG_SCLK_GPIO);
ESP_LOGI(TAG, "CONFIG_NSS_GPIO=%d", CONFIG_NSS_GPIO);
ESP_LOGI(TAG, "CONFIG_RST_GPIO=%d", CONFIG_RST_GPIO);
ESP_LOGI(TAG, "CONFIG_BUSY_GPIO=%d", CONFIG_BUSY_GPIO);
ESP_LOGI(TAG, "CONFIG_TXEN_GPIO=%d", CONFIG_TXEN_GPIO);
ESP_LOGI(TAG, "CONFIG_RXEN_GPIO=%d", CONFIG_RXEN_GPIO);
ESP_LOGI(TAG, "HSPI_MISO_GPIO=%d", HSPI_MISO_GPIO);
ESP_LOGI(TAG, "HSPI_MOSI_GPIO=%d", HSPI_MOSI_GPIO);
ESP_LOGI(TAG, "HSPI_SCK_GPIO=%d", HSPI_SCK_GPIO);
ESP_LOGI(TAG, "HSPI_LORA_CS=%d", HSPI_LORA_CS);
ESP_LOGI(TAG, "LORA_BUSY=%d", LORA_BUSY);
SX126x_SPI_SELECT = CONFIG_NSS_GPIO;
SX126x_RESET = CONFIG_RST_GPIO;
SX126x_BUSY = CONFIG_BUSY_GPIO;
SX126x_TXEN = CONFIG_TXEN_GPIO;
SX126x_RXEN = CONFIG_RXEN_GPIO;
SX126x_SPI_SELECT = HSPI_LORA_CS;
SX126x_BUSY = LORA_BUSY;
txActive = false;
debugPrint = false;
@@ -77,39 +66,22 @@ void LoRaInit(void)
gpio_set_direction(SX126x_SPI_SELECT, GPIO_MODE_OUTPUT);
gpio_set_level(SX126x_SPI_SELECT, 1);
gpio_reset_pin(SX126x_RESET);
gpio_set_direction(SX126x_RESET, GPIO_MODE_OUTPUT);
gpio_reset_pin(SX126x_BUSY);
gpio_set_direction(SX126x_BUSY, GPIO_MODE_INPUT);
if (SX126x_TXEN != -1) {
gpio_reset_pin(SX126x_TXEN);
gpio_set_direction(SX126x_TXEN, GPIO_MODE_OUTPUT);
}
if (SX126x_RXEN != -1) {
gpio_reset_pin(SX126x_RXEN);
gpio_set_direction(SX126x_RXEN, GPIO_MODE_OUTPUT);
}
spi_bus_config_t spi_bus_config = {
.sclk_io_num = CONFIG_SCLK_GPIO,
.mosi_io_num = CONFIG_MOSI_GPIO,
.miso_io_num = CONFIG_MISO_GPIO,
.sclk_io_num = HSPI_SCK_GPIO,
.mosi_io_num = HSPI_MOSI_GPIO,
.miso_io_num = HSPI_MISO_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1
};
esp_err_t ret;
ret = spi_bus_initialize( HOST_ID, &spi_bus_config, SPI_DMA_CH_AUTO );
ESP_LOGI(TAG, "spi_bus_initialize=%d",ret);
assert(ret==ESP_OK);
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 9000000,
.mode = 0,
.spics_io_num = CONFIG_NSS_GPIO,
.spics_io_num = HSPI_LORA_CS,
.queue_size = 7,
.flags = 0,
.pre_cb = NULL
@@ -290,6 +262,8 @@ void LoRaConfig(uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate,
SX126X_IRQ_NONE //interrupts on DIO3
);
ESP_LOGI(TAG, "Almost done setting LoRa");
// Receive state no receive timeoout
SetRx(0xFFFFFF);
}
@@ -417,9 +391,9 @@ void SetTxPower(int8_t txPowerInDbm)
void Reset(void)
{
delay(10);
gpio_set_level(SX126x_RESET,0);
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_LORA_RST, 0);
delay(20);
gpio_set_level(SX126x_RESET,1);
mcp23018_set_pin(MCP23018_DEV_HANDLE, MCP_LORA_RST, 1);
delay(10);
// ensure BUSY is low (state meachine ready)
WaitForIdle(BUSY_WAIT, "Reset", true);
@@ -705,7 +679,6 @@ void SetRx(uint32_t timeout)
ESP_LOGI(TAG, "----- SetRx timeout=%"PRIu32, timeout);
}
SetStandby(SX126X_STANDBY_RC);
SetRxEnable();
uint8_t buf[3];
buf[0] = (uint8_t)((timeout >> 16) & 0xFF);
buf[1] = (uint8_t)((timeout >> 8) & 0xFF);
@@ -723,25 +696,12 @@ void SetRx(uint32_t timeout)
}
void SetRxEnable(void)
{
if (debugPrint) {
ESP_LOGI(TAG, "SetRxEnable:SX126x_TXEN=%d SX126x_RXEN=%d", SX126x_TXEN, SX126x_RXEN);
}
if ((SX126x_TXEN != -1) && (SX126x_RXEN != -1)) {
gpio_set_level(SX126x_RXEN, HIGH);
gpio_set_level(SX126x_TXEN, LOW);
}
}
void SetTx(uint32_t timeoutInMs)
{
if (debugPrint) {
ESP_LOGI(TAG, "----- SetTx timeoutInMs=%"PRIu32, timeoutInMs);
}
SetStandby(SX126X_STANDBY_RC);
SetTxEnable();
uint8_t buf[3];
uint32_t tout = timeoutInMs;
if (timeoutInMs != 0) {
@@ -766,19 +726,6 @@ void SetTx(uint32_t timeoutInMs)
}
}
void SetTxEnable(void)
{
if (debugPrint) {
ESP_LOGI(TAG, "SetTxEnable:SX126x_TXEN=%d SX126x_RXEN=%d", SX126x_TXEN, SX126x_RXEN);
}
if ((SX126x_TXEN != -1) && (SX126x_RXEN != -1)){
gpio_set_level(SX126x_RXEN, LOW);
gpio_set_level(SX126x_TXEN, HIGH);
}
}
int GetPacketLost()
{
return txLost;

2
components/ra01s/ra01s.h → main/hw/sx1262.h Executable file → Normal file
View File

@@ -412,8 +412,6 @@ void SetCad();
uint8_t GetStatus(void);
uint16_t GetIrqStatus(void);
void ClearIrqStatus(uint16_t irq);
void SetTxEnable(void);
void SetRxEnable(void);
void SetRx(uint32_t timeout);
void SetTx(uint32_t timeoutInMs);
int GetPacketLost();

View File

@@ -15,6 +15,7 @@
#include "esp_mac.h"
#include <string.h>
#include "components/radio.h"
#include "components/sensors.h"
#include "components/util.h"
#include "hw/bme680b.h"
@@ -23,31 +24,79 @@
#include "hw/ina260.h"
#include "hw/mcp23018.h"
#include "hw/mpu9250.h"
#include "hw/buscfg.h"
#include "components/sdcard.h"
#include "hw/gps.h"
#define TAG "cantest"
#define CONFIG_FREERTOS_HZ 100
void app_main(void)
{
ESP_LOGI("BOOT", "BRN Systems incorporated CanSat flight firmware build at %s %s", __DATE__, __TIME__);
/* instantiate i2c master bus 0 */
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c0_bus_cfg, &i2c0_bus_hdl));
ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_new_master_bus(&i2c0_bus_cfg, &i2c0_bus_hdl));
// spi_bus_config_t MCPBusCfg = {
// .mosi_io_num = -1,
// .miso_io_num = MCP3550_MISO_GPIO,
// .sclk_io_num = MCP3550_SCK_GPIO,
// .quadwp_io_num = -1,
// .quadhd_io_num = -1,
// .max_transfer_sz = 4,
// };
// ESP_ERROR_CHECK_WITHOUT_ABORT(spi_bus_initialize(SPI2_HOST, &MCPBusCfg, SPI_DMA_DISABLED));
gpio_pullup_en(HSPI_MISO_GPIO);
gpio_pullup_en(HSPI_SD_CS);
spi_bus_config_t HighSpeedBusCfg = {
// CONNECTED to LoRa and SD card
.mosi_io_num = HSPI_MOSI_GPIO,
.miso_io_num = HSPI_MISO_GPIO,
.sclk_io_num = HSPI_SCK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 64, // probably change
};
ESP_ERROR_CHECK_WITHOUT_ABORT(spi_bus_initialize(SPI3_HOST, &HighSpeedBusCfg, SPI_DMA_CH_AUTO));
/* scan i2c devices on i2c master bus 0 and print results */
ESP_ERROR_CHECK(i2c_master_bus_detect_devices(i2c0_bus_hdl));
ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_bus_detect_devices(i2c0_bus_hdl));
mcp23018_init();
initSD();
void servoControllerInit();
ESP_LOGI(TAG, "BEGIN ESP TASKS");
/* create task pinned to the app core */
xTaskCreatePinnedToCore(
xTaskCreate(
i2c_sensors_task,
"I2CTaskBME",
8192,
NULL,
(tskIDLE_PRIORITY + 2),
NULL);
xTaskCreate(
lora_comms_task,
"LoraCommsTask",
8192,
NULL,
APP_CPU_NUM);
(tskIDLE_PRIORITY + 2),
NULL);
xTaskCreate(
gps_task,
"gps_task",
8192,
NULL,
(tskIDLE_PRIORITY + 2),
NULL);
while (1)
{

View File

@@ -426,39 +426,39 @@ static inline esp_err_t bme680_get_cal_factors(bme680_handle_t handle) {
ESP_ARG_CHECK( handle );
/* bme680 attempt to request T1-T3 calibration values from device */
ESP_ERROR_CHECK( bme680_i2c_read_word_from(handle, 0xe9, &handle->dev_cal_factors->par_T1) );
ESP_ERROR_CHECK( bme680_i2c_read_word_from(handle, 0x8a, (uint16_t *)&handle->dev_cal_factors->par_T2) );
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0x8c, (uint8_t *)&handle->dev_cal_factors->par_T3) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_word_from(handle, 0xe9, &handle->dev_cal_factors->par_T1) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_word_from(handle, 0x8a, (uint16_t *)&handle->dev_cal_factors->par_T2) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0x8c, (uint8_t *)&handle->dev_cal_factors->par_T3) );
/* bme680 attempt to request H1-H7 calibration values from device */
ESP_ERROR_CHECK( bme680_i2c_read_from(handle, 0xe2, rx, BIT16_UINT8_BUFFER_SIZE) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_from(handle, 0xe2, rx, BIT16_UINT8_BUFFER_SIZE) );
handle->dev_cal_factors->par_H1 = (uint16_t)(((uint16_t)rx[1] << 4) | (rx[0] & 0x0F));
ESP_ERROR_CHECK( bme680_i2c_read_from(handle, 0xe1, rx, BIT16_UINT8_BUFFER_SIZE) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_from(handle, 0xe1, rx, BIT16_UINT8_BUFFER_SIZE) );
handle->dev_cal_factors->par_H2 = (uint16_t)(((uint16_t)rx[0] << 4) | (rx[1] >> 4));
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0xe4, (uint8_t *)&handle->dev_cal_factors->par_H3) );
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0xe5, (uint8_t *)&handle->dev_cal_factors->par_H4) );
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0xe6, (uint8_t *)&handle->dev_cal_factors->par_H5) );
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0xe7, &handle->dev_cal_factors->par_H6) );
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0xe8, (uint8_t *)&handle->dev_cal_factors->par_H7) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0xe4, (uint8_t *)&handle->dev_cal_factors->par_H3) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0xe5, (uint8_t *)&handle->dev_cal_factors->par_H4) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0xe6, (uint8_t *)&handle->dev_cal_factors->par_H5) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0xe7, &handle->dev_cal_factors->par_H6) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0xe8, (uint8_t *)&handle->dev_cal_factors->par_H7) );
/* bme680 attempt to request P1-P10 calibration values from device */
ESP_ERROR_CHECK( bme680_i2c_read_word_from(handle, 0x8e, &handle->dev_cal_factors->par_P1) );
ESP_ERROR_CHECK( bme680_i2c_read_word_from(handle, 0x90, (uint16_t *)&handle->dev_cal_factors->par_P2) );
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0x92, (uint8_t *)&handle->dev_cal_factors->par_P3) );
ESP_ERROR_CHECK( bme680_i2c_read_word_from(handle, 0x94, (uint16_t *)&handle->dev_cal_factors->par_P4) );
ESP_ERROR_CHECK( bme680_i2c_read_word_from(handle, 0x96, (uint16_t *)&handle->dev_cal_factors->par_P5) );
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0x99, (uint8_t *)&handle->dev_cal_factors->par_P6) );
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0x98, (uint8_t *)&handle->dev_cal_factors->par_P7) );
ESP_ERROR_CHECK( bme680_i2c_read_word_from(handle, 0x9c, (uint16_t *)&handle->dev_cal_factors->par_P8) );
ESP_ERROR_CHECK( bme680_i2c_read_word_from(handle, 0x9e, (uint16_t *)&handle->dev_cal_factors->par_P9) );
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0xa0, &handle->dev_cal_factors->par_P10) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_word_from(handle, 0x8e, &handle->dev_cal_factors->par_P1) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_word_from(handle, 0x90, (uint16_t *)&handle->dev_cal_factors->par_P2) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0x92, (uint8_t *)&handle->dev_cal_factors->par_P3) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_word_from(handle, 0x94, (uint16_t *)&handle->dev_cal_factors->par_P4) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_word_from(handle, 0x96, (uint16_t *)&handle->dev_cal_factors->par_P5) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0x99, (uint8_t *)&handle->dev_cal_factors->par_P6) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0x98, (uint8_t *)&handle->dev_cal_factors->par_P7) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_word_from(handle, 0x9c, (uint16_t *)&handle->dev_cal_factors->par_P8) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_word_from(handle, 0x9e, (uint16_t *)&handle->dev_cal_factors->par_P9) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0xa0, &handle->dev_cal_factors->par_P10) );
/* bme680 attempt to request G1-G3 calibration values from device */
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0xed, (uint8_t *)&handle->dev_cal_factors->par_G1) );
ESP_ERROR_CHECK( bme680_i2c_read_word_from(handle, 0xeb, (uint16_t *)&handle->dev_cal_factors->par_G2) );
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0xee, (uint8_t *)&handle->dev_cal_factors->par_G3) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0xed, (uint8_t *)&handle->dev_cal_factors->par_G1) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_word_from(handle, 0xeb, (uint16_t *)&handle->dev_cal_factors->par_G2) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0xee, (uint8_t *)&handle->dev_cal_factors->par_G3) );
/* bme680 attempt to request gas range and switching error values from device */
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0x02, &handle->dev_cal_factors->res_heat_range) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0x02, &handle->dev_cal_factors->res_heat_range) );
handle->dev_cal_factors->res_heat_range = (handle->dev_cal_factors->res_heat_range & 0x30) / 16;
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0x00, (uint8_t *)&handle->dev_cal_factors->res_heat_val) );
ESP_ERROR_CHECK( bme680_i2c_read_byte_from(handle, 0x04, (uint8_t *)&handle->dev_cal_factors->range_switching_error) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0x00, (uint8_t *)&handle->dev_cal_factors->res_heat_val) );
ESP_ERROR_CHECK_WITHOUT_ABORT( bme680_i2c_read_byte_from(handle, 0x04, (uint8_t *)&handle->dev_cal_factors->range_switching_error) );
handle->dev_cal_factors->range_switching_error = (handle->dev_cal_factors->range_switching_error & 0xf0) / 16;
/*
@@ -1091,6 +1091,8 @@ esp_err_t bme680_get_data(bme680_handle_t handle, bme680_data_t *const data) {
data->heater_stable = adc_data.heater_stable;
data->gas_valid = adc_data.gas_valid;
data->raw_data = adc_data;
/* compute scores */
bme680_compute_iaq(data);

View File

@@ -308,6 +308,22 @@ typedef struct bme680_cal_factors_s {
int8_t range_switching_error;
} bme680_cal_factors_t;
/**
* @brief BME680 ADC data structure definition.
*/
typedef struct bme680_adc_data_s {
uint32_t temperature;
uint16_t humidity;
uint32_t pressure;
uint16_t gas;
bool gas_valid;
uint8_t gas_range;
uint8_t gas_index;
bool heater_stable;
} bme680_adc_data_t;
/**
* @brief BME680 data structure definition.
*/
@@ -325,22 +341,9 @@ typedef struct bme680_data_s {
float temperature_score;
float humidity_score;
float gas_score;
bme680_adc_data_t raw_data;
} bme680_data_t;
/**
* @brief BME680 ADC data structure definition.
*/
typedef struct bme680_adc_data_s {
uint32_t temperature;
uint16_t humidity;
uint32_t pressure;
uint16_t gas;
bool gas_valid;
uint8_t gas_range;
uint8_t gas_index;
bool heater_stable;
} bme680_adc_data_t;
/**
* @brief BME680 configuration structure definition.
*/