Update documentation and do some basic housekeeping

This commit is contained in:
2024-09-25 17:16:20 +02:00
parent c8854931e8
commit aa0cd89d4e
20 changed files with 167 additions and 57 deletions

12
.gitmodules vendored
View File

@@ -1,7 +1,7 @@
[submodule "opt/tools"]
path = opt/tools
url = git@github.com:madcow/drybox-tools.git
[submodule "tools"]
path = opt/tools
url = git@github.com:madcow/drybox-tools.git
[submodule "opt/webgui"]
path = opt/webgui
url = git@github.com:madcow/drybox-webgui.git
[submodule "webgui"]
path = opt/webgui
url = git@github.com:madcow/drybox-webgui.git

View File

@@ -62,7 +62,7 @@ flash: $(TARGET)
-c $(ASP) -p $(ARCH) \
-U lfuse:w:0xff:m \
-U hfuse:w:0x91:m \
-U flash:w:$(TARGET)
-U flash:w:$<
.PHONY: clean
clean:
@@ -85,6 +85,11 @@ listen: ./opt/tools/serial-listen.py
$(E) "[PY3] $<"
$(Q) ./$<
.PHONY: listen-web
listen-web: ./opt/webgui/Makefile
$(E) "[MAK] $(<D)"
$(Q) $(MAKE) -sC $(<D)
$(TMPDIRS):
$(E) "[DIR] $@"
$(Q) $(MKDIR) $@

73
docs/CHANGELOG.md Normal file
View File

@@ -0,0 +1,73 @@
## Changelog
### v0.75-alpha1
- Fix parser tail pointer semantics.
- Improve sensor log readability and minor naming fixes.
- Add multiple README sections and use markdown formatting.
- Reduce RXBUF size to match parser and space out log messages.
- Prevent redundant writes and start implementing parser timeout.
- Fix large serial command input triggering watchdog.
- Merge pull request #5 from madcow/parser.
- Implement serial command parser with floating point support.
- Merge pull request #4 from madcow/eeprom-sentinel-v2.
- Use reference sentinel to efficiently reset dirty flags on rollover.
- Merge pull request #3 from madcow/eeprom-testing.
- Pass variable structure as argument for memory functions.
- Fix possible race condition and ensure proper reset flag detection.
- Check MCUCSR for WDRF flag only and add TODO.
- Implement memory wear leveling algorithm.
- Define memory manager constants and add TODO.
- Merge pull request #2 from madcow/eeprom.
- Dump EEPROM to ensure low level memory access is working correctly.
- Implement raw EEPROM read and write functionality.
- Burn EESAVE fuse to preserve EEPROM on chip erase.
- Define basic EEPROM data structures and functions.
- Merge pull request #1 from madcow/thermistor.
- Implement alternate voltage divider equation.
- Calculate average dewpoint from multiple sensor measurements.
- Test different approach for resistance calculation.
- Ensure math functions are named consistently.
- Make sure dewpoint calculations are correct.
- Split thermistor functions into separate logical units and add TODO.
- Implement all required conversion functions.
- Define conversion functions for TD, T and RH.
- Update planned FREQ, ARCH, MCU and fix superfluous whitespace.
- Define basic structure for primary state machine and update TODOs.
- Remove executable bit from pdf files.
- Add data sheet for ATMEGA1284P-PU.
- Poll ADC mode bit until conversion has finished.
- Set FAN03 to maximum duty and declutter log output.
- Calculate temperature from thermistor resistance.
- Set ADC gain to 6.144V and fix multiplexer channel documentation.
- Define constants for ADC multiplexer settings and describe I2C status flags.
- Define constants for watchdog timeout flags.
- Initialize OCR1A to FAN02\_MIN\_DUTY and add PWM documentation.
- Minor coding style and consistency fixes.
- Jump to reset vector when program exceeds specified watchdog timeout.
- Start implementing watchdog timer.
- Make log output slightly more readable.
- Implement ADS1115 register reading and writing.
- Try setting variable PWM frequency with timer2.
- Set pin for light MOSFET to low in MOS\_Init.
- Remove invisible runtime assertions.
- Rename module TWI to I2C and add comments.
- Read and translate AHT20 sensor readings.
- Move PWM and MOSFET implementations into separate module files.
- Ensure correct naming for PWM devices.
- Calculate PWM\_CYCLE\_TOP from F\_CPU.
- Add more general SetMosState() and SetPwmValue() functions.
- Configure PWM timers for 25 KHz.
- Handle PWM on pin PD4, PD5 and PD7.
- Start working on system initialization.
- Add data sheets for ADS1115 and AHT20.
- Handle I2C multiplexer channel selection.
- Implement USART with bot RX and TX interrupts.
- Rewrite logging system and sync state with clients.
- Handle numeric arguments for TEMP/DEWP commands.
- Update documentation and add USART specification.
- Start implementing proper parser state machine.
- Add optional project submodules 'tools' and 'webgui'.
- Implement ring buffer and auxiliary functions.
- Add swapfiles and bin/ to .gitignore.
- Handle USART\_RXC interrupt.
- Implement basic UART reading.

View File

@@ -3,7 +3,7 @@
### Prerequisites
You must have avr-gcc and avrdude installed. To install both
tools on a debian-based system, please run the following
tools on a Debian-based system, please run the following
command:
apt install avr-gcc avrdude
@@ -12,35 +12,51 @@ The names for these dependencies may differ for other package
managers and distributions. Please check the package sources
on your system for more information.
You will also need an in-circuit programmer for Atmel AVR
controllers like the [USBasp](https://www.fischl.de/usbasp).
If you want to send commands and receive debug output you
must make sure to have a serial interface available at
`/dev/ttyUSB0`.
### Build and Install
Run this next command in the project root directory to
build and install the project using an USBasp programmer:
build and install the project:
make all
If there are any errors during the flashing process you
should find more information in core.log in the bin/
directory. See the Makefile for different settings for
your specific build environment. To remove all build
related auxiliary files you may run these commands:
your specific build environment.
To remove all build related auxiliary files you may run
these commands:
make clean
make distclean
You can listen on the serial debug interface using the
command below (if you have initialized the optional
submodules as described in the next section):
submodules as described in the 'Setting a New Target'
section):
make listen
### Temperature and Dewpoint Targets
Alternatively you can spin up a web server on port 443
to interface with the device using the following command.
Make sure to read section 'Webinterface Example' for more
information.
You can use the script below to send serial commands (you
may need to run `git submodule update --init` if the tools
make listen-web
### Setting a New Target
The script below allows you to send serial commands (you
may need to run `git submodule update --init` if the tools/
directory is empty):
./opt/tools/serial-send.py <command> <args, ...>
./opt/tools/serial-send.py <command> <args ...>
Issue the SET command to update the target settings:
@@ -50,3 +66,19 @@ Settings are stored in EEPROM and are persistent until a new
SET command is issued. Keywords are not case-sensitive. Both
arguments may contain a decimal point followed by a sequence
of digits.
### Webinterface Example
There is an example Python web server utilizing websockets you
can check out in `opt/webgui`. It should give you a basis for
writing a custom network bridge and integrating the device into
your home automation system.
You must generate a self-signed certificate before you run the
server. The configuration script will take care of it:
./opt/webgui/configure
See the README file in the webgui/ directory for prerequisites
and more information. Please be aware that development for the
web server has paused until the core has reached version v1.0.

View File

@@ -12,13 +12,13 @@
#define UNUSED(s) (void)(s)
#define BIT(n) (0x1U << (n))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define CLAMP(n, hi, lo) (MIN((hi), MAX((n), (lo))))
void Info(const char *fmt, ...);
void Error(const char *fmt, ...);
void Print(const char *fmt, ...);
void Info(const char *fmt, ...);
void Error(const char *fmt, ...);
void Print(const char *fmt, ...);
#include <util/delay.h>
#define Sleep(ms) _delay_ms(ms)

View File

@@ -62,19 +62,19 @@ void MEM_Write(mem_block_t *in)
WriteBlock(head, in);
}
int MEM_Read(mem_block_t *out)
bool MEM_Read(mem_block_t *out)
{
int head;
head = GetUsedBlock();
if (head < 0) { // Empty?
return -1;
return false;
}
ReadBlock(head, out);
return 0;
return true;
}
void MEM_Free(void)

View File

@@ -1,6 +1,8 @@
#ifndef MAD_CORE_COMMON_MEMORY_H
#define MAD_CORE_COMMON_MEMORY_H
#include "common/types.h"
typedef struct mem_block_s mem_block_t;
// Important: Must reset EEPROM memory when the size of
@@ -13,7 +15,7 @@ struct mem_block_s {
} __attribute__((packed));
void MEM_Write(mem_block_t *in);
int MEM_Read(mem_block_t *out);
bool MEM_Read(mem_block_t *out);
void MEM_Free(void);
void MEM_Dump(void);

View File

@@ -76,17 +76,11 @@ static int Init(void)
WDT_Enable();
WDT_SetTimeoutFlag(WDT2000); // 2 seconds
// Test persistent settings from EEPROM. There must
// be some sanity checking before these values are
// used.
// See if there are persistent target settings
// stored in EEPROM and load them. Some sanity
// checking must be done before using those.
// mem.temp = 20.50f;
// mem.dewp = 10.25f;
// MEM_Write(&mem);
// MEM_Dump();
// MEM_Free();
if (MEM_Read(&mem) == 0) {
if (MEM_Read(&mem)) { // Any valid blocks found?
Info("Found persistent configuration in EEPROM!");
Info("Using targets TEMP=%.2fC, DEWP=%.2fC.",
mem.temp, mem.dewp);
@@ -95,6 +89,12 @@ static int Init(void)
dewp_target = mem.dewp;
}
// mem.temp = 20.50f;
// mem.dewp = 10.25f;
// MEM_Write(&mem);
// MEM_Dump();
// MEM_Free();
// There is a possiblity to use interrupt signals
// for I2C communication but only as one large
// branching routine for the whole I2C system.
@@ -103,9 +103,9 @@ static int Init(void)
I2C_Init();
PWM_Init();
MOS_Enable(MOS03); // Lights
// MOS_Enable(MOS01); // Peltier
// MOS_Disable(MOS02); // Heating
MOS_Enable(MOS03); // Lights
// MOS_Enable(MOS01); // Peltier
// MOS_Disable(MOS02); // Heating
// Only FAN01 and FAN02 are receiving the correct
// frequency (25 KHz) right now. The 16-bit timer on
@@ -117,9 +117,9 @@ static int Init(void)
// complicated so it might be worth it to switch to
// something like an ATmega328PB.
PWM_SetValue(FAN01, 50); // Fan Peltier Hot side
PWM_SetValue(FAN02, 50); // Fan Peltier Cold Side
// PWM_SetValue(FAN03, 20); // Fan Heating
PWM_SetValue(FAN01, 50); // Fan Peltier Hot side
PWM_SetValue(FAN02, 50); // Fan Peltier Cold Side
// PWM_SetValue(FAN03, 20); // Fan Heating
// The I2C_SetChannel command changes the channel
// setting of the PCA9546 I2C multiplexer. Any
@@ -156,7 +156,7 @@ static void Update(void)
}
}
// Get sensor values
// Get latest sensor values
FetchSensorValues();
// Handle state
@@ -179,23 +179,25 @@ static void SetTarget(float t, float td)
Print("\r\n");
Info("Updating target configuration:");
Info("Setting temperature to %.2fC.", t);
Info("Setting new dewpoint to %.2fC.", td);
Info("Setting dewpoint to %.2fC.", td);
if (MEM_Read(&mem) == 0) {
// Even with level wearing there is a finite number
// of EEPROM write cycles so we should always check
// for redundant values. This could be handled by
// the underlying memory implementation.
if (MEM_Read(&mem)) {
if (t == mem.temp && td == mem.dewp) {
return; // Nothing to do
}
}
mem.temp = t;
mem.dewp = td;
// Update current targets
mem.temp = temp_target = t;
mem.dewp = dewp_target = td;
// Keep in EEPROM
// Store in EEPROM
MEM_Write(&mem);
// Update state
temp_target = t;
dewp_target = td;
}
static void FetchSensorValues(void)
@@ -204,23 +206,19 @@ static void FetchSensorValues(void)
float t[6], rh[3], td[3];
Print("\r\n");
Info("Reading sensor values...");
Info("Fetching sensor values...");
I2C_SetChannel(AHT01);
I2C_AHT20_Read(&t[0], &rh[0]);
I2C_SetChannel(AHT02);
I2C_AHT20_Read(&t[1], &rh[1]);
I2C_SetChannel(AHT03);
I2C_AHT20_Read(&t[2], &rh[2]);
raw = I2C_ADS1115_ReadRaw(ADS01);
t[3] = SteinhartHart(Resistance(raw));
raw = I2C_ADS1115_ReadRaw(ADS02);
t[4] = SteinhartHart(Resistance(raw));
raw = I2C_ADS1115_ReadRaw(ADS03);
t[5] = SteinhartHart(Resistance(raw));
@@ -230,7 +228,7 @@ static void FetchSensorValues(void)
temp = (t[0] + t[1] + t[2]) / 3;
rhum = (rh[0] + rh[1] + rh[2]) / 3;
dewp = Dewpoint(temp, rhum);
dewp = (td[0] + td[1] + td[2]) / 3;
Info("T1=%.2fC, TD1=%.2fC, RH1=%.2f%%, NT1=%.2fC", t[0], td[0], rh[0], t[3]);
Info("T2=%.2fC, TD2=%.2fC, RH2=%.2f%%, NT2=%.2fC", t[1], td[1], rh[1], t[4]);