Commit 070fc42ad57d23ab87979463af5665c7ba93d1b4
receivedSat, 3. Aug 2024, 11:22:30 (by user sx)
Sat, 3 Aug 2024 09:22:30 +0000 (11:22 +0200)
authorManfred Steiner <sx@htl-kaindorf.at>
Sat, 3 Aug 2024 09:22:22 +0000 (11:22 +0200)
committerManfred Steiner <sx@htl-kaindorf.at>
Sat, 3 Aug 2024 09:22:22 +0000 (11:22 +0200)
126 files changed:
software/arduino-nano-5v/test_2024-07-23/.gdb_history [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/.gdbinit [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/.gitignore [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/.vscode/c_cpp_properties.json [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/.vscode/launch.json [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/.vscode/settings.json [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/.vscode/tasks.json [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/Makefile [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/README.md [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/adafruit/bme280.cpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/adafruit/bme280.h [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/adafruit/ens160.cpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/adafruit/ens160.h [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/adafruit/sensor.h [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/i2cmaster.cpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/i2cmaster.hpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/i2cslave.cpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/i2cslave.hpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/main.cpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/main.hpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/units/encoder.cpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/units/encoder.hpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/units/i2c.cpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/units/i2c.hpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/units/ieee485.cpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/units/ieee485.hpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/units/lcd.cpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/units/lcd.hpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/units/led.cpp [new file with mode: 0644]
software/arduino-nano-5v/test_2024-07-23/src/units/led.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/.gdb_history [new file with mode: 0644]
software/nano-644/test_2024-07-23/.gdbinit [new file with mode: 0644]
software/nano-644/test_2024-07-23/.gitignore [new file with mode: 0644]
software/nano-644/test_2024-07-23/.vscode/c_cpp_properties.json [new file with mode: 0644]
software/nano-644/test_2024-07-23/.vscode/launch.json [new file with mode: 0644]
software/nano-644/test_2024-07-23/.vscode/settings.json [new file with mode: 0644]
software/nano-644/test_2024-07-23/.vscode/tasks.json [new file with mode: 0644]
software/nano-644/test_2024-07-23/Makefile [new file with mode: 0644]
software/nano-644/test_2024-07-23/README.md [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/adafruit/bme280.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/adafruit/bme280.h [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/adafruit/ens160.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/adafruit/ens160.h [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/adafruit/sensor.h [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/i2cmaster.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/i2cmaster.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/i2cslave.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/i2cslave.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/main.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/main.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/encoder.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/encoder.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/i2c.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/i2c.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/ieee485.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/ieee485.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/lcd.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/lcd.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/led.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/led.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/modbus.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/modbus.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/motor.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/motor.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/portexp.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/portexp.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/poti.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/poti.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/r2r.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/r2r.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/rgb.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/rgb.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/seg7.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/seg7.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/switch.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/switch.hpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/uart1.cpp [new file with mode: 0644]
software/nano-644/test_2024-07-23/src/units/uart1.hpp [new file with mode: 0644]
software/test_2024-07-23/.gdb_history [deleted file]
software/test_2024-07-23/.gdbinit [deleted file]
software/test_2024-07-23/.gitignore [deleted file]
software/test_2024-07-23/.vscode/c_cpp_properties.json [deleted file]
software/test_2024-07-23/.vscode/launch.json [deleted file]
software/test_2024-07-23/.vscode/settings.json [deleted file]
software/test_2024-07-23/.vscode/tasks.json [deleted file]
software/test_2024-07-23/Makefile [deleted file]
software/test_2024-07-23/README.md [deleted file]
software/test_2024-07-23/src/adafruit/bme280.cpp [deleted file]
software/test_2024-07-23/src/adafruit/bme280.h [deleted file]
software/test_2024-07-23/src/adafruit/ens160.cpp [deleted file]
software/test_2024-07-23/src/adafruit/ens160.h [deleted file]
software/test_2024-07-23/src/adafruit/sensor.h [deleted file]
software/test_2024-07-23/src/i2cmaster.cpp [deleted file]
software/test_2024-07-23/src/i2cmaster.hpp [deleted file]
software/test_2024-07-23/src/i2cslave.cpp [deleted file]
software/test_2024-07-23/src/i2cslave.hpp [deleted file]
software/test_2024-07-23/src/main.cpp [deleted file]
software/test_2024-07-23/src/main.hpp [deleted file]
software/test_2024-07-23/src/units/encoder.cpp [deleted file]
software/test_2024-07-23/src/units/encoder.hpp [deleted file]
software/test_2024-07-23/src/units/i2c.cpp [deleted file]
software/test_2024-07-23/src/units/i2c.hpp [deleted file]
software/test_2024-07-23/src/units/ieee485.cpp [deleted file]
software/test_2024-07-23/src/units/ieee485.hpp [deleted file]
software/test_2024-07-23/src/units/lcd.cpp [deleted file]
software/test_2024-07-23/src/units/lcd.hpp [deleted file]
software/test_2024-07-23/src/units/led.cpp [deleted file]
software/test_2024-07-23/src/units/led.hpp [deleted file]
software/test_2024-07-23/src/units/modbus.cpp [deleted file]
software/test_2024-07-23/src/units/modbus.hpp [deleted file]
software/test_2024-07-23/src/units/motor.cpp [deleted file]
software/test_2024-07-23/src/units/motor.hpp [deleted file]
software/test_2024-07-23/src/units/portexp.cpp [deleted file]
software/test_2024-07-23/src/units/portexp.hpp [deleted file]
software/test_2024-07-23/src/units/poti.cpp [deleted file]
software/test_2024-07-23/src/units/poti.hpp [deleted file]
software/test_2024-07-23/src/units/r2r.cpp [deleted file]
software/test_2024-07-23/src/units/r2r.hpp [deleted file]
software/test_2024-07-23/src/units/rgb.cpp [deleted file]
software/test_2024-07-23/src/units/rgb.hpp [deleted file]
software/test_2024-07-23/src/units/seg7.cpp [deleted file]
software/test_2024-07-23/src/units/seg7.hpp [deleted file]
software/test_2024-07-23/src/units/switch.cpp [deleted file]
software/test_2024-07-23/src/units/switch.hpp [deleted file]
software/test_2024-07-23/src/units/uart1.cpp [deleted file]
software/test_2024-07-23/src/units/uart1.hpp [deleted file]

diff --git a/software/arduino-nano-5v/test_2024-07-23/.gdb_history b/software/arduino-nano-5v/test_2024-07-23/.gdb_history
new file mode 100644 (file)
index 0000000..3339046
--- /dev/null
@@ -0,0 +1,9 @@
+target remote :1234
+layout split
+stepi
+quit
+target remote :1234
+layout split
+stepi
+b *main+9
+quit
diff --git a/software/arduino-nano-5v/test_2024-07-23/.gdbinit b/software/arduino-nano-5v/test_2024-07-23/.gdbinit
new file mode 100644 (file)
index 0000000..139597f
--- /dev/null
@@ -0,0 +1,2 @@
+
+
diff --git a/software/arduino-nano-5v/test_2024-07-23/.gitignore b/software/arduino-nano-5v/test_2024-07-23/.gitignore
new file mode 100644 (file)
index 0000000..a959910
--- /dev/null
@@ -0,0 +1,4 @@
+.depend
+**/build
+**/dist
+**/sim
diff --git a/software/arduino-nano-5v/test_2024-07-23/.vscode/c_cpp_properties.json b/software/arduino-nano-5v/test_2024-07-23/.vscode/c_cpp_properties.json
new file mode 100644 (file)
index 0000000..93c17af
--- /dev/null
@@ -0,0 +1,18 @@
+{
+    "configurations": [
+        {
+            "name": "Linux AVR",
+            "includePath": [
+                "/usr/lib/avr/include/**",
+                "/usr/lib/gcc/avr/**"
+            ],
+            "defines": [],
+            "compilerPath": "/usr/bin/avr-gcc",
+            "compilerArgs": [ "-mmcu=atmega328p", "-DF_CPU=16000000", "-Os" ],
+            "cStandard": "gnu11",
+            "cppStandard": "gnu++11",
+            "intelliSenseMode": "linux-gcc-x64"
+        }
+    ],
+    "version": 4
+}
diff --git a/software/arduino-nano-5v/test_2024-07-23/.vscode/launch.json b/software/arduino-nano-5v/test_2024-07-23/.vscode/launch.json
new file mode 100644 (file)
index 0000000..f29cf2e
--- /dev/null
@@ -0,0 +1,37 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Build",
+            // "request": "launch",
+            "type": "node-terminal",
+            "preLaunchTask": "build"
+        },{
+            "name": "Flash",
+            // "request": "launch",
+            "type": "node-terminal",
+            "preLaunchTask": "flash"
+        },{
+            "name": "Clean",
+            // "request": "launch",
+            "type": "node-terminal",
+            "preLaunchTask": "clean"
+        },{
+            // es muss mit simuc --board arduino dist/programm.elf der Simulator 
+            // gestartet werden. Dessen gdb-stub Ã¶ffnet auf localhost:1234 einen Port
+            "name": "Debug (simuc)",
+            "type": "cppdbg",
+            "request": "launch",
+            "program": "${workspaceFolder}/sim/atmega328p.elf",
+            "cwd": "${workspaceFolder}",
+            "externalConsole": false,
+            "MIMode": "gdb",
+            "miDebuggerPath": "/usr/bin/avr-gdb",
+            "miDebuggerServerAddress": ":1234",
+            "preLaunchTask": "build"
+        }
+    ]
+}
diff --git a/software/arduino-nano-5v/test_2024-07-23/.vscode/settings.json b/software/arduino-nano-5v/test_2024-07-23/.vscode/settings.json
new file mode 100644 (file)
index 0000000..b2e94c9
--- /dev/null
@@ -0,0 +1,26 @@
+{
+    "[c]": {
+        "editor.insertSpaces": true,
+        "editor.tabSize": 3,
+        "editor.detectIndentation": false
+    },
+    "[cpp]": {
+        "editor.insertSpaces": true,
+        "editor.tabSize": 3,
+        "editor.detectIndentation": false
+    },
+    "[h]": {
+        "editor.insertSpaces": true,
+        "editor.tabSize": 3,
+        "editor.detectIndentation": false
+    },
+    "[hpp]": {
+        "editor.insertSpaces": true,
+        "editor.tabSize": 3,
+        "editor.detectIndentation": false
+    },
+    "cSpell.words": [],
+    "cSpell.ignorePaths": [
+        "**/*.json", "**/*.c", "**/*.h", "**/*.cpp", "**/*.hpp", "**/Makefile"
+    ]
+}
diff --git a/software/arduino-nano-5v/test_2024-07-23/.vscode/tasks.json b/software/arduino-nano-5v/test_2024-07-23/.vscode/tasks.json
new file mode 100644 (file)
index 0000000..74fb1c7
--- /dev/null
@@ -0,0 +1,23 @@
+{
+    // See https://go.microsoft.com/fwlink/?LinkId=733558
+    // for the documentation about the tasks.json format
+    "version": "2.0.0",
+    "tasks": [{
+        "label": "build",
+        "type": "shell",
+        "command": "make",
+        "problemMatcher":[
+            "$gcc"
+        ]
+    },{
+        "label": "clean",
+        "type": "shell",
+        "command": "make",
+        "args": [ "clean" ],
+    },{
+        "label": "flash",
+        "type": "shell",
+        "command": "make",
+        "args": [ "flash" ],
+    }]
+}
\ No newline at end of file
diff --git a/software/arduino-nano-5v/test_2024-07-23/Makefile b/software/arduino-nano-5v/test_2024-07-23/Makefile
new file mode 100644 (file)
index 0000000..aa7ba13
--- /dev/null
@@ -0,0 +1,82 @@
+.PHONY: all info flash picocom clean
+$(shell mkdir -p dist >/dev/null)
+$(shell mkdir -p build >/dev/null)
+$(shell mkdir -p sim >/dev/null)
+
+NAME="test_2024-07-23_nano-5v"
+SRC= $(wildcard src/*.c src/*.cpp src/*/*.cpp) 
+OBJ = $(SRC:src/%.c=build/%.o)
+OBJ_SIM = $(SRC:src/%.c=sim/%.o)
+
+DEVICE=atmega328p
+
+CC= avr-g++
+CFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=16000000 -c
+LFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=16000000 -Wl,-u,vfprintf -lprintf_flt -lm
+
+CFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=16000000 -g -c
+LFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=16000000 -g -Wl,-u,vfprintf -lprintf_flt -lm
+
+
+all: dist/$(NAME).elf dist/$(NAME).s dist/$(NAME).hex sim/$(NAME).elf sim/$(NAME).s info
+       
+info:
+       @echo
+       @avr-size --mcu=$(DEVICE) --format=avr dist/$(NAME).elf
+
+.depend: $(SRC)
+       $(CC) -MM $(SRC) > .depend
+
+-include $(DEPENDFILE) 
+
+dist/$(NAME).elf: .depend $(OBJ)
+       $(CC) $(LFLAGS) -o $@ $(OBJ)
+
+dist/%.s: dist/%.elf
+       avr-objdump -d $< > $@
+
+dist/%.hex: dist/%.elf
+       avr-objcopy -O ihex $(HEX_FLASH_FLAGS) $< $@
+
+sim/$(NAME).elf: .depend $(OBJ_SIM)
+               $(CC) $(LFLAGS_SIM) -o $@ $(OBJ_SIM)
+
+
+build/%.o: src/%.c
+       $(CC) $(CFLAGS) -o $@ $<
+
+sim/%.o: src/%.c
+       $(CC) $(CFLAGS_SIM) -o $@ $<
+
+sim/%.s: sim/%.elf
+       avr-objdump -d $< > $@
+
+simuc: sim/$(NAME).elf
+       simuc --board arduino $<
+
+gdb: sim/$(NAME).elf
+       avr-gdb $<
+
+isp-328p:
+       avrdude -c usbasp -p m328p
+
+isp-flash-328p: dist/$(NAME).elf all
+       avrdude -c usbasp -p m328p -e -U flash:w:$<
+
+flash-328p: dist/$(NAME).elf all
+       avrdude -c arduino -p m328p -P /dev/ttyUSB0 -b 115200 -e -U flash:w:$<
+
+
+picocom:
+       # picocom sends CR for ENTER -> convert cr (\r) to lf (\n)
+       picocom -b 115200 --omap crlf /dev/ttyUSB0
+
+isp-fuse-328p:
+       avrdude -c usbasp -p m328p -U lfuse:w:0x62:m -U hfuse:w:0xD9:m -U efuse:w:0xFF:m -U lock:w:0xFF:m
+
+clean:
+       @rm -r dist
+       @rm -r build
+       @rm -r sim
+       @find . -type f -name ".depend" -exec rm {} \;
+       @echo "clean done"
diff --git a/software/arduino-nano-5v/test_2024-07-23/README.md b/software/arduino-nano-5v/test_2024-07-23/README.md
new file mode 100644 (file)
index 0000000..c4d5e14
--- /dev/null
@@ -0,0 +1 @@
+# Testprogramm
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/adafruit/bme280.cpp b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/bme280.cpp
new file mode 100644 (file)
index 0000000..95a0f49
--- /dev/null
@@ -0,0 +1,512 @@
+#include "bme280.h"
+#include <util/delay.h>
+#include <stdio.h>
+
+Adafruit_BME280 theBME280;
+Adafruit_BME280_Temp bm280TempSensor;
+Adafruit_BME280_Pressure bm280PressureSensor;
+Adafruit_BME280_Humidity bm280HumiditySensor;
+
+Adafruit_BME280::Adafruit_BME280() {
+       static I2cMaster i2cDevice;
+       t_fine_adjust = 0;
+       temp_sensor = &bm280TempSensor;
+       pressure_sensor = &bm280PressureSensor;
+       humidity_sensor = &bm280HumiditySensor;
+       i2c_dev = &i2cDevice;
+}
+
+bool Adafruit_BME280::begin (uint8_t addr) {
+       if (!i2c_dev->begin(addr)) {
+               return false;
+       }
+       return init();
+}
+
+bool Adafruit_BME280::init() {
+       _sensorID = read8(BME280_REGISTER_CHIPID);
+       if (_sensorID != 0x60) {
+               return false;
+       }
+       write8(BME280_REGISTER_SOFTRESET, 0xB6);
+       _delay_ms(10); // wait for chip to wake up.
+
+       // if chip is still reading calibration, delay
+       while (isReadingCalibration()) {
+               _delay_ms(10);
+       }
+
+       readCoefficients(); // read trimming parameters, see DS 4.2.2
+       setSampling(); // use defaults
+       _delay_ms(100);
+
+       return true;
+}
+
+/*!
+ *   @brief  setup sensor with given parameters / settings
+ *
+ *   This is simply a overload to the normal begin()-function, so SPI users
+ *   don't get confused about the library requiring an address.
+ *   @param mode the power mode to use for the sensor
+ *   @param tempSampling the temp samping rate to use
+ *   @param pressSampling the pressure sampling rate to use
+ *   @param humSampling the humidity sampling rate to use
+ *   @param filter the filter mode to use
+ *   @param duration the standby duration to use
+ */
+void Adafruit_BME280::setSampling(sensor_mode mode,
+                                                                                        sensor_sampling tempSampling,
+                                                                                        sensor_sampling pressSampling,
+                                                                                        sensor_sampling humSampling,
+                                                                                        sensor_filter filter,
+                                                                                        standby_duration duration) {
+  _measReg.mode = mode;
+  _measReg.osrs_t = tempSampling;
+  _measReg.osrs_p = pressSampling;
+
+  _humReg.osrs_h = humSampling;
+  _configReg.filter = filter;
+  _configReg.t_sb = duration;
+  _configReg.spi3w_en = 0;
+
+  // making sure sensor is in sleep mode before setting configuration
+  // as it otherwise may be ignored
+  write8(BME280_REGISTER_CONTROL, MODE_SLEEP);
+
+  // you must make sure to also set REGISTER_CONTROL after setting the
+  // CONTROLHUMID register, otherwise the values won't be applied (see
+  // DS 5.4.3)
+  write8(BME280_REGISTER_CONTROLHUMID, _humReg.get());
+  write8(BME280_REGISTER_CONFIG, _configReg.get());
+  write8(BME280_REGISTER_CONTROL, _measReg.get());
+}
+
+/*!
+ *   @brief  Writes an 8 bit value over I2C or SPI
+ *   @param reg the register address to write to
+ *   @param value the value to write to the register
+ */
+void Adafruit_BME280::write8(byte reg, byte value) {
+  byte buffer[2];
+  buffer[1] = value;
+  if (i2c_dev) {
+        buffer[0] = reg;
+        i2c_dev->write(buffer, 2);
+  }
+}
+
+/*!
+ *   @brief  Reads an 8 bit value over I2C or SPI
+ *   @param reg the register address to read from
+ *   @returns the data byte read from the device
+ */
+uint8_t Adafruit_BME280::read8(byte reg) {
+  uint8_t buffer[1];
+  if (i2c_dev) {
+        buffer[0] = uint8_t(reg);
+        i2c_dev->write_then_read(buffer, 1, buffer, 1);
+  }
+  return buffer[0];
+}
+
+/*!
+ *   @brief  Reads a 16 bit value over I2C or SPI
+ *   @param reg the register address to read from
+ *   @returns the 16 bit data value read from the device
+ */
+uint16_t Adafruit_BME280::read16(byte reg) {
+  uint8_t buffer[2];
+
+  if (i2c_dev) {
+        buffer[0] = uint8_t(reg);
+        i2c_dev->write_then_read(buffer, 1, buffer, 2);
+  }
+  return uint16_t(buffer[0]) << 8 | uint16_t(buffer[1]);
+}
+
+/*!
+ *   @brief  Reads a signed 16 bit little endian value over I2C or SPI
+ *   @param reg the register address to read from
+ *   @returns the 16 bit data value read from the device
+ */
+uint16_t Adafruit_BME280::read16_LE(byte reg) {
+  uint16_t temp = read16(reg);
+  return (temp >> 8) | (temp << 8);
+}
+
+/*!
+ *   @brief  Reads a signed 16 bit value over I2C or SPI
+ *   @param reg the register address to read from
+ *   @returns the 16 bit data value read from the device
+ */
+int16_t Adafruit_BME280::readS16(byte reg) { return (int16_t)read16(reg); }
+
+/*!
+ *   @brief  Reads a signed little endian 16 bit value over I2C or SPI
+ *   @param reg the register address to read from
+ *   @returns the 16 bit data value read from the device
+ */
+int16_t Adafruit_BME280::readS16_LE(byte reg) {
+  return (int16_t)read16_LE(reg);
+}
+
+/*!
+ *   @brief  Reads a 24 bit value over I2C
+ *   @param reg the register address to read from
+ *   @returns the 24 bit data value read from the device
+ */
+uint32_t Adafruit_BME280::read24(byte reg) {
+  uint8_t buffer[3];
+
+  if (i2c_dev) {
+        buffer[0] = uint8_t(reg);
+        i2c_dev->write_then_read(buffer, 1, buffer, 3);
+  }
+  return uint32_t(buffer[0]) << 16 | uint32_t(buffer[1]) << 8 |
+                       uint32_t(buffer[2]);
+}
+
+/*!
+ *  @brief  Take a new measurement (only possible in forced mode)
+        @returns true in case of success else false
+ */
+bool Adafruit_BME280::takeForcedMeasurement(void) {
+  bool return_value = false;
+  // If we are in forced mode, the BME sensor goes back to sleep after each
+  // measurement and we need to set it to forced mode once at this point, so
+  // it will take the next measurement and then return to sleep again.
+  // In normal mode simply does new measurements periodically.
+  if (_measReg.mode == MODE_FORCED) {
+        return_value = true;
+        // set to forced mode, i.e. "take next measurement"
+        write8(BME280_REGISTER_CONTROL, _measReg.get());
+        // Store current time to measure the timeout
+        uint32_t timeout_start = millis();
+        // wait until measurement has been completed, otherwise we would read the
+        // the values from the last measurement or the timeout occurred after 2 sec.
+        while (read8(BME280_REGISTER_STATUS) & 0x08) {
+               // In case of a timeout, stop the while loop
+               if ((millis() - timeout_start) > 2000) {
+                 return_value = false;
+                 break;
+               }
+               _delay_ms(1);
+        }
+  }
+  return return_value;
+}
+
+/*!
+ *   @brief  Reads the factory-set coefficients
+ */
+void Adafruit_BME280::readCoefficients(void) {
+  _bme280_calib.dig_T1 = read16_LE(BME280_REGISTER_DIG_T1);
+  _bme280_calib.dig_T2 = readS16_LE(BME280_REGISTER_DIG_T2);
+  _bme280_calib.dig_T3 = readS16_LE(BME280_REGISTER_DIG_T3);
+
+  _bme280_calib.dig_P1 = read16_LE(BME280_REGISTER_DIG_P1);
+  _bme280_calib.dig_P2 = readS16_LE(BME280_REGISTER_DIG_P2);
+  _bme280_calib.dig_P3 = readS16_LE(BME280_REGISTER_DIG_P3);
+  _bme280_calib.dig_P4 = readS16_LE(BME280_REGISTER_DIG_P4);
+  _bme280_calib.dig_P5 = readS16_LE(BME280_REGISTER_DIG_P5);
+  _bme280_calib.dig_P6 = readS16_LE(BME280_REGISTER_DIG_P6);
+  _bme280_calib.dig_P7 = readS16_LE(BME280_REGISTER_DIG_P7);
+  _bme280_calib.dig_P8 = readS16_LE(BME280_REGISTER_DIG_P8);
+  _bme280_calib.dig_P9 = readS16_LE(BME280_REGISTER_DIG_P9);
+
+  _bme280_calib.dig_H1 = read8(BME280_REGISTER_DIG_H1);
+  _bme280_calib.dig_H2 = readS16_LE(BME280_REGISTER_DIG_H2);
+  _bme280_calib.dig_H3 = read8(BME280_REGISTER_DIG_H3);
+  _bme280_calib.dig_H4 = ((int8_t)read8(BME280_REGISTER_DIG_H4) << 4) |
+                                                                (read8(BME280_REGISTER_DIG_H4 + 1) & 0xF);
+  _bme280_calib.dig_H5 = ((int8_t)read8(BME280_REGISTER_DIG_H5 + 1) << 4) |
+                                                                (read8(BME280_REGISTER_DIG_H5) >> 4);
+  _bme280_calib.dig_H6 = (int8_t)read8(BME280_REGISTER_DIG_H6);
+}
+
+/*!
+ *   @brief return true if chip is busy reading cal data
+ *   @returns true if reading calibration, false otherwise
+ */
+bool Adafruit_BME280::isReadingCalibration(void) {
+  uint8_t const rStatus = read8(BME280_REGISTER_STATUS);
+
+  return (rStatus & (1 << 0)) != 0;
+}
+
+/*!
+ *   @brief  Returns the temperature from the sensor
+ *   @returns the temperature read from the device
+ */
+float Adafruit_BME280::readTemperature(void) {
+  int32_t var1, var2;
+
+  int32_t adc_T = read24(BME280_REGISTER_TEMPDATA);
+  if (adc_T == 0x800000) // value in case temp measurement was disabled
+        return NAN;
+  adc_T >>= 4;
+
+  var1 = (int32_t)((adc_T / 8) - ((int32_t)_bme280_calib.dig_T1 * 2));
+  var1 = (var1 * ((int32_t)_bme280_calib.dig_T2)) / 2048;
+  var2 = (int32_t)((adc_T / 16) - ((int32_t)_bme280_calib.dig_T1));
+  var2 = (((var2 * var2) / 4096) * ((int32_t)_bme280_calib.dig_T3)) / 16384;
+
+  t_fine = var1 + var2 + t_fine_adjust;
+
+  int32_t T = (t_fine * 5 + 128) / 256;
+
+  return (float)T / 100;
+}
+
+/*!
+ *   @brief  Returns the pressure from the sensor
+ *   @returns the pressure value (in Pascal) read from the device
+ */
+float Adafruit_BME280::readPressure(void) {
+  int64_t var1, var2, var3, var4;
+
+  readTemperature(); // must be done first to get t_fine
+
+  int32_t adc_P = read24(BME280_REGISTER_PRESSUREDATA);
+  if (adc_P == 0x800000) // value in case pressure measurement was disabled
+        return NAN;
+  adc_P >>= 4;
+
+  var1 = ((int64_t)t_fine) - 128000;
+  var2 = var1 * var1 * (int64_t)_bme280_calib.dig_P6;
+  var2 = var2 + ((var1 * (int64_t)_bme280_calib.dig_P5) * 131072);
+  var2 = var2 + (((int64_t)_bme280_calib.dig_P4) * 34359738368);
+  var1 = ((var1 * var1 * (int64_t)_bme280_calib.dig_P3) / 256) +
+                       ((var1 * ((int64_t)_bme280_calib.dig_P2) * 4096));
+  var3 = ((int64_t)1) * 140737488355328;
+  var1 = (var3 + var1) * ((int64_t)_bme280_calib.dig_P1) / 8589934592;
+
+  if (var1 == 0) {
+        return 0; // avoid exception caused by division by zero
+  }
+
+  var4 = 1048576 - adc_P;
+  var4 = (((var4 * 2147483648UL) - var2) * 3125) / var1;
+  var1 = (((int64_t)_bme280_calib.dig_P9) * (var4 / 8192) * (var4 / 8192)) /
+                       33554432;
+  var2 = (((int64_t)_bme280_calib.dig_P8) * var4) / 524288;
+  var4 = ((var4 + var1 + var2) / 256) + (((int64_t)_bme280_calib.dig_P7) * 16);
+
+  float P = var4 / 256.0;
+
+  return P;
+}
+
+/*!
+ *  @brief  Returns the humidity from the sensor
+ *  @returns the humidity value read from the device
+ */
+float Adafruit_BME280::readHumidity(void) {
+  int32_t var1, var2, var3, var4, var5;
+
+  readTemperature(); // must be done first to get t_fine
+
+  int32_t adc_H = read16(BME280_REGISTER_HUMIDDATA);
+  if (adc_H == 0x8000) // value in case humidity measurement was disabled
+        return NAN;
+
+  var1 = t_fine - ((int32_t)76800);
+  var2 = (int32_t)(adc_H * 16384);
+  var3 = (int32_t)(((int32_t)_bme280_calib.dig_H4) * 1048576);
+  var4 = ((int32_t)_bme280_calib.dig_H5) * var1;
+  var5 = (((var2 - var3) - var4) + (int32_t)16384) / 32768;
+  var2 = (var1 * ((int32_t)_bme280_calib.dig_H6)) / 1024;
+  var3 = (var1 * ((int32_t)_bme280_calib.dig_H3)) / 2048;
+  var4 = ((var2 * (var3 + (int32_t)32768)) / 1024) + (int32_t)2097152;
+  var2 = ((var4 * ((int32_t)_bme280_calib.dig_H2)) + 8192) / 16384;
+  var3 = var5 * var2;
+  var4 = ((var3 / 32768) * (var3 / 32768)) / 128;
+  var5 = var3 - ((var4 * ((int32_t)_bme280_calib.dig_H1)) / 16);
+  var5 = (var5 < 0 ? 0 : var5);
+  var5 = (var5 > 419430400 ? 419430400 : var5);
+  uint32_t H = (uint32_t)(var5 / 4096);
+
+  return (float)H / 1024.0;
+}
+
+/*!
+ *   Calculates the altitude (in meters) from the specified atmospheric
+ *   pressure (in hPa), and sea-level pressure (in hPa).
+ *   @param  seaLevel      Sea-level pressure in hPa
+ *   @returns the altitude value read from the device
+ */
+float Adafruit_BME280::readAltitude(float seaLevel) {
+  // Equation taken from BMP180 datasheet (page 16):
+  //  http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
+
+  // Note that using the equation from wikipedia can give bad results
+  // at high altitude. See this thread for more information:
+  //  http://forums.adafruit.com/viewtopic.php?f=22&t=58064
+
+  float atmospheric = readPressure() / 100.0F;
+  return 44330.0 * (1.0 - pow(atmospheric / seaLevel, 0.1903));
+}
+
+/*!
+ *   Calculates the pressure at sea level (in hPa) from the specified
+ * altitude (in meters), and atmospheric pressure (in hPa).
+ *   @param  altitude      Altitude in meters
+ *   @param  atmospheric   Atmospheric pressure in hPa
+ *   @returns the pressure at sea level (in hPa) from the specified altitude
+ */
+float Adafruit_BME280::seaLevelForAltitude(float altitude, float atmospheric) {
+  // Equation taken from BMP180 datasheet (page 17):
+  //  http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
+
+  // Note that using the equation from wikipedia can give bad results
+  // at high altitude. See this thread for more information:
+  //  http://forums.adafruit.com/viewtopic.php?f=22&t=58064
+
+  return atmospheric / pow(1.0 - (altitude / 44330.0), 5.255);
+}
+
+/*!
+ *   Returns Sensor ID found by init() for diagnostics
+ *   @returns Sensor ID 0x60 for BME280, 0x56, 0x57, 0x58 BMP280
+ */
+uint32_t Adafruit_BME280::sensorID(void) { return _sensorID; }
+
+/*!
+ *   Returns the current temperature compensation value in degrees Celsius
+ *   @returns the current temperature compensation value in degrees Celsius
+ */
+float Adafruit_BME280::getTemperatureCompensation(void) {
+  return float((t_fine_adjust * 5) >> 8) / 100.0;
+};
+
+/*!
+ *  Sets a value to be added to each temperature reading. This adjusted
+ *  temperature is used in pressure and humidity readings.
+ *  @param  adjustment  Value to be added to each temperature reading in Celsius
+ */
+void Adafruit_BME280::setTemperatureCompensation(float adjustment) {
+  // convert the value in C into and adjustment to t_fine
+  t_fine_adjust = ((int32_t(adjustment * 100) << 8)) / 5;
+};
+
+
+/**************************************************************************/
+/*!
+        @brief  Gets the sensor_t data for the BME280's temperature sensor
+*/
+/**************************************************************************/
+void Adafruit_BME280_Temp::getSensor(sensor_t *sensor) {
+  /* Clear the sensor_t object */
+  memset(sensor, 0, sizeof(sensor_t));
+
+  /* Insert the sensor name in the fixed length char array */
+  strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1);
+  sensor->name[sizeof(sensor->name) - 1] = 0;
+  sensor->version = 1;
+  sensor->sensor_id = _sensorID;
+  sensor->type = SENSOR_TYPE_AMBIENT_TEMPERATURE;
+  sensor->min_delay = 0;
+  sensor->min_value = -40.0; /* Temperature range -40 ~ +85 C  */
+  sensor->max_value = +85.0;
+  sensor->resolution = 0.01; /*  0.01 C */
+}
+
+/**************************************************************************/
+/*!
+        @brief  Gets the temperature as a standard sensor event
+        @param  event Sensor event object that will be populated
+        @returns True
+*/
+/**************************************************************************/
+bool Adafruit_BME280_Temp::getEvent(sensors_event_t *event) {
+  /* Clear the event */
+  memset(event, 0, sizeof(sensors_event_t));
+
+  event->version = sizeof(sensors_event_t);
+  event->sensor_id = _sensorID;
+  event->type = SENSOR_TYPE_AMBIENT_TEMPERATURE;
+  event->timestamp = millis();
+  event->temperature = theBME280.readTemperature();
+  return true;
+}
+
+/**************************************************************************/
+/*!
+        @brief  Gets the sensor_t data for the BME280's pressure sensor
+*/
+/**************************************************************************/
+void Adafruit_BME280_Pressure::getSensor(sensor_t *sensor) {
+  /* Clear the sensor_t object */
+  memset(sensor, 0, sizeof(sensor_t));
+
+  /* Insert the sensor name in the fixed length char array */
+  strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1);
+  sensor->name[sizeof(sensor->name) - 1] = 0;
+  sensor->version = 1;
+  sensor->sensor_id = _sensorID;
+  sensor->type = SENSOR_TYPE_PRESSURE;
+  sensor->min_delay = 0;
+  sensor->min_value = 300.0; /* 300 ~ 1100 hPa  */
+  sensor->max_value = 1100.0;
+  sensor->resolution = 0.012; /* 0.12 hPa relative */
+}
+
+/**************************************************************************/
+/*!
+        @brief  Gets the pressure as a standard sensor event
+        @param  event Sensor event object that will be populated
+        @returns True
+*/
+/**************************************************************************/
+bool Adafruit_BME280_Pressure::getEvent(sensors_event_t *event) {
+  /* Clear the event */
+  memset(event, 0, sizeof(sensors_event_t));
+
+  event->version = sizeof(sensors_event_t);
+  event->sensor_id = _sensorID;
+  event->type = SENSOR_TYPE_PRESSURE;
+  event->timestamp = millis();
+  event->pressure = theBME280.readPressure() / 100; // convert Pa to hPa
+  return true;
+}
+
+/**************************************************************************/
+/*!
+        @brief  Gets the sensor_t data for the BME280's humidity sensor
+*/
+/**************************************************************************/
+void Adafruit_BME280_Humidity::getSensor(sensor_t *sensor) {
+  /* Clear the sensor_t object */
+  memset(sensor, 0, sizeof(sensor_t));
+
+  /* Insert the sensor name in the fixed length char array */
+  strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1);
+  sensor->name[sizeof(sensor->name) - 1] = 0;
+  sensor->version = 1;
+  sensor->sensor_id = _sensorID;
+  sensor->type = SENSOR_TYPE_RELATIVE_HUMIDITY;
+  sensor->min_delay = 0;
+  sensor->min_value = 0;
+  sensor->max_value = 100; /* 0 - 100 %  */
+  sensor->resolution = 3;  /* 3% accuracy */
+}
+
+/**************************************************************************/
+/*!
+        @brief  Gets the humidity as a standard sensor event
+        @param  event Sensor event object that will be populated
+        @returns True
+*/
+/**************************************************************************/
+bool Adafruit_BME280_Humidity::getEvent(sensors_event_t *event) {
+  /* Clear the event */
+  memset(event, 0, sizeof(sensors_event_t));
+
+  event->version = sizeof(sensors_event_t);
+  event->sensor_id = _sensorID;
+  event->type = SENSOR_TYPE_RELATIVE_HUMIDITY;
+  event->timestamp = millis();
+  event->relative_humidity = theBME280.readHumidity();
+  return true;
+}
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/adafruit/bme280.h b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/bme280.h
new file mode 100644 (file)
index 0000000..b842782
--- /dev/null
@@ -0,0 +1,373 @@
+// https://github.com/adafruit/Adafruit_BME280_Library
+
+/*!
+ * @file Adafruit_BME280.h
+ *
+ * Designed specifically to work with the Adafruit BME280 Breakout
+ * ----> http://www.adafruit.com/products/2650
+ *
+ * These sensors use I2C or SPI to communicate, 2 or 4 pins are required
+ * to interface.
+ *
+ * Adafruit invests time and resources providing this open source code,
+ * please support Adafruit and open-source hardware by purchasing
+ * products from Adafruit!
+ *
+ * Written by Kevin "KTOWN" Townsend for Adafruit Industries.
+ *
+ * BSD license, all text here must be included in any redistribution.
+ * See the LICENSE file for details.
+ *
+ */
+
+#ifndef __BME280_H__
+#define __BME280_H__
+
+// #include "Arduino.h"
+
+// #include <Adafruit_I2CDevice.h>
+// #include <Adafruit_SPIDevice.h>
+// #include <Adafruit_Sensor.h>
+
+
+#include "../i2cmaster.hpp"
+#include "../main.hpp"
+#define byte uint8_t
+
+
+
+#include <stdint.h>
+#include <string.h>
+#include "sensor.h"
+
+/*!
+ *  @brief  default I2C address
+ */
+#define BME280_ADDRESS (0x77)           // Primary I2C Address
+                                        /*!
+                                         *  @brief  alternate I2C address
+                                         */
+#define BME280_ADDRESS_ALTERNATE (0x76) // Alternate Address
+
+/*!
+ *  @brief Register addresses
+ */
+enum {
+  BME280_REGISTER_DIG_T1 = 0x88,
+  BME280_REGISTER_DIG_T2 = 0x8A,
+  BME280_REGISTER_DIG_T3 = 0x8C,
+
+  BME280_REGISTER_DIG_P1 = 0x8E,
+  BME280_REGISTER_DIG_P2 = 0x90,
+  BME280_REGISTER_DIG_P3 = 0x92,
+  BME280_REGISTER_DIG_P4 = 0x94,
+  BME280_REGISTER_DIG_P5 = 0x96,
+  BME280_REGISTER_DIG_P6 = 0x98,
+  BME280_REGISTER_DIG_P7 = 0x9A,
+  BME280_REGISTER_DIG_P8 = 0x9C,
+  BME280_REGISTER_DIG_P9 = 0x9E,
+
+  BME280_REGISTER_DIG_H1 = 0xA1,
+  BME280_REGISTER_DIG_H2 = 0xE1,
+  BME280_REGISTER_DIG_H3 = 0xE3,
+  BME280_REGISTER_DIG_H4 = 0xE4,
+  BME280_REGISTER_DIG_H5 = 0xE5,
+  BME280_REGISTER_DIG_H6 = 0xE7,
+
+  BME280_REGISTER_CHIPID = 0xD0,
+  BME280_REGISTER_VERSION = 0xD1,
+  BME280_REGISTER_SOFTRESET = 0xE0,
+
+  BME280_REGISTER_CAL26 = 0xE1, // R calibration stored in 0xE1-0xF0
+
+  BME280_REGISTER_CONTROLHUMID = 0xF2,
+  BME280_REGISTER_STATUS = 0XF3,
+  BME280_REGISTER_CONTROL = 0xF4,
+  BME280_REGISTER_CONFIG = 0xF5,
+  BME280_REGISTER_PRESSUREDATA = 0xF7,
+  BME280_REGISTER_TEMPDATA = 0xFA,
+  BME280_REGISTER_HUMIDDATA = 0xFD
+};
+
+/**************************************************************************/
+/*!
+    @brief  calibration data
+*/
+/**************************************************************************/
+typedef struct {
+  uint16_t dig_T1; ///< temperature compensation value
+  int16_t dig_T2;  ///< temperature compensation value
+  int16_t dig_T3;  ///< temperature compensation value
+
+  uint16_t dig_P1; ///< pressure compensation value
+  int16_t dig_P2;  ///< pressure compensation value
+  int16_t dig_P3;  ///< pressure compensation value
+  int16_t dig_P4;  ///< pressure compensation value
+  int16_t dig_P5;  ///< pressure compensation value
+  int16_t dig_P6;  ///< pressure compensation value
+  int16_t dig_P7;  ///< pressure compensation value
+  int16_t dig_P8;  ///< pressure compensation value
+  int16_t dig_P9;  ///< pressure compensation value
+
+  uint8_t dig_H1; ///< humidity compensation value
+  int16_t dig_H2; ///< humidity compensation value
+  uint8_t dig_H3; ///< humidity compensation value
+  int16_t dig_H4; ///< humidity compensation value
+  int16_t dig_H5; ///< humidity compensation value
+  int8_t dig_H6;  ///< humidity compensation value
+} bme280_calib_data;
+/*=========================================================================*/
+
+class Adafruit_BME280;
+
+/** Adafruit Unified Sensor interface for temperature component of BME280 */
+class Adafruit_BME280_Temp : public Adafruit_Sensor {
+public:
+  /** @brief Create an Adafruit_Sensor compatible object for the temp sensor
+      @param parent A pointer to the BME280 class */
+  Adafruit_BME280_Temp() { _sensorID = 280; }
+  bool getEvent(sensors_event_t *);
+  void getSensor(sensor_t *);
+
+private:
+  int _sensorID;
+};
+
+/** Adafruit Unified Sensor interface for pressure component of BME280 */
+class Adafruit_BME280_Pressure : public Adafruit_Sensor {
+public:
+  /** @brief Create an Adafruit_Sensor compatible object for the pressure sensor
+      @param parent A pointer to the BME280 class */
+  Adafruit_BME280_Pressure() { _sensorID = 280; }
+  bool getEvent(sensors_event_t *);
+  void getSensor(sensor_t *);
+
+private:
+  int _sensorID;
+};
+
+/** Adafruit Unified Sensor interface for humidity component of BME280 */
+class Adafruit_BME280_Humidity : public Adafruit_Sensor {
+public:
+  /** @brief Create an Adafruit_Sensor compatible object for the humidity sensor
+      @param parent A pointer to the BME280 class */
+  Adafruit_BME280_Humidity() { _sensorID = 280;}
+  bool getEvent(sensors_event_t *);
+  void getSensor(sensor_t *);
+
+private:
+  int _sensorID;
+};
+
+/**************************************************************************/
+/*!
+    @brief  Class that stores state and functions for interacting with BME280 IC
+*/
+/**************************************************************************/
+class Adafruit_BME280 {
+public:
+  /**************************************************************************/
+  /*!
+      @brief  sampling rates
+  */
+  /**************************************************************************/
+  enum sensor_sampling {
+    SAMPLING_NONE = 0b000,
+    SAMPLING_X1 = 0b001,
+    SAMPLING_X2 = 0b010,
+    SAMPLING_X4 = 0b011,
+    SAMPLING_X8 = 0b100,
+    SAMPLING_X16 = 0b101
+  };
+
+  /**************************************************************************/
+  /*!
+      @brief  power modes
+  */
+  /**************************************************************************/
+  enum sensor_mode {
+    MODE_SLEEP = 0b00,
+    MODE_FORCED = 0b01,
+    MODE_NORMAL = 0b11
+  };
+
+  /**************************************************************************/
+  /*!
+      @brief  filter values
+  */
+  /**************************************************************************/
+  enum sensor_filter {
+    FILTER_OFF = 0b000,
+    FILTER_X2 = 0b001,
+    FILTER_X4 = 0b010,
+    FILTER_X8 = 0b011,
+    FILTER_X16 = 0b100
+  };
+
+  /**************************************************************************/
+  /*!
+      @brief  standby duration in ms
+  */
+  /**************************************************************************/
+  enum standby_duration {
+    STANDBY_MS_0_5 = 0b000,
+    STANDBY_MS_10 = 0b110,
+    STANDBY_MS_20 = 0b111,
+    STANDBY_MS_62_5 = 0b001,
+    STANDBY_MS_125 = 0b010,
+    STANDBY_MS_250 = 0b011,
+    STANDBY_MS_500 = 0b100,
+    STANDBY_MS_1000 = 0b101
+  };
+
+  // constructors
+  Adafruit_BME280();
+  
+  bool begin(uint8_t addr = BME280_ADDRESS);
+  bool init();
+
+  void setSampling(sensor_mode mode = MODE_NORMAL,
+                   sensor_sampling tempSampling = SAMPLING_X16,
+                   sensor_sampling pressSampling = SAMPLING_X16,
+                   sensor_sampling humSampling = SAMPLING_X16,
+                   sensor_filter filter = FILTER_OFF,
+                   standby_duration duration = STANDBY_MS_0_5);
+
+  bool takeForcedMeasurement(void);
+  float readTemperature(void);
+  float readPressure(void);
+  float readHumidity(void);
+
+  float readAltitude(float seaLevel);
+  float seaLevelForAltitude(float altitude, float pressure);
+  uint32_t sensorID(void);
+
+  float getTemperatureCompensation(void);
+  void setTemperatureCompensation(float);
+
+protected:
+  I2cMaster *i2c_dev = NULL; ///< Pointer to I2C bus interface
+  // Adafruit_SPIDevice *spi_dev = NULL; ///< Pointer to SPI bus interface
+
+  Adafruit_BME280_Temp *temp_sensor;
+  Adafruit_BME280_Pressure *pressure_sensor;
+  Adafruit_BME280_Humidity *humidity_sensor;
+
+  void readCoefficients(void);
+  bool isReadingCalibration(void);
+
+  void write8(byte reg, byte value);
+  uint8_t read8(byte reg);
+  uint16_t read16(byte reg);
+  uint32_t read24(byte reg);
+  int16_t readS16(byte reg);
+  uint16_t read16_LE(byte reg); // little endian
+  int16_t readS16_LE(byte reg); // little endian
+
+  uint8_t _i2caddr;  //!< I2C addr for the TwoWire interface
+  int32_t _sensorID; //!< ID of the BME Sensor
+  int32_t t_fine; //!< temperature with high resolution, stored as an attribute
+                  //!< as this is used for temperature compensation reading
+                  //!< humidity and pressure
+
+  int32_t t_fine_adjust; //!< add to compensate temp readings and in turn
+                         //!< to pressure and humidity readings
+
+  bme280_calib_data _bme280_calib; //!< here calibration data is stored
+
+  /**************************************************************************/
+  /*!
+      @brief  config register
+  */
+  /**************************************************************************/
+  struct config {
+    // inactive duration (standby time) in normal mode
+    // 000 = 0.5 ms
+    // 001 = 62.5 ms
+    // 010 = 125 ms
+    // 011 = 250 ms
+    // 100 = 500 ms
+    // 101 = 1000 ms
+    // 110 = 10 ms
+    // 111 = 20 ms
+    unsigned int t_sb : 3; ///< inactive duration (standby time) in normal mode
+
+    // filter settings
+    // 000 = filter off
+    // 001 = 2x filter
+    // 010 = 4x filter
+    // 011 = 8x filter
+    // 100 and above = 16x filter
+    unsigned int filter : 3; ///< filter settings
+
+    // unused - don't set
+    unsigned int none : 1;     ///< unused - don't set
+    unsigned int spi3w_en : 1; ///< unused - don't set
+
+    /// @return combined config register
+    unsigned int get() { return (t_sb << 5) | (filter << 2) | spi3w_en; }
+  };
+  config _configReg; //!< config register object
+
+  /**************************************************************************/
+  /*!
+      @brief  ctrl_meas register
+  */
+  /**************************************************************************/
+  struct ctrl_meas {
+    // temperature oversampling
+    // 000 = skipped
+    // 001 = x1
+    // 010 = x2
+    // 011 = x4
+    // 100 = x8
+    // 101 and above = x16
+    unsigned int osrs_t : 3; ///< temperature oversampling
+
+    // pressure oversampling
+    // 000 = skipped
+    // 001 = x1
+    // 010 = x2
+    // 011 = x4
+    // 100 = x8
+    // 101 and above = x16
+    unsigned int osrs_p : 3; ///< pressure oversampling
+
+    // device mode
+    // 00       = sleep
+    // 01 or 10 = forced
+    // 11       = normal
+    unsigned int mode : 2; ///< device mode
+
+    /// @return combined ctrl register
+    unsigned int get() { return (osrs_t << 5) | (osrs_p << 2) | mode; }
+  };
+  ctrl_meas _measReg; //!< measurement register object
+
+  /**************************************************************************/
+  /*!
+      @brief  ctrl_hum register
+  */
+  /**************************************************************************/
+  struct ctrl_hum {
+    /// unused - don't set
+    unsigned int none : 5;
+
+    // pressure oversampling
+    // 000 = skipped
+    // 001 = x1
+    // 010 = x2
+    // 011 = x4
+    // 100 = x8
+    // 101 and above = x16
+    unsigned int osrs_h : 3; ///< pressure oversampling
+
+    /// @return combined ctrl hum register
+    unsigned int get() { return (osrs_h); }
+  };
+  ctrl_hum _humReg; //!< hum register object
+};
+
+extern Adafruit_BME280 theBME280;
+
+#endif
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/adafruit/ens160.cpp b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/ens160.cpp
new file mode 100644 (file)
index 0000000..ef23404
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+  ScioSense_ENS160.h - Library for the ENS160 sensor with I2C interface from ScioSense
+  2023 Mar 23  v6      Christoph Friese        Bugfix measurement routine, prepare next release
+  2021 Nov 25   v5     Martin Herold           Custom mode timing fixed
+  2021 Feb 04  v4      Giuseppe de Pinto       Custom mode fixed
+  2020 Apr 06  v3      Christoph Friese        Changed nomenclature to ScioSense as product shifted from ams
+  2020 Feb 15  v2      Giuseppe Pasetti        Corrected firmware flash option
+  2019 May 05  v1      Christoph Friese        Created
+  based on application note "ENS160 Software Integration.pdf" rev 0.01
+*/
+
+#include "ens160.h"
+#include "math.h"
+#include <util/delay.h>
+#include <stdio.h>
+#include <avr/io.h>
+
+ScioSense_ENS160::ScioSense_ENS160 () {
+   _revENS16x = 0;
+
+   //Isotherm, HP0 252°C / HP1 350°C / HP2 250°C / HP3 324°C / measure every 1008ms
+       _seq_steps[0][0] = 0x7c;
+   _seq_steps[0][1] = 0x0a;
+   _seq_steps[0][2] = 0x7e;
+   _seq_steps[0][3] = 0xaf;
+   _seq_steps[0][4] = 0xaf;
+   _seq_steps[0][5] = 0xa2;
+   _seq_steps[0][6] = 0x00;
+   _seq_steps[0][7] = 0x80;
+}
+
+bool ScioSense_ENS160::begin () {
+   i2cDevice.begin(ENS160_I2CADDR_1);
+       _delay_ms(ENS160_BOOTING);
+       if (reset()) {
+      if (checkPartID()) {
+         if (setMode(ENS160_OPMODE_IDLE)) {
+            if (clearCommand()) {
+               if (getFirmware()) {
+                  return true;
+               }
+            }
+         }
+      }
+   }
+   return false;
+       
+}
+
+bool ScioSense_ENS160::write8 (byte reg, byte value) {
+   byte buffer[2];
+   buffer[1] = value;
+   buffer[0] = reg;
+   return i2cDevice.write(buffer, 2);
+}
+
+bool ScioSense_ENS160::read8 (byte reg, byte *value) {
+   uint8_t buffer[1];
+   buffer[0] = uint8_t(reg);
+   return i2cDevice.write_then_read(buffer, 1, value, 1);
+}
+
+bool ScioSense_ENS160::read16 (byte reg, uint16_t *value) {
+   uint8_t buffer[1];
+   buffer[0] = uint8_t(reg);
+   return i2cDevice.write_then_read(buffer, 1, (uint8_t *)value, 2);
+}
+
+bool ScioSense_ENS160::read16LE (byte reg, uint16_t *value) {
+  uint16_t tmp;
+  if (read16(reg, &tmp)) {
+      *value = ((tmp & 0xff) << 8) | (tmp >> 8);
+      return true;
+  }
+  return false;
+}
+
+bool ScioSense_ENS160::readBytes (byte reg, uint8_t *bytes, uint8_t len) {
+   uint8_t buffer[1];
+   buffer[0] = uint8_t(reg);
+   return i2cDevice.write_then_read(buffer, 1, buffer, len);
+}
+
+
+// Sends a reset to the ENS160. Returns false on I2C problems.
+bool ScioSense_ENS160::reset () {
+       if (write8(ENS160_REG_OPMODE, ENS160_OPMODE_RESET)) {
+      _delay_ms(ENS160_BOOTING);
+      return true;
+   }
+       _delay_ms(ENS160_BOOTING);
+       return false;
+}
+
+// Reads the part ID and confirms valid sensor
+bool ScioSense_ENS160::checkPartID () {
+       uint16_t part_id;
+       
+   read16(ENS160_REG_PART_ID, &part_id);
+       _delay_ms(ENS160_BOOTING);
+
+       if (part_id == ENS160_PARTID) {
+      _revENS16x = 0;
+      return true;
+
+   } else if (part_id == ENS161_PARTID) {
+      _revENS16x = 1;
+      return true;
+   }
+       
+       return false;
+}
+
+// Initialize idle mode and confirms 
+bool ScioSense_ENS160::clearCommand () {
+       uint8_t status;
+       
+       if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_NOP)) {
+          if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_CLRGPR)) {
+             _delay_ms(ENS160_BOOTING);
+         if (read8(ENS160_REG_DATA_STATUS, &status)) {
+            return true;
+         }
+      }
+   }
+       _delay_ms(ENS160_BOOTING);
+       return false;
+}
+
+// Read firmware revisions
+bool ScioSense_ENS160::getFirmware () {
+       uint8_t i2cbuf[3];
+       
+       if (clearCommand()) {
+      _delay_ms(ENS160_BOOTING);
+      if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_GET_APPVER)) {
+         if (readBytes(ENS160_REG_GPR_READ_4, i2cbuf, 3)) {
+            _fw_ver_major = i2cbuf[0];
+            _fw_ver_minor = i2cbuf[1];
+            _fw_ver_build = i2cbuf[2];
+            _revENS16x = this->_fw_ver_major > 6 ? 1 : 0;
+            _delay_ms(ENS160_BOOTING);
+            return true;
+         }
+      }
+   }
+   _delay_ms(ENS160_BOOTING);
+       return false;
+}
+
+// Set operation mode of sensor
+bool ScioSense_ENS160::setMode (uint8_t mode) {
+       //LP only valid for rev>0
+       if ((mode == ENS160_OPMODE_LP) and (_revENS16x == 0)) {
+      return false;
+   }
+       if (write8(ENS160_REG_OPMODE, mode)) {
+      _delay_ms(ENS160_BOOTING);
+      return true;
+   }
+   _delay_ms(ENS160_BOOTING);
+   return false;
+}
+
+// Initialize definition of custom mode with <n> steps
+bool ScioSense_ENS160::initCustomMode (uint16_t stepNum) {
+       if (stepNum > 0) {
+               _stepCount = stepNum;
+               if (setMode(ENS160_OPMODE_IDLE)) {
+         if (clearCommand()) {
+            if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_SETSEQ)) {
+               _delay_ms(ENS160_BOOTING);
+               return true;
+            }
+         }
+      }
+       }
+       _delay_ms(ENS160_BOOTING);
+       return false;
+}
+
+// Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate
+bool ScioSense_ENS160::addCustomStep (uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3) {
+       uint8_t seq_ack;
+       uint8_t temp;
+
+       _delay_ms(ENS160_BOOTING);
+
+       temp = (uint8_t)(((time / 24) - 1) << 6); 
+       if (measureHP0) {
+      temp = temp | 0x20;
+   }
+       if (measureHP1) {
+      temp = temp | 0x10;
+   }
+       if (measureHP2) {
+      temp = temp | 0x08;
+   }
+       if (measureHP3) {
+      temp = temp | 0x04;
+   }
+       if (!write8(ENS160_REG_GPR_WRITE_0, temp)) {
+      return false;
+   }
+   temp = (uint8_t)(((time / 24) - 1) >> 2); 
+       if (!write8(ENS160_REG_GPR_WRITE_1, temp)) {
+      return false;
+   }
+   if (!write8(ENS160_REG_GPR_WRITE_2, (uint8_t)(tempHP0 / 2))) {
+      return false;
+   }
+       if (!write8(ENS160_REG_GPR_WRITE_3, (uint8_t)(tempHP1 / 2))) {
+      return false;
+   }
+       if (write8(ENS160_REG_GPR_WRITE_4, (uint8_t)(tempHP2 / 2))) {
+      return false;
+   }
+       if (write8(ENS160_REG_GPR_WRITE_5, (uint8_t)(tempHP3 / 2))) {
+      return false;
+   }
+
+       if (write8(ENS160_REG_GPR_WRITE_6, (uint8_t)(_stepCount - 1))) {
+      return false;
+   }
+
+   if (_stepCount == 1) {
+      if (!write8(ENS160_REG_GPR_WRITE_7, 128)) {
+         return false;
+      }
+   } else {
+      if (!write8(ENS160_REG_GPR_WRITE_7, 0)) {
+         return false;
+      }
+   }
+   _delay_ms(ENS160_BOOTING);
+
+   if (!read8(ENS160_REG_GPR_READ_7, &seq_ack)) {
+      return false;
+   }
+       _delay_ms(ENS160_BOOTING);
+       
+       if ((ENS160_SEQ_ACK_COMPLETE | _stepCount) != seq_ack) {
+               _stepCount++;
+               return false;
+       }
+
+       return true;
+}
+
+bool ScioSense_ENS160::readStatus (uint8_t *status) {
+   return read8(ENS160_REG_DATA_STATUS, status);
+}
+
+bool ScioSense_ENS160::readData (ENS160_DATA *data) {
+   uint8_t buffer[1] = { 0x21 };
+   return i2cDevice.write_then_read(buffer, 1, (uint8_t *)data, sizeof(ENS160_DATA));
+}
+       
+// Perform prediction measurement and stores result in internal variables
+bool ScioSense_ENS160::measure (bool waitForNew) {
+       uint8_t i2cbuf[8];
+       uint8_t status;
+
+       // Set default status for early bail out
+       if (waitForNew) {
+               do {
+         if (!read8(ENS160_REG_DATA_STATUS, &status)) {
+            return false;
+         }
+         _delay_ms(1);
+               } while (!IS_NEWDAT(status));
+       } else {
+               if (!read8(ENS160_REG_DATA_STATUS, &status)) {
+         return false;
+      }
+       }
+       
+
+       // Read predictions
+       if (IS_NEWDAT(status)) {
+               if (!readBytes(ENS160_REG_DATA_AQI, i2cbuf, 7)) {
+         return false;
+      }
+      return false;
+               _data_aqi = i2cbuf[0];
+               _data_tvoc = i2cbuf[1] | ((uint16_t)i2cbuf[2] << 8);
+               _data_eco2 = i2cbuf[3] | ((uint16_t)i2cbuf[4] << 8);
+               if (_revENS16x > 0) {
+         _data_aqi500 = ((uint16_t)i2cbuf[5]) | ((uint16_t)i2cbuf[6] << 8);
+      } else {
+         _data_aqi500 = 0;
+      }
+      return true;
+       }
+       
+       return false;
+}
+
+// Perfrom raw measurement and stores result in internal variables
+bool ScioSense_ENS160::measureRaw (bool waitForNew) {
+       uint8_t i2cbuf[8];
+       uint8_t status;
+
+       // Set default status for early bail out
+       if (waitForNew) {
+               do {
+                       _delay_ms(1);
+                       if (!read8(ENS160_REG_DATA_STATUS, &status)) {
+            return false;
+         }
+               } while (!IS_NEWGPR(status));
+       } else {
+               if (!read8(ENS160_REG_DATA_STATUS, &status)) {
+         return false;
+      }
+       }
+       
+       if (IS_NEWGPR(status)) {
+               
+               // Read raw resistance values
+               if (!readBytes(ENS160_REG_GPR_READ_0, i2cbuf, 8)) {
+         return false;
+      }
+      _hp0_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8)));
+      _hp1_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8)));
+      _hp2_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8)));
+      _hp3_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8)));
+       
+               // Read baselines
+               if (!readBytes(ENS160_REG_DATA_BL, i2cbuf, 8)) {
+         return false;
+      }
+               _hp0_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8)));
+               _hp1_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8)));
+               _hp2_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8)));
+               _hp3_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8)));
+
+               if (!read8(ENS160_REG_DATA_MISR, i2cbuf)) {
+         return false;
+      }
+               _misr = i2cbuf[0];
+      return true;
+       }
+       
+       return false;
+}
+
+
+// Writes t (degC) and h (%rh) to ENV_DATA. Returns false on I2C problems.
+bool ScioSense_ENS160::set_envdata (float t, float h) {
+       uint16_t t_data = (uint16_t)((t + 273.15f) * 64.0f);
+       uint16_t rh_data = (uint16_t)(h * 512.0f);
+       return this->set_envdata210(t_data, rh_data);
+}
+
+// Writes t and h (in ENS210 format) to ENV_DATA. Returns false on I2C problems.
+bool ScioSense_ENS160::set_envdata210 (uint16_t t, uint16_t h) {
+       //uint16_t temp;
+       uint8_t trh_in[4];
+       
+       //temp = (uint16_t)((t + 273.15f) * 64.0f);
+       trh_in[0] = t & 0xff;
+       trh_in[1] = (t >> 8) & 0xff;
+       
+       //temp = (uint16_t)(h * 512.0f);
+       trh_in[2] = h & 0xff;
+       trh_in[3] = (h >> 8) & 0xff;
+       
+       if (!i2cDevice.writeByteAndBuffer(ENS160_REG_TEMP_IN, trh_in, 4)) {
+      return false;
+   }
+       
+       return true;
+}
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/adafruit/ens160.h b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/ens160.h
new file mode 100644 (file)
index 0000000..fb6925d
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+  ScioSense_ENS160.h - Library for the ENS160 sensor with I2C interface from ScioSense
+  2023 Mar 23  v6      Christoph Friese        Bugfix measurement routine, prepare next release
+  2021 July 29 v4      Christoph Friese        Changed nomenclature to ScioSense as product shifted from ams
+  2020 Apr 06  v3      Christoph Friese        Changed nomenclature to ScioSense as product shifted from ams
+  2020 Feb 15  v2      Giuseppe Pasetti        Corrected firmware flash option
+  2019 May 05  v1      Christoph Friese        Created
+  based on application note "ENS160 Software Integration.pdf" rev 0.01
+*/
+
+#ifndef __SCIOSENSE_ENS160_H_
+#define __SCIOSENSE_ENS160_H_
+
+#include "../i2cmaster.hpp"
+#include <stdint.h>
+#define byte uint8_t
+
+// #if (ARDUINO >= 100)
+//     #include "Arduino.h"
+// #else
+//     #include "WProgram.h"
+// #endif
+
+// #include <Wire.h>
+
+// Chip constants
+#define ENS160_PARTID                          0x0160
+#define ENS161_PARTID                          0x0161
+#define ENS160_BOOTING                         10
+
+// 7-bit I2C slave address of the ENS160
+#define ENS160_I2CADDR_0               0x52            //ADDR low
+#define ENS160_I2CADDR_1               0x53            //ADDR high
+
+// ENS160 registers for version V0
+#define ENS160_REG_PART_ID             0x00            // 2 byte register
+#define ENS160_REG_OPMODE              0x10
+#define ENS160_REG_CONFIG              0x11
+#define ENS160_REG_COMMAND             0x12
+#define ENS160_REG_TEMP_IN             0x13
+#define ENS160_REG_RH_IN               0x15
+#define ENS160_REG_DATA_STATUS         0x20
+#define ENS160_REG_DATA_AQI            0x21
+#define ENS160_REG_DATA_TVOC           0x22
+#define ENS160_REG_DATA_ECO2           0x24                    
+#define ENS160_REG_DATA_BL             0x28
+#define ENS160_REG_DATA_T              0x30
+#define ENS160_REG_DATA_RH             0x32
+#define ENS160_REG_DATA_MISR           0x38
+#define ENS160_REG_GPR_WRITE_0         0x40
+#define ENS160_REG_GPR_WRITE_1         ENS160_REG_GPR_WRITE_0 + 1
+#define ENS160_REG_GPR_WRITE_2         ENS160_REG_GPR_WRITE_0 + 2
+#define ENS160_REG_GPR_WRITE_3         ENS160_REG_GPR_WRITE_0 + 3
+#define ENS160_REG_GPR_WRITE_4         ENS160_REG_GPR_WRITE_0 + 4
+#define ENS160_REG_GPR_WRITE_5         ENS160_REG_GPR_WRITE_0 + 5
+#define ENS160_REG_GPR_WRITE_6         ENS160_REG_GPR_WRITE_0 + 6
+#define ENS160_REG_GPR_WRITE_7         ENS160_REG_GPR_WRITE_0 + 7
+#define ENS160_REG_GPR_READ_0          0x48
+#define ENS160_REG_GPR_READ_4          ENS160_REG_GPR_READ_0 + 4
+#define ENS160_REG_GPR_READ_6          ENS160_REG_GPR_READ_0 + 6
+#define ENS160_REG_GPR_READ_7          ENS160_REG_GPR_READ_0 + 7
+
+//ENS160 data register fields
+#define ENS160_COMMAND_NOP             0x00
+#define ENS160_COMMAND_CLRGPR          0xCC
+#define ENS160_COMMAND_GET_APPVER      0x0E 
+#define ENS160_COMMAND_SETTH           0x02
+#define ENS160_COMMAND_SETSEQ          0xC2
+
+#define ENS160_OPMODE_RESET            0xF0
+#define ENS160_OPMODE_DEP_SLEEP                0x00
+#define ENS160_OPMODE_IDLE             0x01
+#define ENS160_OPMODE_STD              0x02
+#define ENS160_OPMODE_LP               0x03    
+#define ENS160_OPMODE_CUSTOM           0xC0
+
+#define ENS160_BL_CMD_START            0x02
+#define ENS160_BL_CMD_ERASE_APP                0x04
+#define ENS160_BL_CMD_ERASE_BLINE      0x06
+#define ENS160_BL_CMD_WRITE            0x08
+#define ENS160_BL_CMD_VERIFY           0x0A
+#define ENS160_BL_CMD_GET_BLVER                0x0C
+#define ENS160_BL_CMD_GET_APPVER       0x0E
+#define ENS160_BL_CMD_EXITBL           0x12
+
+#define ENS160_SEQ_ACK_NOTCOMPLETE     0x80
+#define ENS160_SEQ_ACK_COMPLETE                0xC0
+
+#define IS_ENS160_SEQ_ACK_NOT_COMPLETE(x)      (ENS160_SEQ_ACK_NOTCOMPLETE == (ENS160_SEQ_ACK_NOTCOMPLETE & (x)))
+#define IS_ENS160_SEQ_ACK_COMPLETE(x)          (ENS160_SEQ_ACK_COMPLETE == (ENS160_SEQ_ACK_COMPLETE & (x)))
+
+#define ENS160_DATA_STATUS_NEWDAT      0x02
+#define ENS160_DATA_STATUS_NEWGPR      0x01
+
+#define IS_NEWDAT(x)                   (ENS160_DATA_STATUS_NEWDAT == (ENS160_DATA_STATUS_NEWDAT & (x)))
+#define IS_NEWGPR(x)                   (ENS160_DATA_STATUS_NEWGPR == (ENS160_DATA_STATUS_NEWGPR & (x)))
+#define IS_NEW_DATA_AVAILABLE(x)       (0 != ((ENS160_DATA_STATUS_NEWDAT | ENS160_DATA_STATUS_NEWGPR ) & (x)))
+
+#define CONVERT_RS_RAW2OHMS_I(x)       (1 << ((x) >> 11))
+#define CONVERT_RS_RAW2OHMS_F(x)       (pow (2, (float)(x) / 2048))
+
+typedef struct {
+   uint8_t aqi;
+   uint16_t tvoc;
+   uint16_t eco2;
+} ENS160_DATA;
+
+class ScioSense_ENS160 {
+               
+       public:
+          ScioSense_ENS160();
+               
+               void                            setI2C(uint8_t sda, uint8_t scl);                               // Function to redefine I2C pins
+               
+               bool                            begin();                                        // Init I2C communication, resets ENS160 and checks its PART_ID. Returns false on I2C problems or wrong PART_ID.
+               uint8_t                         revENS16x()     { return this->_revENS16x; }                    // Report version of sensor (0: ENS160, 1: ENS161)
+               bool                            setMode(uint8_t mode);                                          // Set operation mode of sensor
+
+               bool                            initCustomMode(uint16_t stepNum);                               // Initialize definition of custom mode with <n> steps
+               bool                            addCustomStep(uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3);
+                                                                                                                                                                                       // Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate
+                                                                                                                                                                                       
+      bool  readData (ENS160_DATA *data);
+      bool  readStatus(uint8_t *status);
+               bool                            measure(bool waitForNew);                               // Perform measurement and stores result in internal variables
+               bool                            measureRaw(bool waitForNew);                            // Perform raw measurement and stores result in internal variables
+               bool                            set_envdata(float t, float h);                                  // Writes t (degC) and h (%rh) to ENV_DATA. Returns "0" if I2C transmission is successful
+               bool                            set_envdata210(uint16_t t, uint16_t h);                         // Writes t and h (in ENS210 format) to ENV_DATA. Returns "0" if I2C transmission is successful
+               uint8_t                         getMajorRev()   { return this->_fw_ver_major; }                 // Get major revision number of used firmware
+               uint8_t                         getMinorRev()   { return this->_fw_ver_minor; }                 // Get minor revision number of used firmware
+               uint8_t                         getBuild()              { return this->_fw_ver_build; }         // Get build revision number of used firmware
+
+               uint8_t                         getAQI()                { return this->_data_aqi; }             // Get AQI value of last measurement 
+               uint16_t                        getTVOC()               { return this->_data_tvoc; }            // Get TVOC value of last measurement 
+               uint16_t                        geteCO2()               { return this->_data_eco2; }            // Get eCO2 value of last measurement 
+               uint16_t                        getAQI500()             { return this->_data_aqi500; }          // Get AQI500 value of last measurement 
+               uint32_t                        getHP0()                { return this->_hp0_rs; }               // Get resistance of HP0 of last measurement
+               uint32_t                        getHP1()                { return this->_hp1_rs; }               // Get resistance of HP1 of last measurement
+               uint32_t                        getHP2()                { return this->_hp2_rs; }               // Get resistance of HP2 of last measurement
+               uint32_t                        getHP3()                { return this->_hp3_rs; }               // Get resistance of HP3 of last measurement
+               uint32_t                        getHP0BL()              { return this->_hp0_bl; }               // Get baseline resistance of HP0 of last measurement
+               uint32_t                        getHP1BL()              { return this->_hp1_bl; }               // Get baseline resistance of HP1 of last measurement
+               uint32_t                        getHP2BL()              { return this->_hp2_bl; }               // Get baseline resistance of HP2 of last measurement
+               uint32_t                        getHP3BL()              { return this->_hp3_bl; }               // Get baseline resistance of HP3 of last measurement
+               uint8_t                         getMISR()               { return this->_misr; }                 // Return status code of sensor
+
+       private:
+      I2cMaster i2cDevice;
+               bool                            reset();                                                        // Sends a reset to the ENS160. Returns false on I2C problems.
+               bool                            checkPartID();                                                  // Reads the part ID and confirms valid sensor
+               bool                            clearCommand();                                                 // Initialize idle mode and confirms 
+               bool                            getFirmware();                                                  // Read firmware revisions
+               
+               uint8_t                         _revENS16x;                                                     // ENS160 or ENS161 connected? (FW >7)
+       
+               uint8_t                         _fw_ver_major;
+               uint8_t                         _fw_ver_minor;
+               uint8_t                         _fw_ver_build;
+
+               uint16_t                        _stepCount;                                                     // Counter for custom sequence
+
+               uint8_t                         _data_aqi;
+               uint16_t                        _data_tvoc;
+               uint16_t                        _data_eco2;
+               uint16_t                        _data_aqi500;
+               uint32_t                        _hp0_rs;
+               uint32_t                        _hp0_bl;
+               uint32_t                        _hp1_rs;
+               uint32_t                        _hp1_bl;
+               uint32_t                        _hp2_rs;
+               uint32_t                        _hp2_bl;
+               uint32_t                        _hp3_rs;
+               uint32_t                        _hp3_bl;
+               uint16_t                        _temp;
+               int                             _slaveaddr;                                                     // Slave address of the ENS160
+               uint8_t                         _misr;
+               
+               uint8_t _seq_steps[1][8];
+
+      bool write8(byte reg, byte value);
+      bool read8 (byte reg, byte *value);
+      bool read16 (byte reg, uint16_t *value);
+      bool read16LE (byte reg, uint16_t *value);
+      bool readBytes (byte reg, uint8_t *bytes, uint8_t len);
+};
+
+
+#endif
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/adafruit/sensor.h b/software/arduino-nano-5v/test_2024-07-23/src/adafruit/sensor.h
new file mode 100644 (file)
index 0000000..ac7e454
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software< /span>
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Update by K. Townsend (Adafruit Industries) for lighter typedefs, and
+ * extended sensor support to include color, voltage and current */
+
+#ifndef _ADAFRUIT_SENSOR_H
+#define _ADAFRUIT_SENSOR_H
+
+#ifndef ARDUINO
+#include <stdint.h>
+#elif ARDUINO >= 100
+#include "Arduino.h"
+#include "Print.h"
+#else
+#include "WProgram.h"
+#endif
+
+/* Constants */
+#define SENSORS_GRAVITY_EARTH (9.80665F) /**< Earth's gravity in m/s^2 */
+#define SENSORS_GRAVITY_MOON (1.6F)      /**< The moon's gravity in m/s^2 */
+#define SENSORS_GRAVITY_SUN (275.0F)     /**< The sun's gravity in m/s^2 */
+#define SENSORS_GRAVITY_STANDARD (SENSORS_GRAVITY_EARTH)
+#define SENSORS_MAGFIELD_EARTH_MAX                                             \
+  (60.0F) /**< Maximum magnetic field on Earth's surface */
+#define SENSORS_MAGFIELD_EARTH_MIN                                             \
+  (30.0F) /**< Minimum magnetic field on Earth's surface */
+#define SENSORS_PRESSURE_SEALEVELHPA                                           \
+  (1013.25F) /**< Average sea level pressure is 1013.25 hPa */
+#define SENSORS_DPS_TO_RADS                                                    \
+  (0.017453293F) /**< Degrees/s to rad/s multiplier                            \
+                  */
+#define SENSORS_RADS_TO_DPS                                                    \
+  (57.29577793F) /**< Rad/s to degrees/s  multiplier */
+#define SENSORS_GAUSS_TO_MICROTESLA                                            \
+  (100) /**< Gauss to micro-Tesla multiplier */
+
+/** Sensor types */
+typedef enum {
+  SENSOR_TYPE_ACCELEROMETER = (1), /**< Gravity + linear acceleration */
+  SENSOR_TYPE_MAGNETIC_FIELD = (2),
+  SENSOR_TYPE_ORIENTATION = (3),
+  SENSOR_TYPE_GYROSCOPE = (4),
+  SENSOR_TYPE_LIGHT = (5),
+  SENSOR_TYPE_PRESSURE = (6),
+  SENSOR_TYPE_PROXIMITY = (8),
+  SENSOR_TYPE_GRAVITY = (9),
+  SENSOR_TYPE_LINEAR_ACCELERATION =
+      (10), /**< Acceleration not including gravity */
+  SENSOR_TYPE_ROTATION_VECTOR = (11),
+  SENSOR_TYPE_RELATIVE_HUMIDITY = (12),
+  SENSOR_TYPE_AMBIENT_TEMPERATURE = (13),
+  SENSOR_TYPE_OBJECT_TEMPERATURE = (14),
+  SENSOR_TYPE_VOLTAGE = (15),
+  SENSOR_TYPE_CURRENT = (16),
+  SENSOR_TYPE_COLOR = (17),
+  SENSOR_TYPE_TVOC = (18),
+  SENSOR_TYPE_VOC_INDEX = (19),
+  SENSOR_TYPE_NOX_INDEX = (20),
+  SENSOR_TYPE_CO2 = (21),
+  SENSOR_TYPE_ECO2 = (22),
+  SENSOR_TYPE_PM10_STD = (23),
+  SENSOR_TYPE_PM25_STD = (24),
+  SENSOR_TYPE_PM100_STD = (25),
+  SENSOR_TYPE_PM10_ENV = (26),
+  SENSOR_TYPE_PM25_ENV = (27),
+  SENSOR_TYPE_PM100_ENV = (28),
+  SENSOR_TYPE_GAS_RESISTANCE = (29),
+  SENSOR_TYPE_UNITLESS_PERCENT = (30),
+  SENSOR_TYPE_ALTITUDE = (31)
+} sensors_type_t;
+
+/** struct sensors_vec_s is used to return a vector in a common format. */
+typedef struct {
+  union {
+    float v[3]; ///< 3D vector elements
+    struct {
+      float x; ///< X component of vector
+      float y; ///< Y component of vector
+      float z; ///< Z component of vector
+    };         ///< Struct for holding XYZ component
+    /* Orientation sensors */
+    struct {
+      float roll; /**< Rotation around the longitudinal axis (the plane body, 'X
+                     axis'). Roll is positive and increasing when moving
+                     downward. -90 degrees <= roll <= 90 degrees */
+      float pitch;   /**< Rotation around the lateral axis (the wing span, 'Y
+                        axis'). Pitch is positive and increasing when moving
+                        upwards. -180 degrees <= pitch <= 180 degrees) */
+      float heading; /**< Angle between the longitudinal axis (the plane body)
+                        and magnetic north, measured clockwise when viewing from
+                        the top of the device. 0-359 degrees */
+    };               ///< Struct for holding roll/pitch/heading
+  };                 ///< Union that can hold 3D vector array, XYZ components or
+                     ///< roll/pitch/heading
+  int8_t status;     ///< Status byte
+  uint8_t reserved[3]; ///< Reserved
+} sensors_vec_t;
+
+/** struct sensors_color_s is used to return color data in a common format. */
+typedef struct {
+  union {
+    float c[3]; ///< Raw 3-element data
+    /* RGB color space */
+    struct {
+      float r;   /**< Red component */
+      float g;   /**< Green component */
+      float b;   /**< Blue component */
+    };           ///< RGB data in floating point notation
+  };             ///< Union of various ways to describe RGB colorspace
+  uint32_t rgba; /**< 24-bit RGBA value */
+} sensors_color_t;
+
+/* Sensor event (36 bytes) */
+/** struct sensor_event_s is used to provide a single sensor event in a common
+ * format. */
+typedef struct {
+  int32_t version;   /**< must be sizeof(struct sensors_event_t) */
+  int32_t sensor_id; /**< unique sensor identifier */
+  int32_t type;      /**< sensor type */
+  int32_t reserved0; /**< reserved */
+  int32_t timestamp; /**< time is in milliseconds */
+  union {
+    float data[4];              ///< Raw data */
+    sensors_vec_t acceleration; /**< acceleration values are in meter per second
+                                   per second (m/s^2) */
+    sensors_vec_t
+        magnetic; /**< magnetic vector values are in micro-Tesla (uT) */
+    sensors_vec_t orientation; /**< orientation values are in degrees */
+    sensors_vec_t gyro;        /**< gyroscope values are in rad/s */
+    float temperature; /**< temperature is in degrees centigrade (Celsius) */
+    float distance;    /**< distance in centimeters */
+    float light;       /**< light in SI lux units */
+    float pressure;    /**< pressure in hectopascal (hPa) */
+    float relative_humidity; /**< relative humidity in percent */
+    float current;           /**< current in milliamps (mA) */
+    float voltage;           /**< voltage in volts (V) */
+    float tvoc;              /**< Total Volatile Organic Compounds, in ppb */
+    float voc_index; /**< VOC (Volatile Organic Compound) index where 100 is
+                          normal (unitless) */
+    float nox_index; /**< NOx (Nitrogen Oxides) index where 100 is normal
+                          (unitless) */
+    float CO2;       /**< Measured CO2 in parts per million (ppm) */
+    float eCO2;      /**< equivalent/estimated CO2 in parts per million (ppm
+                        estimated from some other measurement) */
+    float pm10_std;  /**< Standard Particulate Matter <=1.0 in parts per million
+                        (ppm) */
+    float pm25_std;  /**< Standard Particulate Matter <=2.5 in parts per million
+                        (ppm) */
+    float pm100_std; /**< Standard Particulate Matter <=10.0 in parts per
+                        million (ppm) */
+    float pm10_env;  /**< Environmental Particulate Matter <=1.0 in parts per
+                        million (ppm) */
+    float pm25_env;  /**< Environmental Particulate Matter <=2.5 in parts per
+                        million (ppm) */
+    float pm100_env; /**< Environmental Particulate Matter <=10.0 in parts per
+                        million (ppm) */
+    float gas_resistance;   /**< Proportional to the amount of VOC particles in
+                               the air (Ohms) */
+    float unitless_percent; /**<Percentage, unit-less (%) */
+    sensors_color_t color;  /**< color in RGB component values */
+    float altitude; /**< Distance between a reference datum and a point or
+                       object, in meters. */
+  };                ///< Union for the wide ranges of data we can carry
+} sensors_event_t;
+
+/* Sensor details (40 bytes) */
+/** struct sensor_s is used to describe basic information about a specific
+ * sensor. */
+typedef struct {
+  char name[12];     /**< sensor name */
+  int32_t version;   /**< version of the hardware + driver */
+  int32_t sensor_id; /**< unique sensor identifier */
+  int32_t type;      /**< this sensor's type (ex. SENSOR_TYPE_LIGHT) */
+  float max_value;   /**< maximum value of this sensor's value in SI units */
+  float min_value;   /**< minimum value of this sensor's value in SI units */
+  float resolution; /**< smallest difference between two values reported by this
+                       sensor */
+  int32_t min_delay; /**< min delay in microseconds between events. zero = not a
+                        constant rate */
+} sensor_t;
+
+/** @brief Common sensor interface to unify various sensors.
+ * Intentionally modeled after sensors.h in the Android API:
+ * https://github.com/android/platform_hardware_libhardware/blob/master/include/hardware/sensors.h
+ */
+class Adafruit_Sensor {
+public:
+  // Constructor(s)
+  Adafruit_Sensor() {}
+//   virtual ~Adafruit_Sensor() {}
+
+//   // These must be defined by the subclass
+
+//   /*! @brief Whether we should automatically change the range (if possible) for
+//      higher precision
+//       @param enabled True if we will try to autorange */
+//   virtual void enableAutoRange(bool enabled) {
+//     (void)enabled; /* suppress unused warning */
+//   };
+
+//   /*! @brief Get the latest sensor event
+//       @returns True if able to fetch an event */
+//   virtual bool getEvent(sensors_event_t *) = 0;
+//   /*! @brief Get info about the sensor itself */
+//   virtual void getSensor(sensor_t *) = 0;
+
+//   void printSensorDetails(void);
+};
+
+#endif
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/i2cmaster.cpp b/software/arduino-nano-5v/test_2024-07-23/src/i2cmaster.cpp
new file mode 100644 (file)
index 0000000..9b1bd5d
--- /dev/null
@@ -0,0 +1,143 @@
+#include <avr/io.h>
+#include <stdio.h>
+
+#include "i2cmaster.hpp"
+
+I2cMaster::I2cMaster () {
+   address = 0;
+   timer = 0;
+}
+
+void I2cMaster::tick1ms () {
+   if (timer > 0) {
+      timer--;
+   }
+}
+
+bool I2cMaster::begin (uint8_t addr) {
+   this->address = addr;
+   // TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4);
+   TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4);
+   TWCR = (1 << TWEN);
+   return true;
+}
+
+void I2cMaster::end () {
+   TWCR = (1 << TWEN);
+   TWBR = 0;
+}
+
+bool I2cMaster::read (uint8_t *buffer, uint8_t len) {
+   if (start(true)) {
+      if (readBytes(buffer, len)) {
+         if (stop()) {
+            return true;
+         }
+      }
+   }
+   return false;
+}
+
+
+bool I2cMaster::write (const uint8_t *buffer, uint8_t len) {
+   if (start(false)) {
+      if (writeBytes(buffer, len)) {
+         if (stop()) {
+            return true;
+         }
+      }
+   }
+   return false;
+}
+
+bool I2cMaster::writeByteAndBuffer (uint8_t byte, const uint8_t *buffer, uint8_t len) {
+   if (start(false)) {
+      do {
+         TWDR = byte;
+         TWCR = (1 << TWINT) | (1 << TWEN); // send byte
+         while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+         if ((TWSR & 0xf8) != 0x28) {
+            return false;
+         }
+         byte = *buffer++;
+      } while (len-- > 0);
+      return true;
+   }
+   return false;
+}
+
+
+bool I2cMaster::write_then_read (const uint8_t *write_buffer, uint8_t write_len, uint8_t *read_buffer, uint8_t read_len) {
+   if (start(false)) {
+      if (writeBytes(write_buffer, write_len)) {
+         if (start(true)) {
+            if (readBytes(read_buffer, read_len)) {
+               if (stop()) {
+                  return true;
+               }
+            }
+         }
+      }
+   }
+   return false;
+}
+
+// -------------------------------------------------------------
+
+bool I2cMaster::readBytes (uint8_t *buffer, uint8_t len) {
+   while (len-- > 0) {
+      if (len > 0) {
+         TWCR = (1 << TWEA) | (1 << TWINT) | (1 << TWEN); // read data byte with ACK enabled
+      } else {
+         TWCR = (1 << TWINT) | (1 << TWEN); // read data byte with ACK disabled
+      }
+      while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+      uint8_t sr = TWSR & 0xf8;
+      if ((len > 0 && sr != 0x50) || (len == 0 && sr != 0x58)) {
+         return false;
+      }
+      *buffer++ = TWDR;
+   }
+   return true;
+}
+
+bool I2cMaster::writeBytes (const uint8_t *buffer, uint8_t len) {
+   while (len-- > 0) {
+      // printf_P(PSTR("[wB:len=%d, byte=%02x]"), len + 1, *buffer);
+      TWDR = *buffer++;
+      TWCR = (1 << TWINT) | (1 << TWEN); // send data byte
+      timer = 5;
+      while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done
+      if (!timer || (TWSR & 0xf8) != 0x28) {
+         return false;
+      }
+   }
+   return true;
+}
+
+
+bool I2cMaster::start (bool read) {
+   TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition
+   timer = 5;
+   while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done
+   uint8_t sr = TWSR & 0xf8;
+   if (!timer || (sr != 0x08 && sr != 0x10)) {
+      return false;
+   }
+   TWDR = (address << 1) | (read ? 1 : 0); // address + R/nW
+   TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW
+   timer = 5;
+   while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done
+   sr = TWSR & 0xf8;
+   if (!timer || (!read && sr != 0x18) || (read && sr != 0x40)) {
+      return false;
+   }
+   return true;
+}
+
+bool I2cMaster::stop () {
+   TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
+   while (TWCR & ( 1 << TWSTO));
+   return true;
+}
+
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/i2cmaster.hpp b/software/arduino-nano-5v/test_2024-07-23/src/i2cmaster.hpp
new file mode 100644 (file)
index 0000000..e70326c
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef I2C_MASTER
+#define I2C_MASTER
+
+#include <stdint.h>
+
+class I2cMaster {
+   public:
+      static void end ();
+   
+   public:
+      I2cMaster ();
+      void tick1ms ();
+      bool begin (uint8_t addr);
+      bool read (uint8_t *buffer, uint8_t len);
+      bool write(const uint8_t *buffer, uint8_t len);
+      bool write_then_read(const uint8_t *write_buffer, uint8_t write_len, uint8_t *read_buffer, uint8_t read_len);
+      bool writeByteAndBuffer (uint8_t byte, const uint8_t *buffer, uint8_t len);
+
+   private:
+      uint8_t address;
+      uint8_t timer;
+      bool start (bool read);
+      bool stop ();
+      bool writeBytes (const uint8_t *buffer, uint8_t len);
+      bool readBytes (uint8_t *buffer, uint8_t len);
+};
+
+#endif
\ No newline at end of file
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/i2cslave.cpp b/software/arduino-nano-5v/test_2024-07-23/src/i2cslave.cpp
new file mode 100644 (file)
index 0000000..2bf6ac2
--- /dev/null
@@ -0,0 +1,92 @@
+#include <avr/io.h>
+#include <stdio.h>
+#include <util/atomic.h>
+
+#include "i2cslave.hpp"
+
+I2cSlave::I2cSlave () {
+   timer = 0;
+   fromMaster.rIndex = 0;
+   fromMaster.wIndex = 0;
+   toMaster.rIndex = 0;
+   toMaster.wIndex = 0;
+}
+
+void I2cSlave::tick1ms () {
+   if (timer > 0) {
+      timer--;
+   }
+}
+
+bool I2cSlave::begin (uint8_t addr, bool acceptGeneralCalls) {
+   if (addr > 127) {
+      return false;
+   }
+   TWAR = addr << 1 | (acceptGeneralCalls ? 1 : 0);
+   TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4);
+   TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWIE);
+   return true;
+}
+
+void I2cSlave::end () {
+   TWCR = (1 << TWEN);
+   TWBR = 0;
+}
+
+int I2cSlave::read () {
+   return getByte(fromMaster);
+}
+
+void I2cSlave::write (uint8_t byte) {
+   putByte(toMaster, byte);
+}
+
+
+void I2cSlave::putByte (RingBuffer& buffer, uint8_t byte) {
+   ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
+      buffer.data[buffer.wIndex++] = byte;
+      if (buffer.wIndex >= sizeof(buffer.data)) {
+         buffer.wIndex = 0;
+      }
+      if (buffer.wIndex == buffer.rIndex) {
+         buffer.rIndex++;
+         if (buffer.rIndex >= sizeof(buffer.data)) {
+            buffer.rIndex = 0;
+         }
+      }
+   }
+}
+
+int I2cSlave::getByte (RingBuffer& buffer) {
+   uint8_t b;
+   ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
+      if (buffer.rIndex == buffer.wIndex) {
+         return EOF;
+      }
+      b = buffer.data[buffer.rIndex++];
+      if (buffer.rIndex >= sizeof(buffer.data)) {
+         buffer.rIndex = 0;
+      }
+   }
+   return b;
+}
+
+void I2cSlave::handleTWIIsr () {
+   uint8_t sr = TWSR & 0xf8;
+   switch (sr) {
+      case 0x80: { // Previously addressed with own SLA+W; data has been received; ACK has been returned
+         putByte(fromMaster, TWDR);
+         TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); // no TWEA -> only one byte accepted
+         break;
+      }
+      case 0xa8: { // Own SLA+R has been received; ACK has been returned
+         int response = getByte(toMaster);;
+         TWDR = response < 0 ? 0x00 : (uint8_t)response;
+         TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); // no TWEA -> only one byte accepted
+         break;
+      }
+      default: TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (1 << TWIE); break;
+   }
+  
+}
+
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/i2cslave.hpp b/software/arduino-nano-5v/test_2024-07-23/src/i2cslave.hpp
new file mode 100644 (file)
index 0000000..2fe2dc7
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef I2C_SLAVE
+#define I2C_SLAVE
+
+#include <stdint.h>
+
+class I2cSlave {
+   private:
+      typedef struct {
+         uint8_t rIndex;
+         uint8_t wIndex;
+         uint8_t data[8];
+   } RingBuffer;
+
+   public:
+      I2cSlave ();
+      void tick1ms ();
+      bool begin (uint8_t addr, bool acceptGeneralCalls);
+      void end ();
+      void handleTWIIsr ();
+      int read ();
+      void write (uint8_t byte);
+
+   private:
+      uint8_t timer;
+      RingBuffer fromMaster;
+      RingBuffer toMaster;
+      void putByte (RingBuffer& buffer, uint8_t byte);
+      int getByte (RingBuffer& buffer);
+
+};
+
+#endif
\ No newline at end of file
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/main.cpp b/software/arduino-nano-5v/test_2024-07-23/src/main.cpp
new file mode 100644 (file)
index 0000000..f5b0f3e
--- /dev/null
@@ -0,0 +1,277 @@
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+#include <stdio.h>
+#include <util/atomic.h>
+
+#include "main.hpp"
+#include "units/encoder.hpp"
+#include "units/i2c.hpp"
+#include "units/led.hpp"
+#include "units/ieee485.hpp"
+#include "units/led.hpp"
+#include "units/lcd.hpp"
+
+
+// #include "units/switch.hpp"
+// #include "units/rgb.hpp"
+// #include "units/seg7.hpp"
+// #include "units/poti.hpp"
+// #include "units/r2r.hpp"
+// #include "units/motor.hpp"
+// #include "units/portexp.hpp"
+// #include "units/uart1.hpp"
+// #include "units/modbus.hpp"
+
+
+
+
+extern "C" {
+   void __cxa_pure_virtual () {
+   }
+
+   int __cxa_guard_acquire(uint8_t *g) {
+      return 0;
+   }
+
+   void __cxa_guard_release(uint8_t *g) {}
+   
+   void __cxa_guard_abort(uint8_t *g) {}
+
+
+   int uart_putchar(char c, FILE *stream) {
+      if (c == '\n') {
+         uart_putchar('\r', stream);
+      }
+      if (stream == stdout) {
+         loop_until_bit_is_set(UCSR0A, UDRE0);
+         UDR0 = c;
+      } 
+      return 0;
+   }
+
+
+   uint64_t volatile systemMillis = 0;
+   uint8_t volatile uartBuffer[32];
+   uint8_t volatile rIndex = 0;
+   uint8_t volatile wIndex = 0;
+
+   int uart_getchar (FILE *stream) {
+    // if (rIndex == wIndex) {
+    //     // nothing in buffer
+    //     return EOF;
+    // }
+    // printf_P(PSTR(" r%d"), rIndex);
+    while (rIndex == wIndex) {
+        // wait for character
+    }
+    
+    // don't use "char c" because german special characters would lead to negative return -> stream error
+    // char c = uartBuffer[rIndex++];
+    
+    uint8_t c = uartBuffer[rIndex++];
+    // printf_P(PSTR("(%02x) "), c);
+    if (c == '\r') {
+      c = '\n';
+    }
+    putchar(c); // echo on terminal
+    return c;
+   }
+
+   static FILE mystdout = { 0, 0, _FDEV_SETUP_WRITE , 0, 0, uart_putchar, NULL, 0 };
+   static FILE mystdin  = { 0, 0, _FDEV_SETUP_READ  , 0, 0, NULL, uart_getchar, 0 };
+
+   static volatile uint32_t timer1ms = 0;
+   static volatile int keyUart0 = EOF;
+
+   Led led;
+   // Switch sw;
+   // Rgb rgb;
+   // Seg7 seg7;
+   // Poti poti;
+   Encoder encoder;
+   // R2r r2r;
+   // Motor motor;
+   // PortExp portExp;
+   Lcd lcd;
+   // Uart1 uart1;
+   // Modbus modbus;
+   Ieee485 ieee485;
+   I2c i2cSparkfun(I2c::SparkFunEnvCombo);
+   I2c i2cMaster(I2c::Master);
+   I2c i2cSlave(I2c::Slave);
+
+}
+
+void setTimer (uint32_t ms) {
+   ATOMIC_BLOCK(ATOMIC_FORCEON) {
+      timer1ms = ms;
+   }
+}
+
+int wait (uint32_t ms) {
+   setTimer(ms);
+   do {
+      ATOMIC_BLOCK(ATOMIC_FORCEON) {
+         ms = timer1ms;
+      }
+   } while (ms > 0 && keyUart0 == EOF);
+   return keyUart0;
+}
+
+int main () {
+
+   // Nano-644 LEDs (Green, Orange, Red)
+   DDRC |= (1 << DDC4) | (1 << DDC3) | (1 << DDC2);
+   PORTC &= ~((1 << PORT4) | (1 << PORT3) | (1 << PORT2));
+
+   // Nano-644 push button SW2
+   DDRC &= ~(1 << DDC5); 
+   PORTC |= (1 << PORT5); // enable internal pullup resistor
+
+   // UART0 interface on Nano-644
+   UCSR0A = (1 << U2X0);
+   UCSR0B = (1 << RXCIE0) | (1 << RXEN0) | (1 <<TXEN0);
+   UCSR0C = (1 << UCSZ01) | ( 1<< UCSZ00);
+   UBRR0H = 0;
+   UBRR0L = F_CPU / 8 / 115200 - 1;
+
+   TCCR2A = (1 << WGM21);
+   TCCR2B = (1 << CS21); // CTC, F_CPU/8=1.5MHz
+   OCR2A = 150; // 150/1.5E6 = 0.1ms
+   TIMSK2 = (1 << OCIE2A);
+
+   stdout = &mystdout;
+   stdin = &mystdin;
+
+   sei();
+
+   // TestUnit *unit[] = {
+   //    &led, &sw, &rgb, &seg7, &poti, &encoder, &r2r, &motor, &portExp, &lcd, &uart1, &modbus, &ieee485,
+   //    &i2cMaster, &i2cSlave, &i2cSparkfun
+   // };
+
+   TestUnit *unit[] = {
+      &led, &encoder, &lcd,
+      &i2cMaster, &i2cSlave, &i2cSparkfun
+   };
+
+   while (1) {
+      uint16_t i;
+      char s[4];
+      do {
+         printf_P(PSTR("\n\n=============================\n\n"));
+         printf_P(PSTR("Available units:\n\n"));
+         for (i = 0; i < sizeof(unit) / sizeof(unit[0]); i++) {
+            TestUnit *pu = unit[i];
+            printf_P(PSTR("%3x ... "), i);
+            printf_P(pu->getName());
+            printf_P(PSTR("\n"));
+         }
+         printf_P(PSTR("\nSelect unit: "));
+         rIndex = 0; wIndex = 0;
+         fgets(s, sizeof(s), stdin);
+      } while (sscanf(s, "%x", &i) != 1 || i < 0 || i >= sizeof(unit) / sizeof(unit[0]) );
+      
+      TestUnit *pu = unit[i];
+      printf_P(PSTR("\n\n[%s]: "), pu->getName());
+      keyUart0 = EOF;
+
+      for (uint8_t subtest = 0; subtest < 0xff; subtest++) {
+         printf_P(PSTR("\n%4d: "), subtest);
+         if (pu->run(subtest) < 0) {
+            break;
+         }
+         if (keyUart0 == 27) {
+            keyUart0 = EOF;   
+            break;
+         }
+         keyUart0 = EOF;
+      }
+      pu->cleanup();
+   }
+}
+
+uint64_t millis () {
+   volatile uint64_t millis = systemMillis;
+   ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
+      millis = systemMillis;
+   }
+   return millis;
+}
+
+#ifndef USART0_RX_vect
+   #define USART0_RX_vect USART_RX_vect
+#endif
+ISR (USART0_RX_vect) {
+   uint8_t b = UDR0;
+   keyUart0 = b;
+   uartBuffer[wIndex++] = b;
+   // printf_P(PSTR(" w%d(%02x)"), wIndex, b);
+   if (wIndex == rIndex) {
+      // buffer overflow, kick out oldest byte
+      rIndex++;
+   }
+}
+
+#ifdef USART1_RX_vect
+   ISR (USART1_RX_vect) {
+      uint8_t b = UDR1;
+      if (modbus.enabled) {
+         modbus.handleRxByte(b);
+      }
+      if (uart1.enabled) {
+         uart1.handleRxByte(b);
+      }
+      if (ieee485.enabled) {
+         ieee485.handleRxByte(b);
+      }
+   }
+#endif
+
+ISR (TWI_vect) {
+   if (i2cMaster.enabled) {
+      i2cMaster.handleTwiIrq();
+   } else if (i2cSlave.enabled) {
+      i2cSlave.handleTwiIrq();
+   } else if (i2cSparkfun.enabled) {
+      i2cSparkfun.handleTwiIrq();
+   } else {
+      TWCR = (1 << TWINT); // clear interrupt request bit and disable TWI
+   }
+}
+
+ISR (TIMER2_COMPA_vect) { // every 100us
+   static uint16_t timer500ms = 0;
+   static uint8_t timer100us = 0;
+
+   if (encoder.enabled) {
+      encoder.tick100us();
+   }
+   #ifdef MOTOR_HPP
+      if (motor.enabled) {
+         motor.tick100us();
+      }
+   #endif
+
+   timer100us++;
+   if (timer100us >= 10) {
+      timer100us = 0;
+      if (timer1ms > 0) {
+         timer1ms--;
+      }
+      systemMillis++;
+      i2cMaster.tick1ms();
+      i2cSlave.tick1ms();
+      i2cSparkfun.tick1ms();
+   }
+
+   timer500ms++;
+   if (timer500ms >= 5000) {
+      PORTC ^= (1 << PORTC3); // orange LED blinking
+      timer500ms = 0;
+   }
+}
+
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/main.hpp b/software/arduino-nano-5v/test_2024-07-23/src/main.hpp
new file mode 100644 (file)
index 0000000..f60ec89
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef MAIN_HPP
+#define MAIN_HPP
+
+#include <stdint.h>
+#include <avr/pgmspace.h>
+
+#define ENTER '\r'
+#define CTRLC '\003'
+
+extern int wait (uint32_t ms);
+extern uint64_t millis ();
+
+class TestUnit {
+   public:
+      virtual int8_t run (uint8_t subtest) = 0;
+      virtual void cleanup () = 0;
+      virtual PGM_P getName () = 0;
+};
+
+#endif
\ No newline at end of file
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/encoder.cpp b/software/arduino-nano-5v/test_2024-07-23/src/units/encoder.cpp
new file mode 100644 (file)
index 0000000..bc84f48
--- /dev/null
@@ -0,0 +1,86 @@
+#include <stdio.h>
+#include <avr/io.h>
+
+#include "encoder.hpp"
+#include "../main.hpp"
+
+// PB0/T0   ... Encoder A
+// PB1/T1   ... Encoder B
+// PB2/INT2 ... push switch of encoder (pushed = 0)
+
+// Encoder signals on rotation clockwise 1 step:
+// A -----____------  one char app. 1ms..2ms (rotation speed)
+// B -------___-----  
+//                    one step when: A = 0, B= 1->0
+
+// Encoder signals on rotation counterclockwise 1 step:
+// A -----____------  one char app. 1ms..2ms (rotation speed)
+// B --______-----  
+//                    one step when: A = 0, B= 0->1
+
+
+void Encoder::cleanup () {
+   DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0));
+   PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0));
+   enabled = 0;
+}
+
+int8_t Encoder::run (uint8_t subtest) {
+   switch (subtest) {
+      case 0: {
+         printf_P(PSTR("init"));
+         DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0));
+         PORTB |= (1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0); // enable pullup
+         enabled = 1;
+         return 0;
+      }
+
+      case 1: {
+         while (wait(10) == EOF) {
+            printf_P(PSTR("\r  => Encoder (push to clear): "));
+            printf_P(PSTR("%5d (0x%02x) "), count, (uint8_t)count);
+            if ((PINB & (1 << PINB2)) == 0) {
+               reset();
+            }
+         }
+         return 0;
+      }
+
+      case 2: {
+         printf_P(PSTR("end"));
+         break;
+      }
+   }
+
+   return -1;
+}
+
+struct EncoderState {
+   uint8_t a:1; // signal A
+   uint8_t b:1; // signal B
+};
+
+void Encoder::tick100us () {
+   static EncoderState lastState = { 1, 1 };
+   static EncoderState lastStableState = { 1, 1 };
+
+   if ((DDRB & 0x03) || (PORTB & 0x07) != 0x07) {
+      count = 0;
+      return; // Enocder pins not configured
+   }
+   EncoderState nextState = { (PINB & 0x01) == 0x01, (PINB & 0x02) == 0x02 };
+   if (nextState.a == lastState.a && nextState.b == lastState.b) {
+      if (lastStableState.a == 0 && nextState.b != lastStableState.b) {
+         if (nextState.b == 0) {
+            count = count < 127 ? count + 1 : 127;
+         } else {
+            count = count > -128 ? count - 1 : -128;
+         }
+      }
+      lastStableState.a = nextState.a;
+      lastStableState.b = nextState.b;
+   }
+   lastState.a = nextState.a;
+   lastState.b = nextState.b;
+}
+
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/encoder.hpp b/software/arduino-nano-5v/test_2024-07-23/src/units/encoder.hpp
new file mode 100644 (file)
index 0000000..10bcc0f
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef ENCODER_HPP
+#define ENCODER_PP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Encoder : public TestUnit {
+   public:
+      uint8_t enabled;
+      int8_t count;
+
+   public:
+      Encoder () { reset(); enabled = 0; };
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("Encoder"); }
+      void reset () { count = 0; }
+      void tick100us ();
+};
+
+#endif
\ No newline at end of file
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/i2c.cpp b/software/arduino-nano-5v/test_2024-07-23/src/units/i2c.cpp
new file mode 100644 (file)
index 0000000..4c5934f
--- /dev/null
@@ -0,0 +1,300 @@
+#include <stdio.h>
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+#include <math.h>
+
+#include "i2c.hpp"
+#include "../adafruit/bme280.h"
+#include "../main.hpp"
+
+// Sparkfun https://www.sparkfun.com/products/22858
+// ENS160 address 0x53 (air quality sensor)
+// BME280 address 0x77 /humidity/temperature sensor)
+//    register 0xfe: humidity_7:0
+//    register 0xfd: humidity_15:8
+
+PGM_P I2c::getName () {
+   switch (mode) {
+      case SparkFunEnvCombo: return PSTR("I2C-Sparkfun Env-Combo");
+      case Master: return PSTR("I2C-Master");
+      case Slave: return PSTR("I2C-Slave");
+   }
+   return "?";
+}
+
+void I2c::cleanup () {
+   enabled = false;
+   TWCR = (1 << TWEN);
+   TWBR = 0;
+   ADMUX = 0;
+   ADCSRA = 0;
+}
+
+int8_t I2c::run (uint8_t subtest) {
+   if (subtest == 0) {
+      TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4);
+      TWBR = 28; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4);
+      TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4);
+      TWCR = (1 << TWEN);
+      ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V
+      ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128
+      enabled = true;
+      printf_P(PSTR("init"));
+
+   } else if (subtest == 1 && mode == I2c::SparkFunEnvCombo) {
+      printf_P(PSTR(" BM280 ... "));
+      if (!bm280.begin()) {
+         printf_P(PSTR("E1"));
+         return -1;
+      }
+      printf_P(PSTR("OK, ENS160 ... "));
+      if (!ens160.begin()) {
+         printf_P(PSTR("E2"));
+         return -1;
+      }
+      if (!ens160.setMode(ENS160_OPMODE_STD)) {
+         printf_P(PSTR("E3"));
+         return -1;
+      }
+      if (!ens160.set_envdata(25.0, 65)) {
+         printf_P(PSTR("E4"));
+         return -1;
+      }
+
+      printf_P(PSTR("OK"));
+      float accTemp = 0, accHumidity = 0;
+      int8_t accCount = -1;
+
+      do {
+         // BME280 
+         float p = bm280.readPressure();
+         printf_P(PSTR("\n => BM280: P= %.3fbar"), (double)p / 100000.0);
+         float t = bm280.readTemperature();
+         printf_P(PSTR(", T= %.2f°C"), (double)t);
+         float h = bm280.readHumidity();
+         printf_P(PSTR(", H= %.2f%%"), (double)h);
+
+         if (accCount >= 0 && !isnanf(h) && !isnan(t)) {
+            accTemp += t;
+            accHumidity += h;
+            accCount++;
+         }
+
+         bm280.setSampling(
+            Adafruit_BME280::MODE_NORMAL,
+            Adafruit_BME280::SAMPLING_X16,
+            Adafruit_BME280::SAMPLING_X16,
+            Adafruit_BME280::SAMPLING_X16,
+            Adafruit_BME280::FILTER_OFF,
+            Adafruit_BME280::STANDBY_MS_1000
+         );
+
+         // ENS160 only activated every 32s to avoid wrong temperature measuerment
+         // if ES160 would be continously active, the temperature would be 4°C higher
+         // -> ES160 has not enough distance to BM280
+         // This solution causes only a +0.3°C higher temperatur value
+         if (accCount < 0 || accCount >= 32) {
+            printf_P(PSTR("  | ENS160 ("));
+            if (accCount > 0) { 
+               h = accHumidity / accCount;
+               t = accTemp / accCount;
+               accTemp = 0;
+               accHumidity = 0;
+            }
+            accCount = 0;            
+            if (!ens160.set_envdata(t, h)) {
+               printf_P(PSTR("E1)"));
+            } else {
+               printf_P(PSTR("%.1f°C/%.1f%%): "), (double)t, (double)h);
+               if (!ens160.setMode(ENS160_OPMODE_STD)) {
+                  printf_P(PSTR("E2"));
+               } else {
+                  for (uint8_t i = 0; i < 100; i++) {
+                     _delay_ms(15);
+                     uint8_t status;
+                     if (ens160.readStatus(&status)) {
+                        if (status & ENS160_DATA_STATUS_NEWDAT) {
+                           ENS160_DATA data;
+                           if (ens160.readData(&data)) {
+                              printf_P(PSTR(" aqi=%d("), data.aqi);
+                              switch(data.aqi) {
+                                 case 1: printf_P(PSTR("excellent")); break;
+                                 case 2: printf_P(PSTR("good")); break;
+                                 case 3: printf_P(PSTR("moderate")); break;
+                                 case 4: printf_P(PSTR("poor")); break;
+                                 case 5: printf_P(PSTR("unhealthy")); break;
+                                 default: printf_P(PSTR("?")); break;
+                              }
+                              printf_P(PSTR("), tvoc=%dppb"), data.tvoc);
+                              printf_P(PSTR(", eco2=%d("), data.eco2);
+                              if (data.eco2 < 400) {
+                                 printf_P(PSTR("?"));
+                              } else if (data.eco2 < 600) {
+                                 printf_P(PSTR("excellent"));
+                              } else if (data.eco2 < 800) {
+                                 printf_P(PSTR("good"));
+                              } else if (data.eco2 < 1000) {
+                                 printf_P(PSTR("fair"));
+                              } else if (data.eco2 < 1500) {
+                                 printf_P(PSTR("poor"));
+                              } else {
+                                 printf_P(PSTR("bad"));
+                              }
+                              printf_P(PSTR(")"));
+                           }
+                           break;
+                        }
+                     }
+                  }
+               }
+               if (!ens160.setMode(ENS160_OPMODE_IDLE)) {
+                  printf_P(PSTR("E3"));
+               }
+            }
+         }
+
+      } while (wait(1000) == EOF);
+
+   } else if (subtest == 1 && mode == I2c::Master) {
+      if (!master.begin(0x01)) {
+         printf_P(PSTR("E1"));
+         return -1;
+      }
+      do {
+         uint8_t buffer[1];
+         // read poti
+         ADCSRA |= (1 << ADSC); // start ADC
+         while (ADCSRA & (1 << ADSC)) {} // wait for result
+         buffer[0] = ADCH;
+         printf_P(PSTR("\n write 0x%02x"), buffer[0]);
+         if (!master.write(buffer, 1)) {
+            printf_P(PSTR(" -> ERROR"));
+         }
+         printf_P(PSTR(",  read "));
+         if (master.read(buffer, 1)) {
+            printf_P(PSTR("0x%02x"), buffer[0]);   
+         } else {
+            printf_P(PSTR(" -> ERROR"));
+         }
+      } while (wait(1000) == EOF);
+      master.end();
+
+   } else if (subtest == 1 && mode == I2c::Slave) {
+      if (!slave.begin(0x01, false)) {
+         printf_P(PSTR("E1"));
+         return -1;
+      }
+      do {
+         int fromMaster = slave.read();
+         if (fromMaster != EOF) {
+            ADCSRA |= (1 << ADSC); // start ADC
+            while (ADCSRA & (1 << ADSC)) {} // wait for result
+            slave.write(ADCH);
+            printf_P(PSTR("\n => from master: 0x%02x -> to master: 0x%02x"), fromMaster, ADCH);
+         }
+      } while (wait(0) == EOF);
+      slave.end();
+
+   } else {
+      printf_P(PSTR("end"));
+      return -1;
+   }
+   wait(500);
+   return 0;
+}
+
+void I2c::handleTwiIrq () {
+   if (mode == I2c::Slave) {
+      DDRD |= (1 << PD7);
+      PORTD |= (1 << PD7);
+      slave.handleTWIIsr();
+      PORTD &= ~(1 << PD7);
+   } else {
+      TWCR |= (1 << TWINT); // clear Interrupt Request
+   }
+}
+
+// uint16_t I2c::startRead (uint8_t address) {
+//    TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition
+//    while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+//    uint8_t sr = TWSR & 0xf8;
+//    if (sr != 0x08 && sr != 0x10) {
+//       return 0x0100 | sr;
+//    }
+
+//    TWDR = (address << 1) | 0x01; // address + READ (R/W = 1)
+//    TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW
+//    while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+//    sr = TWSR & 0xf8;
+//    if (sr != 0x40) {
+//       return 0x0200 | sr;
+//    }
+//    return 0;
+// }
+
+// uint16_t I2c::startWrite (uint8_t address) {
+//    TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition
+//    while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+//    uint8_t sr = TWSR & 0xf8;
+//    if (sr != 0x08 && sr != 0x10) {
+//       return 0x0300 | sr;
+//    }
+
+//    TWDR = (address << 1) | 0x00; // address + WRITE (R/W = 0)
+//    TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW
+//    while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+//    sr = TWSR & 0xf8;
+//    if (sr != 0x18) {
+//       return 0x0400 | sr;
+//    }
+//    return 0;
+// }
+
+// void I2c::stop () {
+//    TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
+// }
+
+// uint16_t I2c::writeData (uint8_t size, const uint8_t *data) {
+//    while (size-- > 0) {
+//       TWDR = *data++;
+//       TWCR = (1 << TWINT) | (1 << TWEN); // send data byte
+//       while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+//       uint8_t sr = TWSR & 0xf8;
+//       if (sr != 0x28) {
+//          return 0x0500 | sr;
+//       }
+//    }
+//    return 0;
+// }
+
+// uint16_t I2c::writeByte (uint8_t data) {
+//    return writeData(1, &data);
+// }
+
+// uint16_t I2c::readData (uint8_t size, uint8_t *data) {
+//    while (size-- > 0) {
+//       if (size > 0) {
+//          TWCR = (1 << TWEA) | (1 << TWINT) | (1 << TWEN); // read data byte with ACK enabled
+//       } else {
+//          TWCR = (1 << TWINT) | (1 << TWEN); // read data byte with ACK disabled
+//       }
+//       while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+//       uint8_t sr = TWSR & 0xf8;
+//       if ((size > 0 && sr != 0x50) || (size == 0 && sr != 0x58)) {
+//          return 0x0600 | sr;
+//       }
+//       *data++ = TWDR;
+//    }
+//    return 0;
+// }
+
+// int32_t I2c::compensateBm280T (int32_t adcT) {
+//    // int32_t var1, var2, t;
+//    // var1 = ((((adcT >> 3) - ((int32_t)bm280.digT[0] << 1))) * ((int32_t)bm280.digT[1])) >> 11;
+//    // var2 = (((((adcT >> 4) - ((int32_t)bm280.digT[0])) * ((adcT >> 4) - ((int32_t)bm280.digT[0]))) >> 12) * ((int32_t)bm280.digT[2])) >> 14;
+//    // bm280.tFine = var1 + var2;
+//    // t = (bm280.tFine * 5 + 128) >> 8;
+//    // return t;
+//    return -1;
+// }
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/i2c.hpp b/software/arduino-nano-5v/test_2024-07-23/src/units/i2c.hpp
new file mode 100644 (file)
index 0000000..e1f1802
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef I2C_HPP
+#define I2C_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include "../adafruit/bme280.h"
+#include "../adafruit/ens160.h"
+#include "../i2cmaster.hpp"
+#include "../i2cslave.hpp"
+
+
+class I2c : public TestUnit {
+   public:
+      typedef enum I2cMode { SparkFunEnvCombo, Master, Slave } I2cMode;
+
+   private:
+      I2cMode mode;
+      Adafruit_BME280 bm280;
+      ScioSense_ENS160 ens160;
+      I2cMaster master;
+      I2cSlave slave;
+
+   public:
+      bool enabled;
+
+   public:
+      I2c (I2cMode mode) { enabled = false; this->mode = mode; }
+      void tick1ms () { master.tick1ms(); slave.tick1ms(); }
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName ();
+      void handleTwiIrq ();
+      uint16_t startRead (uint8_t address);
+      uint16_t startWrite (uint8_t address);
+      void stop ();
+      uint16_t writeByte (uint8_t data);
+      uint16_t writeData (uint8_t size, const uint8_t *data);
+      uint16_t readData (uint8_t size, uint8_t *data);
+      int32_t compensateBm280T (int32_t adcT);
+};
+
+#endif
\ No newline at end of file
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/ieee485.cpp b/software/arduino-nano-5v/test_2024-07-23/src/units/ieee485.cpp
new file mode 100644 (file)
index 0000000..414efb2
--- /dev/null
@@ -0,0 +1,110 @@
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+#include <util/atomic.h>
+
+#include "ieee485.hpp"
+#include "../main.hpp"
+
+#ifdef __AVR_ATmega328P__
+
+// Nano-328P
+// ------------------------------------
+// IEE485 not supported (no UART1)
+
+void Ieee485::cleanup () {}
+int8_t Ieee485::run (uint8_t subtest) {
+   return -1;
+}
+
+#endif
+
+
+#ifdef __AVR_ATmega644P__
+
+// Nano-644
+// ------------------------------------
+// PB0 ... nRE .. Read enable
+// PB1 ...  DE .. Data enable
+
+#define SET_nRE  (PORTB |= (1 << PB0))
+#define CLR_nRE  (PORTB &= ~(1 << PB0))
+#define SET_DE   (PORTB |= (1 << PB1))
+#define CLR_DE   (PORTB &= ~(1 << PB1))
+
+void Ieee485::cleanup () {
+   enabled = 0;
+
+   ADMUX = 0;
+   ADCSRA = 0;
+
+   UCSR1A = 0;
+   UCSR1B = 0;
+   UCSR1C = 0;
+   UBRR1H = 0;
+   UBRR1L = 0;
+   PORTD &= ~(1 << PD2);
+   DDRB &= ~((1 << PB1) | (1 << PB0));
+   PORTB &= ~((1 << PB1) | (1 << PB0));
+}
+
+int8_t Ieee485::run (uint8_t subtest) {
+   if (subtest == 0) {
+      // Poti
+      ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V
+      ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128
+
+      // Modbus
+      SET_nRE;
+      CLR_DE;
+      DDRB |= (1 << PB1) | (1 << PB0);
+
+      // UART1 interface on Nano-644
+      PORTD |= (1 << PD2); // enable RxD1 pullup
+      UCSR1A = (1 << U2X1);
+      UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 <<TXEN1);
+      UCSR1C = (1 << UCSZ11) | ( 1<< UCSZ10);
+      // UCSR1C |= (1 <<UPM11); // even Parity
+      // UCSR1C |= (1 << UPM11) | (1 << UPM10); // odd Parity
+      UBRR1H = 0;
+      UBRR1L = F_CPU / 8 / 9600 - 1;
+
+      enabled = 1;
+      printf_P(PSTR("init"));
+
+   } else if (subtest == 1) {
+      CLR_nRE; CLR_DE;
+      while (wait(500) == EOF) {
+         ADCSRA |= (1 << ADSC); // start ADC
+         while (ADCSRA & (1 << ADSC)) {} // wait for result
+         UCSR1A |= (1 << TXC1);
+         SET_nRE;
+         SET_DE;
+         UDR1 = ADCH;
+         while ((UCSR1A & (1 << TXC1)) == 0) {}
+         CLR_DE;
+         CLR_nRE;
+         printf_P(PSTR("\n  => send Byte 0x%02x"), ADCH);
+         int b;
+         ATOMIC_BLOCK(ATOMIC_FORCEON) {
+            b = receivedByte;
+            receivedByte = -1; 
+         }
+         if (b >= 0) {
+            printf_P(PSTR("\n  => receive Byte: 0x%02x"), b);
+         }
+      }
+
+   } else {
+      printf_P(PSTR("end"));
+      return -1;
+   }
+
+   return 0;
+}
+
+void Ieee485::handleRxByte (uint8_t b) {
+   receivedByte = b;
+}
+
+#endif
\ No newline at end of file
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/ieee485.hpp b/software/arduino-nano-5v/test_2024-07-23/src/units/ieee485.hpp
new file mode 100644 (file)
index 0000000..677963f
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef IEEE485_HPP
+#define IEEE485_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Ieee485 : public TestUnit {
+   public:
+      uint8_t  enabled;
+      int16_t  receivedByte;
+
+   public:
+      Ieee485 () { enabled = 0; receivedByte = -1; }
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual const char *getName () { return PSTR("IEEE485"); }
+      void handleRxByte (uint8_t);
+};
+
+#endif
\ No newline at end of file
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/lcd.cpp b/software/arduino-nano-5v/test_2024-07-23/src/units/lcd.cpp
new file mode 100644 (file)
index 0000000..fa24879
--- /dev/null
@@ -0,0 +1,257 @@
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+#include "lcd.hpp"
+#include "../main.hpp"
+
+#ifdef __AVR_ATmega644P__
+
+   // Nano-644
+   // ---------------------------------------------------------------
+   // PA3 ..... E    --> LCD Enable (Verbindung via J25 erforderlich)
+   // PD6 ..... R/W  --> Read/Write: Read=1, Write=0
+   // PD7 ..... RS   --> Register Select: Command=0, Data=1
+   // PB7:0 ... Data --> Achtung von 5V LCD nicht lesen!
+
+
+   // #define LCD_3V3
+   #ifdef LCD_3V3
+      #define DATA_PIN  PINB
+   #endif
+
+   void Lcd::initDDR () {
+      DDRA |=  (1 << PA3);
+      DDRB = 0xff;
+      DDRD |= (1 << PD7) | (1 << PD6);
+   }
+
+   void Lcd::cleanup () {
+      DDRA &=  ~(1 << PA3);
+      DDRB = 0;
+      DDRD &= ~((1 << PD7) | (1 << PD6));
+   }
+
+   void Lcd::setRS () { PORTD |= (1 << PD7); }
+   void Lcd::clrRS () { PORTD &= ~(1 << PD7); }
+   void Lcd::setRW () { PORTD |= (1 << PD6); }
+   void Lcd::clrRW () { PORTD &= ~(1 << PD6); }
+   void Lcd::setE ()  { PORTA |= (1 << PA3); }
+   void Lcd::clrE ()  { PORTA &= ~(1 << PA3); }
+
+   void Lcd::setData (uint8_t data) {
+      PORTB = data;
+   }
+#endif
+
+#ifdef __AVR_ATmega328P__
+
+   // Arduino Nano (5V)
+   // ---------------------------------------------------------------
+   // PC3 ..... E    --> LCD Enable (Verbindung via J25 erforderlich)
+   // PD3 ..... R/W  --> Read/Write: Read=1, Write=0
+   // PD2 ..... RS   --> Register Select: Command=0, Data=1
+   // PD4 ..... Data0
+   // PB0 ..... Data1
+   // PD7 ..... Data2
+   // PD6 ..... Data3
+   // PB2 ..... Data4
+   // PB3 ..... Data5
+   // PB4 ..... Data6
+   // PB5 ..... Data7
+
+   void Lcd::initDDR () {
+      DDRB |= (1 << PB5) | (1 << PB4) | (1 << PB3) | (1 << PB2) | (1 << PB0);
+      DDRD |= (1 << PD7) | (1 << PD6) | (1 << PD4) | (1 << PD3) | (1 << PD2);
+   }
+   
+   void Lcd::cleanup () {
+      DDRB &= ~((1 << PB5) | (1 << PB4) | (1 << PB3) | (1 << PB2) | (1 << PB0));
+      DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD4) | (1 << PD3) | (1 << PD2));
+   }
+
+   void Lcd::setRS () { PORTD |= (1 << PD2); }
+   void Lcd::clrRS () { PORTD &= ~(1 << PD2); }
+   void Lcd::setRW () { PORTD |= (1 << PD3); }
+   void Lcd::clrRW () { PORTD &= ~(1 << PD3); }
+   void Lcd::setE ()  { PORTC |= (1 << PC3); }
+   void Lcd::clrE ()  { PORTC &= ~(1 << PC3); }
+
+   void Lcd::setData (uint8_t data) {
+      if (data & 0x01) PORTD |= (1 << PD4); else PORTD &= ~((1 << PD4));
+      if (data & 0x02) PORTB |= (1 << PB0); else PORTB &= ~((1 << PB0));
+      if (data & 0x04) PORTD |= (1 << PD7); else PORTD &= ~((1 << PD7));
+      if (data & 0x08) PORTD |= (1 << PD6); else PORTD &= ~((1 << PD6));
+      if (data & 0x10) PORTB |= (1 << PB2); else PORTB &= ~((1 << PB2));
+      if (data & 0x20) PORTB |= (1 << PB3); else PORTB &= ~((1 << PB3));
+      if (data & 0x40) PORTB |= (1 << PB4); else PORTB &= ~((1 << PB4));
+      if (data & 0x80) PORTB |= (1 << PB5); else PORTB &= ~((1 << PB5));
+   }
+#endif   
+
+// Befehle für das Display
+
+#define DISP_CLEAR  0b00000001   // Display clear   
+#define DISP_ON     0b00001111   // Display on      
+#define DISP_OFF    0b00001011   // Display off     
+#define CURSOR_ON   0b00001111   // Cursor on       
+#define CURSOR_OFF  0b00001101   // Cursor off      
+#define BLINK_ON    0b00001111   // Cursor Blink    
+#define BLINK_OFF   0b00001110   // Cursor No Blink 
+
+int8_t Lcd::run (uint8_t subtest) {
+   if (subtest == 0) {
+      init();
+      #ifdef LCD_3V3
+         printf_P(PSTR("init 3.3V LCD"));
+      #else
+         printf_P(PSTR("init 5V LCD"));
+      #endif
+
+   } else if (subtest == 1) {
+      for (uint8_t i = 0; i < 20 * 4; i++) {
+         char c = (char)(i + 32);
+         if (i % 20 == 0) {
+            setCursor(i / 20 + 1, 1);
+         }
+         writeData(c);
+         while (isBusy()) {};
+      }
+      // setCursor(1, 1);
+      // writeString(" 1234567890<>,;.:-_#+");
+      // setCursor(2, 1);
+      // writeString("abcdefghijklmnopqrst");
+      // setCursor(3, 1);
+      // writeString("uvwxyzABCDEFGHIJKLMN");
+      // setCursor(4, 1);
+      // writeString("OPQRSTUVWXYZ ");
+      printf_P(PSTR("LCD beschrieben"));
+      while (wait(1) == EOF) {
+      }
+
+   } else {
+      printf_P(PSTR("end"));
+      return -1;
+   }
+   wait(500);
+   return 0;
+}
+
+void Lcd::init () {
+   clrRW();
+   clrRS();
+   clrE();
+   setData(0);
+   initDDR();
+   _delay_ms(16); // min 15ms warten für Reset des Displays
+
+   setData( 0b00111011 );   // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display
+   clrRW();                 // write
+   clrRS();                    // command
+   setE();                  // E = 1 (transfer start)
+   _delay_us(10);                // min. 10us 
+   clrE();                  // E = 0 (transfer end)
+   _delay_ms(5);            // min. 4.1ms
+
+   setData( 0b00111011 );   // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display
+   clrRW();                 // write
+   clrRS();                    // command
+   setE();                  // E = 1 (transfer start)
+   _delay_us(10);                // min. 10us 
+   clrE();                  // E = 0 (transfer end)
+   _delay_us(100);          // min. 100us
+
+   setData( 0b00111011 );   // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display
+   clrRW();                 // write
+   clrRS();                    // command
+   setE();                  // E = 1 (transfer start)
+   _delay_us(10);                // min. 10us 
+   clrE();                  // E = 0 (transfer end)
+   _delay_us(100);          // min. 100us
+
+   writeCommand(DISP_OFF);     // Display aus
+   while(isBusy()) {};
+   writeCommand(DISP_ON);      // Display ein
+   while(isBusy()) {};
+   writeCommand( BLINK_OFF & CURSOR_OFF);      // Blink aus und Cursor aus
+   while(isBusy()) {};
+   writeCommand(DISP_CLEAR);// Clear display
+   while(isBusy()) {};
+}
+
+uint8_t Lcd::isBusy () {
+   #ifdef LCD_3V3
+      // DIR_DATA_PORT = 0;
+      // SET_RW_PIN; // read
+      // CLR_RS_PIN;   // command
+      // SET_E_PIN;  // E = 1 (transfer start)
+      // _delay_us(10);
+      // uint8_t busy = DATA_PIN & 0x80; // read bit 7 (busy bit)
+      // CLR_E_PIN;  // E = 0 (transfer end)
+      // CLR_RW_PIN;
+      // DIR_DATA_PORT = 0xff;
+      // return busy != 0;
+      _delay_us(200);
+      return 0;
+   #else
+      _delay_us(200);
+      return 0;
+   #endif
+   
+}
+
+void Lcd::writeCommand (uint8_t cmd) {
+   setData(cmd);
+   clrRW();                 // write
+   clrRS();                    // command
+   setE();                  // E = 1 (transfer start)
+   _delay_us(10);                // min. 10us 
+   clrE();                  // E = 0 (transfer end)
+   writeData(0);
+}
+
+void Lcd::setDDRamAddr (uint8_t address) {
+   setData(address | 0x80);
+   clrRW();                 // write
+   clrRS();                    // command
+   setE();                  // E = 1 (transfer start)
+   _delay_us(10);                // min. 10us 
+   clrE();                  // E = 0 (transfer end)
+   _delay_us(10);           // min. 10us
+   writeData(0);
+}
+
+void Lcd::writeString (const char *s) {
+   while (*s) {
+      writeData(*s++);
+      while (isBusy()) {};
+   }
+}
+
+void Lcd::writeData (uint8_t data) {
+   setData(data);
+   setRS();                    // data
+   clrRW();                 // write
+   setE();                  // E = 1 (transfer start)
+   _delay_us(10);                // min. 10us 
+   clrE();                  // E = 0 (transfer end)
+   _delay_us(10);           // min. 10us
+   clrRS();
+   writeData(0);
+}
+
+void Lcd::setCursor (uint8_t row, uint8_t column) {
+   uint8_t b;
+       if (column > 20) {
+               return;
+       }
+       switch (row) {
+               case 1: b = 0x00 + column - 1; break;
+               case 2: b = 0x40 + column - 1; break;
+      case 3: b = 0x14 + column - 1; break;
+      case 4: b = 0x54 + column - 1; break;
+               default: return;
+       }
+       setDDRamAddr(b);
+   while (isBusy()) {};
+}
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/lcd.hpp b/software/arduino-nano-5v/test_2024-07-23/src/units/lcd.hpp
new file mode 100644 (file)
index 0000000..142a13f
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef LCD_HPP
+#define LCD_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Lcd : public TestUnit {
+   public:
+      Lcd () {};
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("Lcd"); }
+   
+   private:
+      void init ();
+      void initDDR ();
+      uint8_t isBusy ();
+      void writeCommand (uint8_t);
+      void setDDRamAddr (uint8_t);
+      void writeString (const char *s);
+      void writeData (uint8_t);
+      void setCursor (uint8_t row, uint8_t column);
+      void setRS ();
+      void clrRS ();
+      void setRW ();
+      void clrRW ();
+      void setE ();
+      void clrE ();
+      void setData (uint8_t data);
+
+};
+
+#endif
\ No newline at end of file
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/led.cpp b/software/arduino-nano-5v/test_2024-07-23/src/units/led.cpp
new file mode 100644 (file)
index 0000000..6dc6253
--- /dev/null
@@ -0,0 +1,134 @@
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+#include "led.hpp"
+#include "../main.hpp"
+
+#ifdef __AVR_ATmega644P__
+
+   // Nano-644
+   // ---------------------------------------------------------------
+   // PD4 ..... Red
+   // PD5 ..... Orange/Yellow
+   // PD6 ..... Green
+   // PD7 ..... Blue
+
+   void Led::initDDR () {
+      DDRD |= (1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4);
+   }
+
+   void Led::cleanup () {
+      DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4));
+      PORTD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4));
+   }
+
+   void Led::setLed (LED led, bool on) {
+      if (on) {
+         switch(led) {
+            case RED: PORTD |= (1 << PD4); break;
+            case ORANGE: PORTD |= (1 << PD5); break;
+            case GREEN: PORTD |= (1 << PD6); break;
+            case BLUE: PORTD |= (1 << PD7); break;
+         }
+      } else {
+         switch(led) {
+            case RED: PORTD &= ~(1 << PD4); break;
+            case ORANGE: PORTD &= ~(1 << PD5); break;
+            case GREEN: PORTD &= ~(1 << PD6); break;
+            case BLUE: PORTD &= ~(1 << PD7); break;
+         }
+      }
+   }
+
+   void Led::ledToggle (LED led) {
+      case RED: PORTD ^= (1 << PD4); break;
+      case ORANGE: PORTD ^= (1 << PD5); break;
+      case GREEN: PORTD ^= (1 << PD6); break;
+      case BLUE: PORTD ^= (1 << PD7); break;
+   }
+
+#endif
+
+#ifdef __AVR_ATmega328P__
+
+   // Arduino-Nano-5V
+   // ---------------------------------------------------------------
+   // PD5 ..... Red
+   // PB1 ..... Orange/Yellow
+   // PD3 ..... Green
+   // PD2 ..... Blue
+
+   void Led::initDDR () {
+      DDRD |= (1 << PD5) | (1 << PD3) | (1 << PD2);
+      DDRB |= (1 << PB1);
+   }
+
+   void Led::cleanup () {
+      DDRD &= ~((1 << PD5) | (1 << PD3) | (1 << PD2));
+      DDRB &= ~(1 << PB1);
+      PORTD &= ~((1 << PD5) | (1 << PD3) | (1 << PD2));
+      PORTB &= ~(1 << PB1);
+   }
+
+   void Led::setLed (LED led, bool on) {
+      if (on) {
+         switch(led) {
+            case RED: PORTD |= (1 << PD5); break;
+            case ORANGE: PORTB |= (1 << PB1); break;
+            case GREEN: PORTD |= (1 << PD3); break;
+            case BLUE: PORTD |= (1 << PD2); break;
+         }
+      } else {
+         switch(led) {
+            case RED: PORTD &= ~(1 << PD5); break;
+            case ORANGE: PORTB &= ~(1 << PB1); break;
+            case GREEN: PORTD &= ~(1 << PD3); break;
+            case BLUE: PORTD &= ~(1 << PD2); break;
+         }
+      }
+   }
+
+   void Led::ledToggle (LED led) {
+      switch(led) {
+         case RED: PORTD ^= (1 << PD5); break;
+         case ORANGE: PORTB ^= (1 << PB1); break;
+         case GREEN: PORTD ^= (1 << PD3); break;
+         case BLUE: PORTD ^= (1 << PD2); break;
+       }
+   }
+
+#endif
+
+void Led::ledOn (LED led) {
+   setLed(led, true);
+}
+
+void Led::ledOff (LED led) {
+   setLed(led, false);
+}
+
+
+int8_t Led::run (uint8_t subtest) {
+   if (subtest == 0) {
+      initDDR();
+      printf_P(PSTR("init"));
+
+   } else if (subtest <= 16) {
+      subtest = (subtest - 1) % 4;
+      switch (subtest) {
+         case 0: ledOff(BLUE); ledOn(RED); break;
+         case 1: ledOff(RED); ledOn(ORANGE); break;
+         case 2: ledOff(ORANGE); ledOn(GREEN); break;
+         case 3: ledOff(GREEN); ledOn(BLUE); break;
+      }
+      printf_P(PSTR("Test LED PD%d"), subtest + 4);
+
+   } else {
+      printf_P(PSTR("end"));
+      return -1;
+   }
+   wait(500);
+   return 0;
+}
+
diff --git a/software/arduino-nano-5v/test_2024-07-23/src/units/led.hpp b/software/arduino-nano-5v/test_2024-07-23/src/units/led.hpp
new file mode 100644 (file)
index 0000000..0a28f2d
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef LED_HPP
+#define LED_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Led : public TestUnit {
+   public:
+      enum LED { RED, ORANGE, GREEN, BLUE };
+
+
+   public:
+      Led () {};
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("Led"); }
+
+      void setLed (LED led, bool on);
+      void ledOn (LED led);
+      void ledOff (LED led);
+      void ledToggle (LED led);
+
+   private:
+      void initDDR ();
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/.gdb_history b/software/nano-644/test_2024-07-23/.gdb_history
new file mode 100644 (file)
index 0000000..3339046
--- /dev/null
@@ -0,0 +1,9 @@
+target remote :1234
+layout split
+stepi
+quit
+target remote :1234
+layout split
+stepi
+b *main+9
+quit
diff --git a/software/nano-644/test_2024-07-23/.gdbinit b/software/nano-644/test_2024-07-23/.gdbinit
new file mode 100644 (file)
index 0000000..139597f
--- /dev/null
@@ -0,0 +1,2 @@
+
+
diff --git a/software/nano-644/test_2024-07-23/.gitignore b/software/nano-644/test_2024-07-23/.gitignore
new file mode 100644 (file)
index 0000000..a959910
--- /dev/null
@@ -0,0 +1,4 @@
+.depend
+**/build
+**/dist
+**/sim
diff --git a/software/nano-644/test_2024-07-23/.vscode/c_cpp_properties.json b/software/nano-644/test_2024-07-23/.vscode/c_cpp_properties.json
new file mode 100644 (file)
index 0000000..3a57c79
--- /dev/null
@@ -0,0 +1,18 @@
+{
+    "configurations": [
+        {
+            "name": "Linux AVR",
+            "includePath": [
+                "/usr/lib/avr/include/**",
+                "/usr/lib/gcc/avr/**"
+            ],
+            "defines": [],
+            "compilerPath": "/usr/bin/avr-gcc",
+            "compilerArgs": [ "-mmcu=atmega644p", "-DF_CPU=12000000", "-Os" ],
+            "cStandard": "gnu11",
+            "cppStandard": "gnu++11",
+            "intelliSenseMode": "linux-gcc-x64"
+        }
+    ],
+    "version": 4
+}
diff --git a/software/nano-644/test_2024-07-23/.vscode/launch.json b/software/nano-644/test_2024-07-23/.vscode/launch.json
new file mode 100644 (file)
index 0000000..f29cf2e
--- /dev/null
@@ -0,0 +1,37 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Build",
+            // "request": "launch",
+            "type": "node-terminal",
+            "preLaunchTask": "build"
+        },{
+            "name": "Flash",
+            // "request": "launch",
+            "type": "node-terminal",
+            "preLaunchTask": "flash"
+        },{
+            "name": "Clean",
+            // "request": "launch",
+            "type": "node-terminal",
+            "preLaunchTask": "clean"
+        },{
+            // es muss mit simuc --board arduino dist/programm.elf der Simulator 
+            // gestartet werden. Dessen gdb-stub Ã¶ffnet auf localhost:1234 einen Port
+            "name": "Debug (simuc)",
+            "type": "cppdbg",
+            "request": "launch",
+            "program": "${workspaceFolder}/sim/atmega328p.elf",
+            "cwd": "${workspaceFolder}",
+            "externalConsole": false,
+            "MIMode": "gdb",
+            "miDebuggerPath": "/usr/bin/avr-gdb",
+            "miDebuggerServerAddress": ":1234",
+            "preLaunchTask": "build"
+        }
+    ]
+}
diff --git a/software/nano-644/test_2024-07-23/.vscode/settings.json b/software/nano-644/test_2024-07-23/.vscode/settings.json
new file mode 100644 (file)
index 0000000..b2e94c9
--- /dev/null
@@ -0,0 +1,26 @@
+{
+    "[c]": {
+        "editor.insertSpaces": true,
+        "editor.tabSize": 3,
+        "editor.detectIndentation": false
+    },
+    "[cpp]": {
+        "editor.insertSpaces": true,
+        "editor.tabSize": 3,
+        "editor.detectIndentation": false
+    },
+    "[h]": {
+        "editor.insertSpaces": true,
+        "editor.tabSize": 3,
+        "editor.detectIndentation": false
+    },
+    "[hpp]": {
+        "editor.insertSpaces": true,
+        "editor.tabSize": 3,
+        "editor.detectIndentation": false
+    },
+    "cSpell.words": [],
+    "cSpell.ignorePaths": [
+        "**/*.json", "**/*.c", "**/*.h", "**/*.cpp", "**/*.hpp", "**/Makefile"
+    ]
+}
diff --git a/software/nano-644/test_2024-07-23/.vscode/tasks.json b/software/nano-644/test_2024-07-23/.vscode/tasks.json
new file mode 100644 (file)
index 0000000..74fb1c7
--- /dev/null
@@ -0,0 +1,23 @@
+{
+    // See https://go.microsoft.com/fwlink/?LinkId=733558
+    // for the documentation about the tasks.json format
+    "version": "2.0.0",
+    "tasks": [{
+        "label": "build",
+        "type": "shell",
+        "command": "make",
+        "problemMatcher":[
+            "$gcc"
+        ]
+    },{
+        "label": "clean",
+        "type": "shell",
+        "command": "make",
+        "args": [ "clean" ],
+    },{
+        "label": "flash",
+        "type": "shell",
+        "command": "make",
+        "args": [ "flash" ],
+    }]
+}
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/Makefile b/software/nano-644/test_2024-07-23/Makefile
new file mode 100644 (file)
index 0000000..12a996a
--- /dev/null
@@ -0,0 +1,82 @@
+.PHONY: all info flash picocom clean
+$(shell mkdir -p dist >/dev/null)
+$(shell mkdir -p build >/dev/null)
+$(shell mkdir -p sim >/dev/null)
+
+NAME="test_2024-07-23_nano-644"
+SRC= $(wildcard src/*.c src/*.cpp src/*/*.cpp) 
+OBJ = $(SRC:src/%.c=build/%.o)
+OBJ_SIM = $(SRC:src/%.c=sim/%.o)
+
+DEVICE=atmega644p
+
+CC= avr-g++
+CFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=12000000 -c
+LFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=12000000 -Wl,-u,vfprintf -lprintf_flt -lm
+
+CFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=12000000 -g -c
+LFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=12000000 -g -Wl,-u,vfprintf -lprintf_flt -lm
+
+
+all: dist/$(NAME).elf dist/$(NAME).s dist/$(NAME).hex sim/$(NAME).elf sim/$(NAME).s info
+       
+info:
+       @echo
+       @avr-size --mcu=$(DEVICE) --format=avr dist/$(NAME).elf
+
+.depend: $(SRC)
+       $(CC) -MM $(SRC) > .depend
+
+-include $(DEPENDFILE) 
+
+dist/$(NAME).elf: .depend $(OBJ)
+       $(CC) $(LFLAGS) -o $@ $(OBJ)
+
+dist/%.s: dist/%.elf
+       avr-objdump -d $< > $@
+
+dist/%.hex: dist/%.elf
+       avr-objcopy -O ihex $(HEX_FLASH_FLAGS) $< $@
+
+sim/$(NAME).elf: .depend $(OBJ_SIM)
+               $(CC) $(LFLAGS_SIM) -o $@ $(OBJ_SIM)
+
+
+build/%.o: src/%.c
+       $(CC) $(CFLAGS) -o $@ $<
+
+sim/%.o: src/%.c
+       $(CC) $(CFLAGS_SIM) -o $@ $<
+
+sim/%.s: sim/%.elf
+       avr-objdump -d $< > $@
+
+simuc: sim/$(NAME).elf
+       simuc --board arduino $<
+
+gdb: sim/$(NAME).elf
+       avr-gdb $<
+
+isp:
+       avrdude -c usbasp -p m644p
+
+isp-flash: dist/$(NAME).elf all
+       avrdude -c usbasp -p m644p -e -U flash:w:$<
+
+flash: dist/$(NAME).elf all
+       avrdude -c arduino -p m644p -P /dev/ttyUSB0 -b 115200 -e -U flash:w:$<
+
+
+picocom:
+       # picocom sends CR for ENTER -> convert cr (\r) to lf (\n)
+       picocom -b 115200 --omap crlf /dev/ttyUSB0
+
+isp-fuse:
+       avrdude -c usbasp -p m644p -U lfuse:w:0xFF:m -U hfuse:w:0xD9:m -U efuse:w:0xFF:m -U lock:w:0xFF:m
+
+clean:
+       @rm -r dist
+       @rm -r build
+       @rm -r sim
+       @find . -type f -name ".depend" -exec rm {} \;
+       @echo "clean done"
diff --git a/software/nano-644/test_2024-07-23/README.md b/software/nano-644/test_2024-07-23/README.md
new file mode 100644 (file)
index 0000000..c4d5e14
--- /dev/null
@@ -0,0 +1 @@
+# Testprogramm
diff --git a/software/nano-644/test_2024-07-23/src/adafruit/bme280.cpp b/software/nano-644/test_2024-07-23/src/adafruit/bme280.cpp
new file mode 100644 (file)
index 0000000..95a0f49
--- /dev/null
@@ -0,0 +1,512 @@
+#include "bme280.h"
+#include <util/delay.h>
+#include <stdio.h>
+
+Adafruit_BME280 theBME280;
+Adafruit_BME280_Temp bm280TempSensor;
+Adafruit_BME280_Pressure bm280PressureSensor;
+Adafruit_BME280_Humidity bm280HumiditySensor;
+
+Adafruit_BME280::Adafruit_BME280() {
+       static I2cMaster i2cDevice;
+       t_fine_adjust = 0;
+       temp_sensor = &bm280TempSensor;
+       pressure_sensor = &bm280PressureSensor;
+       humidity_sensor = &bm280HumiditySensor;
+       i2c_dev = &i2cDevice;
+}
+
+bool Adafruit_BME280::begin (uint8_t addr) {
+       if (!i2c_dev->begin(addr)) {
+               return false;
+       }
+       return init();
+}
+
+bool Adafruit_BME280::init() {
+       _sensorID = read8(BME280_REGISTER_CHIPID);
+       if (_sensorID != 0x60) {
+               return false;
+       }
+       write8(BME280_REGISTER_SOFTRESET, 0xB6);
+       _delay_ms(10); // wait for chip to wake up.
+
+       // if chip is still reading calibration, delay
+       while (isReadingCalibration()) {
+               _delay_ms(10);
+       }
+
+       readCoefficients(); // read trimming parameters, see DS 4.2.2
+       setSampling(); // use defaults
+       _delay_ms(100);
+
+       return true;
+}
+
+/*!
+ *   @brief  setup sensor with given parameters / settings
+ *
+ *   This is simply a overload to the normal begin()-function, so SPI users
+ *   don't get confused about the library requiring an address.
+ *   @param mode the power mode to use for the sensor
+ *   @param tempSampling the temp samping rate to use
+ *   @param pressSampling the pressure sampling rate to use
+ *   @param humSampling the humidity sampling rate to use
+ *   @param filter the filter mode to use
+ *   @param duration the standby duration to use
+ */
+void Adafruit_BME280::setSampling(sensor_mode mode,
+                                                                                        sensor_sampling tempSampling,
+                                                                                        sensor_sampling pressSampling,
+                                                                                        sensor_sampling humSampling,
+                                                                                        sensor_filter filter,
+                                                                                        standby_duration duration) {
+  _measReg.mode = mode;
+  _measReg.osrs_t = tempSampling;
+  _measReg.osrs_p = pressSampling;
+
+  _humReg.osrs_h = humSampling;
+  _configReg.filter = filter;
+  _configReg.t_sb = duration;
+  _configReg.spi3w_en = 0;
+
+  // making sure sensor is in sleep mode before setting configuration
+  // as it otherwise may be ignored
+  write8(BME280_REGISTER_CONTROL, MODE_SLEEP);
+
+  // you must make sure to also set REGISTER_CONTROL after setting the
+  // CONTROLHUMID register, otherwise the values won't be applied (see
+  // DS 5.4.3)
+  write8(BME280_REGISTER_CONTROLHUMID, _humReg.get());
+  write8(BME280_REGISTER_CONFIG, _configReg.get());
+  write8(BME280_REGISTER_CONTROL, _measReg.get());
+}
+
+/*!
+ *   @brief  Writes an 8 bit value over I2C or SPI
+ *   @param reg the register address to write to
+ *   @param value the value to write to the register
+ */
+void Adafruit_BME280::write8(byte reg, byte value) {
+  byte buffer[2];
+  buffer[1] = value;
+  if (i2c_dev) {
+        buffer[0] = reg;
+        i2c_dev->write(buffer, 2);
+  }
+}
+
+/*!
+ *   @brief  Reads an 8 bit value over I2C or SPI
+ *   @param reg the register address to read from
+ *   @returns the data byte read from the device
+ */
+uint8_t Adafruit_BME280::read8(byte reg) {
+  uint8_t buffer[1];
+  if (i2c_dev) {
+        buffer[0] = uint8_t(reg);
+        i2c_dev->write_then_read(buffer, 1, buffer, 1);
+  }
+  return buffer[0];
+}
+
+/*!
+ *   @brief  Reads a 16 bit value over I2C or SPI
+ *   @param reg the register address to read from
+ *   @returns the 16 bit data value read from the device
+ */
+uint16_t Adafruit_BME280::read16(byte reg) {
+  uint8_t buffer[2];
+
+  if (i2c_dev) {
+        buffer[0] = uint8_t(reg);
+        i2c_dev->write_then_read(buffer, 1, buffer, 2);
+  }
+  return uint16_t(buffer[0]) << 8 | uint16_t(buffer[1]);
+}
+
+/*!
+ *   @brief  Reads a signed 16 bit little endian value over I2C or SPI
+ *   @param reg the register address to read from
+ *   @returns the 16 bit data value read from the device
+ */
+uint16_t Adafruit_BME280::read16_LE(byte reg) {
+  uint16_t temp = read16(reg);
+  return (temp >> 8) | (temp << 8);
+}
+
+/*!
+ *   @brief  Reads a signed 16 bit value over I2C or SPI
+ *   @param reg the register address to read from
+ *   @returns the 16 bit data value read from the device
+ */
+int16_t Adafruit_BME280::readS16(byte reg) { return (int16_t)read16(reg); }
+
+/*!
+ *   @brief  Reads a signed little endian 16 bit value over I2C or SPI
+ *   @param reg the register address to read from
+ *   @returns the 16 bit data value read from the device
+ */
+int16_t Adafruit_BME280::readS16_LE(byte reg) {
+  return (int16_t)read16_LE(reg);
+}
+
+/*!
+ *   @brief  Reads a 24 bit value over I2C
+ *   @param reg the register address to read from
+ *   @returns the 24 bit data value read from the device
+ */
+uint32_t Adafruit_BME280::read24(byte reg) {
+  uint8_t buffer[3];
+
+  if (i2c_dev) {
+        buffer[0] = uint8_t(reg);
+        i2c_dev->write_then_read(buffer, 1, buffer, 3);
+  }
+  return uint32_t(buffer[0]) << 16 | uint32_t(buffer[1]) << 8 |
+                       uint32_t(buffer[2]);
+}
+
+/*!
+ *  @brief  Take a new measurement (only possible in forced mode)
+        @returns true in case of success else false
+ */
+bool Adafruit_BME280::takeForcedMeasurement(void) {
+  bool return_value = false;
+  // If we are in forced mode, the BME sensor goes back to sleep after each
+  // measurement and we need to set it to forced mode once at this point, so
+  // it will take the next measurement and then return to sleep again.
+  // In normal mode simply does new measurements periodically.
+  if (_measReg.mode == MODE_FORCED) {
+        return_value = true;
+        // set to forced mode, i.e. "take next measurement"
+        write8(BME280_REGISTER_CONTROL, _measReg.get());
+        // Store current time to measure the timeout
+        uint32_t timeout_start = millis();
+        // wait until measurement has been completed, otherwise we would read the
+        // the values from the last measurement or the timeout occurred after 2 sec.
+        while (read8(BME280_REGISTER_STATUS) & 0x08) {
+               // In case of a timeout, stop the while loop
+               if ((millis() - timeout_start) > 2000) {
+                 return_value = false;
+                 break;
+               }
+               _delay_ms(1);
+        }
+  }
+  return return_value;
+}
+
+/*!
+ *   @brief  Reads the factory-set coefficients
+ */
+void Adafruit_BME280::readCoefficients(void) {
+  _bme280_calib.dig_T1 = read16_LE(BME280_REGISTER_DIG_T1);
+  _bme280_calib.dig_T2 = readS16_LE(BME280_REGISTER_DIG_T2);
+  _bme280_calib.dig_T3 = readS16_LE(BME280_REGISTER_DIG_T3);
+
+  _bme280_calib.dig_P1 = read16_LE(BME280_REGISTER_DIG_P1);
+  _bme280_calib.dig_P2 = readS16_LE(BME280_REGISTER_DIG_P2);
+  _bme280_calib.dig_P3 = readS16_LE(BME280_REGISTER_DIG_P3);
+  _bme280_calib.dig_P4 = readS16_LE(BME280_REGISTER_DIG_P4);
+  _bme280_calib.dig_P5 = readS16_LE(BME280_REGISTER_DIG_P5);
+  _bme280_calib.dig_P6 = readS16_LE(BME280_REGISTER_DIG_P6);
+  _bme280_calib.dig_P7 = readS16_LE(BME280_REGISTER_DIG_P7);
+  _bme280_calib.dig_P8 = readS16_LE(BME280_REGISTER_DIG_P8);
+  _bme280_calib.dig_P9 = readS16_LE(BME280_REGISTER_DIG_P9);
+
+  _bme280_calib.dig_H1 = read8(BME280_REGISTER_DIG_H1);
+  _bme280_calib.dig_H2 = readS16_LE(BME280_REGISTER_DIG_H2);
+  _bme280_calib.dig_H3 = read8(BME280_REGISTER_DIG_H3);
+  _bme280_calib.dig_H4 = ((int8_t)read8(BME280_REGISTER_DIG_H4) << 4) |
+                                                                (read8(BME280_REGISTER_DIG_H4 + 1) & 0xF);
+  _bme280_calib.dig_H5 = ((int8_t)read8(BME280_REGISTER_DIG_H5 + 1) << 4) |
+                                                                (read8(BME280_REGISTER_DIG_H5) >> 4);
+  _bme280_calib.dig_H6 = (int8_t)read8(BME280_REGISTER_DIG_H6);
+}
+
+/*!
+ *   @brief return true if chip is busy reading cal data
+ *   @returns true if reading calibration, false otherwise
+ */
+bool Adafruit_BME280::isReadingCalibration(void) {
+  uint8_t const rStatus = read8(BME280_REGISTER_STATUS);
+
+  return (rStatus & (1 << 0)) != 0;
+}
+
+/*!
+ *   @brief  Returns the temperature from the sensor
+ *   @returns the temperature read from the device
+ */
+float Adafruit_BME280::readTemperature(void) {
+  int32_t var1, var2;
+
+  int32_t adc_T = read24(BME280_REGISTER_TEMPDATA);
+  if (adc_T == 0x800000) // value in case temp measurement was disabled
+        return NAN;
+  adc_T >>= 4;
+
+  var1 = (int32_t)((adc_T / 8) - ((int32_t)_bme280_calib.dig_T1 * 2));
+  var1 = (var1 * ((int32_t)_bme280_calib.dig_T2)) / 2048;
+  var2 = (int32_t)((adc_T / 16) - ((int32_t)_bme280_calib.dig_T1));
+  var2 = (((var2 * var2) / 4096) * ((int32_t)_bme280_calib.dig_T3)) / 16384;
+
+  t_fine = var1 + var2 + t_fine_adjust;
+
+  int32_t T = (t_fine * 5 + 128) / 256;
+
+  return (float)T / 100;
+}
+
+/*!
+ *   @brief  Returns the pressure from the sensor
+ *   @returns the pressure value (in Pascal) read from the device
+ */
+float Adafruit_BME280::readPressure(void) {
+  int64_t var1, var2, var3, var4;
+
+  readTemperature(); // must be done first to get t_fine
+
+  int32_t adc_P = read24(BME280_REGISTER_PRESSUREDATA);
+  if (adc_P == 0x800000) // value in case pressure measurement was disabled
+        return NAN;
+  adc_P >>= 4;
+
+  var1 = ((int64_t)t_fine) - 128000;
+  var2 = var1 * var1 * (int64_t)_bme280_calib.dig_P6;
+  var2 = var2 + ((var1 * (int64_t)_bme280_calib.dig_P5) * 131072);
+  var2 = var2 + (((int64_t)_bme280_calib.dig_P4) * 34359738368);
+  var1 = ((var1 * var1 * (int64_t)_bme280_calib.dig_P3) / 256) +
+                       ((var1 * ((int64_t)_bme280_calib.dig_P2) * 4096));
+  var3 = ((int64_t)1) * 140737488355328;
+  var1 = (var3 + var1) * ((int64_t)_bme280_calib.dig_P1) / 8589934592;
+
+  if (var1 == 0) {
+        return 0; // avoid exception caused by division by zero
+  }
+
+  var4 = 1048576 - adc_P;
+  var4 = (((var4 * 2147483648UL) - var2) * 3125) / var1;
+  var1 = (((int64_t)_bme280_calib.dig_P9) * (var4 / 8192) * (var4 / 8192)) /
+                       33554432;
+  var2 = (((int64_t)_bme280_calib.dig_P8) * var4) / 524288;
+  var4 = ((var4 + var1 + var2) / 256) + (((int64_t)_bme280_calib.dig_P7) * 16);
+
+  float P = var4 / 256.0;
+
+  return P;
+}
+
+/*!
+ *  @brief  Returns the humidity from the sensor
+ *  @returns the humidity value read from the device
+ */
+float Adafruit_BME280::readHumidity(void) {
+  int32_t var1, var2, var3, var4, var5;
+
+  readTemperature(); // must be done first to get t_fine
+
+  int32_t adc_H = read16(BME280_REGISTER_HUMIDDATA);
+  if (adc_H == 0x8000) // value in case humidity measurement was disabled
+        return NAN;
+
+  var1 = t_fine - ((int32_t)76800);
+  var2 = (int32_t)(adc_H * 16384);
+  var3 = (int32_t)(((int32_t)_bme280_calib.dig_H4) * 1048576);
+  var4 = ((int32_t)_bme280_calib.dig_H5) * var1;
+  var5 = (((var2 - var3) - var4) + (int32_t)16384) / 32768;
+  var2 = (var1 * ((int32_t)_bme280_calib.dig_H6)) / 1024;
+  var3 = (var1 * ((int32_t)_bme280_calib.dig_H3)) / 2048;
+  var4 = ((var2 * (var3 + (int32_t)32768)) / 1024) + (int32_t)2097152;
+  var2 = ((var4 * ((int32_t)_bme280_calib.dig_H2)) + 8192) / 16384;
+  var3 = var5 * var2;
+  var4 = ((var3 / 32768) * (var3 / 32768)) / 128;
+  var5 = var3 - ((var4 * ((int32_t)_bme280_calib.dig_H1)) / 16);
+  var5 = (var5 < 0 ? 0 : var5);
+  var5 = (var5 > 419430400 ? 419430400 : var5);
+  uint32_t H = (uint32_t)(var5 / 4096);
+
+  return (float)H / 1024.0;
+}
+
+/*!
+ *   Calculates the altitude (in meters) from the specified atmospheric
+ *   pressure (in hPa), and sea-level pressure (in hPa).
+ *   @param  seaLevel      Sea-level pressure in hPa
+ *   @returns the altitude value read from the device
+ */
+float Adafruit_BME280::readAltitude(float seaLevel) {
+  // Equation taken from BMP180 datasheet (page 16):
+  //  http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
+
+  // Note that using the equation from wikipedia can give bad results
+  // at high altitude. See this thread for more information:
+  //  http://forums.adafruit.com/viewtopic.php?f=22&t=58064
+
+  float atmospheric = readPressure() / 100.0F;
+  return 44330.0 * (1.0 - pow(atmospheric / seaLevel, 0.1903));
+}
+
+/*!
+ *   Calculates the pressure at sea level (in hPa) from the specified
+ * altitude (in meters), and atmospheric pressure (in hPa).
+ *   @param  altitude      Altitude in meters
+ *   @param  atmospheric   Atmospheric pressure in hPa
+ *   @returns the pressure at sea level (in hPa) from the specified altitude
+ */
+float Adafruit_BME280::seaLevelForAltitude(float altitude, float atmospheric) {
+  // Equation taken from BMP180 datasheet (page 17):
+  //  http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
+
+  // Note that using the equation from wikipedia can give bad results
+  // at high altitude. See this thread for more information:
+  //  http://forums.adafruit.com/viewtopic.php?f=22&t=58064
+
+  return atmospheric / pow(1.0 - (altitude / 44330.0), 5.255);
+}
+
+/*!
+ *   Returns Sensor ID found by init() for diagnostics
+ *   @returns Sensor ID 0x60 for BME280, 0x56, 0x57, 0x58 BMP280
+ */
+uint32_t Adafruit_BME280::sensorID(void) { return _sensorID; }
+
+/*!
+ *   Returns the current temperature compensation value in degrees Celsius
+ *   @returns the current temperature compensation value in degrees Celsius
+ */
+float Adafruit_BME280::getTemperatureCompensation(void) {
+  return float((t_fine_adjust * 5) >> 8) / 100.0;
+};
+
+/*!
+ *  Sets a value to be added to each temperature reading. This adjusted
+ *  temperature is used in pressure and humidity readings.
+ *  @param  adjustment  Value to be added to each temperature reading in Celsius
+ */
+void Adafruit_BME280::setTemperatureCompensation(float adjustment) {
+  // convert the value in C into and adjustment to t_fine
+  t_fine_adjust = ((int32_t(adjustment * 100) << 8)) / 5;
+};
+
+
+/**************************************************************************/
+/*!
+        @brief  Gets the sensor_t data for the BME280's temperature sensor
+*/
+/**************************************************************************/
+void Adafruit_BME280_Temp::getSensor(sensor_t *sensor) {
+  /* Clear the sensor_t object */
+  memset(sensor, 0, sizeof(sensor_t));
+
+  /* Insert the sensor name in the fixed length char array */
+  strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1);
+  sensor->name[sizeof(sensor->name) - 1] = 0;
+  sensor->version = 1;
+  sensor->sensor_id = _sensorID;
+  sensor->type = SENSOR_TYPE_AMBIENT_TEMPERATURE;
+  sensor->min_delay = 0;
+  sensor->min_value = -40.0; /* Temperature range -40 ~ +85 C  */
+  sensor->max_value = +85.0;
+  sensor->resolution = 0.01; /*  0.01 C */
+}
+
+/**************************************************************************/
+/*!
+        @brief  Gets the temperature as a standard sensor event
+        @param  event Sensor event object that will be populated
+        @returns True
+*/
+/**************************************************************************/
+bool Adafruit_BME280_Temp::getEvent(sensors_event_t *event) {
+  /* Clear the event */
+  memset(event, 0, sizeof(sensors_event_t));
+
+  event->version = sizeof(sensors_event_t);
+  event->sensor_id = _sensorID;
+  event->type = SENSOR_TYPE_AMBIENT_TEMPERATURE;
+  event->timestamp = millis();
+  event->temperature = theBME280.readTemperature();
+  return true;
+}
+
+/**************************************************************************/
+/*!
+        @brief  Gets the sensor_t data for the BME280's pressure sensor
+*/
+/**************************************************************************/
+void Adafruit_BME280_Pressure::getSensor(sensor_t *sensor) {
+  /* Clear the sensor_t object */
+  memset(sensor, 0, sizeof(sensor_t));
+
+  /* Insert the sensor name in the fixed length char array */
+  strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1);
+  sensor->name[sizeof(sensor->name) - 1] = 0;
+  sensor->version = 1;
+  sensor->sensor_id = _sensorID;
+  sensor->type = SENSOR_TYPE_PRESSURE;
+  sensor->min_delay = 0;
+  sensor->min_value = 300.0; /* 300 ~ 1100 hPa  */
+  sensor->max_value = 1100.0;
+  sensor->resolution = 0.012; /* 0.12 hPa relative */
+}
+
+/**************************************************************************/
+/*!
+        @brief  Gets the pressure as a standard sensor event
+        @param  event Sensor event object that will be populated
+        @returns True
+*/
+/**************************************************************************/
+bool Adafruit_BME280_Pressure::getEvent(sensors_event_t *event) {
+  /* Clear the event */
+  memset(event, 0, sizeof(sensors_event_t));
+
+  event->version = sizeof(sensors_event_t);
+  event->sensor_id = _sensorID;
+  event->type = SENSOR_TYPE_PRESSURE;
+  event->timestamp = millis();
+  event->pressure = theBME280.readPressure() / 100; // convert Pa to hPa
+  return true;
+}
+
+/**************************************************************************/
+/*!
+        @brief  Gets the sensor_t data for the BME280's humidity sensor
+*/
+/**************************************************************************/
+void Adafruit_BME280_Humidity::getSensor(sensor_t *sensor) {
+  /* Clear the sensor_t object */
+  memset(sensor, 0, sizeof(sensor_t));
+
+  /* Insert the sensor name in the fixed length char array */
+  strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1);
+  sensor->name[sizeof(sensor->name) - 1] = 0;
+  sensor->version = 1;
+  sensor->sensor_id = _sensorID;
+  sensor->type = SENSOR_TYPE_RELATIVE_HUMIDITY;
+  sensor->min_delay = 0;
+  sensor->min_value = 0;
+  sensor->max_value = 100; /* 0 - 100 %  */
+  sensor->resolution = 3;  /* 3% accuracy */
+}
+
+/**************************************************************************/
+/*!
+        @brief  Gets the humidity as a standard sensor event
+        @param  event Sensor event object that will be populated
+        @returns True
+*/
+/**************************************************************************/
+bool Adafruit_BME280_Humidity::getEvent(sensors_event_t *event) {
+  /* Clear the event */
+  memset(event, 0, sizeof(sensors_event_t));
+
+  event->version = sizeof(sensors_event_t);
+  event->sensor_id = _sensorID;
+  event->type = SENSOR_TYPE_RELATIVE_HUMIDITY;
+  event->timestamp = millis();
+  event->relative_humidity = theBME280.readHumidity();
+  return true;
+}
diff --git a/software/nano-644/test_2024-07-23/src/adafruit/bme280.h b/software/nano-644/test_2024-07-23/src/adafruit/bme280.h
new file mode 100644 (file)
index 0000000..b842782
--- /dev/null
@@ -0,0 +1,373 @@
+// https://github.com/adafruit/Adafruit_BME280_Library
+
+/*!
+ * @file Adafruit_BME280.h
+ *
+ * Designed specifically to work with the Adafruit BME280 Breakout
+ * ----> http://www.adafruit.com/products/2650
+ *
+ * These sensors use I2C or SPI to communicate, 2 or 4 pins are required
+ * to interface.
+ *
+ * Adafruit invests time and resources providing this open source code,
+ * please support Adafruit and open-source hardware by purchasing
+ * products from Adafruit!
+ *
+ * Written by Kevin "KTOWN" Townsend for Adafruit Industries.
+ *
+ * BSD license, all text here must be included in any redistribution.
+ * See the LICENSE file for details.
+ *
+ */
+
+#ifndef __BME280_H__
+#define __BME280_H__
+
+// #include "Arduino.h"
+
+// #include <Adafruit_I2CDevice.h>
+// #include <Adafruit_SPIDevice.h>
+// #include <Adafruit_Sensor.h>
+
+
+#include "../i2cmaster.hpp"
+#include "../main.hpp"
+#define byte uint8_t
+
+
+
+#include <stdint.h>
+#include <string.h>
+#include "sensor.h"
+
+/*!
+ *  @brief  default I2C address
+ */
+#define BME280_ADDRESS (0x77)           // Primary I2C Address
+                                        /*!
+                                         *  @brief  alternate I2C address
+                                         */
+#define BME280_ADDRESS_ALTERNATE (0x76) // Alternate Address
+
+/*!
+ *  @brief Register addresses
+ */
+enum {
+  BME280_REGISTER_DIG_T1 = 0x88,
+  BME280_REGISTER_DIG_T2 = 0x8A,
+  BME280_REGISTER_DIG_T3 = 0x8C,
+
+  BME280_REGISTER_DIG_P1 = 0x8E,
+  BME280_REGISTER_DIG_P2 = 0x90,
+  BME280_REGISTER_DIG_P3 = 0x92,
+  BME280_REGISTER_DIG_P4 = 0x94,
+  BME280_REGISTER_DIG_P5 = 0x96,
+  BME280_REGISTER_DIG_P6 = 0x98,
+  BME280_REGISTER_DIG_P7 = 0x9A,
+  BME280_REGISTER_DIG_P8 = 0x9C,
+  BME280_REGISTER_DIG_P9 = 0x9E,
+
+  BME280_REGISTER_DIG_H1 = 0xA1,
+  BME280_REGISTER_DIG_H2 = 0xE1,
+  BME280_REGISTER_DIG_H3 = 0xE3,
+  BME280_REGISTER_DIG_H4 = 0xE4,
+  BME280_REGISTER_DIG_H5 = 0xE5,
+  BME280_REGISTER_DIG_H6 = 0xE7,
+
+  BME280_REGISTER_CHIPID = 0xD0,
+  BME280_REGISTER_VERSION = 0xD1,
+  BME280_REGISTER_SOFTRESET = 0xE0,
+
+  BME280_REGISTER_CAL26 = 0xE1, // R calibration stored in 0xE1-0xF0
+
+  BME280_REGISTER_CONTROLHUMID = 0xF2,
+  BME280_REGISTER_STATUS = 0XF3,
+  BME280_REGISTER_CONTROL = 0xF4,
+  BME280_REGISTER_CONFIG = 0xF5,
+  BME280_REGISTER_PRESSUREDATA = 0xF7,
+  BME280_REGISTER_TEMPDATA = 0xFA,
+  BME280_REGISTER_HUMIDDATA = 0xFD
+};
+
+/**************************************************************************/
+/*!
+    @brief  calibration data
+*/
+/**************************************************************************/
+typedef struct {
+  uint16_t dig_T1; ///< temperature compensation value
+  int16_t dig_T2;  ///< temperature compensation value
+  int16_t dig_T3;  ///< temperature compensation value
+
+  uint16_t dig_P1; ///< pressure compensation value
+  int16_t dig_P2;  ///< pressure compensation value
+  int16_t dig_P3;  ///< pressure compensation value
+  int16_t dig_P4;  ///< pressure compensation value
+  int16_t dig_P5;  ///< pressure compensation value
+  int16_t dig_P6;  ///< pressure compensation value
+  int16_t dig_P7;  ///< pressure compensation value
+  int16_t dig_P8;  ///< pressure compensation value
+  int16_t dig_P9;  ///< pressure compensation value
+
+  uint8_t dig_H1; ///< humidity compensation value
+  int16_t dig_H2; ///< humidity compensation value
+  uint8_t dig_H3; ///< humidity compensation value
+  int16_t dig_H4; ///< humidity compensation value
+  int16_t dig_H5; ///< humidity compensation value
+  int8_t dig_H6;  ///< humidity compensation value
+} bme280_calib_data;
+/*=========================================================================*/
+
+class Adafruit_BME280;
+
+/** Adafruit Unified Sensor interface for temperature component of BME280 */
+class Adafruit_BME280_Temp : public Adafruit_Sensor {
+public:
+  /** @brief Create an Adafruit_Sensor compatible object for the temp sensor
+      @param parent A pointer to the BME280 class */
+  Adafruit_BME280_Temp() { _sensorID = 280; }
+  bool getEvent(sensors_event_t *);
+  void getSensor(sensor_t *);
+
+private:
+  int _sensorID;
+};
+
+/** Adafruit Unified Sensor interface for pressure component of BME280 */
+class Adafruit_BME280_Pressure : public Adafruit_Sensor {
+public:
+  /** @brief Create an Adafruit_Sensor compatible object for the pressure sensor
+      @param parent A pointer to the BME280 class */
+  Adafruit_BME280_Pressure() { _sensorID = 280; }
+  bool getEvent(sensors_event_t *);
+  void getSensor(sensor_t *);
+
+private:
+  int _sensorID;
+};
+
+/** Adafruit Unified Sensor interface for humidity component of BME280 */
+class Adafruit_BME280_Humidity : public Adafruit_Sensor {
+public:
+  /** @brief Create an Adafruit_Sensor compatible object for the humidity sensor
+      @param parent A pointer to the BME280 class */
+  Adafruit_BME280_Humidity() { _sensorID = 280;}
+  bool getEvent(sensors_event_t *);
+  void getSensor(sensor_t *);
+
+private:
+  int _sensorID;
+};
+
+/**************************************************************************/
+/*!
+    @brief  Class that stores state and functions for interacting with BME280 IC
+*/
+/**************************************************************************/
+class Adafruit_BME280 {
+public:
+  /**************************************************************************/
+  /*!
+      @brief  sampling rates
+  */
+  /**************************************************************************/
+  enum sensor_sampling {
+    SAMPLING_NONE = 0b000,
+    SAMPLING_X1 = 0b001,
+    SAMPLING_X2 = 0b010,
+    SAMPLING_X4 = 0b011,
+    SAMPLING_X8 = 0b100,
+    SAMPLING_X16 = 0b101
+  };
+
+  /**************************************************************************/
+  /*!
+      @brief  power modes
+  */
+  /**************************************************************************/
+  enum sensor_mode {
+    MODE_SLEEP = 0b00,
+    MODE_FORCED = 0b01,
+    MODE_NORMAL = 0b11
+  };
+
+  /**************************************************************************/
+  /*!
+      @brief  filter values
+  */
+  /**************************************************************************/
+  enum sensor_filter {
+    FILTER_OFF = 0b000,
+    FILTER_X2 = 0b001,
+    FILTER_X4 = 0b010,
+    FILTER_X8 = 0b011,
+    FILTER_X16 = 0b100
+  };
+
+  /**************************************************************************/
+  /*!
+      @brief  standby duration in ms
+  */
+  /**************************************************************************/
+  enum standby_duration {
+    STANDBY_MS_0_5 = 0b000,
+    STANDBY_MS_10 = 0b110,
+    STANDBY_MS_20 = 0b111,
+    STANDBY_MS_62_5 = 0b001,
+    STANDBY_MS_125 = 0b010,
+    STANDBY_MS_250 = 0b011,
+    STANDBY_MS_500 = 0b100,
+    STANDBY_MS_1000 = 0b101
+  };
+
+  // constructors
+  Adafruit_BME280();
+  
+  bool begin(uint8_t addr = BME280_ADDRESS);
+  bool init();
+
+  void setSampling(sensor_mode mode = MODE_NORMAL,
+                   sensor_sampling tempSampling = SAMPLING_X16,
+                   sensor_sampling pressSampling = SAMPLING_X16,
+                   sensor_sampling humSampling = SAMPLING_X16,
+                   sensor_filter filter = FILTER_OFF,
+                   standby_duration duration = STANDBY_MS_0_5);
+
+  bool takeForcedMeasurement(void);
+  float readTemperature(void);
+  float readPressure(void);
+  float readHumidity(void);
+
+  float readAltitude(float seaLevel);
+  float seaLevelForAltitude(float altitude, float pressure);
+  uint32_t sensorID(void);
+
+  float getTemperatureCompensation(void);
+  void setTemperatureCompensation(float);
+
+protected:
+  I2cMaster *i2c_dev = NULL; ///< Pointer to I2C bus interface
+  // Adafruit_SPIDevice *spi_dev = NULL; ///< Pointer to SPI bus interface
+
+  Adafruit_BME280_Temp *temp_sensor;
+  Adafruit_BME280_Pressure *pressure_sensor;
+  Adafruit_BME280_Humidity *humidity_sensor;
+
+  void readCoefficients(void);
+  bool isReadingCalibration(void);
+
+  void write8(byte reg, byte value);
+  uint8_t read8(byte reg);
+  uint16_t read16(byte reg);
+  uint32_t read24(byte reg);
+  int16_t readS16(byte reg);
+  uint16_t read16_LE(byte reg); // little endian
+  int16_t readS16_LE(byte reg); // little endian
+
+  uint8_t _i2caddr;  //!< I2C addr for the TwoWire interface
+  int32_t _sensorID; //!< ID of the BME Sensor
+  int32_t t_fine; //!< temperature with high resolution, stored as an attribute
+                  //!< as this is used for temperature compensation reading
+                  //!< humidity and pressure
+
+  int32_t t_fine_adjust; //!< add to compensate temp readings and in turn
+                         //!< to pressure and humidity readings
+
+  bme280_calib_data _bme280_calib; //!< here calibration data is stored
+
+  /**************************************************************************/
+  /*!
+      @brief  config register
+  */
+  /**************************************************************************/
+  struct config {
+    // inactive duration (standby time) in normal mode
+    // 000 = 0.5 ms
+    // 001 = 62.5 ms
+    // 010 = 125 ms
+    // 011 = 250 ms
+    // 100 = 500 ms
+    // 101 = 1000 ms
+    // 110 = 10 ms
+    // 111 = 20 ms
+    unsigned int t_sb : 3; ///< inactive duration (standby time) in normal mode
+
+    // filter settings
+    // 000 = filter off
+    // 001 = 2x filter
+    // 010 = 4x filter
+    // 011 = 8x filter
+    // 100 and above = 16x filter
+    unsigned int filter : 3; ///< filter settings
+
+    // unused - don't set
+    unsigned int none : 1;     ///< unused - don't set
+    unsigned int spi3w_en : 1; ///< unused - don't set
+
+    /// @return combined config register
+    unsigned int get() { return (t_sb << 5) | (filter << 2) | spi3w_en; }
+  };
+  config _configReg; //!< config register object
+
+  /**************************************************************************/
+  /*!
+      @brief  ctrl_meas register
+  */
+  /**************************************************************************/
+  struct ctrl_meas {
+    // temperature oversampling
+    // 000 = skipped
+    // 001 = x1
+    // 010 = x2
+    // 011 = x4
+    // 100 = x8
+    // 101 and above = x16
+    unsigned int osrs_t : 3; ///< temperature oversampling
+
+    // pressure oversampling
+    // 000 = skipped
+    // 001 = x1
+    // 010 = x2
+    // 011 = x4
+    // 100 = x8
+    // 101 and above = x16
+    unsigned int osrs_p : 3; ///< pressure oversampling
+
+    // device mode
+    // 00       = sleep
+    // 01 or 10 = forced
+    // 11       = normal
+    unsigned int mode : 2; ///< device mode
+
+    /// @return combined ctrl register
+    unsigned int get() { return (osrs_t << 5) | (osrs_p << 2) | mode; }
+  };
+  ctrl_meas _measReg; //!< measurement register object
+
+  /**************************************************************************/
+  /*!
+      @brief  ctrl_hum register
+  */
+  /**************************************************************************/
+  struct ctrl_hum {
+    /// unused - don't set
+    unsigned int none : 5;
+
+    // pressure oversampling
+    // 000 = skipped
+    // 001 = x1
+    // 010 = x2
+    // 011 = x4
+    // 100 = x8
+    // 101 and above = x16
+    unsigned int osrs_h : 3; ///< pressure oversampling
+
+    /// @return combined ctrl hum register
+    unsigned int get() { return (osrs_h); }
+  };
+  ctrl_hum _humReg; //!< hum register object
+};
+
+extern Adafruit_BME280 theBME280;
+
+#endif
diff --git a/software/nano-644/test_2024-07-23/src/adafruit/ens160.cpp b/software/nano-644/test_2024-07-23/src/adafruit/ens160.cpp
new file mode 100644 (file)
index 0000000..ef23404
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+  ScioSense_ENS160.h - Library for the ENS160 sensor with I2C interface from ScioSense
+  2023 Mar 23  v6      Christoph Friese        Bugfix measurement routine, prepare next release
+  2021 Nov 25   v5     Martin Herold           Custom mode timing fixed
+  2021 Feb 04  v4      Giuseppe de Pinto       Custom mode fixed
+  2020 Apr 06  v3      Christoph Friese        Changed nomenclature to ScioSense as product shifted from ams
+  2020 Feb 15  v2      Giuseppe Pasetti        Corrected firmware flash option
+  2019 May 05  v1      Christoph Friese        Created
+  based on application note "ENS160 Software Integration.pdf" rev 0.01
+*/
+
+#include "ens160.h"
+#include "math.h"
+#include <util/delay.h>
+#include <stdio.h>
+#include <avr/io.h>
+
+ScioSense_ENS160::ScioSense_ENS160 () {
+   _revENS16x = 0;
+
+   //Isotherm, HP0 252°C / HP1 350°C / HP2 250°C / HP3 324°C / measure every 1008ms
+       _seq_steps[0][0] = 0x7c;
+   _seq_steps[0][1] = 0x0a;
+   _seq_steps[0][2] = 0x7e;
+   _seq_steps[0][3] = 0xaf;
+   _seq_steps[0][4] = 0xaf;
+   _seq_steps[0][5] = 0xa2;
+   _seq_steps[0][6] = 0x00;
+   _seq_steps[0][7] = 0x80;
+}
+
+bool ScioSense_ENS160::begin () {
+   i2cDevice.begin(ENS160_I2CADDR_1);
+       _delay_ms(ENS160_BOOTING);
+       if (reset()) {
+      if (checkPartID()) {
+         if (setMode(ENS160_OPMODE_IDLE)) {
+            if (clearCommand()) {
+               if (getFirmware()) {
+                  return true;
+               }
+            }
+         }
+      }
+   }
+   return false;
+       
+}
+
+bool ScioSense_ENS160::write8 (byte reg, byte value) {
+   byte buffer[2];
+   buffer[1] = value;
+   buffer[0] = reg;
+   return i2cDevice.write(buffer, 2);
+}
+
+bool ScioSense_ENS160::read8 (byte reg, byte *value) {
+   uint8_t buffer[1];
+   buffer[0] = uint8_t(reg);
+   return i2cDevice.write_then_read(buffer, 1, value, 1);
+}
+
+bool ScioSense_ENS160::read16 (byte reg, uint16_t *value) {
+   uint8_t buffer[1];
+   buffer[0] = uint8_t(reg);
+   return i2cDevice.write_then_read(buffer, 1, (uint8_t *)value, 2);
+}
+
+bool ScioSense_ENS160::read16LE (byte reg, uint16_t *value) {
+  uint16_t tmp;
+  if (read16(reg, &tmp)) {
+      *value = ((tmp & 0xff) << 8) | (tmp >> 8);
+      return true;
+  }
+  return false;
+}
+
+bool ScioSense_ENS160::readBytes (byte reg, uint8_t *bytes, uint8_t len) {
+   uint8_t buffer[1];
+   buffer[0] = uint8_t(reg);
+   return i2cDevice.write_then_read(buffer, 1, buffer, len);
+}
+
+
+// Sends a reset to the ENS160. Returns false on I2C problems.
+bool ScioSense_ENS160::reset () {
+       if (write8(ENS160_REG_OPMODE, ENS160_OPMODE_RESET)) {
+      _delay_ms(ENS160_BOOTING);
+      return true;
+   }
+       _delay_ms(ENS160_BOOTING);
+       return false;
+}
+
+// Reads the part ID and confirms valid sensor
+bool ScioSense_ENS160::checkPartID () {
+       uint16_t part_id;
+       
+   read16(ENS160_REG_PART_ID, &part_id);
+       _delay_ms(ENS160_BOOTING);
+
+       if (part_id == ENS160_PARTID) {
+      _revENS16x = 0;
+      return true;
+
+   } else if (part_id == ENS161_PARTID) {
+      _revENS16x = 1;
+      return true;
+   }
+       
+       return false;
+}
+
+// Initialize idle mode and confirms 
+bool ScioSense_ENS160::clearCommand () {
+       uint8_t status;
+       
+       if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_NOP)) {
+          if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_CLRGPR)) {
+             _delay_ms(ENS160_BOOTING);
+         if (read8(ENS160_REG_DATA_STATUS, &status)) {
+            return true;
+         }
+      }
+   }
+       _delay_ms(ENS160_BOOTING);
+       return false;
+}
+
+// Read firmware revisions
+bool ScioSense_ENS160::getFirmware () {
+       uint8_t i2cbuf[3];
+       
+       if (clearCommand()) {
+      _delay_ms(ENS160_BOOTING);
+      if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_GET_APPVER)) {
+         if (readBytes(ENS160_REG_GPR_READ_4, i2cbuf, 3)) {
+            _fw_ver_major = i2cbuf[0];
+            _fw_ver_minor = i2cbuf[1];
+            _fw_ver_build = i2cbuf[2];
+            _revENS16x = this->_fw_ver_major > 6 ? 1 : 0;
+            _delay_ms(ENS160_BOOTING);
+            return true;
+         }
+      }
+   }
+   _delay_ms(ENS160_BOOTING);
+       return false;
+}
+
+// Set operation mode of sensor
+bool ScioSense_ENS160::setMode (uint8_t mode) {
+       //LP only valid for rev>0
+       if ((mode == ENS160_OPMODE_LP) and (_revENS16x == 0)) {
+      return false;
+   }
+       if (write8(ENS160_REG_OPMODE, mode)) {
+      _delay_ms(ENS160_BOOTING);
+      return true;
+   }
+   _delay_ms(ENS160_BOOTING);
+   return false;
+}
+
+// Initialize definition of custom mode with <n> steps
+bool ScioSense_ENS160::initCustomMode (uint16_t stepNum) {
+       if (stepNum > 0) {
+               _stepCount = stepNum;
+               if (setMode(ENS160_OPMODE_IDLE)) {
+         if (clearCommand()) {
+            if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_SETSEQ)) {
+               _delay_ms(ENS160_BOOTING);
+               return true;
+            }
+         }
+      }
+       }
+       _delay_ms(ENS160_BOOTING);
+       return false;
+}
+
+// Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate
+bool ScioSense_ENS160::addCustomStep (uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3) {
+       uint8_t seq_ack;
+       uint8_t temp;
+
+       _delay_ms(ENS160_BOOTING);
+
+       temp = (uint8_t)(((time / 24) - 1) << 6); 
+       if (measureHP0) {
+      temp = temp | 0x20;
+   }
+       if (measureHP1) {
+      temp = temp | 0x10;
+   }
+       if (measureHP2) {
+      temp = temp | 0x08;
+   }
+       if (measureHP3) {
+      temp = temp | 0x04;
+   }
+       if (!write8(ENS160_REG_GPR_WRITE_0, temp)) {
+      return false;
+   }
+   temp = (uint8_t)(((time / 24) - 1) >> 2); 
+       if (!write8(ENS160_REG_GPR_WRITE_1, temp)) {
+      return false;
+   }
+   if (!write8(ENS160_REG_GPR_WRITE_2, (uint8_t)(tempHP0 / 2))) {
+      return false;
+   }
+       if (!write8(ENS160_REG_GPR_WRITE_3, (uint8_t)(tempHP1 / 2))) {
+      return false;
+   }
+       if (write8(ENS160_REG_GPR_WRITE_4, (uint8_t)(tempHP2 / 2))) {
+      return false;
+   }
+       if (write8(ENS160_REG_GPR_WRITE_5, (uint8_t)(tempHP3 / 2))) {
+      return false;
+   }
+
+       if (write8(ENS160_REG_GPR_WRITE_6, (uint8_t)(_stepCount - 1))) {
+      return false;
+   }
+
+   if (_stepCount == 1) {
+      if (!write8(ENS160_REG_GPR_WRITE_7, 128)) {
+         return false;
+      }
+   } else {
+      if (!write8(ENS160_REG_GPR_WRITE_7, 0)) {
+         return false;
+      }
+   }
+   _delay_ms(ENS160_BOOTING);
+
+   if (!read8(ENS160_REG_GPR_READ_7, &seq_ack)) {
+      return false;
+   }
+       _delay_ms(ENS160_BOOTING);
+       
+       if ((ENS160_SEQ_ACK_COMPLETE | _stepCount) != seq_ack) {
+               _stepCount++;
+               return false;
+       }
+
+       return true;
+}
+
+bool ScioSense_ENS160::readStatus (uint8_t *status) {
+   return read8(ENS160_REG_DATA_STATUS, status);
+}
+
+bool ScioSense_ENS160::readData (ENS160_DATA *data) {
+   uint8_t buffer[1] = { 0x21 };
+   return i2cDevice.write_then_read(buffer, 1, (uint8_t *)data, sizeof(ENS160_DATA));
+}
+       
+// Perform prediction measurement and stores result in internal variables
+bool ScioSense_ENS160::measure (bool waitForNew) {
+       uint8_t i2cbuf[8];
+       uint8_t status;
+
+       // Set default status for early bail out
+       if (waitForNew) {
+               do {
+         if (!read8(ENS160_REG_DATA_STATUS, &status)) {
+            return false;
+         }
+         _delay_ms(1);
+               } while (!IS_NEWDAT(status));
+       } else {
+               if (!read8(ENS160_REG_DATA_STATUS, &status)) {
+         return false;
+      }
+       }
+       
+
+       // Read predictions
+       if (IS_NEWDAT(status)) {
+               if (!readBytes(ENS160_REG_DATA_AQI, i2cbuf, 7)) {
+         return false;
+      }
+      return false;
+               _data_aqi = i2cbuf[0];
+               _data_tvoc = i2cbuf[1] | ((uint16_t)i2cbuf[2] << 8);
+               _data_eco2 = i2cbuf[3] | ((uint16_t)i2cbuf[4] << 8);
+               if (_revENS16x > 0) {
+         _data_aqi500 = ((uint16_t)i2cbuf[5]) | ((uint16_t)i2cbuf[6] << 8);
+      } else {
+         _data_aqi500 = 0;
+      }
+      return true;
+       }
+       
+       return false;
+}
+
+// Perfrom raw measurement and stores result in internal variables
+bool ScioSense_ENS160::measureRaw (bool waitForNew) {
+       uint8_t i2cbuf[8];
+       uint8_t status;
+
+       // Set default status for early bail out
+       if (waitForNew) {
+               do {
+                       _delay_ms(1);
+                       if (!read8(ENS160_REG_DATA_STATUS, &status)) {
+            return false;
+         }
+               } while (!IS_NEWGPR(status));
+       } else {
+               if (!read8(ENS160_REG_DATA_STATUS, &status)) {
+         return false;
+      }
+       }
+       
+       if (IS_NEWGPR(status)) {
+               
+               // Read raw resistance values
+               if (!readBytes(ENS160_REG_GPR_READ_0, i2cbuf, 8)) {
+         return false;
+      }
+      _hp0_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8)));
+      _hp1_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8)));
+      _hp2_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8)));
+      _hp3_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8)));
+       
+               // Read baselines
+               if (!readBytes(ENS160_REG_DATA_BL, i2cbuf, 8)) {
+         return false;
+      }
+               _hp0_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8)));
+               _hp1_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8)));
+               _hp2_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8)));
+               _hp3_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8)));
+
+               if (!read8(ENS160_REG_DATA_MISR, i2cbuf)) {
+         return false;
+      }
+               _misr = i2cbuf[0];
+      return true;
+       }
+       
+       return false;
+}
+
+
+// Writes t (degC) and h (%rh) to ENV_DATA. Returns false on I2C problems.
+bool ScioSense_ENS160::set_envdata (float t, float h) {
+       uint16_t t_data = (uint16_t)((t + 273.15f) * 64.0f);
+       uint16_t rh_data = (uint16_t)(h * 512.0f);
+       return this->set_envdata210(t_data, rh_data);
+}
+
+// Writes t and h (in ENS210 format) to ENV_DATA. Returns false on I2C problems.
+bool ScioSense_ENS160::set_envdata210 (uint16_t t, uint16_t h) {
+       //uint16_t temp;
+       uint8_t trh_in[4];
+       
+       //temp = (uint16_t)((t + 273.15f) * 64.0f);
+       trh_in[0] = t & 0xff;
+       trh_in[1] = (t >> 8) & 0xff;
+       
+       //temp = (uint16_t)(h * 512.0f);
+       trh_in[2] = h & 0xff;
+       trh_in[3] = (h >> 8) & 0xff;
+       
+       if (!i2cDevice.writeByteAndBuffer(ENS160_REG_TEMP_IN, trh_in, 4)) {
+      return false;
+   }
+       
+       return true;
+}
diff --git a/software/nano-644/test_2024-07-23/src/adafruit/ens160.h b/software/nano-644/test_2024-07-23/src/adafruit/ens160.h
new file mode 100644 (file)
index 0000000..fb6925d
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+  ScioSense_ENS160.h - Library for the ENS160 sensor with I2C interface from ScioSense
+  2023 Mar 23  v6      Christoph Friese        Bugfix measurement routine, prepare next release
+  2021 July 29 v4      Christoph Friese        Changed nomenclature to ScioSense as product shifted from ams
+  2020 Apr 06  v3      Christoph Friese        Changed nomenclature to ScioSense as product shifted from ams
+  2020 Feb 15  v2      Giuseppe Pasetti        Corrected firmware flash option
+  2019 May 05  v1      Christoph Friese        Created
+  based on application note "ENS160 Software Integration.pdf" rev 0.01
+*/
+
+#ifndef __SCIOSENSE_ENS160_H_
+#define __SCIOSENSE_ENS160_H_
+
+#include "../i2cmaster.hpp"
+#include <stdint.h>
+#define byte uint8_t
+
+// #if (ARDUINO >= 100)
+//     #include "Arduino.h"
+// #else
+//     #include "WProgram.h"
+// #endif
+
+// #include <Wire.h>
+
+// Chip constants
+#define ENS160_PARTID                          0x0160
+#define ENS161_PARTID                          0x0161
+#define ENS160_BOOTING                         10
+
+// 7-bit I2C slave address of the ENS160
+#define ENS160_I2CADDR_0               0x52            //ADDR low
+#define ENS160_I2CADDR_1               0x53            //ADDR high
+
+// ENS160 registers for version V0
+#define ENS160_REG_PART_ID             0x00            // 2 byte register
+#define ENS160_REG_OPMODE              0x10
+#define ENS160_REG_CONFIG              0x11
+#define ENS160_REG_COMMAND             0x12
+#define ENS160_REG_TEMP_IN             0x13
+#define ENS160_REG_RH_IN               0x15
+#define ENS160_REG_DATA_STATUS         0x20
+#define ENS160_REG_DATA_AQI            0x21
+#define ENS160_REG_DATA_TVOC           0x22
+#define ENS160_REG_DATA_ECO2           0x24                    
+#define ENS160_REG_DATA_BL             0x28
+#define ENS160_REG_DATA_T              0x30
+#define ENS160_REG_DATA_RH             0x32
+#define ENS160_REG_DATA_MISR           0x38
+#define ENS160_REG_GPR_WRITE_0         0x40
+#define ENS160_REG_GPR_WRITE_1         ENS160_REG_GPR_WRITE_0 + 1
+#define ENS160_REG_GPR_WRITE_2         ENS160_REG_GPR_WRITE_0 + 2
+#define ENS160_REG_GPR_WRITE_3         ENS160_REG_GPR_WRITE_0 + 3
+#define ENS160_REG_GPR_WRITE_4         ENS160_REG_GPR_WRITE_0 + 4
+#define ENS160_REG_GPR_WRITE_5         ENS160_REG_GPR_WRITE_0 + 5
+#define ENS160_REG_GPR_WRITE_6         ENS160_REG_GPR_WRITE_0 + 6
+#define ENS160_REG_GPR_WRITE_7         ENS160_REG_GPR_WRITE_0 + 7
+#define ENS160_REG_GPR_READ_0          0x48
+#define ENS160_REG_GPR_READ_4          ENS160_REG_GPR_READ_0 + 4
+#define ENS160_REG_GPR_READ_6          ENS160_REG_GPR_READ_0 + 6
+#define ENS160_REG_GPR_READ_7          ENS160_REG_GPR_READ_0 + 7
+
+//ENS160 data register fields
+#define ENS160_COMMAND_NOP             0x00
+#define ENS160_COMMAND_CLRGPR          0xCC
+#define ENS160_COMMAND_GET_APPVER      0x0E 
+#define ENS160_COMMAND_SETTH           0x02
+#define ENS160_COMMAND_SETSEQ          0xC2
+
+#define ENS160_OPMODE_RESET            0xF0
+#define ENS160_OPMODE_DEP_SLEEP                0x00
+#define ENS160_OPMODE_IDLE             0x01
+#define ENS160_OPMODE_STD              0x02
+#define ENS160_OPMODE_LP               0x03    
+#define ENS160_OPMODE_CUSTOM           0xC0
+
+#define ENS160_BL_CMD_START            0x02
+#define ENS160_BL_CMD_ERASE_APP                0x04
+#define ENS160_BL_CMD_ERASE_BLINE      0x06
+#define ENS160_BL_CMD_WRITE            0x08
+#define ENS160_BL_CMD_VERIFY           0x0A
+#define ENS160_BL_CMD_GET_BLVER                0x0C
+#define ENS160_BL_CMD_GET_APPVER       0x0E
+#define ENS160_BL_CMD_EXITBL           0x12
+
+#define ENS160_SEQ_ACK_NOTCOMPLETE     0x80
+#define ENS160_SEQ_ACK_COMPLETE                0xC0
+
+#define IS_ENS160_SEQ_ACK_NOT_COMPLETE(x)      (ENS160_SEQ_ACK_NOTCOMPLETE == (ENS160_SEQ_ACK_NOTCOMPLETE & (x)))
+#define IS_ENS160_SEQ_ACK_COMPLETE(x)          (ENS160_SEQ_ACK_COMPLETE == (ENS160_SEQ_ACK_COMPLETE & (x)))
+
+#define ENS160_DATA_STATUS_NEWDAT      0x02
+#define ENS160_DATA_STATUS_NEWGPR      0x01
+
+#define IS_NEWDAT(x)                   (ENS160_DATA_STATUS_NEWDAT == (ENS160_DATA_STATUS_NEWDAT & (x)))
+#define IS_NEWGPR(x)                   (ENS160_DATA_STATUS_NEWGPR == (ENS160_DATA_STATUS_NEWGPR & (x)))
+#define IS_NEW_DATA_AVAILABLE(x)       (0 != ((ENS160_DATA_STATUS_NEWDAT | ENS160_DATA_STATUS_NEWGPR ) & (x)))
+
+#define CONVERT_RS_RAW2OHMS_I(x)       (1 << ((x) >> 11))
+#define CONVERT_RS_RAW2OHMS_F(x)       (pow (2, (float)(x) / 2048))
+
+typedef struct {
+   uint8_t aqi;
+   uint16_t tvoc;
+   uint16_t eco2;
+} ENS160_DATA;
+
+class ScioSense_ENS160 {
+               
+       public:
+          ScioSense_ENS160();
+               
+               void                            setI2C(uint8_t sda, uint8_t scl);                               // Function to redefine I2C pins
+               
+               bool                            begin();                                        // Init I2C communication, resets ENS160 and checks its PART_ID. Returns false on I2C problems or wrong PART_ID.
+               uint8_t                         revENS16x()     { return this->_revENS16x; }                    // Report version of sensor (0: ENS160, 1: ENS161)
+               bool                            setMode(uint8_t mode);                                          // Set operation mode of sensor
+
+               bool                            initCustomMode(uint16_t stepNum);                               // Initialize definition of custom mode with <n> steps
+               bool                            addCustomStep(uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3);
+                                                                                                                                                                                       // Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate
+                                                                                                                                                                                       
+      bool  readData (ENS160_DATA *data);
+      bool  readStatus(uint8_t *status);
+               bool                            measure(bool waitForNew);                               // Perform measurement and stores result in internal variables
+               bool                            measureRaw(bool waitForNew);                            // Perform raw measurement and stores result in internal variables
+               bool                            set_envdata(float t, float h);                                  // Writes t (degC) and h (%rh) to ENV_DATA. Returns "0" if I2C transmission is successful
+               bool                            set_envdata210(uint16_t t, uint16_t h);                         // Writes t and h (in ENS210 format) to ENV_DATA. Returns "0" if I2C transmission is successful
+               uint8_t                         getMajorRev()   { return this->_fw_ver_major; }                 // Get major revision number of used firmware
+               uint8_t                         getMinorRev()   { return this->_fw_ver_minor; }                 // Get minor revision number of used firmware
+               uint8_t                         getBuild()              { return this->_fw_ver_build; }         // Get build revision number of used firmware
+
+               uint8_t                         getAQI()                { return this->_data_aqi; }             // Get AQI value of last measurement 
+               uint16_t                        getTVOC()               { return this->_data_tvoc; }            // Get TVOC value of last measurement 
+               uint16_t                        geteCO2()               { return this->_data_eco2; }            // Get eCO2 value of last measurement 
+               uint16_t                        getAQI500()             { return this->_data_aqi500; }          // Get AQI500 value of last measurement 
+               uint32_t                        getHP0()                { return this->_hp0_rs; }               // Get resistance of HP0 of last measurement
+               uint32_t                        getHP1()                { return this->_hp1_rs; }               // Get resistance of HP1 of last measurement
+               uint32_t                        getHP2()                { return this->_hp2_rs; }               // Get resistance of HP2 of last measurement
+               uint32_t                        getHP3()                { return this->_hp3_rs; }               // Get resistance of HP3 of last measurement
+               uint32_t                        getHP0BL()              { return this->_hp0_bl; }               // Get baseline resistance of HP0 of last measurement
+               uint32_t                        getHP1BL()              { return this->_hp1_bl; }               // Get baseline resistance of HP1 of last measurement
+               uint32_t                        getHP2BL()              { return this->_hp2_bl; }               // Get baseline resistance of HP2 of last measurement
+               uint32_t                        getHP3BL()              { return this->_hp3_bl; }               // Get baseline resistance of HP3 of last measurement
+               uint8_t                         getMISR()               { return this->_misr; }                 // Return status code of sensor
+
+       private:
+      I2cMaster i2cDevice;
+               bool                            reset();                                                        // Sends a reset to the ENS160. Returns false on I2C problems.
+               bool                            checkPartID();                                                  // Reads the part ID and confirms valid sensor
+               bool                            clearCommand();                                                 // Initialize idle mode and confirms 
+               bool                            getFirmware();                                                  // Read firmware revisions
+               
+               uint8_t                         _revENS16x;                                                     // ENS160 or ENS161 connected? (FW >7)
+       
+               uint8_t                         _fw_ver_major;
+               uint8_t                         _fw_ver_minor;
+               uint8_t                         _fw_ver_build;
+
+               uint16_t                        _stepCount;                                                     // Counter for custom sequence
+
+               uint8_t                         _data_aqi;
+               uint16_t                        _data_tvoc;
+               uint16_t                        _data_eco2;
+               uint16_t                        _data_aqi500;
+               uint32_t                        _hp0_rs;
+               uint32_t                        _hp0_bl;
+               uint32_t                        _hp1_rs;
+               uint32_t                        _hp1_bl;
+               uint32_t                        _hp2_rs;
+               uint32_t                        _hp2_bl;
+               uint32_t                        _hp3_rs;
+               uint32_t                        _hp3_bl;
+               uint16_t                        _temp;
+               int                             _slaveaddr;                                                     // Slave address of the ENS160
+               uint8_t                         _misr;
+               
+               uint8_t _seq_steps[1][8];
+
+      bool write8(byte reg, byte value);
+      bool read8 (byte reg, byte *value);
+      bool read16 (byte reg, uint16_t *value);
+      bool read16LE (byte reg, uint16_t *value);
+      bool readBytes (byte reg, uint8_t *bytes, uint8_t len);
+};
+
+
+#endif
diff --git a/software/nano-644/test_2024-07-23/src/adafruit/sensor.h b/software/nano-644/test_2024-07-23/src/adafruit/sensor.h
new file mode 100644 (file)
index 0000000..ac7e454
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software< /span>
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Update by K. Townsend (Adafruit Industries) for lighter typedefs, and
+ * extended sensor support to include color, voltage and current */
+
+#ifndef _ADAFRUIT_SENSOR_H
+#define _ADAFRUIT_SENSOR_H
+
+#ifndef ARDUINO
+#include <stdint.h>
+#elif ARDUINO >= 100
+#include "Arduino.h"
+#include "Print.h"
+#else
+#include "WProgram.h"
+#endif
+
+/* Constants */
+#define SENSORS_GRAVITY_EARTH (9.80665F) /**< Earth's gravity in m/s^2 */
+#define SENSORS_GRAVITY_MOON (1.6F)      /**< The moon's gravity in m/s^2 */
+#define SENSORS_GRAVITY_SUN (275.0F)     /**< The sun's gravity in m/s^2 */
+#define SENSORS_GRAVITY_STANDARD (SENSORS_GRAVITY_EARTH)
+#define SENSORS_MAGFIELD_EARTH_MAX                                             \
+  (60.0F) /**< Maximum magnetic field on Earth's surface */
+#define SENSORS_MAGFIELD_EARTH_MIN                                             \
+  (30.0F) /**< Minimum magnetic field on Earth's surface */
+#define SENSORS_PRESSURE_SEALEVELHPA                                           \
+  (1013.25F) /**< Average sea level pressure is 1013.25 hPa */
+#define SENSORS_DPS_TO_RADS                                                    \
+  (0.017453293F) /**< Degrees/s to rad/s multiplier                            \
+                  */
+#define SENSORS_RADS_TO_DPS                                                    \
+  (57.29577793F) /**< Rad/s to degrees/s  multiplier */
+#define SENSORS_GAUSS_TO_MICROTESLA                                            \
+  (100) /**< Gauss to micro-Tesla multiplier */
+
+/** Sensor types */
+typedef enum {
+  SENSOR_TYPE_ACCELEROMETER = (1), /**< Gravity + linear acceleration */
+  SENSOR_TYPE_MAGNETIC_FIELD = (2),
+  SENSOR_TYPE_ORIENTATION = (3),
+  SENSOR_TYPE_GYROSCOPE = (4),
+  SENSOR_TYPE_LIGHT = (5),
+  SENSOR_TYPE_PRESSURE = (6),
+  SENSOR_TYPE_PROXIMITY = (8),
+  SENSOR_TYPE_GRAVITY = (9),
+  SENSOR_TYPE_LINEAR_ACCELERATION =
+      (10), /**< Acceleration not including gravity */
+  SENSOR_TYPE_ROTATION_VECTOR = (11),
+  SENSOR_TYPE_RELATIVE_HUMIDITY = (12),
+  SENSOR_TYPE_AMBIENT_TEMPERATURE = (13),
+  SENSOR_TYPE_OBJECT_TEMPERATURE = (14),
+  SENSOR_TYPE_VOLTAGE = (15),
+  SENSOR_TYPE_CURRENT = (16),
+  SENSOR_TYPE_COLOR = (17),
+  SENSOR_TYPE_TVOC = (18),
+  SENSOR_TYPE_VOC_INDEX = (19),
+  SENSOR_TYPE_NOX_INDEX = (20),
+  SENSOR_TYPE_CO2 = (21),
+  SENSOR_TYPE_ECO2 = (22),
+  SENSOR_TYPE_PM10_STD = (23),
+  SENSOR_TYPE_PM25_STD = (24),
+  SENSOR_TYPE_PM100_STD = (25),
+  SENSOR_TYPE_PM10_ENV = (26),
+  SENSOR_TYPE_PM25_ENV = (27),
+  SENSOR_TYPE_PM100_ENV = (28),
+  SENSOR_TYPE_GAS_RESISTANCE = (29),
+  SENSOR_TYPE_UNITLESS_PERCENT = (30),
+  SENSOR_TYPE_ALTITUDE = (31)
+} sensors_type_t;
+
+/** struct sensors_vec_s is used to return a vector in a common format. */
+typedef struct {
+  union {
+    float v[3]; ///< 3D vector elements
+    struct {
+      float x; ///< X component of vector
+      float y; ///< Y component of vector
+      float z; ///< Z component of vector
+    };         ///< Struct for holding XYZ component
+    /* Orientation sensors */
+    struct {
+      float roll; /**< Rotation around the longitudinal axis (the plane body, 'X
+                     axis'). Roll is positive and increasing when moving
+                     downward. -90 degrees <= roll <= 90 degrees */
+      float pitch;   /**< Rotation around the lateral axis (the wing span, 'Y
+                        axis'). Pitch is positive and increasing when moving
+                        upwards. -180 degrees <= pitch <= 180 degrees) */
+      float heading; /**< Angle between the longitudinal axis (the plane body)
+                        and magnetic north, measured clockwise when viewing from
+                        the top of the device. 0-359 degrees */
+    };               ///< Struct for holding roll/pitch/heading
+  };                 ///< Union that can hold 3D vector array, XYZ components or
+                     ///< roll/pitch/heading
+  int8_t status;     ///< Status byte
+  uint8_t reserved[3]; ///< Reserved
+} sensors_vec_t;
+
+/** struct sensors_color_s is used to return color data in a common format. */
+typedef struct {
+  union {
+    float c[3]; ///< Raw 3-element data
+    /* RGB color space */
+    struct {
+      float r;   /**< Red component */
+      float g;   /**< Green component */
+      float b;   /**< Blue component */
+    };           ///< RGB data in floating point notation
+  };             ///< Union of various ways to describe RGB colorspace
+  uint32_t rgba; /**< 24-bit RGBA value */
+} sensors_color_t;
+
+/* Sensor event (36 bytes) */
+/** struct sensor_event_s is used to provide a single sensor event in a common
+ * format. */
+typedef struct {
+  int32_t version;   /**< must be sizeof(struct sensors_event_t) */
+  int32_t sensor_id; /**< unique sensor identifier */
+  int32_t type;      /**< sensor type */
+  int32_t reserved0; /**< reserved */
+  int32_t timestamp; /**< time is in milliseconds */
+  union {
+    float data[4];              ///< Raw data */
+    sensors_vec_t acceleration; /**< acceleration values are in meter per second
+                                   per second (m/s^2) */
+    sensors_vec_t
+        magnetic; /**< magnetic vector values are in micro-Tesla (uT) */
+    sensors_vec_t orientation; /**< orientation values are in degrees */
+    sensors_vec_t gyro;        /**< gyroscope values are in rad/s */
+    float temperature; /**< temperature is in degrees centigrade (Celsius) */
+    float distance;    /**< distance in centimeters */
+    float light;       /**< light in SI lux units */
+    float pressure;    /**< pressure in hectopascal (hPa) */
+    float relative_humidity; /**< relative humidity in percent */
+    float current;           /**< current in milliamps (mA) */
+    float voltage;           /**< voltage in volts (V) */
+    float tvoc;              /**< Total Volatile Organic Compounds, in ppb */
+    float voc_index; /**< VOC (Volatile Organic Compound) index where 100 is
+                          normal (unitless) */
+    float nox_index; /**< NOx (Nitrogen Oxides) index where 100 is normal
+                          (unitless) */
+    float CO2;       /**< Measured CO2 in parts per million (ppm) */
+    float eCO2;      /**< equivalent/estimated CO2 in parts per million (ppm
+                        estimated from some other measurement) */
+    float pm10_std;  /**< Standard Particulate Matter <=1.0 in parts per million
+                        (ppm) */
+    float pm25_std;  /**< Standard Particulate Matter <=2.5 in parts per million
+                        (ppm) */
+    float pm100_std; /**< Standard Particulate Matter <=10.0 in parts per
+                        million (ppm) */
+    float pm10_env;  /**< Environmental Particulate Matter <=1.0 in parts per
+                        million (ppm) */
+    float pm25_env;  /**< Environmental Particulate Matter <=2.5 in parts per
+                        million (ppm) */
+    float pm100_env; /**< Environmental Particulate Matter <=10.0 in parts per
+                        million (ppm) */
+    float gas_resistance;   /**< Proportional to the amount of VOC particles in
+                               the air (Ohms) */
+    float unitless_percent; /**<Percentage, unit-less (%) */
+    sensors_color_t color;  /**< color in RGB component values */
+    float altitude; /**< Distance between a reference datum and a point or
+                       object, in meters. */
+  };                ///< Union for the wide ranges of data we can carry
+} sensors_event_t;
+
+/* Sensor details (40 bytes) */
+/** struct sensor_s is used to describe basic information about a specific
+ * sensor. */
+typedef struct {
+  char name[12];     /**< sensor name */
+  int32_t version;   /**< version of the hardware + driver */
+  int32_t sensor_id; /**< unique sensor identifier */
+  int32_t type;      /**< this sensor's type (ex. SENSOR_TYPE_LIGHT) */
+  float max_value;   /**< maximum value of this sensor's value in SI units */
+  float min_value;   /**< minimum value of this sensor's value in SI units */
+  float resolution; /**< smallest difference between two values reported by this
+                       sensor */
+  int32_t min_delay; /**< min delay in microseconds between events. zero = not a
+                        constant rate */
+} sensor_t;
+
+/** @brief Common sensor interface to unify various sensors.
+ * Intentionally modeled after sensors.h in the Android API:
+ * https://github.com/android/platform_hardware_libhardware/blob/master/include/hardware/sensors.h
+ */
+class Adafruit_Sensor {
+public:
+  // Constructor(s)
+  Adafruit_Sensor() {}
+//   virtual ~Adafruit_Sensor() {}
+
+//   // These must be defined by the subclass
+
+//   /*! @brief Whether we should automatically change the range (if possible) for
+//      higher precision
+//       @param enabled True if we will try to autorange */
+//   virtual void enableAutoRange(bool enabled) {
+//     (void)enabled; /* suppress unused warning */
+//   };
+
+//   /*! @brief Get the latest sensor event
+//       @returns True if able to fetch an event */
+//   virtual bool getEvent(sensors_event_t *) = 0;
+//   /*! @brief Get info about the sensor itself */
+//   virtual void getSensor(sensor_t *) = 0;
+
+//   void printSensorDetails(void);
+};
+
+#endif
diff --git a/software/nano-644/test_2024-07-23/src/i2cmaster.cpp b/software/nano-644/test_2024-07-23/src/i2cmaster.cpp
new file mode 100644 (file)
index 0000000..9b1bd5d
--- /dev/null
@@ -0,0 +1,143 @@
+#include <avr/io.h>
+#include <stdio.h>
+
+#include "i2cmaster.hpp"
+
+I2cMaster::I2cMaster () {
+   address = 0;
+   timer = 0;
+}
+
+void I2cMaster::tick1ms () {
+   if (timer > 0) {
+      timer--;
+   }
+}
+
+bool I2cMaster::begin (uint8_t addr) {
+   this->address = addr;
+   // TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4);
+   TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4);
+   TWCR = (1 << TWEN);
+   return true;
+}
+
+void I2cMaster::end () {
+   TWCR = (1 << TWEN);
+   TWBR = 0;
+}
+
+bool I2cMaster::read (uint8_t *buffer, uint8_t len) {
+   if (start(true)) {
+      if (readBytes(buffer, len)) {
+         if (stop()) {
+            return true;
+         }
+      }
+   }
+   return false;
+}
+
+
+bool I2cMaster::write (const uint8_t *buffer, uint8_t len) {
+   if (start(false)) {
+      if (writeBytes(buffer, len)) {
+         if (stop()) {
+            return true;
+         }
+      }
+   }
+   return false;
+}
+
+bool I2cMaster::writeByteAndBuffer (uint8_t byte, const uint8_t *buffer, uint8_t len) {
+   if (start(false)) {
+      do {
+         TWDR = byte;
+         TWCR = (1 << TWINT) | (1 << TWEN); // send byte
+         while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+         if ((TWSR & 0xf8) != 0x28) {
+            return false;
+         }
+         byte = *buffer++;
+      } while (len-- > 0);
+      return true;
+   }
+   return false;
+}
+
+
+bool I2cMaster::write_then_read (const uint8_t *write_buffer, uint8_t write_len, uint8_t *read_buffer, uint8_t read_len) {
+   if (start(false)) {
+      if (writeBytes(write_buffer, write_len)) {
+         if (start(true)) {
+            if (readBytes(read_buffer, read_len)) {
+               if (stop()) {
+                  return true;
+               }
+            }
+         }
+      }
+   }
+   return false;
+}
+
+// -------------------------------------------------------------
+
+bool I2cMaster::readBytes (uint8_t *buffer, uint8_t len) {
+   while (len-- > 0) {
+      if (len > 0) {
+         TWCR = (1 << TWEA) | (1 << TWINT) | (1 << TWEN); // read data byte with ACK enabled
+      } else {
+         TWCR = (1 << TWINT) | (1 << TWEN); // read data byte with ACK disabled
+      }
+      while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+      uint8_t sr = TWSR & 0xf8;
+      if ((len > 0 && sr != 0x50) || (len == 0 && sr != 0x58)) {
+         return false;
+      }
+      *buffer++ = TWDR;
+   }
+   return true;
+}
+
+bool I2cMaster::writeBytes (const uint8_t *buffer, uint8_t len) {
+   while (len-- > 0) {
+      // printf_P(PSTR("[wB:len=%d, byte=%02x]"), len + 1, *buffer);
+      TWDR = *buffer++;
+      TWCR = (1 << TWINT) | (1 << TWEN); // send data byte
+      timer = 5;
+      while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done
+      if (!timer || (TWSR & 0xf8) != 0x28) {
+         return false;
+      }
+   }
+   return true;
+}
+
+
+bool I2cMaster::start (bool read) {
+   TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition
+   timer = 5;
+   while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done
+   uint8_t sr = TWSR & 0xf8;
+   if (!timer || (sr != 0x08 && sr != 0x10)) {
+      return false;
+   }
+   TWDR = (address << 1) | (read ? 1 : 0); // address + R/nW
+   TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW
+   timer = 5;
+   while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done
+   sr = TWSR & 0xf8;
+   if (!timer || (!read && sr != 0x18) || (read && sr != 0x40)) {
+      return false;
+   }
+   return true;
+}
+
+bool I2cMaster::stop () {
+   TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
+   while (TWCR & ( 1 << TWSTO));
+   return true;
+}
+
diff --git a/software/nano-644/test_2024-07-23/src/i2cmaster.hpp b/software/nano-644/test_2024-07-23/src/i2cmaster.hpp
new file mode 100644 (file)
index 0000000..e70326c
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef I2C_MASTER
+#define I2C_MASTER
+
+#include <stdint.h>
+
+class I2cMaster {
+   public:
+      static void end ();
+   
+   public:
+      I2cMaster ();
+      void tick1ms ();
+      bool begin (uint8_t addr);
+      bool read (uint8_t *buffer, uint8_t len);
+      bool write(const uint8_t *buffer, uint8_t len);
+      bool write_then_read(const uint8_t *write_buffer, uint8_t write_len, uint8_t *read_buffer, uint8_t read_len);
+      bool writeByteAndBuffer (uint8_t byte, const uint8_t *buffer, uint8_t len);
+
+   private:
+      uint8_t address;
+      uint8_t timer;
+      bool start (bool read);
+      bool stop ();
+      bool writeBytes (const uint8_t *buffer, uint8_t len);
+      bool readBytes (uint8_t *buffer, uint8_t len);
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/i2cslave.cpp b/software/nano-644/test_2024-07-23/src/i2cslave.cpp
new file mode 100644 (file)
index 0000000..2bf6ac2
--- /dev/null
@@ -0,0 +1,92 @@
+#include <avr/io.h>
+#include <stdio.h>
+#include <util/atomic.h>
+
+#include "i2cslave.hpp"
+
+I2cSlave::I2cSlave () {
+   timer = 0;
+   fromMaster.rIndex = 0;
+   fromMaster.wIndex = 0;
+   toMaster.rIndex = 0;
+   toMaster.wIndex = 0;
+}
+
+void I2cSlave::tick1ms () {
+   if (timer > 0) {
+      timer--;
+   }
+}
+
+bool I2cSlave::begin (uint8_t addr, bool acceptGeneralCalls) {
+   if (addr > 127) {
+      return false;
+   }
+   TWAR = addr << 1 | (acceptGeneralCalls ? 1 : 0);
+   TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4);
+   TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWIE);
+   return true;
+}
+
+void I2cSlave::end () {
+   TWCR = (1 << TWEN);
+   TWBR = 0;
+}
+
+int I2cSlave::read () {
+   return getByte(fromMaster);
+}
+
+void I2cSlave::write (uint8_t byte) {
+   putByte(toMaster, byte);
+}
+
+
+void I2cSlave::putByte (RingBuffer& buffer, uint8_t byte) {
+   ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
+      buffer.data[buffer.wIndex++] = byte;
+      if (buffer.wIndex >= sizeof(buffer.data)) {
+         buffer.wIndex = 0;
+      }
+      if (buffer.wIndex == buffer.rIndex) {
+         buffer.rIndex++;
+         if (buffer.rIndex >= sizeof(buffer.data)) {
+            buffer.rIndex = 0;
+         }
+      }
+   }
+}
+
+int I2cSlave::getByte (RingBuffer& buffer) {
+   uint8_t b;
+   ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
+      if (buffer.rIndex == buffer.wIndex) {
+         return EOF;
+      }
+      b = buffer.data[buffer.rIndex++];
+      if (buffer.rIndex >= sizeof(buffer.data)) {
+         buffer.rIndex = 0;
+      }
+   }
+   return b;
+}
+
+void I2cSlave::handleTWIIsr () {
+   uint8_t sr = TWSR & 0xf8;
+   switch (sr) {
+      case 0x80: { // Previously addressed with own SLA+W; data has been received; ACK has been returned
+         putByte(fromMaster, TWDR);
+         TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); // no TWEA -> only one byte accepted
+         break;
+      }
+      case 0xa8: { // Own SLA+R has been received; ACK has been returned
+         int response = getByte(toMaster);;
+         TWDR = response < 0 ? 0x00 : (uint8_t)response;
+         TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); // no TWEA -> only one byte accepted
+         break;
+      }
+      default: TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (1 << TWIE); break;
+   }
+  
+}
+
diff --git a/software/nano-644/test_2024-07-23/src/i2cslave.hpp b/software/nano-644/test_2024-07-23/src/i2cslave.hpp
new file mode 100644 (file)
index 0000000..2fe2dc7
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef I2C_SLAVE
+#define I2C_SLAVE
+
+#include <stdint.h>
+
+class I2cSlave {
+   private:
+      typedef struct {
+         uint8_t rIndex;
+         uint8_t wIndex;
+         uint8_t data[8];
+   } RingBuffer;
+
+   public:
+      I2cSlave ();
+      void tick1ms ();
+      bool begin (uint8_t addr, bool acceptGeneralCalls);
+      void end ();
+      void handleTWIIsr ();
+      int read ();
+      void write (uint8_t byte);
+
+   private:
+      uint8_t timer;
+      RingBuffer fromMaster;
+      RingBuffer toMaster;
+      void putByte (RingBuffer& buffer, uint8_t byte);
+      int getByte (RingBuffer& buffer);
+
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/main.cpp b/software/nano-644/test_2024-07-23/src/main.cpp
new file mode 100644 (file)
index 0000000..0223927
--- /dev/null
@@ -0,0 +1,259 @@
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+#include <stdio.h>
+#include <util/atomic.h>
+
+#include "main.hpp"
+#include "units/led.hpp"
+#include "units/switch.hpp"
+#include "units/rgb.hpp"
+#include "units/seg7.hpp"
+#include "units/poti.hpp"
+#include "units/encoder.hpp"
+#include "units/r2r.hpp"
+#include "units/motor.hpp"
+#include "units/portexp.hpp"
+#include "units/lcd.hpp"
+#include "units/uart1.hpp"
+#include "units/modbus.hpp"
+#include "units/ieee485.hpp"
+#include "units/i2c.hpp"
+
+
+extern "C" {
+   void __cxa_pure_virtual () {
+   }
+
+   int __cxa_guard_acquire(uint8_t *g) {
+      return 0;
+   }
+
+   void __cxa_guard_release(uint8_t *g) {}
+   
+   void __cxa_guard_abort(uint8_t *g) {}
+
+
+   int uart_putchar(char c, FILE *stream) {
+      if (c == '\n') {
+         uart_putchar('\r', stream);
+      }
+      if (stream == stdout) {
+         loop_until_bit_is_set(UCSR0A, UDRE0);
+         UDR0 = c;
+      } 
+      return 0;
+   }
+
+
+   uint64_t volatile systemMillis = 0;
+   uint8_t volatile uartBuffer[32];
+   uint8_t volatile rIndex = 0;
+   uint8_t volatile wIndex = 0;
+
+   int uart_getchar (FILE *stream) {
+    // if (rIndex == wIndex) {
+    //     // nothing in buffer
+    //     return EOF;
+    // }
+    // printf_P(PSTR(" r%d"), rIndex);
+    while (rIndex == wIndex) {
+        // wait for character
+    }
+    
+    // don't use "char c" because german special characters would lead to negative return -> stream error
+    // char c = uartBuffer[rIndex++];
+    
+    uint8_t c = uartBuffer[rIndex++];
+    // printf_P(PSTR("(%02x) "), c);
+    if (c == '\r') {
+      c = '\n';
+    }
+    putchar(c); // echo on terminal
+    return c;
+   }
+
+   static FILE mystdout = { 0, 0, _FDEV_SETUP_WRITE , 0, 0, uart_putchar, NULL, 0 };
+   static FILE mystdin  = { 0, 0, _FDEV_SETUP_READ  , 0, 0, NULL, uart_getchar, 0 };
+
+   static volatile uint32_t timer1ms = 0;
+   static volatile int keyUart0 = EOF;
+
+   Led led;
+   Switch sw;
+   Rgb rgb;
+   Seg7 seg7;
+   Poti poti;
+   Encoder encoder;
+   R2r r2r;
+   Motor motor;
+   PortExp portExp;
+   Lcd lcd;
+   Uart1 uart1;
+   Modbus modbus;
+   Ieee485 ieee485;
+   I2c i2cSparkfun(I2c::SparkFunEnvCombo);
+   I2c i2cMaster(I2c::Master);
+   I2c i2cSlave(I2c::Slave);
+
+}
+
+void setTimer (uint32_t ms) {
+   ATOMIC_BLOCK(ATOMIC_FORCEON) {
+      timer1ms = ms;
+   }
+}
+
+int wait (uint32_t ms) {
+   setTimer(ms);
+   do {
+      ATOMIC_BLOCK(ATOMIC_FORCEON) {
+         ms = timer1ms;
+      }
+   } while (ms > 0 && keyUart0 == EOF);
+   return keyUart0;
+}
+
+int main () {
+
+   // Nano-644 LEDs (Green, Orange, Red)
+   DDRC |= (1 << DDC4) | (1 << DDC3) | (1 << DDC2);
+   PORTC &= ~((1 << PORT4) | (1 << PORT3) | (1 << PORT2));
+
+   // Nano-644 push button SW2
+   DDRC &= ~(1 << DDC5); 
+   PORTC |= (1 << PORT5); // enable internal pullup resistor
+
+   // UART0 interface on Nano-644
+   UCSR0A = (1 << U2X0);
+   UCSR0B = (1 << RXCIE0) | (1 << RXEN0) | (1 <<TXEN0);
+   UCSR0C = (1 << UCSZ01) | ( 1<< UCSZ00);
+   UBRR0H = 0;
+   UBRR0L = F_CPU / 8 / 115200 - 1;
+
+   TCCR2A = (1 << WGM21);
+   TCCR2B = (1 << CS21); // CTC, F_CPU/8=1.5MHz
+   OCR2A = 150; // 150/1.5E6 = 0.1ms
+   TIMSK2 = (1 << OCIE2A);
+
+   stdout = &mystdout;
+   stdin = &mystdin;
+
+   sei();
+
+   TestUnit *unit[] = {
+      &led, &sw, &rgb, &seg7, &poti, &encoder, &r2r, &motor, &portExp, &lcd, &uart1, &modbus, &ieee485,
+      &i2cMaster, &i2cSlave, &i2cSparkfun
+   };
+
+   while (1) {
+      uint16_t i;
+      char s[4];
+      do {
+         printf_P(PSTR("\n\n=============================\n\n"));
+         printf_P(PSTR("Available units:\n\n"));
+         for (i = 0; i < sizeof(unit) / sizeof(unit[0]); i++) {
+            TestUnit *pu = unit[i];
+            printf_P(PSTR("%3x ... "), i);
+            printf_P(pu->getName());
+         }
+         printf_P(PSTR("\nSelect unit: "));
+         rIndex = 0; wIndex = 0;
+         fgets(s, sizeof(s), stdin);
+      } while (sscanf(s, "%x", &i) != 1 || i < 0 || i >= sizeof(unit) / sizeof(unit[0]) );
+      
+      TestUnit *pu = unit[i];
+      printf_P(PSTR("\n\n[%s]: "), pu->getName());
+      keyUart0 = EOF;
+
+      for (uint8_t subtest = 0; subtest < 0xff; subtest++) {
+         printf_P(PSTR("\n%4d: "), subtest);
+         if (pu->run(subtest) < 0) {
+            break;
+         }
+         if (keyUart0 == 27) {
+            keyUart0 = EOF;   
+            break;
+         }
+         keyUart0 = EOF;
+      }
+      pu->cleanup();
+   }
+}
+
+uint64_t millis () {
+   volatile uint64_t millis = systemMillis;
+   ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
+      millis = systemMillis;
+   }
+   return millis;
+}
+
+ISR (USART0_RX_vect) {
+   uint8_t b = UDR0;
+   keyUart0 = b;
+   uartBuffer[wIndex++] = b;
+   // printf_P(PSTR(" w%d(%02x)"), wIndex, b);
+   if (wIndex == rIndex) {
+      // buffer overflow, kick out oldest byte
+      rIndex++;
+   }
+}
+
+ISR (USART1_RX_vect) {
+   uint8_t b = UDR1;
+   if (modbus.enabled) {
+      modbus.handleRxByte(b);
+   }
+   if (uart1.enabled) {
+      uart1.handleRxByte(b);
+   }
+   if (ieee485.enabled) {
+      ieee485.handleRxByte(b);
+   }
+}
+
+ISR (TWI_vect) {
+   if (i2cMaster.enabled) {
+      i2cMaster.handleTwiIrq();
+   } else if (i2cSlave.enabled) {
+      i2cSlave.handleTwiIrq();
+   } else if (i2cSparkfun.enabled) {
+      i2cSparkfun.handleTwiIrq();
+   } else {
+      TWCR = (1 << TWINT); // clear interrupt request bit and disable TWI
+   }
+}
+
+ISR (TIMER2_COMPA_vect) { // every 100us
+   static uint16_t timer500ms = 0;
+   static uint8_t timer100us = 0;
+
+   if (encoder.enabled) {
+      encoder.tick100us();
+   }
+   if (motor.enabled) {
+      motor.tick100us();
+   }
+
+   timer100us++;
+   if (timer100us >= 10) {
+      timer100us = 0;
+      if (timer1ms > 0) {
+         timer1ms--;
+      }
+      systemMillis++;
+      i2cMaster.tick1ms();
+      i2cSlave.tick1ms();
+      i2cSparkfun.tick1ms();
+   }
+
+   timer500ms++;
+   if (timer500ms >= 5000) {
+      PORTC ^= (1 << PORTC3); // orange LED blinking
+      timer500ms = 0;
+   }
+}
+
diff --git a/software/nano-644/test_2024-07-23/src/main.hpp b/software/nano-644/test_2024-07-23/src/main.hpp
new file mode 100644 (file)
index 0000000..f60ec89
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef MAIN_HPP
+#define MAIN_HPP
+
+#include <stdint.h>
+#include <avr/pgmspace.h>
+
+#define ENTER '\r'
+#define CTRLC '\003'
+
+extern int wait (uint32_t ms);
+extern uint64_t millis ();
+
+class TestUnit {
+   public:
+      virtual int8_t run (uint8_t subtest) = 0;
+      virtual void cleanup () = 0;
+      virtual PGM_P getName () = 0;
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/units/encoder.cpp b/software/nano-644/test_2024-07-23/src/units/encoder.cpp
new file mode 100644 (file)
index 0000000..bc84f48
--- /dev/null
@@ -0,0 +1,86 @@
+#include <stdio.h>
+#include <avr/io.h>
+
+#include "encoder.hpp"
+#include "../main.hpp"
+
+// PB0/T0   ... Encoder A
+// PB1/T1   ... Encoder B
+// PB2/INT2 ... push switch of encoder (pushed = 0)
+
+// Encoder signals on rotation clockwise 1 step:
+// A -----____------  one char app. 1ms..2ms (rotation speed)
+// B -------___-----  
+//                    one step when: A = 0, B= 1->0
+
+// Encoder signals on rotation counterclockwise 1 step:
+// A -----____------  one char app. 1ms..2ms (rotation speed)
+// B --______-----  
+//                    one step when: A = 0, B= 0->1
+
+
+void Encoder::cleanup () {
+   DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0));
+   PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0));
+   enabled = 0;
+}
+
+int8_t Encoder::run (uint8_t subtest) {
+   switch (subtest) {
+      case 0: {
+         printf_P(PSTR("init"));
+         DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0));
+         PORTB |= (1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0); // enable pullup
+         enabled = 1;
+         return 0;
+      }
+
+      case 1: {
+         while (wait(10) == EOF) {
+            printf_P(PSTR("\r  => Encoder (push to clear): "));
+            printf_P(PSTR("%5d (0x%02x) "), count, (uint8_t)count);
+            if ((PINB & (1 << PINB2)) == 0) {
+               reset();
+            }
+         }
+         return 0;
+      }
+
+      case 2: {
+         printf_P(PSTR("end"));
+         break;
+      }
+   }
+
+   return -1;
+}
+
+struct EncoderState {
+   uint8_t a:1; // signal A
+   uint8_t b:1; // signal B
+};
+
+void Encoder::tick100us () {
+   static EncoderState lastState = { 1, 1 };
+   static EncoderState lastStableState = { 1, 1 };
+
+   if ((DDRB & 0x03) || (PORTB & 0x07) != 0x07) {
+      count = 0;
+      return; // Enocder pins not configured
+   }
+   EncoderState nextState = { (PINB & 0x01) == 0x01, (PINB & 0x02) == 0x02 };
+   if (nextState.a == lastState.a && nextState.b == lastState.b) {
+      if (lastStableState.a == 0 && nextState.b != lastStableState.b) {
+         if (nextState.b == 0) {
+            count = count < 127 ? count + 1 : 127;
+         } else {
+            count = count > -128 ? count - 1 : -128;
+         }
+      }
+      lastStableState.a = nextState.a;
+      lastStableState.b = nextState.b;
+   }
+   lastState.a = nextState.a;
+   lastState.b = nextState.b;
+}
+
diff --git a/software/nano-644/test_2024-07-23/src/units/encoder.hpp b/software/nano-644/test_2024-07-23/src/units/encoder.hpp
new file mode 100644 (file)
index 0000000..10bcc0f
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef ENCODER_HPP
+#define ENCODER_PP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Encoder : public TestUnit {
+   public:
+      uint8_t enabled;
+      int8_t count;
+
+   public:
+      Encoder () { reset(); enabled = 0; };
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("Encoder"); }
+      void reset () { count = 0; }
+      void tick100us ();
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/units/i2c.cpp b/software/nano-644/test_2024-07-23/src/units/i2c.cpp
new file mode 100644 (file)
index 0000000..4c5934f
--- /dev/null
@@ -0,0 +1,300 @@
+#include <stdio.h>
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+#include <math.h>
+
+#include "i2c.hpp"
+#include "../adafruit/bme280.h"
+#include "../main.hpp"
+
+// Sparkfun https://www.sparkfun.com/products/22858
+// ENS160 address 0x53 (air quality sensor)
+// BME280 address 0x77 /humidity/temperature sensor)
+//    register 0xfe: humidity_7:0
+//    register 0xfd: humidity_15:8
+
+PGM_P I2c::getName () {
+   switch (mode) {
+      case SparkFunEnvCombo: return PSTR("I2C-Sparkfun Env-Combo");
+      case Master: return PSTR("I2C-Master");
+      case Slave: return PSTR("I2C-Slave");
+   }
+   return "?";
+}
+
+void I2c::cleanup () {
+   enabled = false;
+   TWCR = (1 << TWEN);
+   TWBR = 0;
+   ADMUX = 0;
+   ADCSRA = 0;
+}
+
+int8_t I2c::run (uint8_t subtest) {
+   if (subtest == 0) {
+      TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4);
+      TWBR = 28; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4);
+      TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4);
+      TWCR = (1 << TWEN);
+      ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V
+      ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128
+      enabled = true;
+      printf_P(PSTR("init"));
+
+   } else if (subtest == 1 && mode == I2c::SparkFunEnvCombo) {
+      printf_P(PSTR(" BM280 ... "));
+      if (!bm280.begin()) {
+         printf_P(PSTR("E1"));
+         return -1;
+      }
+      printf_P(PSTR("OK, ENS160 ... "));
+      if (!ens160.begin()) {
+         printf_P(PSTR("E2"));
+         return -1;
+      }
+      if (!ens160.setMode(ENS160_OPMODE_STD)) {
+         printf_P(PSTR("E3"));
+         return -1;
+      }
+      if (!ens160.set_envdata(25.0, 65)) {
+         printf_P(PSTR("E4"));
+         return -1;
+      }
+
+      printf_P(PSTR("OK"));
+      float accTemp = 0, accHumidity = 0;
+      int8_t accCount = -1;
+
+      do {
+         // BME280 
+         float p = bm280.readPressure();
+         printf_P(PSTR("\n => BM280: P= %.3fbar"), (double)p / 100000.0);
+         float t = bm280.readTemperature();
+         printf_P(PSTR(", T= %.2f°C"), (double)t);
+         float h = bm280.readHumidity();
+         printf_P(PSTR(", H= %.2f%%"), (double)h);
+
+         if (accCount >= 0 && !isnanf(h) && !isnan(t)) {
+            accTemp += t;
+            accHumidity += h;
+            accCount++;
+         }
+
+         bm280.setSampling(
+            Adafruit_BME280::MODE_NORMAL,
+            Adafruit_BME280::SAMPLING_X16,
+            Adafruit_BME280::SAMPLING_X16,
+            Adafruit_BME280::SAMPLING_X16,
+            Adafruit_BME280::FILTER_OFF,
+            Adafruit_BME280::STANDBY_MS_1000
+         );
+
+         // ENS160 only activated every 32s to avoid wrong temperature measuerment
+         // if ES160 would be continously active, the temperature would be 4°C higher
+         // -> ES160 has not enough distance to BM280
+         // This solution causes only a +0.3°C higher temperatur value
+         if (accCount < 0 || accCount >= 32) {
+            printf_P(PSTR("  | ENS160 ("));
+            if (accCount > 0) { 
+               h = accHumidity / accCount;
+               t = accTemp / accCount;
+               accTemp = 0;
+               accHumidity = 0;
+            }
+            accCount = 0;            
+            if (!ens160.set_envdata(t, h)) {
+               printf_P(PSTR("E1)"));
+            } else {
+               printf_P(PSTR("%.1f°C/%.1f%%): "), (double)t, (double)h);
+               if (!ens160.setMode(ENS160_OPMODE_STD)) {
+                  printf_P(PSTR("E2"));
+               } else {
+                  for (uint8_t i = 0; i < 100; i++) {
+                     _delay_ms(15);
+                     uint8_t status;
+                     if (ens160.readStatus(&status)) {
+                        if (status & ENS160_DATA_STATUS_NEWDAT) {
+                           ENS160_DATA data;
+                           if (ens160.readData(&data)) {
+                              printf_P(PSTR(" aqi=%d("), data.aqi);
+                              switch(data.aqi) {
+                                 case 1: printf_P(PSTR("excellent")); break;
+                                 case 2: printf_P(PSTR("good")); break;
+                                 case 3: printf_P(PSTR("moderate")); break;
+                                 case 4: printf_P(PSTR("poor")); break;
+                                 case 5: printf_P(PSTR("unhealthy")); break;
+                                 default: printf_P(PSTR("?")); break;
+                              }
+                              printf_P(PSTR("), tvoc=%dppb"), data.tvoc);
+                              printf_P(PSTR(", eco2=%d("), data.eco2);
+                              if (data.eco2 < 400) {
+                                 printf_P(PSTR("?"));
+                              } else if (data.eco2 < 600) {
+                                 printf_P(PSTR("excellent"));
+                              } else if (data.eco2 < 800) {
+                                 printf_P(PSTR("good"));
+                              } else if (data.eco2 < 1000) {
+                                 printf_P(PSTR("fair"));
+                              } else if (data.eco2 < 1500) {
+                                 printf_P(PSTR("poor"));
+                              } else {
+                                 printf_P(PSTR("bad"));
+                              }
+                              printf_P(PSTR(")"));
+                           }
+                           break;
+                        }
+                     }
+                  }
+               }
+               if (!ens160.setMode(ENS160_OPMODE_IDLE)) {
+                  printf_P(PSTR("E3"));
+               }
+            }
+         }
+
+      } while (wait(1000) == EOF);
+
+   } else if (subtest == 1 && mode == I2c::Master) {
+      if (!master.begin(0x01)) {
+         printf_P(PSTR("E1"));
+         return -1;
+      }
+      do {
+         uint8_t buffer[1];
+         // read poti
+         ADCSRA |= (1 << ADSC); // start ADC
+         while (ADCSRA & (1 << ADSC)) {} // wait for result
+         buffer[0] = ADCH;
+         printf_P(PSTR("\n write 0x%02x"), buffer[0]);
+         if (!master.write(buffer, 1)) {
+            printf_P(PSTR(" -> ERROR"));
+         }
+         printf_P(PSTR(",  read "));
+         if (master.read(buffer, 1)) {
+            printf_P(PSTR("0x%02x"), buffer[0]);   
+         } else {
+            printf_P(PSTR(" -> ERROR"));
+         }
+      } while (wait(1000) == EOF);
+      master.end();
+
+   } else if (subtest == 1 && mode == I2c::Slave) {
+      if (!slave.begin(0x01, false)) {
+         printf_P(PSTR("E1"));
+         return -1;
+      }
+      do {
+         int fromMaster = slave.read();
+         if (fromMaster != EOF) {
+            ADCSRA |= (1 << ADSC); // start ADC
+            while (ADCSRA & (1 << ADSC)) {} // wait for result
+            slave.write(ADCH);
+            printf_P(PSTR("\n => from master: 0x%02x -> to master: 0x%02x"), fromMaster, ADCH);
+         }
+      } while (wait(0) == EOF);
+      slave.end();
+
+   } else {
+      printf_P(PSTR("end"));
+      return -1;
+   }
+   wait(500);
+   return 0;
+}
+
+void I2c::handleTwiIrq () {
+   if (mode == I2c::Slave) {
+      DDRD |= (1 << PD7);
+      PORTD |= (1 << PD7);
+      slave.handleTWIIsr();
+      PORTD &= ~(1 << PD7);
+   } else {
+      TWCR |= (1 << TWINT); // clear Interrupt Request
+   }
+}
+
+// uint16_t I2c::startRead (uint8_t address) {
+//    TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition
+//    while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+//    uint8_t sr = TWSR & 0xf8;
+//    if (sr != 0x08 && sr != 0x10) {
+//       return 0x0100 | sr;
+//    }
+
+//    TWDR = (address << 1) | 0x01; // address + READ (R/W = 1)
+//    TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW
+//    while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+//    sr = TWSR & 0xf8;
+//    if (sr != 0x40) {
+//       return 0x0200 | sr;
+//    }
+//    return 0;
+// }
+
+// uint16_t I2c::startWrite (uint8_t address) {
+//    TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition
+//    while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+//    uint8_t sr = TWSR & 0xf8;
+//    if (sr != 0x08 && sr != 0x10) {
+//       return 0x0300 | sr;
+//    }
+
+//    TWDR = (address << 1) | 0x00; // address + WRITE (R/W = 0)
+//    TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW
+//    while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+//    sr = TWSR & 0xf8;
+//    if (sr != 0x18) {
+//       return 0x0400 | sr;
+//    }
+//    return 0;
+// }
+
+// void I2c::stop () {
+//    TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
+// }
+
+// uint16_t I2c::writeData (uint8_t size, const uint8_t *data) {
+//    while (size-- > 0) {
+//       TWDR = *data++;
+//       TWCR = (1 << TWINT) | (1 << TWEN); // send data byte
+//       while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+//       uint8_t sr = TWSR & 0xf8;
+//       if (sr != 0x28) {
+//          return 0x0500 | sr;
+//       }
+//    }
+//    return 0;
+// }
+
+// uint16_t I2c::writeByte (uint8_t data) {
+//    return writeData(1, &data);
+// }
+
+// uint16_t I2c::readData (uint8_t size, uint8_t *data) {
+//    while (size-- > 0) {
+//       if (size > 0) {
+//          TWCR = (1 << TWEA) | (1 << TWINT) | (1 << TWEN); // read data byte with ACK enabled
+//       } else {
+//          TWCR = (1 << TWINT) | (1 << TWEN); // read data byte with ACK disabled
+//       }
+//       while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
+//       uint8_t sr = TWSR & 0xf8;
+//       if ((size > 0 && sr != 0x50) || (size == 0 && sr != 0x58)) {
+//          return 0x0600 | sr;
+//       }
+//       *data++ = TWDR;
+//    }
+//    return 0;
+// }
+
+// int32_t I2c::compensateBm280T (int32_t adcT) {
+//    // int32_t var1, var2, t;
+//    // var1 = ((((adcT >> 3) - ((int32_t)bm280.digT[0] << 1))) * ((int32_t)bm280.digT[1])) >> 11;
+//    // var2 = (((((adcT >> 4) - ((int32_t)bm280.digT[0])) * ((adcT >> 4) - ((int32_t)bm280.digT[0]))) >> 12) * ((int32_t)bm280.digT[2])) >> 14;
+//    // bm280.tFine = var1 + var2;
+//    // t = (bm280.tFine * 5 + 128) >> 8;
+//    // return t;
+//    return -1;
+// }
diff --git a/software/nano-644/test_2024-07-23/src/units/i2c.hpp b/software/nano-644/test_2024-07-23/src/units/i2c.hpp
new file mode 100644 (file)
index 0000000..e1f1802
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef I2C_HPP
+#define I2C_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include "../adafruit/bme280.h"
+#include "../adafruit/ens160.h"
+#include "../i2cmaster.hpp"
+#include "../i2cslave.hpp"
+
+
+class I2c : public TestUnit {
+   public:
+      typedef enum I2cMode { SparkFunEnvCombo, Master, Slave } I2cMode;
+
+   private:
+      I2cMode mode;
+      Adafruit_BME280 bm280;
+      ScioSense_ENS160 ens160;
+      I2cMaster master;
+      I2cSlave slave;
+
+   public:
+      bool enabled;
+
+   public:
+      I2c (I2cMode mode) { enabled = false; this->mode = mode; }
+      void tick1ms () { master.tick1ms(); slave.tick1ms(); }
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName ();
+      void handleTwiIrq ();
+      uint16_t startRead (uint8_t address);
+      uint16_t startWrite (uint8_t address);
+      void stop ();
+      uint16_t writeByte (uint8_t data);
+      uint16_t writeData (uint8_t size, const uint8_t *data);
+      uint16_t readData (uint8_t size, uint8_t *data);
+      int32_t compensateBm280T (int32_t adcT);
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/units/ieee485.cpp b/software/nano-644/test_2024-07-23/src/units/ieee485.cpp
new file mode 100644 (file)
index 0000000..89e25ec
--- /dev/null
@@ -0,0 +1,91 @@
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+#include <util/atomic.h>
+
+#include "ieee485.hpp"
+#include "../main.hpp"
+
+// PB0 ... nRE .. Read enable
+// PB1 ...  DE .. Data enable
+
+#define SET_nRE  (PORTB |= (1 << PB0))
+#define CLR_nRE  (PORTB &= ~(1 << PB0))
+#define SET_DE   (PORTB |= (1 << PB1))
+#define CLR_DE   (PORTB &= ~(1 << PB1))
+
+
+void Ieee485::cleanup () {
+   enabled = 0;
+
+   ADMUX = 0;
+   ADCSRA = 0;
+
+   UCSR1A = 0;
+   UCSR1B = 0;
+   UCSR1C = 0;
+   UBRR1H = 0;
+   UBRR1L = 0;
+   PORTD &= ~(1 << PD2);
+   DDRB &= ~((1 << PB1) | (1 << PB0));
+   PORTB &= ~((1 << PB1) | (1 << PB0));
+}
+
+int8_t Ieee485::run (uint8_t subtest) {
+   if (subtest == 0) {
+      // Poti
+      ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V
+      ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128
+
+      // Modbus
+      SET_nRE;
+      CLR_DE;
+      DDRB |= (1 << PB1) | (1 << PB0);
+
+      // UART1 interface on Nano-644
+      PORTD |= (1 << PD2); // enable RxD1 pullup
+      UCSR1A = (1 << U2X1);
+      UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 <<TXEN1);
+      UCSR1C = (1 << UCSZ11) | ( 1<< UCSZ10);
+      // UCSR1C |= (1 <<UPM11); // even Parity
+      // UCSR1C |= (1 << UPM11) | (1 << UPM10); // odd Parity
+      UBRR1H = 0;
+      UBRR1L = F_CPU / 8 / 9600 - 1;
+
+      enabled = 1;
+      printf_P(PSTR("init"));
+
+   } else if (subtest == 1) {
+      CLR_nRE; CLR_DE;
+      while (wait(500) == EOF) {
+         ADCSRA |= (1 << ADSC); // start ADC
+         while (ADCSRA & (1 << ADSC)) {} // wait for result
+         UCSR1A |= (1 << TXC1);
+         SET_nRE;
+         SET_DE;
+         UDR1 = ADCH;
+         while ((UCSR1A & (1 << TXC1)) == 0) {}
+         CLR_DE;
+         CLR_nRE;
+         printf_P(PSTR("\n  => send Byte 0x%02x"), ADCH);
+         int b;
+         ATOMIC_BLOCK(ATOMIC_FORCEON) {
+            b = receivedByte;
+            receivedByte = -1; 
+         }
+         if (b >= 0) {
+            printf_P(PSTR("\n  => receive Byte: 0x%02x"), b);
+         }
+      }
+
+   } else {
+      printf_P(PSTR("end"));
+      return -1;
+   }
+
+   return 0;
+}
+
+void Ieee485::handleRxByte (uint8_t b) {
+   receivedByte = b;
+}
diff --git a/software/nano-644/test_2024-07-23/src/units/ieee485.hpp b/software/nano-644/test_2024-07-23/src/units/ieee485.hpp
new file mode 100644 (file)
index 0000000..677963f
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef IEEE485_HPP
+#define IEEE485_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Ieee485 : public TestUnit {
+   public:
+      uint8_t  enabled;
+      int16_t  receivedByte;
+
+   public:
+      Ieee485 () { enabled = 0; receivedByte = -1; }
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual const char *getName () { return PSTR("IEEE485"); }
+      void handleRxByte (uint8_t);
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/units/lcd.cpp b/software/nano-644/test_2024-07-23/src/units/lcd.cpp
new file mode 100644 (file)
index 0000000..91d0845
--- /dev/null
@@ -0,0 +1,224 @@
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+#include "lcd.hpp"
+#include "../main.hpp"
+
+// PA3 ..... E    --> LCD Enable (Verbindung via J25 erforderlich)
+// PD6 ..... R/W  --> Read/Write: Read=1, Write=0
+// PD7 ..... RS   --> Register Select: Command=0, Data=1
+// PB7:0 ... Data --> Achtung von 5V LCD nicht lesen!
+
+// #define LCD_3V3
+#ifdef LCD_3V3
+   #define DATA_PIN  PINB
+#endif
+
+#define DATA_PORT      PORTB
+#define DIR_DATA_PORT  DDRB
+
+#define PORT_RS PD7
+#define SET_RS_PIN   (PORTD |= (1 << PORT_RS))
+#define CLR_RS_PIN   (PORTD &= ~(1 << PORT_RS))
+#define SET_DIR_RS   (DDRD |= (1 << PORT_RS))
+#define CLR_DIR_RS   (DDRD &= ~(1 << PORT_RS))
+
+#define PORT_RW PD6
+#define SET_RW_PIN   (PORTD |= (1 << PORT_RW))
+#define CLR_RW_PIN   (PORTD &= ~(1 << PORT_RW))
+#define SET_DIR_RW   (DDRD |= (1 << PORT_RW))
+#define CLR_DIR_RW   (DDRD &= ~(1 << PORT_RW))
+
+#define SET_E_PIN   (PORTA |= (1 << PA3))
+#define CLR_E_PIN   (PORTA &= ~(1 << PA3))
+#define SET_DIR_E   (DDRA |= (1 << PA3))
+#define CLR_DIR_E   (DDRA &= ~(1 << PA3))
+
+// Befehle für das Display
+
+#define DISP_CLEAR  0b00000001   // Display clear   
+#define DISP_ON     0b00001111   // Display on      
+#define DISP_OFF    0b00001011   // Display off     
+#define CURSOR_ON   0b00001111   // Cursor on       
+#define CURSOR_OFF  0b00001101   // Cursor off      
+#define BLINK_ON    0b00001111   // Cursor Blink    
+#define BLINK_OFF   0b00001110   // Cursor No Blink 
+
+
+void Lcd::cleanup () {
+   DDRA &= ~(1 << PA3);
+   PORTA &= ~(1 << PA3);
+   DDRD &= ~((1 << DDD7) | (1 << DDD6));
+   PORTD &= ~((1 << PORTD7) | (1 << PORTD6));
+   PORTB = 0;
+   DDRB = 0;
+}
+
+int8_t Lcd::run (uint8_t subtest) {
+   if (subtest == 0) {
+      // DDRA |= (1 << PA3);
+      // PORTA &= ~(1 << PA3);
+      // DDRD |= (1 << DDD7) | (1 << DDD6);
+      // PORTD &= ~((1 << PORTD7) | (1 << PORTD6));
+      // PORTB = 0;
+      // DDRB = 0xff;
+      init();
+      #ifdef LCD_3V3
+         printf_P(PSTR("init 3.3V LCD"));
+      #else
+         printf_P(PSTR("init 5V LCD"));
+      #endif
+
+   } else if (subtest == 1) {
+      for (uint8_t i = 0; i < 20 * 4; i++) {
+         char c = (char)(i + 32);
+         if (i % 20 == 0) {
+            setCursor(i / 20 + 1, 1);
+         }
+         writeData(c);
+         while (isBusy()) {};
+      }
+      // setCursor(1, 1);
+      // writeString(" 1234567890<>,;.:-_#+");
+      // setCursor(2, 1);
+      // writeString("abcdefghijklmnopqrst");
+      // setCursor(3, 1);
+      // writeString("uvwxyzABCDEFGHIJKLMN");
+      // setCursor(4, 1);
+      // writeString("OPQRSTUVWXYZ ");
+      printf_P(PSTR("LCD beschrieben"));
+      while (wait(1) == EOF) {
+      }
+
+   } else {
+      printf_P(PSTR("end"));
+      return -1;
+   }
+   wait(500);
+   return 0;
+}
+
+void Lcd::init () {
+   CLR_RW_PIN; // write
+   CLR_RS_PIN; // command
+   CLR_E_PIN;  // E = 0
+
+   SET_DIR_RW;
+   SET_DIR_RS;
+   SET_DIR_E;
+
+   DATA_PORT = 0; 
+   DIR_DATA_PORT = 0xff;
+   _delay_ms(16); // min 15ms warten für Reset des Displays
+
+   DATA_PORT = 0b00111011; // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display
+   CLR_RW_PIN;             // write
+   CLR_RS_PIN;             // command
+   SET_E_PIN;              // E = 1 (transfer start)
+   _delay_us(10);               // min. 10us 
+   CLR_E_PIN;              // E = 0 (transfer end)
+   _delay_ms(5);           // min. 4.1ms
+
+   DATA_PORT = 0b00111011; // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display
+   CLR_RW_PIN;             // write
+   CLR_RS_PIN;             // command
+   SET_E_PIN;              // E = 1 (transfer start)
+   _delay_us(10);               // min. 10us 
+   CLR_E_PIN;              // E = 0 (transfer end)
+   _delay_us(100);         // min. 100us
+
+   DATA_PORT = 0b00111011;     // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display
+   CLR_RW_PIN;             // write
+   CLR_RS_PIN;             // command
+   SET_E_PIN;              // E = 1 (transfer start)
+   _delay_us(10);               // min. 10us 
+   CLR_E_PIN;              // E = 0 (transfer end)
+   _delay_us(100);         // min. 100us
+
+   writeCommand(DISP_OFF);     // Display aus
+   while(isBusy()) {};
+   writeCommand(DISP_ON);      // Display ein
+   while(isBusy()) {};
+   writeCommand( BLINK_OFF & CURSOR_OFF);      // Blink aus und Cursor aus
+   while(isBusy()) {};
+   writeCommand(DISP_CLEAR);// Clear display
+   while(isBusy()) {};
+}
+
+uint8_t Lcd::isBusy () {
+   #ifdef LCD_3V3
+      DIR_DATA_PORT = 0;
+      SET_RW_PIN; // read
+      CLR_RS_PIN;      // command
+      SET_E_PIN;  // E = 1 (transfer start)
+      _delay_us(10);
+      uint8_t busy = DATA_PIN & 0x80; // read bit 7 (busy bit)
+      CLR_E_PIN;  // E = 0 (transfer end)
+      CLR_RW_PIN;
+      DIR_DATA_PORT = 0xff;
+      return busy != 0;
+   #else
+      _delay_us(200);
+      return 0;
+   #endif
+   
+}
+
+void Lcd::writeCommand (uint8_t cmd) {
+   DATA_PORT = cmd;
+   CLR_RW_PIN;      // write
+   CLR_RS_PIN;      // command
+   SET_E_PIN;       // E = 1 (transfer start)
+   _delay_us(10);        // min. 10us 
+   CLR_E_PIN;       // E = 0 (transfer end)
+   _delay_us(10);   // min. 10us
+   DATA_PORT = 0;
+}
+
+void Lcd::setDDRamAddr (uint8_t address) {
+   DATA_PORT = address | 0x80;
+   CLR_RW_PIN;      // write
+   CLR_RS_PIN;      // command
+   SET_E_PIN;       // E = 1 (transfer start)
+   _delay_us(10);        // min. 10us 
+   CLR_E_PIN;       // E = 0 (transfer end)
+   _delay_us(10);   // min. 10us
+   DATA_PORT = 0;
+}
+
+void Lcd::writeString (const char *s) {
+   while (*s) {
+      writeData(*s++);
+      while (isBusy()) {};
+   }
+}
+
+void Lcd::writeData (uint8_t data) {
+   DATA_PORT = data;               // Write data to port
+   SET_RS_PIN; // data
+   CLR_RW_PIN; // command
+   SET_E_PIN;       // E = 1 (transfer start)
+   _delay_us(10);        // min. 10us 
+   CLR_E_PIN;       // E = 0 (transfer end)
+   _delay_us(10);   // min. 10us
+   CLR_RS_PIN;
+   DATA_PORT = 0;
+}
+
+void Lcd::setCursor (uint8_t row, uint8_t column) {
+   uint8_t b;
+       if (column > 20) {
+               return;
+       }
+       switch (row) {
+               case 1: b = 0x00 + column - 1; break;
+               case 2: b = 0x40 + column - 1; break;
+      case 3: b = 0x14 + column - 1; break;
+      case 4: b = 0x54 + column - 1; break;
+               default: return;
+       }
+       setDDRamAddr(b);
+   while (isBusy()) {};
+}
+
diff --git a/software/nano-644/test_2024-07-23/src/units/lcd.hpp b/software/nano-644/test_2024-07-23/src/units/lcd.hpp
new file mode 100644 (file)
index 0000000..14561b0
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef LCD_HPP
+#define LCD_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Lcd : public TestUnit {
+   public:
+      Lcd () {};
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("Lcd"); }
+
+      void init ();
+      uint8_t isBusy ();
+      void writeCommand (uint8_t);
+      void setDDRamAddr (uint8_t);
+      void writeString (const char *s);
+      void writeData (uint8_t);
+      void setCursor (uint8_t row, uint8_t column);
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/units/led.cpp b/software/nano-644/test_2024-07-23/src/units/led.cpp
new file mode 100644 (file)
index 0000000..a35ca81
--- /dev/null
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+#include "led.hpp"
+#include "../main.hpp"
+
+void Led::cleanup () {
+   DDRD &= ~((1 << DDD7) | (1 << DDD6) | (1 << DDD5) | (1 << DDD4));
+   PORTD &= ~((1 << PORTD7) | (1 << PORTD6) | (1 << PORTD5) | (1 << PORTD4));
+}
+
+int8_t Led::run (uint8_t subtest) {
+   if (subtest == 0) {
+      DDRD |= (1 << DDD7) | (1 << DDD6) | (1 << DDD5) | (1 << DDD4);
+      PORTD &= ~((1 << PORTD7) | (1 << PORTD6) | (1 << PORTD5) | (1 << PORTD4));
+      printf_P(PSTR("init"));
+
+   } else if (subtest <= 16) {
+      subtest = (subtest - 1) % 4;
+      PORTD &= ~((1 << PORTD7) | (1 << PORTD6) | (1 << PORTD5) | (1 << PORTD4));
+      PORTD |= (1 << (subtest + 4));
+      printf_P(PSTR("Test LED PD%d"), subtest + 4);
+
+   } else {
+      printf_P(PSTR("end"));
+      return -1;
+   }
+   wait(500);
+   return 0;
+}
+
diff --git a/software/nano-644/test_2024-07-23/src/units/led.hpp b/software/nano-644/test_2024-07-23/src/units/led.hpp
new file mode 100644 (file)
index 0000000..acff045
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef LED_HPP
+#define LED_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Led : public TestUnit {
+   public:
+      Led () {};
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("Led"); }
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/units/modbus.cpp b/software/nano-644/test_2024-07-23/src/units/modbus.cpp
new file mode 100644 (file)
index 0000000..ec74d50
--- /dev/null
@@ -0,0 +1,145 @@
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+#include "modbus.hpp"
+#include "../main.hpp"
+
+// PB0 ... nRE .. Read enable
+// PB1 ...  DE .. Data enable
+
+#define SET_nRE  (PORTB |= (1 << PB0))
+#define CLR_nRE  (PORTB &= ~(1 << PB0))
+#define SET_DE   (PORTB |= (1 << PB1))
+#define CLR_DE   (PORTB &= ~(1 << PB1))
+
+
+void Modbus::cleanup () {
+   enabled = 0;
+   UCSR1A = 0;
+   UCSR1B = 0;
+   UCSR1C = 0;
+   UBRR1H = 0;
+   UBRR1L = 0;
+   PORTD &= ~(1 << PD2);
+   DDRB &= ~((1 << PB1) | (1 << PB0));
+   PORTB &= ~((1 << PB1) | (1 << PB0));
+}
+
+int8_t Modbus::run (uint8_t subtest) {
+   if (subtest == 0) {
+      SET_nRE;
+      CLR_DE;
+      DDRB |= (1 << PB1) | (1 << PB0);
+
+      // UART1 interface on Nano-644
+      PORTD |= (1 << PD2); // enable RxD1 pullup
+      UCSR1A = (1 << U2X1);
+      UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 <<TXEN1);
+      UCSR1C = (1 << UCSZ11) | ( 1<< UCSZ10);
+      // UCSR1C |= (1 <<UPM11); // even Parity
+      // UCSR1C |= (1 << UPM11) | (1 << UPM10); // odd Parity
+      UBRR1H = 0;
+      UBRR1L = F_CPU / 8 / 9600 - 1;
+
+      enabled = 1;
+      printf_P(PSTR("init"));
+
+   } else if (subtest >= 1 && subtest <= 4) {
+      uint8_t nre, de, b;
+      switch (subtest) {
+         case 1: nre = 1; de = 0; b = 0x01; break;
+         case 2: nre = 1; de = 1; b = 0x8e; break;
+         case 3: nre = 0; de = 0; b = 0x55; break;
+         case 4: nre = 0; de = 1; b = 0xaa; break;
+         default: return -1;
+      }
+      printf_P(PSTR(" DE=%u, nRE=%u send 0x%02x... "), de, nre, b);
+      if (nre) {
+         SET_nRE;
+      } else {
+         CLR_nRE;
+      }
+      if (de) {
+         SET_DE;
+      } else {
+         CLR_DE;
+      }
+      _delay_us(100);
+      receivedBytes = 0;
+      UDR1 = b;
+      _delay_ms(1);
+      if (receivedBytes > 0) {
+         printf_P(PSTR("0x%02x received"), received[0]);
+         receivedBytes = 0;
+      } else {
+         printf_P(PSTR("no byte received"));
+      }
+      printf_P(PSTR(" ... press key to proceed"));
+      while (wait(0xffffffff) == EOF) {}
+      CLR_DE;
+      SET_nRE;
+
+   } else if (subtest == 5) {
+      static uint8_t frame[] = { 0x01, 0x04, 0x00, 0x00, 0x00, 0x02, 0x71, 0xcb };
+      printf_P(PSTR("Modbus: lese Spannung von Eastron SDM-230 (Einphasenzähler)"));
+      SET_DE;
+      CLR_nRE;
+      _delay_us(100);
+      do {
+         SET_DE;
+         receivedBytes = 0;
+         for (uint8_t i = 0; i < sizeof(frame); i++) {
+            UCSR1A |= (1 << TXC1);
+            UDR1 = frame[i];
+            while ((UCSR1A & (1 <<UDRE1)) == 0) {}
+         }
+         while ((UCSR1A & (1 << TXC1)) == 0) {}
+         CLR_DE;
+         printf_P(PSTR("\n => Sending:"));
+         for (uint8_t i = 0; i < sizeof(frame); i++) {
+            printf_P(PSTR(" 0x%02x"), frame[i]);
+         }
+         int k = wait(100);
+      
+         printf_P(PSTR("\n       RxD1:"));
+         if (receivedBytes == 0) {
+            printf_P(PSTR("?"));
+         } else {
+            for (uint8_t i = 0; i < receivedBytes; i++) {
+               if (i == sizeof(frame)) {
+                  printf_P(PSTR("  "));
+               }
+               printf_P(PSTR(" 0x%02x"), received[i]);
+            }
+         }
+         if (receivedBytes >= 16) {
+            union {
+               uint8_t b[4];
+               float value;
+            } f;
+            f.b[0] = received[14];
+            f.b[1] = received[13];
+            f.b[2] = received[12];
+            f.b[3] = received[11];
+            printf_P(PSTR(" -> %4.8fV\n"), (double)f.value);
+         }
+         if (k != EOF) {
+            break;
+         }
+         
+      } while (wait(1000) == EOF);
+
+   } else {
+      printf_P(PSTR("end"));
+      return -1;
+   }
+   wait(500);
+   return 0;
+}
+
+void Modbus::handleRxByte (uint8_t b) {
+   if (receivedBytes < sizeof(received)) {
+      received[receivedBytes++] = b;
+   }
+}
diff --git a/software/nano-644/test_2024-07-23/src/units/modbus.hpp b/software/nano-644/test_2024-07-23/src/units/modbus.hpp
new file mode 100644 (file)
index 0000000..d0f6c48
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef MODBUS_HPP
+#define MODBUS_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Modbus : public TestUnit {
+   public:
+      uint8_t  enabled;
+      uint8_t  receivedBytes;
+      uint8_t  received[16];
+
+
+   public:
+      Modbus () { enabled = 0; receivedBytes = 0; }
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("Modbus"); }
+      void handleRxByte (uint8_t);
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/units/motor.cpp b/software/nano-644/test_2024-07-23/src/units/motor.cpp
new file mode 100644 (file)
index 0000000..429d39a
--- /dev/null
@@ -0,0 +1,107 @@
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/atomic.h>
+
+#include "motor.hpp"
+#include "../main.hpp"
+
+void Motor::cleanup () {
+   ADMUX = 0;
+   ADCSRA = 0;
+   TCCR0A = 0;
+   TCCR0B = 0;
+   DDRB &= ~((1 << PB4) | (1 << PB3));
+   PORTA &= ~(1 << PORTA3);
+   enabled = 0;
+}
+
+int8_t Motor::run (uint8_t subtest) {
+   switch (subtest) {
+      case 0: {
+         printf_P(PSTR("init"));
+         ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V
+         ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128
+         TCCR0A = (1 << COM0A1) | (1 << WGM01) | (1 << WGM00); // Fast PWM on OC0A
+         // TCCR0B = (1 << CS02) | ( 1 << CS00); // f = 12 MHz / 1024 = 11,71875 kHz -> fPWM=45Hz
+         TCCR0B = (1 << CS02); // f = 12 MHz / 256 = 46,875 kHz -> fPWM=183,1Hz
+         DDRB |= (1 << PB4) | (1 << PB3); // Motor enable
+         PORTA |= (1 << PORTA3); // push button for Motor enable control
+         enabled = 1;
+         return 0;
+      }
+
+      case 1: {
+         printf_P(PSTR("\n"));
+         while (wait(10) == EOF) {
+
+            ADCSRA |= (1 << ADSC); // start ADC
+            while (ADCSRA & (1 << ADSC)) {} // wait for result
+            printf_P(PSTR("\r  => ADC0=%3d"), ADCH);
+
+            ADMUX = (1 << ADLAR) | (1 << REFS1) | (1 << REFS0) | 2; // ADC2, VREF=2.5V
+
+            uint8_t dutyCycle = 0xff - ADCH;
+            if (dutyCycle <= 1) {
+               dutyCycle = 0;
+            } else if (dutyCycle > 254) {
+               dutyCycle = 255;
+            }
+            OCR0A = dutyCycle;
+            printf_P(PSTR("  PWM/OC0A=%3d"), dutyCycle);
+
+            ADCSRA |= (1 << ADSC); // start ADC
+            while (ADCSRA & (1 << ADSC)) {} // wait for result
+            printf_P(PSTR(" ADC2=%3d"), ADCH);
+            ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V
+
+            if ((PINA & (1<< PA3)) == 0) {
+               PORTB &= ~(1 << PORTB4);
+            } else {
+               PORTB |= (1 << PORTB4);
+            }
+
+            uint16_t timer;
+            ATOMIC_BLOCK(ATOMIC_FORCEON) {
+               timer = rpmTimer;
+            }
+            float rpm = 60.0 / (float)timer / 0.0001;
+            if (timer > 0) {
+               printf_P(PSTR("  n= %4d U/min"), (int)rpm);
+            } else {
+               printf_P(PSTR("  no rotation"));
+            }
+
+         }
+         return 0;
+      }
+
+      case 2: {
+         printf_P(PSTR("end"));
+         break;
+      }
+   }
+
+   return -1;
+}
+
+void Motor::tick100us () {
+   static uint16_t timerH = 0;
+   static uint16_t timerL = 0;
+   static uint8_t pinb = 0;
+   if ((PINB & 0x01) && PINB != pinb && timerL > 10) {
+      rpmTimer = timerL + timerH;
+      timerL = 0;
+      timerH = 0;
+   }
+   if (PINB & 0x01) {
+      timerH = timerH < 0x4000 ? timerH + 1 : 0x4000;
+   } else {
+      timerL = timerL < 0x4000 ? timerL + 1 : 0x4000;
+   }
+   if (timerH >= 0x4000 || timerL >= 0x4000) {
+      rpmTimer = 0; // no ratation detected
+   }
+   pinb = PINB;
+}
+
+
diff --git a/software/nano-644/test_2024-07-23/src/units/motor.hpp b/software/nano-644/test_2024-07-23/src/units/motor.hpp
new file mode 100644 (file)
index 0000000..74623ca
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef MOTOR_HPP
+#define MOTOR_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Motor : public TestUnit {
+   public:
+      uint8_t  enabled;
+      uint16_t rpmTimer;
+
+   public:
+      Motor () { enabled = 0; rpmTimer = 0; };
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("Motor"); }
+      void tick100us ();
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/units/portexp.cpp b/software/nano-644/test_2024-07-23/src/units/portexp.cpp
new file mode 100644 (file)
index 0000000..bcc2052
--- /dev/null
@@ -0,0 +1,113 @@
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+#include "portexp.hpp"
+#include "../main.hpp"
+
+// Port-Expander MCP23S17
+
+// SN-Print Stecker IO16
+// MCP23S17 | IO16 (MEGA2560) | Ampel-Print |   | MCP23S17 | IO16 (MEGA2560) | Ampel-Print |
+// ------------------------------------------   --------------------------------------------
+//     GPA0 |   IO16O7 (PA7)  | Taster RU   |   |     GPB0 |   IO16U7 (PC7)  | Taster LU   |
+//     GPA1 |   IO16O6 (PA6)  | Taster RO   |   |     GPB1 |   IO16U6 (PC6)  | Taster LO   |
+//     GPA2 |   IO16O5 (PA5)  | U-Gruen     |   |     GPB2 |   IO16U5 (PC5)  | R-Gruen     |
+//     GPA3 |   IO16O4 (PA4)  | U-Gelb      |   |     GPB3 |   IO16U4 (PC4)  | R-Gelb      |
+//     GPA4 |   IO16O3 (PA3)  | U-Rot       |   |     GPB4 |   IO16U3 (PC3)  | R-Rot       |
+//     GPA5 |   IO16O2 (PA2)  | L-Gruen     |   |     GPB5 |   IO16U2 (PC2)  | O-Gruen     |
+//     GPA6 |   IO16O1 (PA1)  | L-Gelb      |   |     GPB6 |   IO16U1 (PC1)  | O-Gelb      |
+//     GPA7 |   IO16O0 (PA0)  | L-Rot       |   |     GPB7 |   IO16U0 (PC0)  | O-Rot       |
+
+
+void PortExp::cleanup () {
+   DDRB &= ~(1 << PB6); // // SPI MISO (=PB6)
+   DDRB &= ~((1 << PB7) | (1 << PB5)); // SPI SCK (=PB7) and SPI MOSI (=PB5)
+   DDRA &= ~(1 << PA7);
+   PORTA &= ~(1 << PA7); // SPI nCS
+   SPCR = 0;
+   
+}
+
+int8_t writeByte (uint8_t addr, uint8_t b) {
+
+   PORTA &= ~(1 << PA7); // SPI nCS  1 -> 0
+
+   SPDR = 0x40; // WRITE BYTE
+   while ((SPSR & (1 << SPIF)) == 0) {}
+   if (SPDR != 0) {
+      printf_P(PSTR("E1"));
+      PORTA |= (1 << PA7); // SPI nCS  0 -> 1
+      return -1;
+   }
+
+   SPDR = addr; // register address
+   while ((SPSR & (1 << SPIF)) == 0) {}
+   if (SPDR != 0) {
+      printf_P(PSTR("E2"));
+      PORTA |= (1 << PA7); // SPI nCS  0 -> 1
+      return -1;
+   }
+
+   SPDR = b; // value
+   while ((SPSR & (1 << SPIF)) == 0) {}
+   if (SPDR != 0) {
+      printf_P(PSTR("E3"));
+      PORTA |= (1 << PA7); // SPI nCS  0 -> 1
+      return -1;
+   }
+
+   PORTA |= (1 << PA7); // SPI nCS  0 -> 1
+
+   _delay_us(5);
+   return 0;
+}
+
+int8_t PortExp::run (uint8_t subtest) {
+   if (subtest == 0) {
+      PRR &= (1 << PRSPI);
+      PORTA |= (1 << PA7);
+      DDRA |= (1 << PA7); // SPI nCS
+      // PORTB/DDRB must be configured before SPCR !!
+      PORTB |= (1 << PB4); // nSS must be HIGH, otherwise SPI master will not become active!!
+      DDRB |= (1 << PB7) | (1 << PB5) | (1 << PB4); // SPI SCK (=PB7) and SPI MOSI (=PB5)
+
+      // SPCR |= (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0); // SPI enable , Master, f=12MHz/128=93,75kHz
+      SPCR = (1 << SPE) | (1 << MSTR); // SPI enable , Master, f=12MHz/4 = 3MHz
+      printf_P(PSTR("init"));
+
+   } else if (subtest == 1) {
+      while (wait(500) == EOF) {
+         printf_P(PSTR("\n => start ..."));
+         for (uint8_t i = 0; i < 8; i++) {
+            writeByte(0, ~(1 << i)); // IODIRA (Bank = 0)
+            // writeByte(0, 0x00); // IODIRA (Bank = 0) - all output
+            writeByte(0x12, (1 << i)); // GPIOA (Bank = 0)
+            printf_P(PSTR("\n  Bank0 - GPA%d = 1"), i);
+            wait(200);
+            writeByte(0x12, 0); // GPIOA (Bank = 0)
+            printf_P(PSTR("\n  Bank0 - GPA%d = 0"), i);
+            writeByte(0, 0xff); // IODIRA (Bank = 0)
+            wait(200);
+         }
+         for (uint8_t i = 0; i < 8; i++) {
+            writeByte(1, ~(1 << i)); // IODIRB (Bank = 0)
+            // writeByte(1, 0x00); // IODIRB (Bank = 0) - all output
+            writeByte(0x13, (1 << i)); // GPIOB (Bank = 0)
+            printf_P(PSTR("\n  Bank0 - GPB%d = 1"), i);
+            wait(200);
+            writeByte(0x13, 0); // GPIOB (Bank = 0)
+            printf_P(PSTR("\n  Bank0 - GPB%d = 0"), i);
+            writeByte(1, 0xff); // IODIRB (Bank = 0)
+            wait(200);
+         }
+      }
+
+   } else {
+      printf_P(PSTR("end"));
+      return -1;
+   }
+   wait(500);
+   return 0;
+}
+
diff --git a/software/nano-644/test_2024-07-23/src/units/portexp.hpp b/software/nano-644/test_2024-07-23/src/units/portexp.hpp
new file mode 100644 (file)
index 0000000..cacea83
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef PORTEXP_HPP
+#define PORTEXP_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class PortExp : public TestUnit {
+   public:
+      PortExp () {};
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("PortExp"); }
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/units/poti.cpp b/software/nano-644/test_2024-07-23/src/units/poti.cpp
new file mode 100644 (file)
index 0000000..d316a0b
--- /dev/null
@@ -0,0 +1,41 @@
+#include <stdio.h>
+#include <avr/io.h>
+
+#include "poti.hpp"
+#include "../main.hpp"
+
+void Poti::cleanup () {
+   ADMUX = 0;
+   ADCSRA = 0;
+}
+
+int8_t Poti::run (uint8_t subtest) {
+   switch (subtest) {
+      case 0: {
+         printf_P(PSTR("init"));
+         ADMUX = (1 << REFS0); // ADC0, VREF=AVCC=3.3V
+         ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128
+         return 0;
+      }
+
+      case 1: {
+         printf_P(PSTR("\n"));
+         while (wait(10) == EOF) {
+            printf_P(PSTR("\r  => Measure ADC0: "));
+            ADCSRA |= (1 << ADSC); // start ADC
+            while (ADCSRA & (1 << ADSC)) {} // wait for result
+            printf_P(PSTR("%4d (0x%03x)"), ADC, ADC);
+         }
+         return 0;
+      }
+
+      case 2: {
+         printf_P(PSTR("end"));
+         break;
+      }
+   }
+
+   return -1;
+}
+
+
diff --git a/software/nano-644/test_2024-07-23/src/units/poti.hpp b/software/nano-644/test_2024-07-23/src/units/poti.hpp
new file mode 100644 (file)
index 0000000..2ca46e3
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef POTI_HPP
+#define POTI_PP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Poti : public TestUnit {
+   public:
+      Poti () {};
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("Poti"); }
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/units/r2r.cpp b/software/nano-644/test_2024-07-23/src/units/r2r.cpp
new file mode 100644 (file)
index 0000000..60f9ab1
--- /dev/null
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <avr/io.h>
+
+#include "r2r.hpp"
+#include "../main.hpp"
+
+void R2r::cleanup () {
+   ADMUX = 0;
+   ADCSRA = 0;
+}
+
+int8_t R2r::run (uint8_t subtest) {
+   switch (subtest) {
+      case 0: {
+         printf_P(PSTR("init"));
+         ADMUX = (1 << REFS0) | 2; // ADC2, VREF=AVCC=3.3V
+         ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128
+         return 0;
+      }
+
+      case 1: {
+         printf_P(PSTR("\n"));
+         while (wait(10) == EOF) {
+            printf_P(PSTR("\r  => Measure ADC2: "));
+            ADCSRA |= (1 << ADSC); // start ADC
+            while (ADCSRA & (1 << ADSC)) {} // wait for result
+            printf_P(PSTR("%4d (0x%03x)"), ADC, ADC);
+            uint8_t sw = (ADC + 32) / 64;
+            printf_P(PSTR("  SW9:6 = %d %d% d %d  "), sw >> 3, (sw >> 2) & 0x01, (sw >> 1) & 0x01, sw & 0x01 );
+         }
+         return 0;
+      }
+
+      case 2: {
+         printf_P(PSTR("end"));
+         break;
+      }
+   }
+
+   return -1;
+}
+
+
diff --git a/software/nano-644/test_2024-07-23/src/units/r2r.hpp b/software/nano-644/test_2024-07-23/src/units/r2r.hpp
new file mode 100644 (file)
index 0000000..6088ce5
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef R2R_HPP
+#define R2R_PP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class R2r : public TestUnit {
+   public:
+      R2r () {};
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("R2R"); }
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/units/rgb.cpp b/software/nano-644/test_2024-07-23/src/units/rgb.cpp
new file mode 100644 (file)
index 0000000..6f08cb5
--- /dev/null
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <avr/io.h>
+
+#include "rgb.hpp"
+#include "../main.hpp"
+
+void Rgb::cleanup () {
+   PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0));
+   DDRB &= ~((1 << DDB2) | (1 << DDB1) | (1 << DDB0));
+}
+
+int8_t Rgb::run (uint8_t subtest) {
+   switch (subtest) {
+      case 0: {
+         DDRB |= (1 << DDB2) | (1 << DDB1) | (1 << DDB0);
+         PORTB |= (1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0); // all OFF
+         printf_P(PSTR("init"));
+         return 0;
+      }
+
+      case 1: {
+         PORTB &= ~(1 << PORTB0); // ON
+         printf_P(PSTR("Red"));
+         wait(2000);
+         return 0;
+      }
+
+      case 2: {
+         PORTB |= (1 << PORTB0);
+         PORTB &= ~(1 << PORTB1); // ON
+         printf_P(PSTR("Green"));
+         wait(2000);
+         return 0;
+      }
+
+      case 3: {
+         PORTB |= (1 << PORTB1);
+         PORTB &= ~(1 << PORTB2); // ON
+         printf_P(PSTR("Blue"));
+         wait(2000);
+         return 0;
+      }
+
+      case 4: {
+         PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0));
+         printf_P(PSTR("All"));
+         wait(2000);
+         return 0;
+      }
+
+      case 5: {
+         printf_P(PSTR("end"));
+         break;
+      }
+   }
+
+   return -1;
+}
+
+
diff --git a/software/nano-644/test_2024-07-23/src/units/rgb.hpp b/software/nano-644/test_2024-07-23/src/units/rgb.hpp
new file mode 100644 (file)
index 0000000..cb5641d
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef RGB_HPP
+#define RGB_PP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Rgb : public TestUnit {
+   public:
+      Rgb () {};
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("Rgb"); }
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/units/seg7.cpp b/software/nano-644/test_2024-07-23/src/units/seg7.cpp
new file mode 100644 (file)
index 0000000..6ca477e
--- /dev/null
@@ -0,0 +1,95 @@
+#include <stdio.h>
+#include <avr/io.h>
+
+#include "seg7.hpp"
+#include "../main.hpp"
+
+// PA0 Cathode Char 1
+// PA1 Cathode Char 2
+// PA2 Cathode Char 3
+// PA3 Cathode Char 4
+
+// PB0 Anode Segment A
+// PB1 Anode Segment B
+// PB2 Anode Segment C
+// PB3 Anode Segment D
+// PB4 Anode Segment E
+// PB5 Anode Segment F
+// PB6 Anode Segment G
+// PB7 Anode DP
+
+// PD5 nOE (Output Enable) for all LEDs
+// PD6 Anode L1:2
+// PD7 Anode L3
+
+const char *segName[] = { "A", "B", "C", "D", "E", "F", "G", "DP" };
+
+void Seg7::cleanup () {
+   PORTA &= ~((1 <<PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0));
+   DDRA &= ~((1 <<DDA3) | (1 << DDA2) | (1 << DDA1) | (1 << DDA0));
+   PORTD &= ~((1 <<PORTD7) | (1 << PORTD6) | (1 << PORTD5));
+   DDRD &= ~((1 <<DDD7) | (1 << DDD6) | (1 << DDD5));
+   PORTB = 0;
+   DDRB = 0;
+}
+
+int8_t Seg7::run (uint8_t subtest) {
+   if (subtest == 0) {
+      PORTA &= ~((1 <<PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0));
+      DDRA |= (1 <<DDA3) | (1 << DDA2) | (1 << DDA1) | (1 << DDA0);
+      PORTB = 0;
+      DDRB = 0xff;
+      PORTD &= ~((1 <<PORTD7) | (1 << PORTD6));
+      PORTD |= (1 << PORTD5);
+      DDRD |= (1 <<DDD7) | (1 << DDD6) | (1 << DDD5);
+      printf_P(PSTR("init"));
+      return 0;
+
+   } else if (subtest == 1) {
+      PORTA |= 0x0f; // all segment cathodes conected to GND
+      PORTB = 0xff; // all segments ON
+      PORTD |= (1 << PORTD7) | (1 << PORTD6); // L1, L2, L3 ON
+      PORTD &= ~(1 << PORTD5); // Output enable
+      printf_P(PSTR("ON"));
+      wait(2000);
+      PORTB = 0x0; // all segments OFF
+      PORTD &= ~((1 << PORTD7) | (1 << PORTD6)); // L1, L2, L3 OFF
+      return 0;
+
+   } else if (subtest == 2) {
+      printf_P(PSTR("OFF"));
+      wait(1000);
+      return 0;
+
+   } else if (subtest == 3) {
+      PORTD |= (1 << PORTD6); // L1/L2 ON
+      printf_P(PSTR("L1/L2 ON"));
+      wait(1000);
+      PORTD &= ~(1 << PORTD6); // L1/L2 OFF
+      return 0;
+
+   } else if (subtest == 4) {
+      PORTD |= (1 << PORTD7); // L3 ON
+      printf_P(PSTR("L1/L2 ON"));
+      wait(1000);
+      PORTD &= ~(1 << PORTD7); // L3 OFF
+      return 0;
+
+   } else if (subtest < (5 + 4 * 8)) {
+      uint8_t chIndex = (subtest - 5) / 8;
+      uint8_t segIndex = (subtest - 5) % 8;
+      PORTA &= ~0x0f; // all segment cathodes disconnected from GND
+      PORTA |= (1 << chIndex);
+      PORTB = (1 << segIndex);
+      printf_P(PSTR("Char %d - %s"), chIndex, segName[segIndex]);
+      wait(400);
+      return 0;
+
+   } else {
+      printf_P(PSTR("end"));
+   }
+
+   return -1;
+}
+
+
diff --git a/software/nano-644/test_2024-07-23/src/units/seg7.hpp b/software/nano-644/test_2024-07-23/src/units/seg7.hpp
new file mode 100644 (file)
index 0000000..70c1084
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef SEG7_HPP
+#define SEG7_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Seg7 : public TestUnit {
+   public:
+      Seg7 () {};
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("Seg7"); }
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/units/switch.cpp b/software/nano-644/test_2024-07-23/src/units/switch.cpp
new file mode 100644 (file)
index 0000000..9f738e5
--- /dev/null
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+#include "switch.hpp"
+#include "../main.hpp"
+
+void Switch::cleanup () {
+   PORTA &= ~((1 << PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0));
+}
+
+
+int8_t Switch::run (uint8_t subtest) {
+   if (subtest == 0) {
+      DDRA &= ~((1 << DDA3) | (1 << DDA2) | (1 << DDA1) | (1 << DDA0));
+      // enable internal pullup resistor
+      PORTA |= (1 << PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0);
+      wait(10);
+      return 0;
+
+   } else if (subtest <= 17) {
+      uint8_t bit = (subtest - 1) / 4;
+      switch ((subtest - 1) % 4) {
+         case 0: case 2: {
+            if ((PINA & (1 << bit)) == 0) {
+               printf_P(PSTR("Release SW%d (PA%d) "), bit + 1, bit);
+               while ((PINA & (1 << bit)) == 0 && wait(0) == EOF) {}
+               wait(10);
+            }
+            return 0;
+         }
+
+         case 1: {
+            if ((PINA & (1 << bit)) != 0) {
+               printf_P(PSTR("Press SW%d (PA%d) "), bit + 1, bit);
+               while ((PINA & (1 << bit)) != 0 && wait(0) == EOF) {}
+               wait(10);
+            }
+            return 0;
+         }
+
+         case 3: {
+            printf_P(PSTR("end"));
+            return 0;
+         }
+      }
+
+   }
+   
+   return -1;
+}
diff --git a/software/nano-644/test_2024-07-23/src/units/switch.hpp b/software/nano-644/test_2024-07-23/src/units/switch.hpp
new file mode 100644 (file)
index 0000000..a4caf2f
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef SWITCH_HPP
+#define SWITCH_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Switch : public TestUnit {
+   public:
+      Switch () {};
+      virtual void cleanup ();            
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("Switch"); }
+};
+
+#endif
\ No newline at end of file
diff --git a/software/nano-644/test_2024-07-23/src/units/uart1.cpp b/software/nano-644/test_2024-07-23/src/units/uart1.cpp
new file mode 100644 (file)
index 0000000..02f411e
--- /dev/null
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/delay.h>
+
+#include "uart1.hpp"
+#include "../main.hpp"
+
+int uart1_putchar(char c, FILE *stream) {
+   if (c == '\n') {
+      uart1_putchar('\r', stream);
+   }
+   loop_until_bit_is_set(UCSR1A, UDRE1);
+   UDR1 = c;
+   return 0;
+}
+
+static FILE mystderr = { 0, 0, _FDEV_SETUP_WRITE , 0, 0, uart1_putchar, NULL, 0 };
+
+void Uart1::cleanup () {
+   enabled = 0;
+   UCSR1A = 0;
+   UCSR1B = 0;
+   UCSR1C = 0;
+   UBRR1H = 0;
+   UBRR1L = 0;
+   stderr = NULL;
+}
+
+int8_t Uart1::run (uint8_t subtest) {
+   if (subtest == 0) {
+      // UART1 interface on Nano-644
+      PORTD |= (1 << PD2); // enable RxD1 pullup
+      UCSR1A = (1 << U2X1);
+      UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 <<TXEN1);
+      UCSR1C = (1 << UCSZ11) | ( 1<< UCSZ10);
+      UBRR1H = 0;
+      UBRR1L = F_CPU / 8 / 115200 - 1;
+      stderr = &mystderr;
+      enabled = 1;
+      printf_P(PSTR("init"));
+
+   } else if (subtest == 1) {
+      do {
+         printf_P(PSTR("\n => send text via UART1 now..."));
+         fprintf_P(stderr, PSTR("Hello UART1, ECHO-Modus active\n"));
+      } while (wait(5000) == EOF);
+
+   } else {
+      printf_P(PSTR("end"));
+      return -1;
+   }
+   wait(500);
+   return 0;
+}
+
+void Uart1::handleRxByte (uint8_t b) {
+   uart1_putchar(b, stderr);
+}
diff --git a/software/nano-644/test_2024-07-23/src/units/uart1.hpp b/software/nano-644/test_2024-07-23/src/units/uart1.hpp
new file mode 100644 (file)
index 0000000..16b6425
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef UART1_HPP
+#define UART1_HPP
+
+#include <stdint.h>
+#include "../main.hpp"
+#include <avr/pgmspace.h>
+
+class Uart1 : public TestUnit {
+   public:
+      uint8_t  enabled;
+
+   public:
+      Uart1 () { enabled = 0; }
+      virtual void cleanup ();
+      virtual int8_t run (uint8_t subtest);
+      virtual PGM_P getName () { return PSTR("Uart1"); }
+      void handleRxByte (uint8_t);
+};
+
+#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/.gdb_history b/software/test_2024-07-23/.gdb_history
deleted file mode 100644 (file)
index 3339046..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-target remote :1234
-layout split
-stepi
-quit
-target remote :1234
-layout split
-stepi
-b *main+9
-quit
diff --git a/software/test_2024-07-23/.gdbinit b/software/test_2024-07-23/.gdbinit
deleted file mode 100644 (file)
index 139597f..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/software/test_2024-07-23/.gitignore b/software/test_2024-07-23/.gitignore
deleted file mode 100644 (file)
index a959910..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-.depend
-**/build
-**/dist
-**/sim
diff --git a/software/test_2024-07-23/.vscode/c_cpp_properties.json b/software/test_2024-07-23/.vscode/c_cpp_properties.json
deleted file mode 100644 (file)
index 3a57c79..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "configurations": [
-        {
-            "name": "Linux AVR",
-            "includePath": [
-                "/usr/lib/avr/include/**",
-                "/usr/lib/gcc/avr/**"
-            ],
-            "defines": [],
-            "compilerPath": "/usr/bin/avr-gcc",
-            "compilerArgs": [ "-mmcu=atmega644p", "-DF_CPU=12000000", "-Os" ],
-            "cStandard": "gnu11",
-            "cppStandard": "gnu++11",
-            "intelliSenseMode": "linux-gcc-x64"
-        }
-    ],
-    "version": 4
-}
diff --git a/software/test_2024-07-23/.vscode/launch.json b/software/test_2024-07-23/.vscode/launch.json
deleted file mode 100644 (file)
index f29cf2e..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-{
-    // Use IntelliSense to learn about possible attributes.
-    // Hover to view descriptions of existing attributes.
-    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
-    "version": "0.2.0",
-    "configurations": [
-        {
-            "name": "Build",
-            // "request": "launch",
-            "type": "node-terminal",
-            "preLaunchTask": "build"
-        },{
-            "name": "Flash",
-            // "request": "launch",
-            "type": "node-terminal",
-            "preLaunchTask": "flash"
-        },{
-            "name": "Clean",
-            // "request": "launch",
-            "type": "node-terminal",
-            "preLaunchTask": "clean"
-        },{
-            // es muss mit simuc --board arduino dist/programm.elf der Simulator 
-            // gestartet werden. Dessen gdb-stub Ã¶ffnet auf localhost:1234 einen Port
-            "name": "Debug (simuc)",
-            "type": "cppdbg",
-            "request": "launch",
-            "program": "${workspaceFolder}/sim/atmega328p.elf",
-            "cwd": "${workspaceFolder}",
-            "externalConsole": false,
-            "MIMode": "gdb",
-            "miDebuggerPath": "/usr/bin/avr-gdb",
-            "miDebuggerServerAddress": ":1234",
-            "preLaunchTask": "build"
-        }
-    ]
-}
diff --git a/software/test_2024-07-23/.vscode/settings.json b/software/test_2024-07-23/.vscode/settings.json
deleted file mode 100644 (file)
index b2e94c9..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-    "[c]": {
-        "editor.insertSpaces": true,
-        "editor.tabSize": 3,
-        "editor.detectIndentation": false
-    },
-    "[cpp]": {
-        "editor.insertSpaces": true,
-        "editor.tabSize": 3,
-        "editor.detectIndentation": false
-    },
-    "[h]": {
-        "editor.insertSpaces": true,
-        "editor.tabSize": 3,
-        "editor.detectIndentation": false
-    },
-    "[hpp]": {
-        "editor.insertSpaces": true,
-        "editor.tabSize": 3,
-        "editor.detectIndentation": false
-    },
-    "cSpell.words": [],
-    "cSpell.ignorePaths": [
-        "**/*.json", "**/*.c", "**/*.h", "**/*.cpp", "**/*.hpp", "**/Makefile"
-    ]
-}
diff --git a/software/test_2024-07-23/.vscode/tasks.json b/software/test_2024-07-23/.vscode/tasks.json
deleted file mode 100644 (file)
index 74fb1c7..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    // See https://go.microsoft.com/fwlink/?LinkId=733558
-    // for the documentation about the tasks.json format
-    "version": "2.0.0",
-    "tasks": [{
-        "label": "build",
-        "type": "shell",
-        "command": "make",
-        "problemMatcher":[
-            "$gcc"
-        ]
-    },{
-        "label": "clean",
-        "type": "shell",
-        "command": "make",
-        "args": [ "clean" ],
-    },{
-        "label": "flash",
-        "type": "shell",
-        "command": "make",
-        "args": [ "flash" ],
-    }]
-}
\ No newline at end of file
diff --git a/software/test_2024-07-23/Makefile b/software/test_2024-07-23/Makefile
deleted file mode 100644 (file)
index 12a996a..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-.PHONY: all info flash picocom clean
-$(shell mkdir -p dist >/dev/null)
-$(shell mkdir -p build >/dev/null)
-$(shell mkdir -p sim >/dev/null)
-
-NAME="test_2024-07-23_nano-644"
-SRC= $(wildcard src/*.c src/*.cpp src/*/*.cpp) 
-OBJ = $(SRC:src/%.c=build/%.o)
-OBJ_SIM = $(SRC:src/%.c=sim/%.o)
-
-DEVICE=atmega644p
-
-CC= avr-g++
-CFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=12000000 -c
-LFLAGS= -Wall -mmcu=$(DEVICE) -Os -DF_CPU=12000000 -Wl,-u,vfprintf -lprintf_flt -lm
-
-CFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=12000000 -g -c
-LFLAGS_SIM= -Wall -mmcu=$(DEVICE) -Og -DF_CPU=12000000 -g -Wl,-u,vfprintf -lprintf_flt -lm
-
-
-all: dist/$(NAME).elf dist/$(NAME).s dist/$(NAME).hex sim/$(NAME).elf sim/$(NAME).s info
-       
-info:
-       @echo
-       @avr-size --mcu=$(DEVICE) --format=avr dist/$(NAME).elf
-
-.depend: $(SRC)
-       $(CC) -MM $(SRC) > .depend
-
--include $(DEPENDFILE) 
-
-dist/$(NAME).elf: .depend $(OBJ)
-       $(CC) $(LFLAGS) -o $@ $(OBJ)
-
-dist/%.s: dist/%.elf
-       avr-objdump -d $< > $@
-
-dist/%.hex: dist/%.elf
-       avr-objcopy -O ihex $(HEX_FLASH_FLAGS) $< $@
-
-sim/$(NAME).elf: .depend $(OBJ_SIM)
-               $(CC) $(LFLAGS_SIM) -o $@ $(OBJ_SIM)
-
-
-build/%.o: src/%.c
-       $(CC) $(CFLAGS) -o $@ $<
-
-sim/%.o: src/%.c
-       $(CC) $(CFLAGS_SIM) -o $@ $<
-
-sim/%.s: sim/%.elf
-       avr-objdump -d $< > $@
-
-simuc: sim/$(NAME).elf
-       simuc --board arduino $<
-
-gdb: sim/$(NAME).elf
-       avr-gdb $<
-
-isp:
-       avrdude -c usbasp -p m644p
-
-isp-flash: dist/$(NAME).elf all
-       avrdude -c usbasp -p m644p -e -U flash:w:$<
-
-flash: dist/$(NAME).elf all
-       avrdude -c arduino -p m644p -P /dev/ttyUSB0 -b 115200 -e -U flash:w:$<
-
-
-picocom:
-       # picocom sends CR for ENTER -> convert cr (\r) to lf (\n)
-       picocom -b 115200 --omap crlf /dev/ttyUSB0
-
-isp-fuse:
-       avrdude -c usbasp -p m644p -U lfuse:w:0xFF:m -U hfuse:w:0xD9:m -U efuse:w:0xFF:m -U lock:w:0xFF:m
-
-clean:
-       @rm -r dist
-       @rm -r build
-       @rm -r sim
-       @find . -type f -name ".depend" -exec rm {} \;
-       @echo "clean done"
diff --git a/software/test_2024-07-23/README.md b/software/test_2024-07-23/README.md
deleted file mode 100644 (file)
index c4d5e14..0000000
+++ /dev/null
@@ -1 +0,0 @@
-# Testprogramm
diff --git a/software/test_2024-07-23/src/adafruit/bme280.cpp b/software/test_2024-07-23/src/adafruit/bme280.cpp
deleted file mode 100644 (file)
index 95a0f49..0000000
+++ /dev/null
@@ -1,512 +0,0 @@
-#include "bme280.h"
-#include <util/delay.h>
-#include <stdio.h>
-
-Adafruit_BME280 theBME280;
-Adafruit_BME280_Temp bm280TempSensor;
-Adafruit_BME280_Pressure bm280PressureSensor;
-Adafruit_BME280_Humidity bm280HumiditySensor;
-
-Adafruit_BME280::Adafruit_BME280() {
-       static I2cMaster i2cDevice;
-       t_fine_adjust = 0;
-       temp_sensor = &bm280TempSensor;
-       pressure_sensor = &bm280PressureSensor;
-       humidity_sensor = &bm280HumiditySensor;
-       i2c_dev = &i2cDevice;
-}
-
-bool Adafruit_BME280::begin (uint8_t addr) {
-       if (!i2c_dev->begin(addr)) {
-               return false;
-       }
-       return init();
-}
-
-bool Adafruit_BME280::init() {
-       _sensorID = read8(BME280_REGISTER_CHIPID);
-       if (_sensorID != 0x60) {
-               return false;
-       }
-       write8(BME280_REGISTER_SOFTRESET, 0xB6);
-       _delay_ms(10); // wait for chip to wake up.
-
-       // if chip is still reading calibration, delay
-       while (isReadingCalibration()) {
-               _delay_ms(10);
-       }
-
-       readCoefficients(); // read trimming parameters, see DS 4.2.2
-       setSampling(); // use defaults
-       _delay_ms(100);
-
-       return true;
-}
-
-/*!
- *   @brief  setup sensor with given parameters / settings
- *
- *   This is simply a overload to the normal begin()-function, so SPI users
- *   don't get confused about the library requiring an address.
- *   @param mode the power mode to use for the sensor
- *   @param tempSampling the temp samping rate to use
- *   @param pressSampling the pressure sampling rate to use
- *   @param humSampling the humidity sampling rate to use
- *   @param filter the filter mode to use
- *   @param duration the standby duration to use
- */
-void Adafruit_BME280::setSampling(sensor_mode mode,
-                                                                                        sensor_sampling tempSampling,
-                                                                                        sensor_sampling pressSampling,
-                                                                                        sensor_sampling humSampling,
-                                                                                        sensor_filter filter,
-                                                                                        standby_duration duration) {
-  _measReg.mode = mode;
-  _measReg.osrs_t = tempSampling;
-  _measReg.osrs_p = pressSampling;
-
-  _humReg.osrs_h = humSampling;
-  _configReg.filter = filter;
-  _configReg.t_sb = duration;
-  _configReg.spi3w_en = 0;
-
-  // making sure sensor is in sleep mode before setting configuration
-  // as it otherwise may be ignored
-  write8(BME280_REGISTER_CONTROL, MODE_SLEEP);
-
-  // you must make sure to also set REGISTER_CONTROL after setting the
-  // CONTROLHUMID register, otherwise the values won't be applied (see
-  // DS 5.4.3)
-  write8(BME280_REGISTER_CONTROLHUMID, _humReg.get());
-  write8(BME280_REGISTER_CONFIG, _configReg.get());
-  write8(BME280_REGISTER_CONTROL, _measReg.get());
-}
-
-/*!
- *   @brief  Writes an 8 bit value over I2C or SPI
- *   @param reg the register address to write to
- *   @param value the value to write to the register
- */
-void Adafruit_BME280::write8(byte reg, byte value) {
-  byte buffer[2];
-  buffer[1] = value;
-  if (i2c_dev) {
-        buffer[0] = reg;
-        i2c_dev->write(buffer, 2);
-  }
-}
-
-/*!
- *   @brief  Reads an 8 bit value over I2C or SPI
- *   @param reg the register address to read from
- *   @returns the data byte read from the device
- */
-uint8_t Adafruit_BME280::read8(byte reg) {
-  uint8_t buffer[1];
-  if (i2c_dev) {
-        buffer[0] = uint8_t(reg);
-        i2c_dev->write_then_read(buffer, 1, buffer, 1);
-  }
-  return buffer[0];
-}
-
-/*!
- *   @brief  Reads a 16 bit value over I2C or SPI
- *   @param reg the register address to read from
- *   @returns the 16 bit data value read from the device
- */
-uint16_t Adafruit_BME280::read16(byte reg) {
-  uint8_t buffer[2];
-
-  if (i2c_dev) {
-        buffer[0] = uint8_t(reg);
-        i2c_dev->write_then_read(buffer, 1, buffer, 2);
-  }
-  return uint16_t(buffer[0]) << 8 | uint16_t(buffer[1]);
-}
-
-/*!
- *   @brief  Reads a signed 16 bit little endian value over I2C or SPI
- *   @param reg the register address to read from
- *   @returns the 16 bit data value read from the device
- */
-uint16_t Adafruit_BME280::read16_LE(byte reg) {
-  uint16_t temp = read16(reg);
-  return (temp >> 8) | (temp << 8);
-}
-
-/*!
- *   @brief  Reads a signed 16 bit value over I2C or SPI
- *   @param reg the register address to read from
- *   @returns the 16 bit data value read from the device
- */
-int16_t Adafruit_BME280::readS16(byte reg) { return (int16_t)read16(reg); }
-
-/*!
- *   @brief  Reads a signed little endian 16 bit value over I2C or SPI
- *   @param reg the register address to read from
- *   @returns the 16 bit data value read from the device
- */
-int16_t Adafruit_BME280::readS16_LE(byte reg) {
-  return (int16_t)read16_LE(reg);
-}
-
-/*!
- *   @brief  Reads a 24 bit value over I2C
- *   @param reg the register address to read from
- *   @returns the 24 bit data value read from the device
- */
-uint32_t Adafruit_BME280::read24(byte reg) {
-  uint8_t buffer[3];
-
-  if (i2c_dev) {
-        buffer[0] = uint8_t(reg);
-        i2c_dev->write_then_read(buffer, 1, buffer, 3);
-  }
-  return uint32_t(buffer[0]) << 16 | uint32_t(buffer[1]) << 8 |
-                       uint32_t(buffer[2]);
-}
-
-/*!
- *  @brief  Take a new measurement (only possible in forced mode)
-        @returns true in case of success else false
- */
-bool Adafruit_BME280::takeForcedMeasurement(void) {
-  bool return_value = false;
-  // If we are in forced mode, the BME sensor goes back to sleep after each
-  // measurement and we need to set it to forced mode once at this point, so
-  // it will take the next measurement and then return to sleep again.
-  // In normal mode simply does new measurements periodically.
-  if (_measReg.mode == MODE_FORCED) {
-        return_value = true;
-        // set to forced mode, i.e. "take next measurement"
-        write8(BME280_REGISTER_CONTROL, _measReg.get());
-        // Store current time to measure the timeout
-        uint32_t timeout_start = millis();
-        // wait until measurement has been completed, otherwise we would read the
-        // the values from the last measurement or the timeout occurred after 2 sec.
-        while (read8(BME280_REGISTER_STATUS) & 0x08) {
-               // In case of a timeout, stop the while loop
-               if ((millis() - timeout_start) > 2000) {
-                 return_value = false;
-                 break;
-               }
-               _delay_ms(1);
-        }
-  }
-  return return_value;
-}
-
-/*!
- *   @brief  Reads the factory-set coefficients
- */
-void Adafruit_BME280::readCoefficients(void) {
-  _bme280_calib.dig_T1 = read16_LE(BME280_REGISTER_DIG_T1);
-  _bme280_calib.dig_T2 = readS16_LE(BME280_REGISTER_DIG_T2);
-  _bme280_calib.dig_T3 = readS16_LE(BME280_REGISTER_DIG_T3);
-
-  _bme280_calib.dig_P1 = read16_LE(BME280_REGISTER_DIG_P1);
-  _bme280_calib.dig_P2 = readS16_LE(BME280_REGISTER_DIG_P2);
-  _bme280_calib.dig_P3 = readS16_LE(BME280_REGISTER_DIG_P3);
-  _bme280_calib.dig_P4 = readS16_LE(BME280_REGISTER_DIG_P4);
-  _bme280_calib.dig_P5 = readS16_LE(BME280_REGISTER_DIG_P5);
-  _bme280_calib.dig_P6 = readS16_LE(BME280_REGISTER_DIG_P6);
-  _bme280_calib.dig_P7 = readS16_LE(BME280_REGISTER_DIG_P7);
-  _bme280_calib.dig_P8 = readS16_LE(BME280_REGISTER_DIG_P8);
-  _bme280_calib.dig_P9 = readS16_LE(BME280_REGISTER_DIG_P9);
-
-  _bme280_calib.dig_H1 = read8(BME280_REGISTER_DIG_H1);
-  _bme280_calib.dig_H2 = readS16_LE(BME280_REGISTER_DIG_H2);
-  _bme280_calib.dig_H3 = read8(BME280_REGISTER_DIG_H3);
-  _bme280_calib.dig_H4 = ((int8_t)read8(BME280_REGISTER_DIG_H4) << 4) |
-                                                                (read8(BME280_REGISTER_DIG_H4 + 1) & 0xF);
-  _bme280_calib.dig_H5 = ((int8_t)read8(BME280_REGISTER_DIG_H5 + 1) << 4) |
-                                                                (read8(BME280_REGISTER_DIG_H5) >> 4);
-  _bme280_calib.dig_H6 = (int8_t)read8(BME280_REGISTER_DIG_H6);
-}
-
-/*!
- *   @brief return true if chip is busy reading cal data
- *   @returns true if reading calibration, false otherwise
- */
-bool Adafruit_BME280::isReadingCalibration(void) {
-  uint8_t const rStatus = read8(BME280_REGISTER_STATUS);
-
-  return (rStatus & (1 << 0)) != 0;
-}
-
-/*!
- *   @brief  Returns the temperature from the sensor
- *   @returns the temperature read from the device
- */
-float Adafruit_BME280::readTemperature(void) {
-  int32_t var1, var2;
-
-  int32_t adc_T = read24(BME280_REGISTER_TEMPDATA);
-  if (adc_T == 0x800000) // value in case temp measurement was disabled
-        return NAN;
-  adc_T >>= 4;
-
-  var1 = (int32_t)((adc_T / 8) - ((int32_t)_bme280_calib.dig_T1 * 2));
-  var1 = (var1 * ((int32_t)_bme280_calib.dig_T2)) / 2048;
-  var2 = (int32_t)((adc_T / 16) - ((int32_t)_bme280_calib.dig_T1));
-  var2 = (((var2 * var2) / 4096) * ((int32_t)_bme280_calib.dig_T3)) / 16384;
-
-  t_fine = var1 + var2 + t_fine_adjust;
-
-  int32_t T = (t_fine * 5 + 128) / 256;
-
-  return (float)T / 100;
-}
-
-/*!
- *   @brief  Returns the pressure from the sensor
- *   @returns the pressure value (in Pascal) read from the device
- */
-float Adafruit_BME280::readPressure(void) {
-  int64_t var1, var2, var3, var4;
-
-  readTemperature(); // must be done first to get t_fine
-
-  int32_t adc_P = read24(BME280_REGISTER_PRESSUREDATA);
-  if (adc_P == 0x800000) // value in case pressure measurement was disabled
-        return NAN;
-  adc_P >>= 4;
-
-  var1 = ((int64_t)t_fine) - 128000;
-  var2 = var1 * var1 * (int64_t)_bme280_calib.dig_P6;
-  var2 = var2 + ((var1 * (int64_t)_bme280_calib.dig_P5) * 131072);
-  var2 = var2 + (((int64_t)_bme280_calib.dig_P4) * 34359738368);
-  var1 = ((var1 * var1 * (int64_t)_bme280_calib.dig_P3) / 256) +
-                       ((var1 * ((int64_t)_bme280_calib.dig_P2) * 4096));
-  var3 = ((int64_t)1) * 140737488355328;
-  var1 = (var3 + var1) * ((int64_t)_bme280_calib.dig_P1) / 8589934592;
-
-  if (var1 == 0) {
-        return 0; // avoid exception caused by division by zero
-  }
-
-  var4 = 1048576 - adc_P;
-  var4 = (((var4 * 2147483648UL) - var2) * 3125) / var1;
-  var1 = (((int64_t)_bme280_calib.dig_P9) * (var4 / 8192) * (var4 / 8192)) /
-                       33554432;
-  var2 = (((int64_t)_bme280_calib.dig_P8) * var4) / 524288;
-  var4 = ((var4 + var1 + var2) / 256) + (((int64_t)_bme280_calib.dig_P7) * 16);
-
-  float P = var4 / 256.0;
-
-  return P;
-}
-
-/*!
- *  @brief  Returns the humidity from the sensor
- *  @returns the humidity value read from the device
- */
-float Adafruit_BME280::readHumidity(void) {
-  int32_t var1, var2, var3, var4, var5;
-
-  readTemperature(); // must be done first to get t_fine
-
-  int32_t adc_H = read16(BME280_REGISTER_HUMIDDATA);
-  if (adc_H == 0x8000) // value in case humidity measurement was disabled
-        return NAN;
-
-  var1 = t_fine - ((int32_t)76800);
-  var2 = (int32_t)(adc_H * 16384);
-  var3 = (int32_t)(((int32_t)_bme280_calib.dig_H4) * 1048576);
-  var4 = ((int32_t)_bme280_calib.dig_H5) * var1;
-  var5 = (((var2 - var3) - var4) + (int32_t)16384) / 32768;
-  var2 = (var1 * ((int32_t)_bme280_calib.dig_H6)) / 1024;
-  var3 = (var1 * ((int32_t)_bme280_calib.dig_H3)) / 2048;
-  var4 = ((var2 * (var3 + (int32_t)32768)) / 1024) + (int32_t)2097152;
-  var2 = ((var4 * ((int32_t)_bme280_calib.dig_H2)) + 8192) / 16384;
-  var3 = var5 * var2;
-  var4 = ((var3 / 32768) * (var3 / 32768)) / 128;
-  var5 = var3 - ((var4 * ((int32_t)_bme280_calib.dig_H1)) / 16);
-  var5 = (var5 < 0 ? 0 : var5);
-  var5 = (var5 > 419430400 ? 419430400 : var5);
-  uint32_t H = (uint32_t)(var5 / 4096);
-
-  return (float)H / 1024.0;
-}
-
-/*!
- *   Calculates the altitude (in meters) from the specified atmospheric
- *   pressure (in hPa), and sea-level pressure (in hPa).
- *   @param  seaLevel      Sea-level pressure in hPa
- *   @returns the altitude value read from the device
- */
-float Adafruit_BME280::readAltitude(float seaLevel) {
-  // Equation taken from BMP180 datasheet (page 16):
-  //  http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
-
-  // Note that using the equation from wikipedia can give bad results
-  // at high altitude. See this thread for more information:
-  //  http://forums.adafruit.com/viewtopic.php?f=22&t=58064
-
-  float atmospheric = readPressure() / 100.0F;
-  return 44330.0 * (1.0 - pow(atmospheric / seaLevel, 0.1903));
-}
-
-/*!
- *   Calculates the pressure at sea level (in hPa) from the specified
- * altitude (in meters), and atmospheric pressure (in hPa).
- *   @param  altitude      Altitude in meters
- *   @param  atmospheric   Atmospheric pressure in hPa
- *   @returns the pressure at sea level (in hPa) from the specified altitude
- */
-float Adafruit_BME280::seaLevelForAltitude(float altitude, float atmospheric) {
-  // Equation taken from BMP180 datasheet (page 17):
-  //  http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
-
-  // Note that using the equation from wikipedia can give bad results
-  // at high altitude. See this thread for more information:
-  //  http://forums.adafruit.com/viewtopic.php?f=22&t=58064
-
-  return atmospheric / pow(1.0 - (altitude / 44330.0), 5.255);
-}
-
-/*!
- *   Returns Sensor ID found by init() for diagnostics
- *   @returns Sensor ID 0x60 for BME280, 0x56, 0x57, 0x58 BMP280
- */
-uint32_t Adafruit_BME280::sensorID(void) { return _sensorID; }
-
-/*!
- *   Returns the current temperature compensation value in degrees Celsius
- *   @returns the current temperature compensation value in degrees Celsius
- */
-float Adafruit_BME280::getTemperatureCompensation(void) {
-  return float((t_fine_adjust * 5) >> 8) / 100.0;
-};
-
-/*!
- *  Sets a value to be added to each temperature reading. This adjusted
- *  temperature is used in pressure and humidity readings.
- *  @param  adjustment  Value to be added to each temperature reading in Celsius
- */
-void Adafruit_BME280::setTemperatureCompensation(float adjustment) {
-  // convert the value in C into and adjustment to t_fine
-  t_fine_adjust = ((int32_t(adjustment * 100) << 8)) / 5;
-};
-
-
-/**************************************************************************/
-/*!
-        @brief  Gets the sensor_t data for the BME280's temperature sensor
-*/
-/**************************************************************************/
-void Adafruit_BME280_Temp::getSensor(sensor_t *sensor) {
-  /* Clear the sensor_t object */
-  memset(sensor, 0, sizeof(sensor_t));
-
-  /* Insert the sensor name in the fixed length char array */
-  strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1);
-  sensor->name[sizeof(sensor->name) - 1] = 0;
-  sensor->version = 1;
-  sensor->sensor_id = _sensorID;
-  sensor->type = SENSOR_TYPE_AMBIENT_TEMPERATURE;
-  sensor->min_delay = 0;
-  sensor->min_value = -40.0; /* Temperature range -40 ~ +85 C  */
-  sensor->max_value = +85.0;
-  sensor->resolution = 0.01; /*  0.01 C */
-}
-
-/**************************************************************************/
-/*!
-        @brief  Gets the temperature as a standard sensor event
-        @param  event Sensor event object that will be populated
-        @returns True
-*/
-/**************************************************************************/
-bool Adafruit_BME280_Temp::getEvent(sensors_event_t *event) {
-  /* Clear the event */
-  memset(event, 0, sizeof(sensors_event_t));
-
-  event->version = sizeof(sensors_event_t);
-  event->sensor_id = _sensorID;
-  event->type = SENSOR_TYPE_AMBIENT_TEMPERATURE;
-  event->timestamp = millis();
-  event->temperature = theBME280.readTemperature();
-  return true;
-}
-
-/**************************************************************************/
-/*!
-        @brief  Gets the sensor_t data for the BME280's pressure sensor
-*/
-/**************************************************************************/
-void Adafruit_BME280_Pressure::getSensor(sensor_t *sensor) {
-  /* Clear the sensor_t object */
-  memset(sensor, 0, sizeof(sensor_t));
-
-  /* Insert the sensor name in the fixed length char array */
-  strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1);
-  sensor->name[sizeof(sensor->name) - 1] = 0;
-  sensor->version = 1;
-  sensor->sensor_id = _sensorID;
-  sensor->type = SENSOR_TYPE_PRESSURE;
-  sensor->min_delay = 0;
-  sensor->min_value = 300.0; /* 300 ~ 1100 hPa  */
-  sensor->max_value = 1100.0;
-  sensor->resolution = 0.012; /* 0.12 hPa relative */
-}
-
-/**************************************************************************/
-/*!
-        @brief  Gets the pressure as a standard sensor event
-        @param  event Sensor event object that will be populated
-        @returns True
-*/
-/**************************************************************************/
-bool Adafruit_BME280_Pressure::getEvent(sensors_event_t *event) {
-  /* Clear the event */
-  memset(event, 0, sizeof(sensors_event_t));
-
-  event->version = sizeof(sensors_event_t);
-  event->sensor_id = _sensorID;
-  event->type = SENSOR_TYPE_PRESSURE;
-  event->timestamp = millis();
-  event->pressure = theBME280.readPressure() / 100; // convert Pa to hPa
-  return true;
-}
-
-/**************************************************************************/
-/*!
-        @brief  Gets the sensor_t data for the BME280's humidity sensor
-*/
-/**************************************************************************/
-void Adafruit_BME280_Humidity::getSensor(sensor_t *sensor) {
-  /* Clear the sensor_t object */
-  memset(sensor, 0, sizeof(sensor_t));
-
-  /* Insert the sensor name in the fixed length char array */
-  strncpy(sensor->name, "BME280", sizeof(sensor->name) - 1);
-  sensor->name[sizeof(sensor->name) - 1] = 0;
-  sensor->version = 1;
-  sensor->sensor_id = _sensorID;
-  sensor->type = SENSOR_TYPE_RELATIVE_HUMIDITY;
-  sensor->min_delay = 0;
-  sensor->min_value = 0;
-  sensor->max_value = 100; /* 0 - 100 %  */
-  sensor->resolution = 3;  /* 3% accuracy */
-}
-
-/**************************************************************************/
-/*!
-        @brief  Gets the humidity as a standard sensor event
-        @param  event Sensor event object that will be populated
-        @returns True
-*/
-/**************************************************************************/
-bool Adafruit_BME280_Humidity::getEvent(sensors_event_t *event) {
-  /* Clear the event */
-  memset(event, 0, sizeof(sensors_event_t));
-
-  event->version = sizeof(sensors_event_t);
-  event->sensor_id = _sensorID;
-  event->type = SENSOR_TYPE_RELATIVE_HUMIDITY;
-  event->timestamp = millis();
-  event->relative_humidity = theBME280.readHumidity();
-  return true;
-}
diff --git a/software/test_2024-07-23/src/adafruit/bme280.h b/software/test_2024-07-23/src/adafruit/bme280.h
deleted file mode 100644 (file)
index b842782..0000000
+++ /dev/null
@@ -1,373 +0,0 @@
-// https://github.com/adafruit/Adafruit_BME280_Library
-
-/*!
- * @file Adafruit_BME280.h
- *
- * Designed specifically to work with the Adafruit BME280 Breakout
- * ----> http://www.adafruit.com/products/2650
- *
- * These sensors use I2C or SPI to communicate, 2 or 4 pins are required
- * to interface.
- *
- * Adafruit invests time and resources providing this open source code,
- * please support Adafruit and open-source hardware by purchasing
- * products from Adafruit!
- *
- * Written by Kevin "KTOWN" Townsend for Adafruit Industries.
- *
- * BSD license, all text here must be included in any redistribution.
- * See the LICENSE file for details.
- *
- */
-
-#ifndef __BME280_H__
-#define __BME280_H__
-
-// #include "Arduino.h"
-
-// #include <Adafruit_I2CDevice.h>
-// #include <Adafruit_SPIDevice.h>
-// #include <Adafruit_Sensor.h>
-
-
-#include "../i2cmaster.hpp"
-#include "../main.hpp"
-#define byte uint8_t
-
-
-
-#include <stdint.h>
-#include <string.h>
-#include "sensor.h"
-
-/*!
- *  @brief  default I2C address
- */
-#define BME280_ADDRESS (0x77)           // Primary I2C Address
-                                        /*!
-                                         *  @brief  alternate I2C address
-                                         */
-#define BME280_ADDRESS_ALTERNATE (0x76) // Alternate Address
-
-/*!
- *  @brief Register addresses
- */
-enum {
-  BME280_REGISTER_DIG_T1 = 0x88,
-  BME280_REGISTER_DIG_T2 = 0x8A,
-  BME280_REGISTER_DIG_T3 = 0x8C,
-
-  BME280_REGISTER_DIG_P1 = 0x8E,
-  BME280_REGISTER_DIG_P2 = 0x90,
-  BME280_REGISTER_DIG_P3 = 0x92,
-  BME280_REGISTER_DIG_P4 = 0x94,
-  BME280_REGISTER_DIG_P5 = 0x96,
-  BME280_REGISTER_DIG_P6 = 0x98,
-  BME280_REGISTER_DIG_P7 = 0x9A,
-  BME280_REGISTER_DIG_P8 = 0x9C,
-  BME280_REGISTER_DIG_P9 = 0x9E,
-
-  BME280_REGISTER_DIG_H1 = 0xA1,
-  BME280_REGISTER_DIG_H2 = 0xE1,
-  BME280_REGISTER_DIG_H3 = 0xE3,
-  BME280_REGISTER_DIG_H4 = 0xE4,
-  BME280_REGISTER_DIG_H5 = 0xE5,
-  BME280_REGISTER_DIG_H6 = 0xE7,
-
-  BME280_REGISTER_CHIPID = 0xD0,
-  BME280_REGISTER_VERSION = 0xD1,
-  BME280_REGISTER_SOFTRESET = 0xE0,
-
-  BME280_REGISTER_CAL26 = 0xE1, // R calibration stored in 0xE1-0xF0
-
-  BME280_REGISTER_CONTROLHUMID = 0xF2,
-  BME280_REGISTER_STATUS = 0XF3,
-  BME280_REGISTER_CONTROL = 0xF4,
-  BME280_REGISTER_CONFIG = 0xF5,
-  BME280_REGISTER_PRESSUREDATA = 0xF7,
-  BME280_REGISTER_TEMPDATA = 0xFA,
-  BME280_REGISTER_HUMIDDATA = 0xFD
-};
-
-/**************************************************************************/
-/*!
-    @brief  calibration data
-*/
-/**************************************************************************/
-typedef struct {
-  uint16_t dig_T1; ///< temperature compensation value
-  int16_t dig_T2;  ///< temperature compensation value
-  int16_t dig_T3;  ///< temperature compensation value
-
-  uint16_t dig_P1; ///< pressure compensation value
-  int16_t dig_P2;  ///< pressure compensation value
-  int16_t dig_P3;  ///< pressure compensation value
-  int16_t dig_P4;  ///< pressure compensation value
-  int16_t dig_P5;  ///< pressure compensation value
-  int16_t dig_P6;  ///< pressure compensation value
-  int16_t dig_P7;  ///< pressure compensation value
-  int16_t dig_P8;  ///< pressure compensation value
-  int16_t dig_P9;  ///< pressure compensation value
-
-  uint8_t dig_H1; ///< humidity compensation value
-  int16_t dig_H2; ///< humidity compensation value
-  uint8_t dig_H3; ///< humidity compensation value
-  int16_t dig_H4; ///< humidity compensation value
-  int16_t dig_H5; ///< humidity compensation value
-  int8_t dig_H6;  ///< humidity compensation value
-} bme280_calib_data;
-/*=========================================================================*/
-
-class Adafruit_BME280;
-
-/** Adafruit Unified Sensor interface for temperature component of BME280 */
-class Adafruit_BME280_Temp : public Adafruit_Sensor {
-public:
-  /** @brief Create an Adafruit_Sensor compatible object for the temp sensor
-      @param parent A pointer to the BME280 class */
-  Adafruit_BME280_Temp() { _sensorID = 280; }
-  bool getEvent(sensors_event_t *);
-  void getSensor(sensor_t *);
-
-private:
-  int _sensorID;
-};
-
-/** Adafruit Unified Sensor interface for pressure component of BME280 */
-class Adafruit_BME280_Pressure : public Adafruit_Sensor {
-public:
-  /** @brief Create an Adafruit_Sensor compatible object for the pressure sensor
-      @param parent A pointer to the BME280 class */
-  Adafruit_BME280_Pressure() { _sensorID = 280; }
-  bool getEvent(sensors_event_t *);
-  void getSensor(sensor_t *);
-
-private:
-  int _sensorID;
-};
-
-/** Adafruit Unified Sensor interface for humidity component of BME280 */
-class Adafruit_BME280_Humidity : public Adafruit_Sensor {
-public:
-  /** @brief Create an Adafruit_Sensor compatible object for the humidity sensor
-      @param parent A pointer to the BME280 class */
-  Adafruit_BME280_Humidity() { _sensorID = 280;}
-  bool getEvent(sensors_event_t *);
-  void getSensor(sensor_t *);
-
-private:
-  int _sensorID;
-};
-
-/**************************************************************************/
-/*!
-    @brief  Class that stores state and functions for interacting with BME280 IC
-*/
-/**************************************************************************/
-class Adafruit_BME280 {
-public:
-  /**************************************************************************/
-  /*!
-      @brief  sampling rates
-  */
-  /**************************************************************************/
-  enum sensor_sampling {
-    SAMPLING_NONE = 0b000,
-    SAMPLING_X1 = 0b001,
-    SAMPLING_X2 = 0b010,
-    SAMPLING_X4 = 0b011,
-    SAMPLING_X8 = 0b100,
-    SAMPLING_X16 = 0b101
-  };
-
-  /**************************************************************************/
-  /*!
-      @brief  power modes
-  */
-  /**************************************************************************/
-  enum sensor_mode {
-    MODE_SLEEP = 0b00,
-    MODE_FORCED = 0b01,
-    MODE_NORMAL = 0b11
-  };
-
-  /**************************************************************************/
-  /*!
-      @brief  filter values
-  */
-  /**************************************************************************/
-  enum sensor_filter {
-    FILTER_OFF = 0b000,
-    FILTER_X2 = 0b001,
-    FILTER_X4 = 0b010,
-    FILTER_X8 = 0b011,
-    FILTER_X16 = 0b100
-  };
-
-  /**************************************************************************/
-  /*!
-      @brief  standby duration in ms
-  */
-  /**************************************************************************/
-  enum standby_duration {
-    STANDBY_MS_0_5 = 0b000,
-    STANDBY_MS_10 = 0b110,
-    STANDBY_MS_20 = 0b111,
-    STANDBY_MS_62_5 = 0b001,
-    STANDBY_MS_125 = 0b010,
-    STANDBY_MS_250 = 0b011,
-    STANDBY_MS_500 = 0b100,
-    STANDBY_MS_1000 = 0b101
-  };
-
-  // constructors
-  Adafruit_BME280();
-  
-  bool begin(uint8_t addr = BME280_ADDRESS);
-  bool init();
-
-  void setSampling(sensor_mode mode = MODE_NORMAL,
-                   sensor_sampling tempSampling = SAMPLING_X16,
-                   sensor_sampling pressSampling = SAMPLING_X16,
-                   sensor_sampling humSampling = SAMPLING_X16,
-                   sensor_filter filter = FILTER_OFF,
-                   standby_duration duration = STANDBY_MS_0_5);
-
-  bool takeForcedMeasurement(void);
-  float readTemperature(void);
-  float readPressure(void);
-  float readHumidity(void);
-
-  float readAltitude(float seaLevel);
-  float seaLevelForAltitude(float altitude, float pressure);
-  uint32_t sensorID(void);
-
-  float getTemperatureCompensation(void);
-  void setTemperatureCompensation(float);
-
-protected:
-  I2cMaster *i2c_dev = NULL; ///< Pointer to I2C bus interface
-  // Adafruit_SPIDevice *spi_dev = NULL; ///< Pointer to SPI bus interface
-
-  Adafruit_BME280_Temp *temp_sensor;
-  Adafruit_BME280_Pressure *pressure_sensor;
-  Adafruit_BME280_Humidity *humidity_sensor;
-
-  void readCoefficients(void);
-  bool isReadingCalibration(void);
-
-  void write8(byte reg, byte value);
-  uint8_t read8(byte reg);
-  uint16_t read16(byte reg);
-  uint32_t read24(byte reg);
-  int16_t readS16(byte reg);
-  uint16_t read16_LE(byte reg); // little endian
-  int16_t readS16_LE(byte reg); // little endian
-
-  uint8_t _i2caddr;  //!< I2C addr for the TwoWire interface
-  int32_t _sensorID; //!< ID of the BME Sensor
-  int32_t t_fine; //!< temperature with high resolution, stored as an attribute
-                  //!< as this is used for temperature compensation reading
-                  //!< humidity and pressure
-
-  int32_t t_fine_adjust; //!< add to compensate temp readings and in turn
-                         //!< to pressure and humidity readings
-
-  bme280_calib_data _bme280_calib; //!< here calibration data is stored
-
-  /**************************************************************************/
-  /*!
-      @brief  config register
-  */
-  /**************************************************************************/
-  struct config {
-    // inactive duration (standby time) in normal mode
-    // 000 = 0.5 ms
-    // 001 = 62.5 ms
-    // 010 = 125 ms
-    // 011 = 250 ms
-    // 100 = 500 ms
-    // 101 = 1000 ms
-    // 110 = 10 ms
-    // 111 = 20 ms
-    unsigned int t_sb : 3; ///< inactive duration (standby time) in normal mode
-
-    // filter settings
-    // 000 = filter off
-    // 001 = 2x filter
-    // 010 = 4x filter
-    // 011 = 8x filter
-    // 100 and above = 16x filter
-    unsigned int filter : 3; ///< filter settings
-
-    // unused - don't set
-    unsigned int none : 1;     ///< unused - don't set
-    unsigned int spi3w_en : 1; ///< unused - don't set
-
-    /// @return combined config register
-    unsigned int get() { return (t_sb << 5) | (filter << 2) | spi3w_en; }
-  };
-  config _configReg; //!< config register object
-
-  /**************************************************************************/
-  /*!
-      @brief  ctrl_meas register
-  */
-  /**************************************************************************/
-  struct ctrl_meas {
-    // temperature oversampling
-    // 000 = skipped
-    // 001 = x1
-    // 010 = x2
-    // 011 = x4
-    // 100 = x8
-    // 101 and above = x16
-    unsigned int osrs_t : 3; ///< temperature oversampling
-
-    // pressure oversampling
-    // 000 = skipped
-    // 001 = x1
-    // 010 = x2
-    // 011 = x4
-    // 100 = x8
-    // 101 and above = x16
-    unsigned int osrs_p : 3; ///< pressure oversampling
-
-    // device mode
-    // 00       = sleep
-    // 01 or 10 = forced
-    // 11       = normal
-    unsigned int mode : 2; ///< device mode
-
-    /// @return combined ctrl register
-    unsigned int get() { return (osrs_t << 5) | (osrs_p << 2) | mode; }
-  };
-  ctrl_meas _measReg; //!< measurement register object
-
-  /**************************************************************************/
-  /*!
-      @brief  ctrl_hum register
-  */
-  /**************************************************************************/
-  struct ctrl_hum {
-    /// unused - don't set
-    unsigned int none : 5;
-
-    // pressure oversampling
-    // 000 = skipped
-    // 001 = x1
-    // 010 = x2
-    // 011 = x4
-    // 100 = x8
-    // 101 and above = x16
-    unsigned int osrs_h : 3; ///< pressure oversampling
-
-    /// @return combined ctrl hum register
-    unsigned int get() { return (osrs_h); }
-  };
-  ctrl_hum _humReg; //!< hum register object
-};
-
-extern Adafruit_BME280 theBME280;
-
-#endif
diff --git a/software/test_2024-07-23/src/adafruit/ens160.cpp b/software/test_2024-07-23/src/adafruit/ens160.cpp
deleted file mode 100644 (file)
index ef23404..0000000
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
-  ScioSense_ENS160.h - Library for the ENS160 sensor with I2C interface from ScioSense
-  2023 Mar 23  v6      Christoph Friese        Bugfix measurement routine, prepare next release
-  2021 Nov 25   v5     Martin Herold           Custom mode timing fixed
-  2021 Feb 04  v4      Giuseppe de Pinto       Custom mode fixed
-  2020 Apr 06  v3      Christoph Friese        Changed nomenclature to ScioSense as product shifted from ams
-  2020 Feb 15  v2      Giuseppe Pasetti        Corrected firmware flash option
-  2019 May 05  v1      Christoph Friese        Created
-  based on application note "ENS160 Software Integration.pdf" rev 0.01
-*/
-
-#include "ens160.h"
-#include "math.h"
-#include <util/delay.h>
-#include <stdio.h>
-#include <avr/io.h>
-
-ScioSense_ENS160::ScioSense_ENS160 () {
-   _revENS16x = 0;
-
-   //Isotherm, HP0 252°C / HP1 350°C / HP2 250°C / HP3 324°C / measure every 1008ms
-       _seq_steps[0][0] = 0x7c;
-   _seq_steps[0][1] = 0x0a;
-   _seq_steps[0][2] = 0x7e;
-   _seq_steps[0][3] = 0xaf;
-   _seq_steps[0][4] = 0xaf;
-   _seq_steps[0][5] = 0xa2;
-   _seq_steps[0][6] = 0x00;
-   _seq_steps[0][7] = 0x80;
-}
-
-bool ScioSense_ENS160::begin () {
-   i2cDevice.begin(ENS160_I2CADDR_1);
-       _delay_ms(ENS160_BOOTING);
-       if (reset()) {
-      if (checkPartID()) {
-         if (setMode(ENS160_OPMODE_IDLE)) {
-            if (clearCommand()) {
-               if (getFirmware()) {
-                  return true;
-               }
-            }
-         }
-      }
-   }
-   return false;
-       
-}
-
-bool ScioSense_ENS160::write8 (byte reg, byte value) {
-   byte buffer[2];
-   buffer[1] = value;
-   buffer[0] = reg;
-   return i2cDevice.write(buffer, 2);
-}
-
-bool ScioSense_ENS160::read8 (byte reg, byte *value) {
-   uint8_t buffer[1];
-   buffer[0] = uint8_t(reg);
-   return i2cDevice.write_then_read(buffer, 1, value, 1);
-}
-
-bool ScioSense_ENS160::read16 (byte reg, uint16_t *value) {
-   uint8_t buffer[1];
-   buffer[0] = uint8_t(reg);
-   return i2cDevice.write_then_read(buffer, 1, (uint8_t *)value, 2);
-}
-
-bool ScioSense_ENS160::read16LE (byte reg, uint16_t *value) {
-  uint16_t tmp;
-  if (read16(reg, &tmp)) {
-      *value = ((tmp & 0xff) << 8) | (tmp >> 8);
-      return true;
-  }
-  return false;
-}
-
-bool ScioSense_ENS160::readBytes (byte reg, uint8_t *bytes, uint8_t len) {
-   uint8_t buffer[1];
-   buffer[0] = uint8_t(reg);
-   return i2cDevice.write_then_read(buffer, 1, buffer, len);
-}
-
-
-// Sends a reset to the ENS160. Returns false on I2C problems.
-bool ScioSense_ENS160::reset () {
-       if (write8(ENS160_REG_OPMODE, ENS160_OPMODE_RESET)) {
-      _delay_ms(ENS160_BOOTING);
-      return true;
-   }
-       _delay_ms(ENS160_BOOTING);
-       return false;
-}
-
-// Reads the part ID and confirms valid sensor
-bool ScioSense_ENS160::checkPartID () {
-       uint16_t part_id;
-       
-   read16(ENS160_REG_PART_ID, &part_id);
-       _delay_ms(ENS160_BOOTING);
-
-       if (part_id == ENS160_PARTID) {
-      _revENS16x = 0;
-      return true;
-
-   } else if (part_id == ENS161_PARTID) {
-      _revENS16x = 1;
-      return true;
-   }
-       
-       return false;
-}
-
-// Initialize idle mode and confirms 
-bool ScioSense_ENS160::clearCommand () {
-       uint8_t status;
-       
-       if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_NOP)) {
-          if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_CLRGPR)) {
-             _delay_ms(ENS160_BOOTING);
-         if (read8(ENS160_REG_DATA_STATUS, &status)) {
-            return true;
-         }
-      }
-   }
-       _delay_ms(ENS160_BOOTING);
-       return false;
-}
-
-// Read firmware revisions
-bool ScioSense_ENS160::getFirmware () {
-       uint8_t i2cbuf[3];
-       
-       if (clearCommand()) {
-      _delay_ms(ENS160_BOOTING);
-      if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_GET_APPVER)) {
-         if (readBytes(ENS160_REG_GPR_READ_4, i2cbuf, 3)) {
-            _fw_ver_major = i2cbuf[0];
-            _fw_ver_minor = i2cbuf[1];
-            _fw_ver_build = i2cbuf[2];
-            _revENS16x = this->_fw_ver_major > 6 ? 1 : 0;
-            _delay_ms(ENS160_BOOTING);
-            return true;
-         }
-      }
-   }
-   _delay_ms(ENS160_BOOTING);
-       return false;
-}
-
-// Set operation mode of sensor
-bool ScioSense_ENS160::setMode (uint8_t mode) {
-       //LP only valid for rev>0
-       if ((mode == ENS160_OPMODE_LP) and (_revENS16x == 0)) {
-      return false;
-   }
-       if (write8(ENS160_REG_OPMODE, mode)) {
-      _delay_ms(ENS160_BOOTING);
-      return true;
-   }
-   _delay_ms(ENS160_BOOTING);
-   return false;
-}
-
-// Initialize definition of custom mode with <n> steps
-bool ScioSense_ENS160::initCustomMode (uint16_t stepNum) {
-       if (stepNum > 0) {
-               _stepCount = stepNum;
-               if (setMode(ENS160_OPMODE_IDLE)) {
-         if (clearCommand()) {
-            if (write8(ENS160_REG_COMMAND, ENS160_COMMAND_SETSEQ)) {
-               _delay_ms(ENS160_BOOTING);
-               return true;
-            }
-         }
-      }
-       }
-       _delay_ms(ENS160_BOOTING);
-       return false;
-}
-
-// Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate
-bool ScioSense_ENS160::addCustomStep (uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3) {
-       uint8_t seq_ack;
-       uint8_t temp;
-
-       _delay_ms(ENS160_BOOTING);
-
-       temp = (uint8_t)(((time / 24) - 1) << 6); 
-       if (measureHP0) {
-      temp = temp | 0x20;
-   }
-       if (measureHP1) {
-      temp = temp | 0x10;
-   }
-       if (measureHP2) {
-      temp = temp | 0x08;
-   }
-       if (measureHP3) {
-      temp = temp | 0x04;
-   }
-       if (!write8(ENS160_REG_GPR_WRITE_0, temp)) {
-      return false;
-   }
-   temp = (uint8_t)(((time / 24) - 1) >> 2); 
-       if (!write8(ENS160_REG_GPR_WRITE_1, temp)) {
-      return false;
-   }
-   if (!write8(ENS160_REG_GPR_WRITE_2, (uint8_t)(tempHP0 / 2))) {
-      return false;
-   }
-       if (!write8(ENS160_REG_GPR_WRITE_3, (uint8_t)(tempHP1 / 2))) {
-      return false;
-   }
-       if (write8(ENS160_REG_GPR_WRITE_4, (uint8_t)(tempHP2 / 2))) {
-      return false;
-   }
-       if (write8(ENS160_REG_GPR_WRITE_5, (uint8_t)(tempHP3 / 2))) {
-      return false;
-   }
-
-       if (write8(ENS160_REG_GPR_WRITE_6, (uint8_t)(_stepCount - 1))) {
-      return false;
-   }
-
-   if (_stepCount == 1) {
-      if (!write8(ENS160_REG_GPR_WRITE_7, 128)) {
-         return false;
-      }
-   } else {
-      if (!write8(ENS160_REG_GPR_WRITE_7, 0)) {
-         return false;
-      }
-   }
-   _delay_ms(ENS160_BOOTING);
-
-   if (!read8(ENS160_REG_GPR_READ_7, &seq_ack)) {
-      return false;
-   }
-       _delay_ms(ENS160_BOOTING);
-       
-       if ((ENS160_SEQ_ACK_COMPLETE | _stepCount) != seq_ack) {
-               _stepCount++;
-               return false;
-       }
-
-       return true;
-}
-
-bool ScioSense_ENS160::readStatus (uint8_t *status) {
-   return read8(ENS160_REG_DATA_STATUS, status);
-}
-
-bool ScioSense_ENS160::readData (ENS160_DATA *data) {
-   uint8_t buffer[1] = { 0x21 };
-   return i2cDevice.write_then_read(buffer, 1, (uint8_t *)data, sizeof(ENS160_DATA));
-}
-       
-// Perform prediction measurement and stores result in internal variables
-bool ScioSense_ENS160::measure (bool waitForNew) {
-       uint8_t i2cbuf[8];
-       uint8_t status;
-
-       // Set default status for early bail out
-       if (waitForNew) {
-               do {
-         if (!read8(ENS160_REG_DATA_STATUS, &status)) {
-            return false;
-         }
-         _delay_ms(1);
-               } while (!IS_NEWDAT(status));
-       } else {
-               if (!read8(ENS160_REG_DATA_STATUS, &status)) {
-         return false;
-      }
-       }
-       
-
-       // Read predictions
-       if (IS_NEWDAT(status)) {
-               if (!readBytes(ENS160_REG_DATA_AQI, i2cbuf, 7)) {
-         return false;
-      }
-      return false;
-               _data_aqi = i2cbuf[0];
-               _data_tvoc = i2cbuf[1] | ((uint16_t)i2cbuf[2] << 8);
-               _data_eco2 = i2cbuf[3] | ((uint16_t)i2cbuf[4] << 8);
-               if (_revENS16x > 0) {
-         _data_aqi500 = ((uint16_t)i2cbuf[5]) | ((uint16_t)i2cbuf[6] << 8);
-      } else {
-         _data_aqi500 = 0;
-      }
-      return true;
-       }
-       
-       return false;
-}
-
-// Perfrom raw measurement and stores result in internal variables
-bool ScioSense_ENS160::measureRaw (bool waitForNew) {
-       uint8_t i2cbuf[8];
-       uint8_t status;
-
-       // Set default status for early bail out
-       if (waitForNew) {
-               do {
-                       _delay_ms(1);
-                       if (!read8(ENS160_REG_DATA_STATUS, &status)) {
-            return false;
-         }
-               } while (!IS_NEWGPR(status));
-       } else {
-               if (!read8(ENS160_REG_DATA_STATUS, &status)) {
-         return false;
-      }
-       }
-       
-       if (IS_NEWGPR(status)) {
-               
-               // Read raw resistance values
-               if (!readBytes(ENS160_REG_GPR_READ_0, i2cbuf, 8)) {
-         return false;
-      }
-      _hp0_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8)));
-      _hp1_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8)));
-      _hp2_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8)));
-      _hp3_rs = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8)));
-       
-               // Read baselines
-               if (!readBytes(ENS160_REG_DATA_BL, i2cbuf, 8)) {
-         return false;
-      }
-               _hp0_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[0] | ((uint16_t)i2cbuf[1] << 8)));
-               _hp1_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[2] | ((uint16_t)i2cbuf[3] << 8)));
-               _hp2_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[4] | ((uint16_t)i2cbuf[5] << 8)));
-               _hp3_bl = CONVERT_RS_RAW2OHMS_F((uint32_t)(i2cbuf[6] | ((uint16_t)i2cbuf[7] << 8)));
-
-               if (!read8(ENS160_REG_DATA_MISR, i2cbuf)) {
-         return false;
-      }
-               _misr = i2cbuf[0];
-      return true;
-       }
-       
-       return false;
-}
-
-
-// Writes t (degC) and h (%rh) to ENV_DATA. Returns false on I2C problems.
-bool ScioSense_ENS160::set_envdata (float t, float h) {
-       uint16_t t_data = (uint16_t)((t + 273.15f) * 64.0f);
-       uint16_t rh_data = (uint16_t)(h * 512.0f);
-       return this->set_envdata210(t_data, rh_data);
-}
-
-// Writes t and h (in ENS210 format) to ENV_DATA. Returns false on I2C problems.
-bool ScioSense_ENS160::set_envdata210 (uint16_t t, uint16_t h) {
-       //uint16_t temp;
-       uint8_t trh_in[4];
-       
-       //temp = (uint16_t)((t + 273.15f) * 64.0f);
-       trh_in[0] = t & 0xff;
-       trh_in[1] = (t >> 8) & 0xff;
-       
-       //temp = (uint16_t)(h * 512.0f);
-       trh_in[2] = h & 0xff;
-       trh_in[3] = (h >> 8) & 0xff;
-       
-       if (!i2cDevice.writeByteAndBuffer(ENS160_REG_TEMP_IN, trh_in, 4)) {
-      return false;
-   }
-       
-       return true;
-}
diff --git a/software/test_2024-07-23/src/adafruit/ens160.h b/software/test_2024-07-23/src/adafruit/ens160.h
deleted file mode 100644 (file)
index fb6925d..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
-  ScioSense_ENS160.h - Library for the ENS160 sensor with I2C interface from ScioSense
-  2023 Mar 23  v6      Christoph Friese        Bugfix measurement routine, prepare next release
-  2021 July 29 v4      Christoph Friese        Changed nomenclature to ScioSense as product shifted from ams
-  2020 Apr 06  v3      Christoph Friese        Changed nomenclature to ScioSense as product shifted from ams
-  2020 Feb 15  v2      Giuseppe Pasetti        Corrected firmware flash option
-  2019 May 05  v1      Christoph Friese        Created
-  based on application note "ENS160 Software Integration.pdf" rev 0.01
-*/
-
-#ifndef __SCIOSENSE_ENS160_H_
-#define __SCIOSENSE_ENS160_H_
-
-#include "../i2cmaster.hpp"
-#include <stdint.h>
-#define byte uint8_t
-
-// #if (ARDUINO >= 100)
-//     #include "Arduino.h"
-// #else
-//     #include "WProgram.h"
-// #endif
-
-// #include <Wire.h>
-
-// Chip constants
-#define ENS160_PARTID                          0x0160
-#define ENS161_PARTID                          0x0161
-#define ENS160_BOOTING                         10
-
-// 7-bit I2C slave address of the ENS160
-#define ENS160_I2CADDR_0               0x52            //ADDR low
-#define ENS160_I2CADDR_1               0x53            //ADDR high
-
-// ENS160 registers for version V0
-#define ENS160_REG_PART_ID             0x00            // 2 byte register
-#define ENS160_REG_OPMODE              0x10
-#define ENS160_REG_CONFIG              0x11
-#define ENS160_REG_COMMAND             0x12
-#define ENS160_REG_TEMP_IN             0x13
-#define ENS160_REG_RH_IN               0x15
-#define ENS160_REG_DATA_STATUS         0x20
-#define ENS160_REG_DATA_AQI            0x21
-#define ENS160_REG_DATA_TVOC           0x22
-#define ENS160_REG_DATA_ECO2           0x24                    
-#define ENS160_REG_DATA_BL             0x28
-#define ENS160_REG_DATA_T              0x30
-#define ENS160_REG_DATA_RH             0x32
-#define ENS160_REG_DATA_MISR           0x38
-#define ENS160_REG_GPR_WRITE_0         0x40
-#define ENS160_REG_GPR_WRITE_1         ENS160_REG_GPR_WRITE_0 + 1
-#define ENS160_REG_GPR_WRITE_2         ENS160_REG_GPR_WRITE_0 + 2
-#define ENS160_REG_GPR_WRITE_3         ENS160_REG_GPR_WRITE_0 + 3
-#define ENS160_REG_GPR_WRITE_4         ENS160_REG_GPR_WRITE_0 + 4
-#define ENS160_REG_GPR_WRITE_5         ENS160_REG_GPR_WRITE_0 + 5
-#define ENS160_REG_GPR_WRITE_6         ENS160_REG_GPR_WRITE_0 + 6
-#define ENS160_REG_GPR_WRITE_7         ENS160_REG_GPR_WRITE_0 + 7
-#define ENS160_REG_GPR_READ_0          0x48
-#define ENS160_REG_GPR_READ_4          ENS160_REG_GPR_READ_0 + 4
-#define ENS160_REG_GPR_READ_6          ENS160_REG_GPR_READ_0 + 6
-#define ENS160_REG_GPR_READ_7          ENS160_REG_GPR_READ_0 + 7
-
-//ENS160 data register fields
-#define ENS160_COMMAND_NOP             0x00
-#define ENS160_COMMAND_CLRGPR          0xCC
-#define ENS160_COMMAND_GET_APPVER      0x0E 
-#define ENS160_COMMAND_SETTH           0x02
-#define ENS160_COMMAND_SETSEQ          0xC2
-
-#define ENS160_OPMODE_RESET            0xF0
-#define ENS160_OPMODE_DEP_SLEEP                0x00
-#define ENS160_OPMODE_IDLE             0x01
-#define ENS160_OPMODE_STD              0x02
-#define ENS160_OPMODE_LP               0x03    
-#define ENS160_OPMODE_CUSTOM           0xC0
-
-#define ENS160_BL_CMD_START            0x02
-#define ENS160_BL_CMD_ERASE_APP                0x04
-#define ENS160_BL_CMD_ERASE_BLINE      0x06
-#define ENS160_BL_CMD_WRITE            0x08
-#define ENS160_BL_CMD_VERIFY           0x0A
-#define ENS160_BL_CMD_GET_BLVER                0x0C
-#define ENS160_BL_CMD_GET_APPVER       0x0E
-#define ENS160_BL_CMD_EXITBL           0x12
-
-#define ENS160_SEQ_ACK_NOTCOMPLETE     0x80
-#define ENS160_SEQ_ACK_COMPLETE                0xC0
-
-#define IS_ENS160_SEQ_ACK_NOT_COMPLETE(x)      (ENS160_SEQ_ACK_NOTCOMPLETE == (ENS160_SEQ_ACK_NOTCOMPLETE & (x)))
-#define IS_ENS160_SEQ_ACK_COMPLETE(x)          (ENS160_SEQ_ACK_COMPLETE == (ENS160_SEQ_ACK_COMPLETE & (x)))
-
-#define ENS160_DATA_STATUS_NEWDAT      0x02
-#define ENS160_DATA_STATUS_NEWGPR      0x01
-
-#define IS_NEWDAT(x)                   (ENS160_DATA_STATUS_NEWDAT == (ENS160_DATA_STATUS_NEWDAT & (x)))
-#define IS_NEWGPR(x)                   (ENS160_DATA_STATUS_NEWGPR == (ENS160_DATA_STATUS_NEWGPR & (x)))
-#define IS_NEW_DATA_AVAILABLE(x)       (0 != ((ENS160_DATA_STATUS_NEWDAT | ENS160_DATA_STATUS_NEWGPR ) & (x)))
-
-#define CONVERT_RS_RAW2OHMS_I(x)       (1 << ((x) >> 11))
-#define CONVERT_RS_RAW2OHMS_F(x)       (pow (2, (float)(x) / 2048))
-
-typedef struct {
-   uint8_t aqi;
-   uint16_t tvoc;
-   uint16_t eco2;
-} ENS160_DATA;
-
-class ScioSense_ENS160 {
-               
-       public:
-          ScioSense_ENS160();
-               
-               void                            setI2C(uint8_t sda, uint8_t scl);                               // Function to redefine I2C pins
-               
-               bool                            begin();                                        // Init I2C communication, resets ENS160 and checks its PART_ID. Returns false on I2C problems or wrong PART_ID.
-               uint8_t                         revENS16x()     { return this->_revENS16x; }                    // Report version of sensor (0: ENS160, 1: ENS161)
-               bool                            setMode(uint8_t mode);                                          // Set operation mode of sensor
-
-               bool                            initCustomMode(uint16_t stepNum);                               // Initialize definition of custom mode with <n> steps
-               bool                            addCustomStep(uint16_t time, bool measureHP0, bool measureHP1, bool measureHP2, bool measureHP3, uint16_t tempHP0, uint16_t tempHP1, uint16_t tempHP2, uint16_t tempHP3);
-                                                                                                                                                                                       // Add a step to custom measurement profile with definition of duration, enabled data acquisition and temperature for each hotplate
-                                                                                                                                                                                       
-      bool  readData (ENS160_DATA *data);
-      bool  readStatus(uint8_t *status);
-               bool                            measure(bool waitForNew);                               // Perform measurement and stores result in internal variables
-               bool                            measureRaw(bool waitForNew);                            // Perform raw measurement and stores result in internal variables
-               bool                            set_envdata(float t, float h);                                  // Writes t (degC) and h (%rh) to ENV_DATA. Returns "0" if I2C transmission is successful
-               bool                            set_envdata210(uint16_t t, uint16_t h);                         // Writes t and h (in ENS210 format) to ENV_DATA. Returns "0" if I2C transmission is successful
-               uint8_t                         getMajorRev()   { return this->_fw_ver_major; }                 // Get major revision number of used firmware
-               uint8_t                         getMinorRev()   { return this->_fw_ver_minor; }                 // Get minor revision number of used firmware
-               uint8_t                         getBuild()              { return this->_fw_ver_build; }         // Get build revision number of used firmware
-
-               uint8_t                         getAQI()                { return this->_data_aqi; }             // Get AQI value of last measurement 
-               uint16_t                        getTVOC()               { return this->_data_tvoc; }            // Get TVOC value of last measurement 
-               uint16_t                        geteCO2()               { return this->_data_eco2; }            // Get eCO2 value of last measurement 
-               uint16_t                        getAQI500()             { return this->_data_aqi500; }          // Get AQI500 value of last measurement 
-               uint32_t                        getHP0()                { return this->_hp0_rs; }               // Get resistance of HP0 of last measurement
-               uint32_t                        getHP1()                { return this->_hp1_rs; }               // Get resistance of HP1 of last measurement
-               uint32_t                        getHP2()                { return this->_hp2_rs; }               // Get resistance of HP2 of last measurement
-               uint32_t                        getHP3()                { return this->_hp3_rs; }               // Get resistance of HP3 of last measurement
-               uint32_t                        getHP0BL()              { return this->_hp0_bl; }               // Get baseline resistance of HP0 of last measurement
-               uint32_t                        getHP1BL()              { return this->_hp1_bl; }               // Get baseline resistance of HP1 of last measurement
-               uint32_t                        getHP2BL()              { return this->_hp2_bl; }               // Get baseline resistance of HP2 of last measurement
-               uint32_t                        getHP3BL()              { return this->_hp3_bl; }               // Get baseline resistance of HP3 of last measurement
-               uint8_t                         getMISR()               { return this->_misr; }                 // Return status code of sensor
-
-       private:
-      I2cMaster i2cDevice;
-               bool                            reset();                                                        // Sends a reset to the ENS160. Returns false on I2C problems.
-               bool                            checkPartID();                                                  // Reads the part ID and confirms valid sensor
-               bool                            clearCommand();                                                 // Initialize idle mode and confirms 
-               bool                            getFirmware();                                                  // Read firmware revisions
-               
-               uint8_t                         _revENS16x;                                                     // ENS160 or ENS161 connected? (FW >7)
-       
-               uint8_t                         _fw_ver_major;
-               uint8_t                         _fw_ver_minor;
-               uint8_t                         _fw_ver_build;
-
-               uint16_t                        _stepCount;                                                     // Counter for custom sequence
-
-               uint8_t                         _data_aqi;
-               uint16_t                        _data_tvoc;
-               uint16_t                        _data_eco2;
-               uint16_t                        _data_aqi500;
-               uint32_t                        _hp0_rs;
-               uint32_t                        _hp0_bl;
-               uint32_t                        _hp1_rs;
-               uint32_t                        _hp1_bl;
-               uint32_t                        _hp2_rs;
-               uint32_t                        _hp2_bl;
-               uint32_t                        _hp3_rs;
-               uint32_t                        _hp3_bl;
-               uint16_t                        _temp;
-               int                             _slaveaddr;                                                     // Slave address of the ENS160
-               uint8_t                         _misr;
-               
-               uint8_t _seq_steps[1][8];
-
-      bool write8(byte reg, byte value);
-      bool read8 (byte reg, byte *value);
-      bool read16 (byte reg, uint16_t *value);
-      bool read16LE (byte reg, uint16_t *value);
-      bool readBytes (byte reg, uint8_t *bytes, uint8_t len);
-};
-
-
-#endif
diff --git a/software/test_2024-07-23/src/adafruit/sensor.h b/software/test_2024-07-23/src/adafruit/sensor.h
deleted file mode 100644 (file)
index ac7e454..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software< /span>
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* Update by K. Townsend (Adafruit Industries) for lighter typedefs, and
- * extended sensor support to include color, voltage and current */
-
-#ifndef _ADAFRUIT_SENSOR_H
-#define _ADAFRUIT_SENSOR_H
-
-#ifndef ARDUINO
-#include <stdint.h>
-#elif ARDUINO >= 100
-#include "Arduino.h"
-#include "Print.h"
-#else
-#include "WProgram.h"
-#endif
-
-/* Constants */
-#define SENSORS_GRAVITY_EARTH (9.80665F) /**< Earth's gravity in m/s^2 */
-#define SENSORS_GRAVITY_MOON (1.6F)      /**< The moon's gravity in m/s^2 */
-#define SENSORS_GRAVITY_SUN (275.0F)     /**< The sun's gravity in m/s^2 */
-#define SENSORS_GRAVITY_STANDARD (SENSORS_GRAVITY_EARTH)
-#define SENSORS_MAGFIELD_EARTH_MAX                                             \
-  (60.0F) /**< Maximum magnetic field on Earth's surface */
-#define SENSORS_MAGFIELD_EARTH_MIN                                             \
-  (30.0F) /**< Minimum magnetic field on Earth's surface */
-#define SENSORS_PRESSURE_SEALEVELHPA                                           \
-  (1013.25F) /**< Average sea level pressure is 1013.25 hPa */
-#define SENSORS_DPS_TO_RADS                                                    \
-  (0.017453293F) /**< Degrees/s to rad/s multiplier                            \
-                  */
-#define SENSORS_RADS_TO_DPS                                                    \
-  (57.29577793F) /**< Rad/s to degrees/s  multiplier */
-#define SENSORS_GAUSS_TO_MICROTESLA                                            \
-  (100) /**< Gauss to micro-Tesla multiplier */
-
-/** Sensor types */
-typedef enum {
-  SENSOR_TYPE_ACCELEROMETER = (1), /**< Gravity + linear acceleration */
-  SENSOR_TYPE_MAGNETIC_FIELD = (2),
-  SENSOR_TYPE_ORIENTATION = (3),
-  SENSOR_TYPE_GYROSCOPE = (4),
-  SENSOR_TYPE_LIGHT = (5),
-  SENSOR_TYPE_PRESSURE = (6),
-  SENSOR_TYPE_PROXIMITY = (8),
-  SENSOR_TYPE_GRAVITY = (9),
-  SENSOR_TYPE_LINEAR_ACCELERATION =
-      (10), /**< Acceleration not including gravity */
-  SENSOR_TYPE_ROTATION_VECTOR = (11),
-  SENSOR_TYPE_RELATIVE_HUMIDITY = (12),
-  SENSOR_TYPE_AMBIENT_TEMPERATURE = (13),
-  SENSOR_TYPE_OBJECT_TEMPERATURE = (14),
-  SENSOR_TYPE_VOLTAGE = (15),
-  SENSOR_TYPE_CURRENT = (16),
-  SENSOR_TYPE_COLOR = (17),
-  SENSOR_TYPE_TVOC = (18),
-  SENSOR_TYPE_VOC_INDEX = (19),
-  SENSOR_TYPE_NOX_INDEX = (20),
-  SENSOR_TYPE_CO2 = (21),
-  SENSOR_TYPE_ECO2 = (22),
-  SENSOR_TYPE_PM10_STD = (23),
-  SENSOR_TYPE_PM25_STD = (24),
-  SENSOR_TYPE_PM100_STD = (25),
-  SENSOR_TYPE_PM10_ENV = (26),
-  SENSOR_TYPE_PM25_ENV = (27),
-  SENSOR_TYPE_PM100_ENV = (28),
-  SENSOR_TYPE_GAS_RESISTANCE = (29),
-  SENSOR_TYPE_UNITLESS_PERCENT = (30),
-  SENSOR_TYPE_ALTITUDE = (31)
-} sensors_type_t;
-
-/** struct sensors_vec_s is used to return a vector in a common format. */
-typedef struct {
-  union {
-    float v[3]; ///< 3D vector elements
-    struct {
-      float x; ///< X component of vector
-      float y; ///< Y component of vector
-      float z; ///< Z component of vector
-    };         ///< Struct for holding XYZ component
-    /* Orientation sensors */
-    struct {
-      float roll; /**< Rotation around the longitudinal axis (the plane body, 'X
-                     axis'). Roll is positive and increasing when moving
-                     downward. -90 degrees <= roll <= 90 degrees */
-      float pitch;   /**< Rotation around the lateral axis (the wing span, 'Y
-                        axis'). Pitch is positive and increasing when moving
-                        upwards. -180 degrees <= pitch <= 180 degrees) */
-      float heading; /**< Angle between the longitudinal axis (the plane body)
-                        and magnetic north, measured clockwise when viewing from
-                        the top of the device. 0-359 degrees */
-    };               ///< Struct for holding roll/pitch/heading
-  };                 ///< Union that can hold 3D vector array, XYZ components or
-                     ///< roll/pitch/heading
-  int8_t status;     ///< Status byte
-  uint8_t reserved[3]; ///< Reserved
-} sensors_vec_t;
-
-/** struct sensors_color_s is used to return color data in a common format. */
-typedef struct {
-  union {
-    float c[3]; ///< Raw 3-element data
-    /* RGB color space */
-    struct {
-      float r;   /**< Red component */
-      float g;   /**< Green component */
-      float b;   /**< Blue component */
-    };           ///< RGB data in floating point notation
-  };             ///< Union of various ways to describe RGB colorspace
-  uint32_t rgba; /**< 24-bit RGBA value */
-} sensors_color_t;
-
-/* Sensor event (36 bytes) */
-/** struct sensor_event_s is used to provide a single sensor event in a common
- * format. */
-typedef struct {
-  int32_t version;   /**< must be sizeof(struct sensors_event_t) */
-  int32_t sensor_id; /**< unique sensor identifier */
-  int32_t type;      /**< sensor type */
-  int32_t reserved0; /**< reserved */
-  int32_t timestamp; /**< time is in milliseconds */
-  union {
-    float data[4];              ///< Raw data */
-    sensors_vec_t acceleration; /**< acceleration values are in meter per second
-                                   per second (m/s^2) */
-    sensors_vec_t
-        magnetic; /**< magnetic vector values are in micro-Tesla (uT) */
-    sensors_vec_t orientation; /**< orientation values are in degrees */
-    sensors_vec_t gyro;        /**< gyroscope values are in rad/s */
-    float temperature; /**< temperature is in degrees centigrade (Celsius) */
-    float distance;    /**< distance in centimeters */
-    float light;       /**< light in SI lux units */
-    float pressure;    /**< pressure in hectopascal (hPa) */
-    float relative_humidity; /**< relative humidity in percent */
-    float current;           /**< current in milliamps (mA) */
-    float voltage;           /**< voltage in volts (V) */
-    float tvoc;              /**< Total Volatile Organic Compounds, in ppb */
-    float voc_index; /**< VOC (Volatile Organic Compound) index where 100 is
-                          normal (unitless) */
-    float nox_index; /**< NOx (Nitrogen Oxides) index where 100 is normal
-                          (unitless) */
-    float CO2;       /**< Measured CO2 in parts per million (ppm) */
-    float eCO2;      /**< equivalent/estimated CO2 in parts per million (ppm
-                        estimated from some other measurement) */
-    float pm10_std;  /**< Standard Particulate Matter <=1.0 in parts per million
-                        (ppm) */
-    float pm25_std;  /**< Standard Particulate Matter <=2.5 in parts per million
-                        (ppm) */
-    float pm100_std; /**< Standard Particulate Matter <=10.0 in parts per
-                        million (ppm) */
-    float pm10_env;  /**< Environmental Particulate Matter <=1.0 in parts per
-                        million (ppm) */
-    float pm25_env;  /**< Environmental Particulate Matter <=2.5 in parts per
-                        million (ppm) */
-    float pm100_env; /**< Environmental Particulate Matter <=10.0 in parts per
-                        million (ppm) */
-    float gas_resistance;   /**< Proportional to the amount of VOC particles in
-                               the air (Ohms) */
-    float unitless_percent; /**<Percentage, unit-less (%) */
-    sensors_color_t color;  /**< color in RGB component values */
-    float altitude; /**< Distance between a reference datum and a point or
-                       object, in meters. */
-  };                ///< Union for the wide ranges of data we can carry
-} sensors_event_t;
-
-/* Sensor details (40 bytes) */
-/** struct sensor_s is used to describe basic information about a specific
- * sensor. */
-typedef struct {
-  char name[12];     /**< sensor name */
-  int32_t version;   /**< version of the hardware + driver */
-  int32_t sensor_id; /**< unique sensor identifier */
-  int32_t type;      /**< this sensor's type (ex. SENSOR_TYPE_LIGHT) */
-  float max_value;   /**< maximum value of this sensor's value in SI units */
-  float min_value;   /**< minimum value of this sensor's value in SI units */
-  float resolution; /**< smallest difference between two values reported by this
-                       sensor */
-  int32_t min_delay; /**< min delay in microseconds between events. zero = not a
-                        constant rate */
-} sensor_t;
-
-/** @brief Common sensor interface to unify various sensors.
- * Intentionally modeled after sensors.h in the Android API:
- * https://github.com/android/platform_hardware_libhardware/blob/master/include/hardware/sensors.h
- */
-class Adafruit_Sensor {
-public:
-  // Constructor(s)
-  Adafruit_Sensor() {}
-//   virtual ~Adafruit_Sensor() {}
-
-//   // These must be defined by the subclass
-
-//   /*! @brief Whether we should automatically change the range (if possible) for
-//      higher precision
-//       @param enabled True if we will try to autorange */
-//   virtual void enableAutoRange(bool enabled) {
-//     (void)enabled; /* suppress unused warning */
-//   };
-
-//   /*! @brief Get the latest sensor event
-//       @returns True if able to fetch an event */
-//   virtual bool getEvent(sensors_event_t *) = 0;
-//   /*! @brief Get info about the sensor itself */
-//   virtual void getSensor(sensor_t *) = 0;
-
-//   void printSensorDetails(void);
-};
-
-#endif
diff --git a/software/test_2024-07-23/src/i2cmaster.cpp b/software/test_2024-07-23/src/i2cmaster.cpp
deleted file mode 100644 (file)
index 21ff990..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-#include <avr/io.h>
-#include <stdio.h>
-
-#include "i2cmaster.hpp"
-
-I2cMaster::I2cMaster () {
-   address = 0;
-   timer = 0;
-}
-
-void I2cMaster::tick1ms () {
-   if (timer > 0) {
-      timer--;
-   }
-}
-
-bool I2cMaster::begin (uint8_t addr) {
-   this->address = addr;
-   // TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4);
-   TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4);
-   TWCR = (1 << TWEN);
-   return true;
-}
-
-void I2cMaster::end () {
-   TWCR = (1 << TWEN);
-   TWBR = 0;
-}
-
-bool I2cMaster::read (uint8_t *buffer, uint8_t len) {
-   if (start(true)) {
-      if (readBytes(buffer, len)) {
-         if (stop()) {
-            return true;
-         }
-      }
-   }
-   return false;
-}
-
-
-bool I2cMaster::write (const uint8_t *buffer, uint8_t len) {
-   if (start(false)) {
-      if (writeBytes(buffer, len)) {
-         if (stop()) {
-            return true;
-         }
-      }
-   }
-   return false;
-}
-
-bool I2cMaster::writeByteAndBuffer (uint8_t byte, const uint8_t *buffer, uint8_t len) {
-   if (start(false)) {
-      do {
-         TWDR = byte;
-         TWCR = (1 << TWINT) | (1 << TWEN); // send byte
-         while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
-         if ((TWSR & 0xf8) != 0x28) {
-            return false;
-         }
-         byte = *buffer++;
-      } while (len-- > 0);
-      return true;
-   }
-   return false;
-}
-
-
-bool I2cMaster::write_then_read (const uint8_t *write_buffer, uint8_t write_len, uint8_t *read_buffer, uint8_t read_len) {
-   if (start(false)) {
-      if (writeBytes(write_buffer, write_len)) {
-         if (start(true)) {
-            if (readBytes(read_buffer, read_len)) {
-               if (stop()) {
-                  return true;
-               }
-            }
-         }
-      }
-   }
-   return false;
-}
-
-// -------------------------------------------------------------
-
-bool I2cMaster::readBytes (uint8_t *buffer, uint8_t len) {
-   while (len-- > 0) {
-      if (len > 0) {
-         TWCR = (1 << TWEA) | (1 << TWINT) | (1 << TWEN); // read data byte with ACK enabled
-      } else {
-         TWCR = (1 << TWINT) | (1 << TWEN); // read data byte with ACK disabled
-      }
-      while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
-      uint8_t sr = TWSR & 0xf8;
-      if ((len > 0 && sr != 0x50) || (len == 0 && sr != 0x58)) {
-         return false;
-      }
-      *buffer++ = TWDR;
-   }
-   return true;
-}
-
-bool I2cMaster::writeBytes (const uint8_t *buffer, uint8_t len) {
-   while (len-- > 0) {
-      // printf("[wB:len=%d, byte=%02x]", len + 1, *buffer);
-      TWDR = *buffer++;
-      TWCR = (1 << TWINT) | (1 << TWEN); // send data byte
-      timer = 5;
-      while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done
-      if (!timer || (TWSR & 0xf8) != 0x28) {
-         return false;
-      }
-   }
-   return true;
-}
-
-
-bool I2cMaster::start (bool read) {
-   TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition
-   timer = 5;
-   while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done
-   uint8_t sr = TWSR & 0xf8;
-   if (!timer || (sr != 0x08 && sr != 0x10)) {
-      return false;
-   }
-   TWDR = (address << 1) | (read ? 1 : 0); // address + R/nW
-   TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW
-   timer = 5;
-   while (timer > 0 && !(TWCR & (1 << TWINT))) {}; // wait until last action done
-   sr = TWSR & 0xf8;
-   if (!timer || (!read && sr != 0x18) || (read && sr != 0x40)) {
-      return false;
-   }
-   return true;
-}
-
-bool I2cMaster::stop () {
-   TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
-   while (TWCR & ( 1 << TWSTO));
-   return true;
-}
-
diff --git a/software/test_2024-07-23/src/i2cmaster.hpp b/software/test_2024-07-23/src/i2cmaster.hpp
deleted file mode 100644 (file)
index e70326c..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef I2C_MASTER
-#define I2C_MASTER
-
-#include <stdint.h>
-
-class I2cMaster {
-   public:
-      static void end ();
-   
-   public:
-      I2cMaster ();
-      void tick1ms ();
-      bool begin (uint8_t addr);
-      bool read (uint8_t *buffer, uint8_t len);
-      bool write(const uint8_t *buffer, uint8_t len);
-      bool write_then_read(const uint8_t *write_buffer, uint8_t write_len, uint8_t *read_buffer, uint8_t read_len);
-      bool writeByteAndBuffer (uint8_t byte, const uint8_t *buffer, uint8_t len);
-
-   private:
-      uint8_t address;
-      uint8_t timer;
-      bool start (bool read);
-      bool stop ();
-      bool writeBytes (const uint8_t *buffer, uint8_t len);
-      bool readBytes (uint8_t *buffer, uint8_t len);
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/i2cslave.cpp b/software/test_2024-07-23/src/i2cslave.cpp
deleted file mode 100644 (file)
index 2bf6ac2..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-#include <avr/io.h>
-#include <stdio.h>
-#include <util/atomic.h>
-
-#include "i2cslave.hpp"
-
-I2cSlave::I2cSlave () {
-   timer = 0;
-   fromMaster.rIndex = 0;
-   fromMaster.wIndex = 0;
-   toMaster.rIndex = 0;
-   toMaster.wIndex = 0;
-}
-
-void I2cSlave::tick1ms () {
-   if (timer > 0) {
-      timer--;
-   }
-}
-
-bool I2cSlave::begin (uint8_t addr, bool acceptGeneralCalls) {
-   if (addr > 127) {
-      return false;
-   }
-   TWAR = addr << 1 | (acceptGeneralCalls ? 1 : 0);
-   TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4);
-   TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWIE);
-   return true;
-}
-
-void I2cSlave::end () {
-   TWCR = (1 << TWEN);
-   TWBR = 0;
-}
-
-int I2cSlave::read () {
-   return getByte(fromMaster);
-}
-
-void I2cSlave::write (uint8_t byte) {
-   putByte(toMaster, byte);
-}
-
-
-void I2cSlave::putByte (RingBuffer& buffer, uint8_t byte) {
-   ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
-      buffer.data[buffer.wIndex++] = byte;
-      if (buffer.wIndex >= sizeof(buffer.data)) {
-         buffer.wIndex = 0;
-      }
-      if (buffer.wIndex == buffer.rIndex) {
-         buffer.rIndex++;
-         if (buffer.rIndex >= sizeof(buffer.data)) {
-            buffer.rIndex = 0;
-         }
-      }
-   }
-}
-
-int I2cSlave::getByte (RingBuffer& buffer) {
-   uint8_t b;
-   ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
-      if (buffer.rIndex == buffer.wIndex) {
-         return EOF;
-      }
-      b = buffer.data[buffer.rIndex++];
-      if (buffer.rIndex >= sizeof(buffer.data)) {
-         buffer.rIndex = 0;
-      }
-   }
-   return b;
-}
-
-void I2cSlave::handleTWIIsr () {
-   uint8_t sr = TWSR & 0xf8;
-   switch (sr) {
-      case 0x80: { // Previously addressed with own SLA+W; data has been received; ACK has been returned
-         putByte(fromMaster, TWDR);
-         TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); // no TWEA -> only one byte accepted
-         break;
-      }
-      case 0xa8: { // Own SLA+R has been received; ACK has been returned
-         int response = getByte(toMaster);;
-         TWDR = response < 0 ? 0x00 : (uint8_t)response;
-         TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); // no TWEA -> only one byte accepted
-         break;
-      }
-      default: TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (1 << TWIE); break;
-   }
-  
-}
-
diff --git a/software/test_2024-07-23/src/i2cslave.hpp b/software/test_2024-07-23/src/i2cslave.hpp
deleted file mode 100644 (file)
index 2fe2dc7..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef I2C_SLAVE
-#define I2C_SLAVE
-
-#include <stdint.h>
-
-class I2cSlave {
-   private:
-      typedef struct {
-         uint8_t rIndex;
-         uint8_t wIndex;
-         uint8_t data[8];
-   } RingBuffer;
-
-   public:
-      I2cSlave ();
-      void tick1ms ();
-      bool begin (uint8_t addr, bool acceptGeneralCalls);
-      void end ();
-      void handleTWIIsr ();
-      int read ();
-      void write (uint8_t byte);
-
-   private:
-      uint8_t timer;
-      RingBuffer fromMaster;
-      RingBuffer toMaster;
-      void putByte (RingBuffer& buffer, uint8_t byte);
-      int getByte (RingBuffer& buffer);
-
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/main.cpp b/software/test_2024-07-23/src/main.cpp
deleted file mode 100644 (file)
index c363d60..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-#include <avr/io.h>
-#include <util/delay.h>
-#include <stdio.h>
-#include <avr/interrupt.h>
-#include <util/atomic.h>
-
-#include "main.hpp"
-#include "units/led.hpp"
-#include "units/switch.hpp"
-#include "units/rgb.hpp"
-#include "units/seg7.hpp"
-#include "units/poti.hpp"
-#include "units/encoder.hpp"
-#include "units/r2r.hpp"
-#include "units/motor.hpp"
-#include "units/portexp.hpp"
-#include "units/lcd.hpp"
-#include "units/uart1.hpp"
-#include "units/modbus.hpp"
-#include "units/ieee485.hpp"
-#include "units/i2c.hpp"
-
-
-extern "C" {
-   void __cxa_pure_virtual () {
-   }
-
-   int __cxa_guard_acquire(uint8_t *g) {
-      return 0;
-   }
-
-   void __cxa_guard_release(uint8_t *g) {}
-   
-   void __cxa_guard_abort(uint8_t *g) {}
-
-
-   int uart_putchar(char c, FILE *stream) {
-      if (c == '\n') {
-         uart_putchar('\r', stream);
-      }
-      if (stream == stdout) {
-         loop_until_bit_is_set(UCSR0A, UDRE0);
-         UDR0 = c;
-      } 
-      return 0;
-   }
-
-
-   uint64_t volatile systemMillis = 0;
-   uint8_t volatile uartBuffer[32];
-   uint8_t volatile rIndex = 0;
-   uint8_t volatile wIndex = 0;
-
-   int uart_getchar (FILE *stream) {
-    // if (rIndex == wIndex) {
-    //     // nothing in buffer
-    //     return EOF;
-    // }
-    // printf(" r%d", rIndex);
-    while (rIndex == wIndex) {
-        // wait for character
-    }
-    
-    // don't use "char c" because german special characters would lead to negative return -> stream error
-    // char c = uartBuffer[rIndex++];
-    
-    uint8_t c = uartBuffer[rIndex++];
-    // printf("(%02x) ", c);
-    if (c == '\r') {
-      c = '\n';
-    }
-    putchar(c); // echo on terminal
-    return c;
-   }
-
-   static FILE mystdout = { 0, 0, _FDEV_SETUP_WRITE , 0, 0, uart_putchar, NULL, 0 };
-   static FILE mystdin  = { 0, 0, _FDEV_SETUP_READ  , 0, 0, NULL, uart_getchar, 0 };
-
-   static volatile uint32_t timer1ms = 0;
-   static volatile int keyUart0 = EOF;
-
-   Led led;
-   Switch sw;
-   Rgb rgb;
-   Seg7 seg7;
-   Poti poti;
-   Encoder encoder;
-   R2r r2r;
-   Motor motor;
-   PortExp portExp;
-   Lcd lcd;
-   Uart1 uart1;
-   Modbus modbus;
-   Ieee485 ieee485;
-   I2c i2cSparkfun(I2c::SparkFunEnvCombo);
-   I2c i2cMaster(I2c::Master);
-   I2c i2cSlave(I2c::Slave);
-
-}
-
-void setTimer (uint32_t ms) {
-   ATOMIC_BLOCK(ATOMIC_FORCEON) {
-      timer1ms = ms;
-   }
-}
-
-int wait (uint32_t ms) {
-   setTimer(ms);
-   do {
-      ATOMIC_BLOCK(ATOMIC_FORCEON) {
-         ms = timer1ms;
-      }
-   } while (ms > 0 && keyUart0 == EOF);
-   return keyUart0;
-}
-
-int main () {
-
-   // Nano-644 LEDs (Green, Orange, Red)
-   DDRC |= (1 << DDC4) | (1 << DDC3) | (1 << DDC2);
-   PORTC &= ~((1 << PORT4) | (1 << PORT3) | (1 << PORT2));
-
-   // Nano-644 push button SW2
-   DDRC &= ~(1 << DDC5); 
-   PORTC |= (1 << PORT5); // enable internal pullup resistor
-
-   // UART0 interface on Nano-644
-   UCSR0A = (1 << U2X0);
-   UCSR0B = (1 << RXCIE0) | (1 << RXEN0) | (1 <<TXEN0);
-   UCSR0C = (1 << UCSZ01) | ( 1<< UCSZ00);
-   UBRR0H = 0;
-   UBRR0L = F_CPU / 8 / 115200 - 1;
-
-   TCCR2A = (1 << WGM21);
-   TCCR2B = (1 << CS21); // CTC, F_CPU/8=1.5MHz
-   OCR2A = 150; // 150/1.5E6 = 0.1ms
-   TIMSK2 = (1 << OCIE2A);
-
-   stdout = &mystdout;
-   stdin = &mystdin;
-
-   sei();
-
-   TestUnit *unit[] = {
-      &led, &sw, &rgb, &seg7, &poti, &encoder, &r2r, &motor, &portExp, &lcd, &uart1, &modbus, &ieee485,
-      &i2cMaster, &i2cSlave, &i2cSparkfun
-   };
-
-   while (1) {
-      uint16_t i;
-      char s[4];
-      do {
-         printf("\n\n=============================\n\n");
-         printf("Available units:\n\n");
-         for (i = 0; i < sizeof(unit) / sizeof(unit[0]); i++) {
-            TestUnit *pu = unit[i];
-            printf("%3x ... %s\n", i, pu->getName());
-         }
-         printf("\nSelect unit: ");
-         rIndex = 0; wIndex = 0;
-         fgets(s, sizeof(s), stdin);
-      } while (sscanf(s, "%x", &i) != 1 || i < 0 || i >= sizeof(unit) / sizeof(unit[0]) );
-      
-      TestUnit *pu = unit[i];
-      printf("\n\n[%s]: ", pu->getName());
-      keyUart0 = EOF;
-
-      for (uint8_t subtest = 0; subtest < 0xff; subtest++) {
-         printf("\n%4d: ", subtest);
-         if (pu->run(subtest) < 0) {
-            break;
-         }
-         if (keyUart0 == 27) {
-            keyUart0 = EOF;   
-            break;
-         }
-         keyUart0 = EOF;
-      }
-      pu->cleanup();
-   }
-}
-
-uint64_t millis () {
-   volatile uint64_t millis = systemMillis;
-   ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
-      millis = systemMillis;
-   }
-   return millis;
-}
-
-ISR (USART0_RX_vect) {
-   uint8_t b = UDR0;
-   keyUart0 = b;
-   uartBuffer[wIndex++] = b;
-   // printf(" w%d(%02x)", wIndex, b);
-   if (wIndex == rIndex) {
-      // buffer overflow, kick out oldest byte
-      rIndex++;
-   }
-}
-
-ISR (USART1_RX_vect) {
-   uint8_t b = UDR1;
-   if (modbus.enabled) {
-      modbus.handleRxByte(b);
-   }
-   if (uart1.enabled) {
-      uart1.handleRxByte(b);
-   }
-   if (ieee485.enabled) {
-      ieee485.handleRxByte(b);
-   }
-}
-
-ISR (TWI_vect) {
-   if (i2cMaster.enabled) {
-      i2cMaster.handleTwiIrq();
-   } else if (i2cSlave.enabled) {
-      i2cSlave.handleTwiIrq();
-   } else if (i2cSparkfun.enabled) {
-      i2cSparkfun.handleTwiIrq();
-   } else {
-      TWCR = (1 << TWINT); // clear interrupt request bit and disable TWI
-   }
-}
-
-ISR (TIMER2_COMPA_vect) { // every 100us
-   static uint16_t timer500ms = 0;
-   static uint8_t timer100us = 0;
-
-   if (encoder.enabled) {
-      encoder.tick100us();
-   }
-   if (motor.enabled) {
-      motor.tick100us();
-   }
-
-   timer100us++;
-   if (timer100us >= 10) {
-      timer100us = 0;
-      if (timer1ms > 0) {
-         timer1ms--;
-      }
-      systemMillis++;
-      i2cMaster.tick1ms();
-      i2cSlave.tick1ms();
-      i2cSparkfun.tick1ms();
-   }
-
-   timer500ms++;
-   if (timer500ms >= 5000) {
-      PORTC ^= (1 << PORTC3); // orange LED blinking
-      timer500ms = 0;
-   }
-}
-
diff --git a/software/test_2024-07-23/src/main.hpp b/software/test_2024-07-23/src/main.hpp
deleted file mode 100644 (file)
index de2c1e1..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef MAIN_HPP
-#define MAIN_HPP
-
-#include <stdint.h>
-
-#define ENTER '\r'
-#define CTRLC '\003'
-
-extern int wait (uint32_t ms);
-extern uint64_t millis ();
-
-class TestUnit {
-   public:
-      virtual int8_t run (uint8_t subtest) = 0;
-      virtual void cleanup () = 0;
-      virtual const char *getName () = 0;
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/encoder.cpp b/software/test_2024-07-23/src/units/encoder.cpp
deleted file mode 100644 (file)
index 3b8ce8f..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-#include <stdio.h>
-#include <avr/io.h>
-
-#include "encoder.hpp"
-#include "../main.hpp"
-
-// PB0/T0   ... Encoder A
-// PB1/T1   ... Encoder B
-// PB2/INT2 ... push switch of encoder (pushed = 0)
-
-// Encoder signals on rotation clockwise 1 step:
-// A -----____------  one char app. 1ms..2ms (rotation speed)
-// B -------___-----  
-//                    one step when: A = 0, B= 1->0
-
-// Encoder signals on rotation counterclockwise 1 step:
-// A -----____------  one char app. 1ms..2ms (rotation speed)
-// B --______-----  
-//                    one step when: A = 0, B= 0->1
-
-
-void Encoder::cleanup () {
-   DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0));
-   PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0));
-   enabled = 0;
-}
-
-int8_t Encoder::run (uint8_t subtest) {
-   switch (subtest) {
-      case 0: {
-         printf("init");
-         DDRB &= ~((1 << PB2) | (1 << PB1) | (1 << PB0));
-         PORTB |= (1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0); // enable pullup
-         enabled = 1;
-         return 0;
-      }
-
-      case 1: {
-         while (wait(10) == EOF) {
-            printf("\r  => Encoder (push to clear): ");
-            printf("%5d (0x%02x) ", count, (uint8_t)count);
-            if ((PINB & (1 << PINB2)) == 0) {
-               reset();
-            }
-         }
-         return 0;
-      }
-
-      case 2: {
-         printf("\r  => Encoder end%30s", " ");
-         break;
-      }
-   }
-
-   return -1;
-}
-
-struct EncoderState {
-   uint8_t a:1; // signal A
-   uint8_t b:1; // signal B
-};
-
-void Encoder::tick100us () {
-   static EncoderState lastState = { 1, 1 };
-   static EncoderState lastStableState = { 1, 1 };
-
-   if ((DDRB & 0x03) || (PORTB & 0x07) != 0x07) {
-      count = 0;
-      return; // Enocder pins not configured
-   }
-   EncoderState nextState = { (PINB & 0x01) == 0x01, (PINB & 0x02) == 0x02 };
-   if (nextState.a == lastState.a && nextState.b == lastState.b) {
-      if (lastStableState.a == 0 && nextState.b != lastStableState.b) {
-         if (nextState.b == 0) {
-            count = count < 127 ? count + 1 : 127;
-         } else {
-            count = count > -128 ? count - 1 : -128;
-         }
-      }
-      lastStableState.a = nextState.a;
-      lastStableState.b = nextState.b;
-   }
-   lastState.a = nextState.a;
-   lastState.b = nextState.b;
-}
-
diff --git a/software/test_2024-07-23/src/units/encoder.hpp b/software/test_2024-07-23/src/units/encoder.hpp
deleted file mode 100644 (file)
index f28584c..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef ENCODER_HPP
-#define ENCODER_PP
-
-#include <stdint.h>
-#include "../main.hpp"
-
-
-class Encoder : public TestUnit {
-   public:
-      uint8_t enabled;
-      int8_t count;
-
-   public:
-      Encoder () { reset(); enabled = 0; };
-      virtual void cleanup ();
-      virtual int8_t run (uint8_t subtest);
-      virtual const char *getName () { return "Encoder"; }
-      void reset () { count = 0; }
-      void tick100us ();
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/i2c.cpp b/software/test_2024-07-23/src/units/i2c.cpp
deleted file mode 100644 (file)
index 64454cd..0000000
+++ /dev/null
@@ -1,300 +0,0 @@
-#include <stdio.h>
-#include <avr/io.h>
-#include <util/delay.h>
-#include <math.h>
-
-#include "i2c.hpp"
-#include "../adafruit/bme280.h"
-#include "../main.hpp"
-
-
-// Sparkfun https://www.sparkfun.com/products/22858
-// ENS160 address 0x53 (air quality sensor)
-// BME280 address 0x77 /humidity/temperature sensor)
-//    register 0xfe: humidity_7:0
-//    register 0xfd: humidity_15:8
-
-const char *I2c::getName () {
-   switch (mode) {
-      case SparkFunEnvCombo: return "I2C-Sparkfun Env-Combo";
-      case Master: return "I2C-Master";
-      case Slave: return "I2C-Slave";
-   }
-   return "?";
-}
-
-void I2c::cleanup () {
-   enabled = false;
-   TWCR = (1 << TWEN);
-   TWBR = 0;
-   ADMUX = 0;
-   ADCSRA = 0;
-}
-
-int8_t I2c::run (uint8_t subtest) {
-   if (subtest == 0) {
-      TWBR = 13; // 100kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 100000) / (2 * 100000 * 4);
-      TWBR = 28; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4);
-      TWBR = 100; // 50kHz (TWPS1:0 = 00), TWBR = (F_CPU - 16 * 50000) / (2 * 50000 * 4);
-      TWCR = (1 << TWEN);
-      ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V
-      ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128
-      enabled = true;
-      printf("init");
-
-   } else if (subtest == 1 && mode == I2c::SparkFunEnvCombo) {
-      printf(" BM280 ... ");
-      if (!bm280.begin()) {
-         printf("ERROR");
-         return -1;
-      }
-      printf("OK, ENS160 ... ");
-      if (!ens160.begin()) {
-         printf("ERROR");
-         return -1;
-      }
-      if (!ens160.setMode(ENS160_OPMODE_STD)) {
-         printf("ERROR");
-         return -1;
-      }
-      if (!ens160.set_envdata(25.0, 65)) {
-         printf("ERROR");
-         return -1;
-      }
-
-      printf("OK");
-      float accTemp = 0, accHumidity = 0;
-      int8_t accCount = -1;
-
-      do {
-         // BME280 
-         float p = bm280.readPressure();
-         printf("\n => BM280: P= %.3fbar", (double)p / 100000.0);
-         float t = bm280.readTemperature();
-         printf(", T= %.2f°C", (double)t);
-         float h = bm280.readHumidity();
-         printf(", H= %.2f%%", (double)h);
-
-         if (accCount >= 0 && !isnanf(h) && !isnan(t)) {
-            accTemp += t;
-            accHumidity += h;
-            accCount++;
-         }
-
-         bm280.setSampling(
-            Adafruit_BME280::MODE_NORMAL,
-            Adafruit_BME280::SAMPLING_X16,
-            Adafruit_BME280::SAMPLING_X16,
-            Adafruit_BME280::SAMPLING_X16,
-            Adafruit_BME280::FILTER_OFF,
-            Adafruit_BME280::STANDBY_MS_1000
-         );
-
-         // ENS160 only activated every 32s to avoid wrong temperature measuerment
-         // if ES160 would be continously active, the temperature would be 4°C higher
-         // -> ES160 has not enough distance to BM280
-         // This solution causes only a +0.3°C higher temperatur value
-         if (accCount < 0 || accCount >= 32) {
-            printf("  | ENS160 (");
-            if (accCount > 0) { 
-               h = accHumidity / accCount;
-               t = accTemp / accCount;
-               accTemp = 0;
-               accHumidity = 0;
-            }
-            accCount = 0;            
-            if (!ens160.set_envdata(t, h)) {
-               printf("E1)");
-            } else {
-               printf("%.1f°C/%.1f%%): ", (double)t, (double)h);
-               if (!ens160.setMode(ENS160_OPMODE_STD)) {
-                  printf("E2");
-               } else {
-                  for (uint8_t i = 0; i < 100; i++) {
-                     _delay_ms(15);
-                     uint8_t status;
-                     if (ens160.readStatus(&status)) {
-                        if (status & ENS160_DATA_STATUS_NEWDAT) {
-                           ENS160_DATA data;
-                           if (ens160.readData(&data)) {
-                              printf(" aqi=%d(", data.aqi);
-                              switch(data.aqi) {
-                                 case 1: printf("excellent"); break;
-                                 case 2: printf("good"); break;
-                                 case 3: printf("moderate"); break;
-                                 case 4: printf("poor"); break;
-                                 case 5: printf("unhealthy"); break;
-                                 default: printf("?"); break;
-                              }
-                              printf("), tvoc=%dppb", data.tvoc);
-                              printf(", eco2=%d(", data.eco2);
-                              if (data.eco2 < 400) {
-                                 printf("?");
-                              } else if (data.eco2 < 600) {
-                                 printf("excellent");
-                              } else if (data.eco2 < 800) {
-                                 printf("good");
-                              } else if (data.eco2 < 1000) {
-                                 printf("fair");
-                              } else if (data.eco2 < 1500) {
-                                 printf("poor");
-                              } else {
-                                 printf("dad");
-                              }
-                              printf(")");
-                           }
-                           break;
-                        }
-                     }
-                  }
-               }
-               if (!ens160.setMode(ENS160_OPMODE_IDLE)) {
-                  printf("E3");
-               }
-            }
-         }
-
-      } while (wait(1000) == EOF);
-
-   } else if (subtest == 1 && mode == I2c::Master) {
-      if (!master.begin(0x01)) {
-         printf("E1");
-         return -1;
-      }
-      do {
-         uint8_t buffer[1];
-         // read poti
-         ADCSRA |= (1 << ADSC); // start ADC
-         while (ADCSRA & (1 << ADSC)) {} // wait for result
-         buffer[0] = ADCH;
-         printf("\n write 0x%02x", buffer[0]);
-         if (!master.write(buffer, 1)) {
-            printf(" -> ERROR");
-         }
-         printf(",  read ");
-         if (master.read(buffer, 1)) {
-            printf("0x%02x", buffer[0]);   
-         } else {
-            printf(" -> ERROR");
-         }
-      } while (wait(1000) == EOF);
-      master.end();
-
-   } else if (subtest == 1 && mode == I2c::Slave) {
-      if (!slave.begin(0x01, false)) {
-         printf("E1");
-         return -1;
-      }
-      do {
-         int fromMaster = slave.read();
-         if (fromMaster != EOF) {
-            ADCSRA |= (1 << ADSC); // start ADC
-            while (ADCSRA & (1 << ADSC)) {} // wait for result
-            slave.write(ADCH);
-            printf("\n => from master: 0x%02x -> to master: 0x%02x", fromMaster, ADCH);
-         }
-      } while (wait(0) == EOF);
-      slave.end();
-
-   } else {
-      printf("end");
-      return -1;
-   }
-   wait(500);
-   return 0;
-}
-
-void I2c::handleTwiIrq () {
-   if (mode == I2c::Slave) {
-      DDRD |= (1 << PD7);
-      PORTD |= (1 << PD7);
-      slave.handleTWIIsr();
-      PORTD &= ~(1 << PD7);
-   } else {
-      TWCR |= (1 << TWINT); // clear Interrupt Request
-   }
-}
-
-uint16_t I2c::startRead (uint8_t address) {
-   TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition
-   while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
-   uint8_t sr = TWSR & 0xf8;
-   if (sr != 0x08 && sr != 0x10) {
-      return 0x0100 | sr;
-   }
-
-   TWDR = (address << 1) | 0x01; // address + READ (R/W = 1)
-   TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW
-   while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
-   sr = TWSR & 0xf8;
-   if (sr != 0x40) {
-      return 0x0200 | sr;
-   }
-   return 0;
-}
-
-uint16_t I2c::startWrite (uint8_t address) {
-   TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // send START condition
-   while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
-   uint8_t sr = TWSR & 0xf8;
-   if (sr != 0x08 && sr != 0x10) {
-      return 0x0300 | sr;
-   }
-
-   TWDR = (address << 1) | 0x00; // address + WRITE (R/W = 0)
-   TWCR = (1 << TWINT) | (1 << TWEN); // send address/RW
-   while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
-   sr = TWSR & 0xf8;
-   if (sr != 0x18) {
-      return 0x0400 | sr;
-   }
-   return 0;
-}
-
-void I2c::stop () {
-   TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
-}
-
-uint16_t I2c::writeData (uint8_t size, const uint8_t *data) {
-   while (size-- > 0) {
-      TWDR = *data++;
-      TWCR = (1 << TWINT) | (1 << TWEN); // send data byte
-      while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
-      uint8_t sr = TWSR & 0xf8;
-      if (sr != 0x28) {
-         return 0x0500 | sr;
-      }
-   }
-   return 0;
-}
-
-uint16_t I2c::writeByte (uint8_t data) {
-   return writeData(1, &data);
-}
-
-uint16_t I2c::readData (uint8_t size, uint8_t *data) {
-   while (size-- > 0) {
-      if (size > 0) {
-         TWCR = (1 << TWEA) | (1 << TWINT) | (1 << TWEN); // read data byte with ACK enabled
-      } else {
-         TWCR = (1 << TWINT) | (1 << TWEN); // read data byte with ACK disabled
-      }
-      while (!(TWCR & (1 << TWINT))) {}; // wait until last action done
-      uint8_t sr = TWSR & 0xf8;
-      if ((size > 0 && sr != 0x50) || (size == 0 && sr != 0x58)) {
-         return 0x0600 | sr;
-      }
-      *data++ = TWDR;
-   }
-   return 0;
-}
-
-int32_t I2c::compensateBm280T (int32_t adcT) {
-   // int32_t var1, var2, t;
-   // var1 = ((((adcT >> 3) - ((int32_t)bm280.digT[0] << 1))) * ((int32_t)bm280.digT[1])) >> 11;
-   // var2 = (((((adcT >> 4) - ((int32_t)bm280.digT[0])) * ((adcT >> 4) - ((int32_t)bm280.digT[0]))) >> 12) * ((int32_t)bm280.digT[2])) >> 14;
-   // bm280.tFine = var1 + var2;
-   // t = (bm280.tFine * 5 + 128) >> 8;
-   // return t;
-   return -1;
-}
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/i2c.hpp b/software/test_2024-07-23/src/units/i2c.hpp
deleted file mode 100644 (file)
index b439038..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef I2C_HPP
-#define I2C_HPP
-
-#include <stdint.h>
-#include "../main.hpp"
-#include "../adafruit/bme280.h"
-#include "../adafruit/ens160.h"
-#include "../i2cmaster.hpp"
-#include "../i2cslave.hpp"
-
-
-class I2c : public TestUnit {
-   public:
-      typedef enum I2cMode { SparkFunEnvCombo, Master, Slave } I2cMode;
-
-   private:
-      I2cMode mode;
-      Adafruit_BME280 bm280;
-      ScioSense_ENS160 ens160;
-      I2cMaster master;
-      I2cSlave slave;
-
-   public:
-      bool enabled;
-
-   public:
-      I2c (I2cMode mode) { enabled = false; this->mode = mode; }
-      void tick1ms () { master.tick1ms(); slave.tick1ms(); }
-      virtual void cleanup ();
-      virtual int8_t run (uint8_t subtest);
-      virtual const char *getName ();
-      void handleTwiIrq ();
-      uint16_t startRead (uint8_t address);
-      uint16_t startWrite (uint8_t address);
-      void stop ();
-      uint16_t writeByte (uint8_t data);
-      uint16_t writeData (uint8_t size, const uint8_t *data);
-      uint16_t readData (uint8_t size, uint8_t *data);
-      int32_t compensateBm280T (int32_t adcT);
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/ieee485.cpp b/software/test_2024-07-23/src/units/ieee485.cpp
deleted file mode 100644 (file)
index 4fba45f..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-#include <stdio.h>
-#include <avr/io.h>
-#include <util/delay.h>
-#include <util/atomic.h>
-
-#include "ieee485.hpp"
-#include "../main.hpp"
-
-// PB0 ... nRE .. Read enable
-// PB1 ...  DE .. Data enable
-
-#define SET_nRE  (PORTB |= (1 << PB0))
-#define CLR_nRE  (PORTB &= ~(1 << PB0))
-#define SET_DE   (PORTB |= (1 << PB1))
-#define CLR_DE   (PORTB &= ~(1 << PB1))
-
-
-void Ieee485::cleanup () {
-   enabled = 0;
-
-   ADMUX = 0;
-   ADCSRA = 0;
-
-   UCSR1A = 0;
-   UCSR1B = 0;
-   UCSR1C = 0;
-   UBRR1H = 0;
-   UBRR1L = 0;
-   PORTD &= ~(1 << PD2);
-   DDRB &= ~((1 << PB1) | (1 << PB0));
-   PORTB &= ~((1 << PB1) | (1 << PB0));
-}
-
-int8_t Ieee485::run (uint8_t subtest) {
-   if (subtest == 0) {
-      // Poti
-      ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V
-      ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128
-
-      // Modbus
-      SET_nRE;
-      CLR_DE;
-      DDRB |= (1 << PB1) | (1 << PB0);
-
-      // UART1 interface on Nano-644
-      PORTD |= (1 << PD2); // enable RxD1 pullup
-      UCSR1A = (1 << U2X1);
-      UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 <<TXEN1);
-      UCSR1C = (1 << UCSZ11) | ( 1<< UCSZ10);
-      // UCSR1C |= (1 <<UPM11); // even Parity
-      // UCSR1C |= (1 << UPM11) | (1 << UPM10); // odd Parity
-      UBRR1H = 0;
-      UBRR1L = F_CPU / 8 / 9600 - 1;
-
-      enabled = 1;
-      printf("init");
-
-   } else if (subtest == 1) {
-      CLR_nRE; CLR_DE;
-      while (wait(500) == EOF) {
-         ADCSRA |= (1 << ADSC); // start ADC
-         while (ADCSRA & (1 << ADSC)) {} // wait for result
-         UCSR1A |= (1 << TXC1);
-         SET_nRE;
-         SET_DE;
-         UDR1 = ADCH;
-         while ((UCSR1A & (1 << TXC1)) == 0) {}
-         CLR_DE;
-         CLR_nRE;
-         printf("\n  => send Byte 0x%02x", ADCH);
-         int b;
-         ATOMIC_BLOCK(ATOMIC_FORCEON) {
-            b = receivedByte;
-            receivedByte = -1; 
-         }
-         if (b >= 0) {
-            printf("\n  => receive Byte: 0x%02x", b);
-         }
-      }
-
-   } else {
-      printf("end");
-      return -1;
-   }
-
-   return 0;
-}
-
-void Ieee485::handleRxByte (uint8_t b) {
-   receivedByte = b;
-}
diff --git a/software/test_2024-07-23/src/units/ieee485.hpp b/software/test_2024-07-23/src/units/ieee485.hpp
deleted file mode 100644 (file)
index d72f337..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef IEEE485_HPP
-#define IEEE485_HPP
-
-#include <stdint.h>
-#include "../main.hpp"
-
-class Ieee485 : public TestUnit {
-   public:
-      uint8_t  enabled;
-      int16_t  receivedByte;
-
-   public:
-      Ieee485 () { enabled = 0; receivedByte = -1; }
-      virtual void cleanup ();
-      virtual int8_t run (uint8_t subtest);
-      virtual const char *getName () { return "IEEE485"; }
-      void handleRxByte (uint8_t);
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/lcd.cpp b/software/test_2024-07-23/src/units/lcd.cpp
deleted file mode 100644 (file)
index 0781b6a..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-#include <stdio.h>
-#include <avr/io.h>
-#include <util/delay.h>
-
-#include "lcd.hpp"
-#include "../main.hpp"
-
-// PA3 ..... E    --> LCD Enable (Verbindung via J25 erforderlich)
-// PD6 ..... R/W  --> Read/Write: Read=1, Write=0
-// PD7 ..... RS   --> Register Select: Command=0, Data=1
-// PB7:0 ... Data --> Achtung von 5V LCD nicht lesen!
-
-// #define LCD_3V3
-#ifdef LCD_3V3
-   #define DATA_PIN  PINB
-#endif
-
-#define DATA_PORT      PORTB
-#define DIR_DATA_PORT  DDRB
-
-#define PORT_RS PD7
-#define SET_RS_PIN   (PORTD |= (1 << PORT_RS))
-#define CLR_RS_PIN   (PORTD &= ~(1 << PORT_RS))
-#define SET_DIR_RS   (DDRD |= (1 << PORT_RS))
-#define CLR_DIR_RS   (DDRD &= ~(1 << PORT_RS))
-
-#define PORT_RW PD6
-#define SET_RW_PIN   (PORTD |= (1 << PORT_RW))
-#define CLR_RW_PIN   (PORTD &= ~(1 << PORT_RW))
-#define SET_DIR_RW   (DDRD |= (1 << PORT_RW))
-#define CLR_DIR_RW   (DDRD &= ~(1 << PORT_RW))
-
-#define SET_E_PIN   (PORTA |= (1 << PA3))
-#define CLR_E_PIN   (PORTA &= ~(1 << PA3))
-#define SET_DIR_E   (DDRA |= (1 << PA3))
-#define CLR_DIR_E   (DDRA &= ~(1 << PA3))
-
-// Befehle für das Display
-
-#define DISP_CLEAR  0b00000001   // Display clear   
-#define DISP_ON     0b00001111   // Display on      
-#define DISP_OFF    0b00001011   // Display off     
-#define CURSOR_ON   0b00001111   // Cursor on       
-#define CURSOR_OFF  0b00001101   // Cursor off      
-#define BLINK_ON    0b00001111   // Cursor Blink    
-#define BLINK_OFF   0b00001110   // Cursor No Blink 
-
-
-void Lcd::cleanup () {
-   DDRA &= ~(1 << PA3);
-   PORTA &= ~(1 << PA3);
-   DDRD &= ~((1 << DDD7) | (1 << DDD6));
-   PORTD &= ~((1 << PORTD7) | (1 << PORTD6));
-   PORTB = 0;
-   DDRB = 0;
-}
-
-int8_t Lcd::run (uint8_t subtest) {
-   if (subtest == 0) {
-      // DDRA |= (1 << PA3);
-      // PORTA &= ~(1 << PA3);
-      // DDRD |= (1 << DDD7) | (1 << DDD6);
-      // PORTD &= ~((1 << PORTD7) | (1 << PORTD6));
-      // PORTB = 0;
-      // DDRB = 0xff;
-      init();
-      #ifdef LCD_3V3
-         printf("init 3.3V LCD");
-      #else
-         printf("init 5V LCD");
-      #endif
-
-   } else if (subtest == 1) {
-      for (uint8_t i = 0; i < 20 * 4; i++) {
-         char c = (char)(i + 32);
-         if (i % 20 == 0) {
-            setCursor(i / 20 + 1, 1);
-         }
-         writeData(c);
-         while (isBusy()) {};
-      }
-      // setCursor(1, 1);
-      // writeString(" 1234567890<>,;.:-_#+");
-      // setCursor(2, 1);
-      // writeString("abcdefghijklmnopqrst");
-      // setCursor(3, 1);
-      // writeString("uvwxyzABCDEFGHIJKLMN");
-      // setCursor(4, 1);
-      // writeString("OPQRSTUVWXYZ ");
-      printf("LCD beschrieben");
-      while (wait(1) == EOF) {
-      }
-
-   } else {
-      printf("end");
-      return -1;
-   }
-   wait(500);
-   return 0;
-}
-
-void Lcd::init () {
-   CLR_RW_PIN; // write
-   CLR_RS_PIN; // command
-   CLR_E_PIN;  // E = 0
-
-   SET_DIR_RW;
-   SET_DIR_RS;
-   SET_DIR_E;
-
-   DATA_PORT = 0; 
-   DIR_DATA_PORT = 0xff;
-   _delay_ms(16); // min 15ms warten für Reset des Displays
-
-   DATA_PORT = 0b00111011; // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display
-   CLR_RW_PIN;             // write
-   CLR_RS_PIN;             // command
-   SET_E_PIN;              // E = 1 (transfer start)
-   _delay_us(10);               // min. 10us 
-   CLR_E_PIN;              // E = 0 (transfer end)
-   _delay_ms(5);           // min. 4.1ms
-
-   DATA_PORT = 0b00111011; // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display
-   CLR_RW_PIN;             // write
-   CLR_RS_PIN;             // command
-   SET_E_PIN;              // E = 1 (transfer start)
-   _delay_us(10);               // min. 10us 
-   CLR_E_PIN;              // E = 0 (transfer end)
-   _delay_us(100);         // min. 100us
-
-   DATA_PORT = 0b00111011;     // 8bit Modus, 5x7 Zeichen, Mehrzeilen Display
-   CLR_RW_PIN;             // write
-   CLR_RS_PIN;             // command
-   SET_E_PIN;              // E = 1 (transfer start)
-   _delay_us(10);               // min. 10us 
-   CLR_E_PIN;              // E = 0 (transfer end)
-   _delay_us(100);         // min. 100us
-
-   writeCommand(DISP_OFF);     // Display aus
-   while(isBusy()) {};
-   writeCommand(DISP_ON);      // Display ein
-   while(isBusy()) {};
-   writeCommand( BLINK_OFF & CURSOR_OFF);      // Blink aus und Cursor aus
-   while(isBusy()) {};
-   writeCommand(DISP_CLEAR);// Clear display
-   while(isBusy()) {};
-}
-
-uint8_t Lcd::isBusy () {
-   #ifdef LCD_3V3
-      DIR_DATA_PORT = 0;
-      SET_RW_PIN; // read
-      CLR_RS_PIN;      // command
-      SET_E_PIN;  // E = 1 (transfer start)
-      _delay_us(10);
-      uint8_t busy = DATA_PIN & 0x80; // read bit 7 (busy bit)
-      CLR_E_PIN;  // E = 0 (transfer end)
-      CLR_RW_PIN;
-      DIR_DATA_PORT = 0xff;
-      return busy != 0;
-   #else
-      _delay_us(200);
-      return 0;
-   #endif
-   
-}
-
-void Lcd::writeCommand (uint8_t cmd) {
-   DATA_PORT = cmd;
-   CLR_RW_PIN;      // write
-   CLR_RS_PIN;      // command
-   SET_E_PIN;       // E = 1 (transfer start)
-   _delay_us(10);        // min. 10us 
-   CLR_E_PIN;       // E = 0 (transfer end)
-   _delay_us(10);   // min. 10us
-   DATA_PORT = 0;
-}
-
-void Lcd::setDDRamAddr (uint8_t address) {
-   DATA_PORT = address | 0x80;
-   CLR_RW_PIN;      // write
-   CLR_RS_PIN;      // command
-   SET_E_PIN;       // E = 1 (transfer start)
-   _delay_us(10);        // min. 10us 
-   CLR_E_PIN;       // E = 0 (transfer end)
-   _delay_us(10);   // min. 10us
-   DATA_PORT = 0;
-}
-
-void Lcd::writeString (const char *s) {
-   while (*s) {
-      writeData(*s++);
-      while (isBusy()) {};
-   }
-}
-
-void Lcd::writeData (uint8_t data) {
-   DATA_PORT = data;               // Write data to port
-   SET_RS_PIN; // data
-   CLR_RW_PIN; // command
-   SET_E_PIN;       // E = 1 (transfer start)
-   _delay_us(10);        // min. 10us 
-   CLR_E_PIN;       // E = 0 (transfer end)
-   _delay_us(10);   // min. 10us
-   CLR_RS_PIN;
-   DATA_PORT = 0;
-}
-
-void Lcd::setCursor (uint8_t row, uint8_t column) {
-   uint8_t b;
-       if (column > 20) {
-               return;
-       }
-       switch (row) {
-               case 1: b = 0x00 + column - 1; break;
-               case 2: b = 0x40 + column - 1; break;
-      case 3: b = 0x14 + column - 1; break;
-      case 4: b = 0x54 + column - 1; break;
-               default: return;
-       }
-       setDDRamAddr(b);
-   while (isBusy()) {};
-}
-
diff --git a/software/test_2024-07-23/src/units/lcd.hpp b/software/test_2024-07-23/src/units/lcd.hpp
deleted file mode 100644 (file)
index 11e7749..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef LCD_HPP
-#define LCD_HPP
-
-#include <stdint.h>
-#include "../main.hpp"
-
-class Lcd : public TestUnit {
-   public:
-      Lcd () {};
-      virtual void cleanup ();
-      virtual int8_t run (uint8_t subtest);
-      virtual const char *getName () { return "Lcd"; }
-
-      void init ();
-      uint8_t isBusy ();
-      void writeCommand (uint8_t);
-      void setDDRamAddr (uint8_t);
-      void writeString (const char *s);
-      void writeData (uint8_t);
-      void setCursor (uint8_t row, uint8_t column);
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/led.cpp b/software/test_2024-07-23/src/units/led.cpp
deleted file mode 100644 (file)
index fe48a40..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#include <stdio.h>
-#include <avr/io.h>
-#include <util/delay.h>
-
-#include "led.hpp"
-#include "../main.hpp"
-
-void Led::cleanup () {
-   DDRD &= ~((1 << DDD7) | (1 << DDD6) | (1 << DDD5) | (1 << DDD4));
-   PORTD &= ~((1 << PORTD7) | (1 << PORTD6) | (1 << PORTD5) | (1 << PORTD4));
-}
-
-int8_t Led::run (uint8_t subtest) {
-   if (subtest == 0) {
-      DDRD |= (1 << DDD7) | (1 << DDD6) | (1 << DDD5) | (1 << DDD4);
-      PORTD &= ~((1 << PORTD7) | (1 << PORTD6) | (1 << PORTD5) | (1 << PORTD4));
-      printf("init");
-
-   } else if (subtest <= 16) {
-      subtest = (subtest - 1) % 4;
-      PORTD &= ~((1 << PORTD7) | (1 << PORTD6) | (1 << PORTD5) | (1 << PORTD4));
-      PORTD |= (1 << (subtest + 4));
-      printf("Test LED PD%d", subtest + 4);
-
-   } else {
-      printf("end");
-      return -1;
-   }
-   wait(500);
-   return 0;
-}
-
diff --git a/software/test_2024-07-23/src/units/led.hpp b/software/test_2024-07-23/src/units/led.hpp
deleted file mode 100644 (file)
index a76983b..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef LED_HPP
-#define LED_HPP
-
-#include <stdint.h>
-#include "../main.hpp"
-
-class Led : public TestUnit {
-   public:
-      Led () {};
-      virtual void cleanup ();
-      virtual int8_t run (uint8_t subtest);
-      virtual const char *getName () { return "Led"; }
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/modbus.cpp b/software/test_2024-07-23/src/units/modbus.cpp
deleted file mode 100644 (file)
index 28b1bca..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-#include <stdio.h>
-#include <avr/io.h>
-#include <util/delay.h>
-
-#include "modbus.hpp"
-#include "../main.hpp"
-
-// PB0 ... nRE .. Read enable
-// PB1 ...  DE .. Data enable
-
-#define SET_nRE  (PORTB |= (1 << PB0))
-#define CLR_nRE  (PORTB &= ~(1 << PB0))
-#define SET_DE   (PORTB |= (1 << PB1))
-#define CLR_DE   (PORTB &= ~(1 << PB1))
-
-
-void Modbus::cleanup () {
-   enabled = 0;
-   UCSR1A = 0;
-   UCSR1B = 0;
-   UCSR1C = 0;
-   UBRR1H = 0;
-   UBRR1L = 0;
-   PORTD &= ~(1 << PD2);
-   DDRB &= ~((1 << PB1) | (1 << PB0));
-   PORTB &= ~((1 << PB1) | (1 << PB0));
-}
-
-int8_t Modbus::run (uint8_t subtest) {
-   if (subtest == 0) {
-      SET_nRE;
-      CLR_DE;
-      DDRB |= (1 << PB1) | (1 << PB0);
-
-      // UART1 interface on Nano-644
-      PORTD |= (1 << PD2); // enable RxD1 pullup
-      UCSR1A = (1 << U2X1);
-      UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 <<TXEN1);
-      UCSR1C = (1 << UCSZ11) | ( 1<< UCSZ10);
-      // UCSR1C |= (1 <<UPM11); // even Parity
-      // UCSR1C |= (1 << UPM11) | (1 << UPM10); // odd Parity
-      UBRR1H = 0;
-      UBRR1L = F_CPU / 8 / 9600 - 1;
-
-      enabled = 1;
-      printf("init");
-
-   } else if (subtest >= 1 && subtest <= 4) {
-      uint8_t nre, de, b;
-      switch (subtest) {
-         case 1: nre = 1; de = 0; b = 0x01; break;
-         case 2: nre = 1; de = 1; b = 0x8e; break;
-         case 3: nre = 0; de = 0; b = 0x55; break;
-         case 4: nre = 0; de = 1; b = 0xaa; break;
-         default: return -1;
-      }
-      printf(" DE=%u, nRE=%u send 0x%02x... ", de, nre, b);
-      if (nre) {
-         SET_nRE;
-      } else {
-         CLR_nRE;
-      }
-      if (de) {
-         SET_DE;
-      } else {
-         CLR_DE;
-      }
-      _delay_us(100);
-      receivedBytes = 0;
-      UDR1 = b;
-      _delay_ms(1);
-      if (receivedBytes > 0) {
-         printf("0x%02x received", received[0]);
-         receivedBytes = 0;
-      } else {
-         printf("no byte received");
-      }
-      printf(" ... press key to proceed");
-      while (wait(0xffffffff) == EOF) {}
-      CLR_DE;
-      SET_nRE;
-
-   } else if (subtest == 5) {
-      static uint8_t frame[] = { 0x01, 0x04, 0x00, 0x00, 0x00, 0x02, 0x71, 0xcb };
-      printf("Modbus: lese Spannung von Eastron SDM-230 (Einphasenzähler)");
-      SET_DE;
-      CLR_nRE;
-      _delay_us(100);
-      do {
-         SET_DE;
-         receivedBytes = 0;
-         for (uint8_t i = 0; i < sizeof(frame); i++) {
-            UCSR1A |= (1 << TXC1);
-            UDR1 = frame[i];
-            while ((UCSR1A & (1 <<UDRE1)) == 0) {}
-         }
-         while ((UCSR1A & (1 << TXC1)) == 0) {}
-         CLR_DE;
-         printf("\n => Sending:");
-         for (uint8_t i = 0; i < sizeof(frame); i++) {
-            printf(" 0x%02x", frame[i]);
-         }
-         int k = wait(100);
-      
-         printf("\n       RxD1:");
-         if (receivedBytes == 0) {
-            printf("?");
-         } else {
-            for (uint8_t i = 0; i < receivedBytes; i++) {
-               if (i == sizeof(frame)) {
-                  printf("  ");
-               }
-               printf(" 0x%02x", received[i]);
-            }
-         }
-         if (receivedBytes >= 16) {
-            union {
-               uint8_t b[4];
-               float value;
-            } f;
-            f.b[0] = received[14];
-            f.b[1] = received[13];
-            f.b[2] = received[12];
-            f.b[3] = received[11];
-            printf(" -> %4.8fV\n", (double)f.value);
-         }
-         if (k != EOF) {
-            break;
-         }
-         
-      } while (wait(1000) == EOF);
-
-   } else {
-      printf("end");
-      return -1;
-   }
-   wait(500);
-   return 0;
-}
-
-void Modbus::handleRxByte (uint8_t b) {
-   if (receivedBytes < sizeof(received)) {
-      received[receivedBytes++] = b;
-   }
-}
diff --git a/software/test_2024-07-23/src/units/modbus.hpp b/software/test_2024-07-23/src/units/modbus.hpp
deleted file mode 100644 (file)
index 14580f8..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef MODBUS_HPP
-#define MODBUS_HPP
-
-#include <stdint.h>
-#include "../main.hpp"
-
-class Modbus : public TestUnit {
-   public:
-      uint8_t  enabled;
-      uint8_t  receivedBytes;
-      uint8_t  received[16];
-
-
-   public:
-      Modbus () { enabled = 0; receivedBytes = 0; }
-      virtual void cleanup ();
-      virtual int8_t run (uint8_t subtest);
-      virtual const char *getName () { return "Modbus"; }
-      void handleRxByte (uint8_t);
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/motor.cpp b/software/test_2024-07-23/src/units/motor.cpp
deleted file mode 100644 (file)
index be6abd8..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-#include <stdio.h>
-#include <avr/io.h>
-#include <util/atomic.h>
-
-#include "motor.hpp"
-#include "../main.hpp"
-
-void Motor::cleanup () {
-   ADMUX = 0;
-   ADCSRA = 0;
-   TCCR0A = 0;
-   TCCR0B = 0;
-   DDRB &= ~((1 << PB4) | (1 << PB3));
-   PORTA &= ~(1 << PORTA3);
-   enabled = 0;
-}
-
-int8_t Motor::run (uint8_t subtest) {
-   switch (subtest) {
-      case 0: {
-         printf("init");
-         ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V
-         ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128
-         TCCR0A = (1 << COM0A1) | (1 << WGM01) | (1 << WGM00); // Fast PWM on OC0A
-         // TCCR0B = (1 << CS02) | ( 1 << CS00); // f = 12 MHz / 1024 = 11,71875 kHz -> fPWM=45Hz
-         TCCR0B = (1 << CS02); // f = 12 MHz / 256 = 46,875 kHz -> fPWM=183,1Hz
-         DDRB |= (1 << PB4) | (1 << PB3); // Motor enable
-         PORTA |= (1 << PORTA3); // push button for Motor enable control
-         enabled = 1;
-         return 0;
-      }
-
-      case 1: {
-         printf("\n");
-         while (wait(10) == EOF) {
-
-            ADCSRA |= (1 << ADSC); // start ADC
-            while (ADCSRA & (1 << ADSC)) {} // wait for result
-            printf("\r  => ADC0=%3d", ADCH);
-
-            ADMUX = (1 << ADLAR) | (1 << REFS1) | (1 << REFS0) | 2; // ADC2, VREF=2.5V
-
-            uint8_t dutyCycle = 0xff - ADCH;
-            if (dutyCycle <= 1) {
-               dutyCycle = 0;
-            } else if (dutyCycle > 254) {
-               dutyCycle = 255;
-            }
-            OCR0A = dutyCycle;
-            printf("  PWM/OC0A=%3d", dutyCycle);
-
-            ADCSRA |= (1 << ADSC); // start ADC
-            while (ADCSRA & (1 << ADSC)) {} // wait for result
-            printf(" ADC2=%3d", ADCH);
-            ADMUX = (1 << ADLAR) | (1 << REFS0); // ADC0, VREF=AVCC=3.3V
-
-            if ((PINA & (1<< PA3)) == 0) {
-               PORTB &= ~(1 << PORTB4);
-            } else {
-               PORTB |= (1 << PORTB4);
-            }
-
-            uint16_t timer;
-            ATOMIC_BLOCK(ATOMIC_FORCEON) {
-               timer = rpmTimer;
-            }
-            float rpm = 60.0 / (float)timer / 0.0001;
-            if (timer > 0) {
-               printf("  n= %4d U/min", (int)rpm);
-            } else {
-               printf("  no rotation");
-            }
-
-         }
-         return 0;
-      }
-
-      case 2: {
-         printf("\r  => end");
-         break;
-      }
-   }
-
-   return -1;
-}
-
-void Motor::tick100us () {
-   static uint16_t timerH = 0;
-   static uint16_t timerL = 0;
-   static uint8_t pinb = 0;
-   if ((PINB & 0x01) && PINB != pinb && timerL > 10) {
-      rpmTimer = timerL + timerH;
-      timerL = 0;
-      timerH = 0;
-   }
-   if (PINB & 0x01) {
-      timerH = timerH < 0x4000 ? timerH + 1 : 0x4000;
-   } else {
-      timerL = timerL < 0x4000 ? timerL + 1 : 0x4000;
-   }
-   if (timerH >= 0x4000 || timerL >= 0x4000) {
-      rpmTimer = 0; // no ratation detected
-   }
-   pinb = PINB;
-}
-
-
diff --git a/software/test_2024-07-23/src/units/motor.hpp b/software/test_2024-07-23/src/units/motor.hpp
deleted file mode 100644 (file)
index 35fcf9c..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef MOTOR_HPP
-#define MOTOR_HPP
-
-#include <stdint.h>
-#include "../main.hpp"
-
-class Motor : public TestUnit {
-   public:
-      uint8_t  enabled;
-      uint16_t rpmTimer;
-
-   public:
-      Motor () { enabled = 0; rpmTimer = 0; };
-      virtual void cleanup ();
-      virtual int8_t run (uint8_t subtest);
-      virtual const char *getName () { return "Motor"; }
-      void tick100us ();
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/portexp.cpp b/software/test_2024-07-23/src/units/portexp.cpp
deleted file mode 100644 (file)
index 5ed192e..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-#include <stdio.h>
-#include <avr/io.h>
-#include <util/delay.h>
-
-#include "portexp.hpp"
-#include "../main.hpp"
-
-// Port-Expander MCP23S17
-
-// SN-Print Stecker IO16
-// MCP23S17 | IO16 (MEGA2560) | Ampel-Print |   | MCP23S17 | IO16 (MEGA2560) | Ampel-Print |
-// ------------------------------------------   --------------------------------------------
-//     GPA0 |   IO16O7 (PA7)  | Taster RU   |   |     GPB0 |   IO16U7 (PC7)  | Taster LU   |
-//     GPA1 |   IO16O6 (PA6)  | Taster RO   |   |     GPB1 |   IO16U6 (PC6)  | Taster LO   |
-//     GPA2 |   IO16O5 (PA5)  | U-Gruen     |   |     GPB2 |   IO16U5 (PC5)  | R-Gruen     |
-//     GPA3 |   IO16O4 (PA4)  | U-Gelb      |   |     GPB3 |   IO16U4 (PC4)  | R-Gelb      |
-//     GPA4 |   IO16O3 (PA3)  | U-Rot       |   |     GPB4 |   IO16U3 (PC3)  | R-Rot       |
-//     GPA5 |   IO16O2 (PA2)  | L-Gruen     |   |     GPB5 |   IO16U2 (PC2)  | O-Gruen     |
-//     GPA6 |   IO16O1 (PA1)  | L-Gelb      |   |     GPB6 |   IO16U1 (PC1)  | O-Gelb      |
-//     GPA7 |   IO16O0 (PA0)  | L-Rot       |   |     GPB7 |   IO16U0 (PC0)  | O-Rot       |
-
-
-void PortExp::cleanup () {
-   DDRB &= ~(1 << PB6); // // SPI MISO (=PB6)
-   DDRB &= ~((1 << PB7) | (1 << PB5)); // SPI SCK (=PB7) and SPI MOSI (=PB5)
-   DDRA &= ~(1 << PA7);
-   PORTA &= ~(1 << PA7); // SPI nCS
-   SPCR = 0;
-   
-}
-
-int8_t writeByte (uint8_t addr, uint8_t b) {
-
-   PORTA &= ~(1 << PA7); // SPI nCS  1 -> 0
-
-   SPDR = 0x40; // WRITE BYTE
-   while ((SPSR & (1 << SPIF)) == 0) {}
-   if (SPDR != 0) {
-      printf("E1"); 
-      PORTA |= (1 << PA7); // SPI nCS  0 -> 1
-      return -1;
-   }
-
-   SPDR = addr; // register address
-   while ((SPSR & (1 << SPIF)) == 0) {}
-   if (SPDR != 0) {
-      printf("E2"); 
-      PORTA |= (1 << PA7); // SPI nCS  0 -> 1
-      return -1;
-   }
-
-   SPDR = b; // value
-   while ((SPSR & (1 << SPIF)) == 0) {}
-   if (SPDR != 0) {
-      printf("E3"); 
-      PORTA |= (1 << PA7); // SPI nCS  0 -> 1
-      return -1;
-   }
-
-   PORTA |= (1 << PA7); // SPI nCS  0 -> 1
-
-   _delay_us(5);
-   return 0;
-}
-
-int8_t PortExp::run (uint8_t subtest) {
-   if (subtest == 0) {
-      PRR &= (1 << PRSPI);
-      PORTA |= (1 << PA7);
-      DDRA |= (1 << PA7); // SPI nCS
-      // PORTB/DDRB must be configured before SPCR !!
-      PORTB |= (1 << PB4); // nSS must be HIGH, otherwise SPI master will not become active!!
-      DDRB |= (1 << PB7) | (1 << PB5) | (1 << PB4); // SPI SCK (=PB7) and SPI MOSI (=PB5)
-
-      // SPCR |= (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0); // SPI enable , Master, f=12MHz/128=93,75kHz
-      SPCR = (1 << SPE) | (1 << MSTR); // SPI enable , Master, f=12MHz/4 = 3MHz
-      printf("init");
-
-   } else if (subtest == 1) {
-      while (wait(500) == EOF) {
-         printf("\n => start ...");
-         for (uint8_t i = 0; i < 8; i++) {
-            writeByte(0, ~(1 << i)); // IODIRA (Bank = 0)
-            // writeByte(0, 0x00); // IODIRA (Bank = 0) - all output
-            writeByte(0x12, (1 << i)); // GPIOA (Bank = 0)
-            printf("\n  Bank0 - GPA%d = 1", i);
-            wait(200);
-            writeByte(0x12, 0); // GPIOA (Bank = 0)
-            printf("\n  Bank0 - GPA%d = 0", i);
-            writeByte(0, 0xff); // IODIRA (Bank = 0)
-            wait(200);
-         }
-         for (uint8_t i = 0; i < 8; i++) {
-            writeByte(1, ~(1 << i)); // IODIRB (Bank = 0)
-            // writeByte(1, 0x00); // IODIRB (Bank = 0) - all output
-            writeByte(0x13, (1 << i)); // GPIOB (Bank = 0)
-            printf("\n  Bank0 - GPB%d = 1", i);
-            wait(200);
-            writeByte(0x13, 0); // GPIOB (Bank = 0)
-            printf("\n  Bank0 - GPB%d = 0", i);
-            writeByte(1, 0xff); // IODIRB (Bank = 0)
-            wait(200);
-         }
-      }
-
-   } else {
-      printf("end");
-      return -1;
-   }
-   wait(500);
-   return 0;
-}
-
diff --git a/software/test_2024-07-23/src/units/portexp.hpp b/software/test_2024-07-23/src/units/portexp.hpp
deleted file mode 100644 (file)
index 7db858a..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef PORTEXP_HPP
-#define PORTEXP_HPP
-
-#include <stdint.h>
-#include "../main.hpp"
-
-class PortExp : public TestUnit {
-   public:
-      PortExp () {};
-      virtual void cleanup ();
-      virtual int8_t run (uint8_t subtest);
-      virtual const char *getName () { return "PortExp"; }
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/poti.cpp b/software/test_2024-07-23/src/units/poti.cpp
deleted file mode 100644 (file)
index 5f8fcdb..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#include <stdio.h>
-#include <avr/io.h>
-
-#include "poti.hpp"
-#include "../main.hpp"
-
-void Poti::cleanup () {
-   ADMUX = 0;
-   ADCSRA = 0;
-}
-
-int8_t Poti::run (uint8_t subtest) {
-   switch (subtest) {
-      case 0: {
-         printf("init");
-         ADMUX = (1 << REFS0); // ADC0, VREF=AVCC=3.3V
-         ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128
-         return 0;
-      }
-
-      case 1: {
-         printf("\n");
-         while (wait(10) == EOF) {
-            printf("\r  => Measure ADC0: ");
-            ADCSRA |= (1 << ADSC); // start ADC
-            while (ADCSRA & (1 << ADSC)) {} // wait for result
-            printf("%4d (0x%03x)", ADC, ADC);
-         }
-         return 0;
-      }
-
-      case 2: {
-         printf("\r  => ADC end%20s", " ");
-         break;
-      }
-   }
-
-   return -1;
-}
-
-
diff --git a/software/test_2024-07-23/src/units/poti.hpp b/software/test_2024-07-23/src/units/poti.hpp
deleted file mode 100644 (file)
index df368e8..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef POTI_HPP
-#define POTI_PP
-
-#include <stdint.h>
-#include "../main.hpp"
-
-class Poti : public TestUnit {
-   public:
-      Poti () {};
-      virtual void cleanup ();
-      virtual int8_t run (uint8_t subtest);
-      virtual const char *getName () { return "Poti"; }
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/r2r.cpp b/software/test_2024-07-23/src/units/r2r.cpp
deleted file mode 100644 (file)
index 40b24e1..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#include <stdio.h>
-#include <avr/io.h>
-
-#include "r2r.hpp"
-#include "../main.hpp"
-
-void R2r::cleanup () {
-   ADMUX = 0;
-   ADCSRA = 0;
-}
-
-int8_t R2r::run (uint8_t subtest) {
-   switch (subtest) {
-      case 0: {
-         printf("init");
-         ADMUX = (1 << REFS0) | 2; // ADC2, VREF=AVCC=3.3V
-         ADCSRA = (1 << ADEN) | 7; // ADC Enable, Prescaler 128
-         return 0;
-      }
-
-      case 1: {
-         printf("\n");
-         while (wait(10) == EOF) {
-            printf("\r  => Measure ADC2: ");
-            ADCSRA |= (1 << ADSC); // start ADC
-            while (ADCSRA & (1 << ADSC)) {} // wait for result
-            printf("%4d (0x%03x)", ADC, ADC);
-            uint8_t sw = (ADC + 32) / 64;
-            printf("  SW9:6 = %d %d% d %d  ", sw >> 3, (sw >> 2) & 0x01, (sw >> 1) & 0x01, sw & 0x01 );
-         }
-         return 0;
-      }
-
-      case 2: {
-         printf("\r  => R2R end%20s", " ");
-         break;
-      }
-   }
-
-   return -1;
-}
-
-
diff --git a/software/test_2024-07-23/src/units/r2r.hpp b/software/test_2024-07-23/src/units/r2r.hpp
deleted file mode 100644 (file)
index 974c7a7..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef R2R_HPP
-#define R2R_PP
-
-#include <stdint.h>
-#include "../main.hpp"
-
-class R2r : public TestUnit {
-   public:
-      R2r () {};
-      virtual void cleanup ();
-      virtual int8_t run (uint8_t subtest);
-      virtual const char *getName () { return "R2R"; }
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/rgb.cpp b/software/test_2024-07-23/src/units/rgb.cpp
deleted file mode 100644 (file)
index 200bffd..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-#include <stdio.h>
-#include <avr/io.h>
-
-#include "rgb.hpp"
-#include "../main.hpp"
-
-void Rgb::cleanup () {
-   PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0));
-   DDRB &= ~((1 << DDB2) | (1 << DDB1) | (1 << DDB0));
-}
-
-int8_t Rgb::run (uint8_t subtest) {
-   switch (subtest) {
-      case 0: {
-         DDRB |= (1 << DDB2) | (1 << DDB1) | (1 << DDB0);
-         PORTB |= (1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0); // all OFF
-         printf("init");
-         return 0;
-      }
-
-      case 1: {
-         PORTB &= ~(1 << PORTB0); // ON
-         printf("Red");
-         wait(2000);
-         return 0;
-      }
-
-      case 2: {
-         PORTB |= (1 << PORTB0);
-         PORTB &= ~(1 << PORTB1); // ON
-         printf("Green");
-         wait(2000);
-         return 0;
-      }
-
-      case 3: {
-         PORTB |= (1 << PORTB1);
-         PORTB &= ~(1 << PORTB2); // ON
-         printf("Blue");
-         wait(2000);
-         return 0;
-      }
-
-      case 4: {
-         PORTB &= ~((1 << PORTB2) | (1 << PORTB1) | (1 << PORTB0));
-         printf("All");
-         wait(2000);
-         return 0;
-      }
-
-      case 5: {
-         printf("end");
-         break;
-      }
-   }
-
-   return -1;
-}
-
-
diff --git a/software/test_2024-07-23/src/units/rgb.hpp b/software/test_2024-07-23/src/units/rgb.hpp
deleted file mode 100644 (file)
index 2fa8a72..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef RGB_HPP
-#define RGB_PP
-
-#include <stdint.h>
-#include "../main.hpp"
-
-class Rgb : public TestUnit {
-   public:
-      Rgb () {};
-      virtual void cleanup ();
-      virtual int8_t run (uint8_t subtest);
-      virtual const char *getName () { return "Rgb"; }
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/seg7.cpp b/software/test_2024-07-23/src/units/seg7.cpp
deleted file mode 100644 (file)
index d2fdbb9..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-#include <stdio.h>
-#include <avr/io.h>
-
-#include "seg7.hpp"
-#include "../main.hpp"
-
-// PA0 Cathode Char 1
-// PA1 Cathode Char 2
-// PA2 Cathode Char 3
-// PA3 Cathode Char 4
-
-// PB0 Anode Segment A
-// PB1 Anode Segment B
-// PB2 Anode Segment C
-// PB3 Anode Segment D
-// PB4 Anode Segment E
-// PB5 Anode Segment F
-// PB6 Anode Segment G
-// PB7 Anode DP
-
-// PD5 nOE (Output Enable) for all LEDs
-// PD6 Anode L1:2
-// PD7 Anode L3
-
-const char *segName[] = { "A", "B", "C", "D", "E", "F", "G", "DP" };
-
-void Seg7::cleanup () {
-   PORTA &= ~((1 <<PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0));
-   DDRA &= ~((1 <<DDA3) | (1 << DDA2) | (1 << DDA1) | (1 << DDA0));
-   PORTD &= ~((1 <<PORTD7) | (1 << PORTD6) | (1 << PORTD5));
-   DDRD &= ~((1 <<DDD7) | (1 << DDD6) | (1 << DDD5));
-   PORTB = 0;
-   DDRB = 0;
-}
-
-int8_t Seg7::run (uint8_t subtest) {
-   if (subtest == 0) {
-      PORTA &= ~((1 <<PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0));
-      DDRA |= (1 <<DDA3) | (1 << DDA2) | (1 << DDA1) | (1 << DDA0);
-      PORTB = 0;
-      DDRB = 0xff;
-      PORTD &= ~((1 <<PORTD7) | (1 << PORTD6));
-      PORTD |= (1 << PORTD5);
-      DDRD |= (1 <<DDD7) | (1 << DDD6) | (1 << DDD5);
-      printf("init");
-      return 0;
-
-   } else if (subtest == 1) {
-      PORTA |= 0x0f; // all segment cathodes conected to GND
-      PORTB = 0xff; // all segments ON
-      PORTD |= (1 << PORTD7) | (1 << PORTD6); // L1, L2, L3 ON
-      PORTD &= ~(1 << PORTD5); // Output enable
-      printf("ON");
-      wait(2000);
-      PORTB = 0x0; // all segments OFF
-      PORTD &= ~((1 << PORTD7) | (1 << PORTD6)); // L1, L2, L3 OFF
-      return 0;
-
-   } else if (subtest == 2) {
-      printf("OFF");
-      wait(1000);
-      return 0;
-
-   } else if (subtest == 3) {
-      PORTD |= (1 << PORTD6); // L1/L2 ON
-      printf("L1/L2 ON");
-      wait(1000);
-      PORTD &= ~(1 << PORTD6); // L1/L2 OFF
-      return 0;
-
-   } else if (subtest == 4) {
-      PORTD |= (1 << PORTD7); // L3 ON
-      printf("L1/L2 ON");
-      wait(1000);
-      PORTD &= ~(1 << PORTD7); // L3 OFF
-      return 0;
-
-   } else if (subtest < (5 + 4 * 8)) {
-      uint8_t chIndex = (subtest - 5) / 8;
-      uint8_t segIndex = (subtest - 5) % 8;
-      PORTA &= ~0x0f; // all segment cathodes disconnected from GND
-      PORTA |= (1 << chIndex);
-      PORTB = (1 << segIndex);
-      printf("Char %d - %s", chIndex, segName[segIndex]);
-      wait(400);
-      return 0;
-
-   } else {
-      printf("end");
-   }
-
-   return -1;
-}
-
-
diff --git a/software/test_2024-07-23/src/units/seg7.hpp b/software/test_2024-07-23/src/units/seg7.hpp
deleted file mode 100644 (file)
index 70b4d15..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef SEG7_HPP
-#define SEG7_HPP
-
-#include <stdint.h>
-#include "../main.hpp"
-
-class Seg7 : public TestUnit {
-   public:
-      Seg7 () {};
-      virtual void cleanup ();
-      virtual int8_t run (uint8_t subtest);
-      virtual const char *getName () { return "Seg7"; }
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/switch.cpp b/software/test_2024-07-23/src/units/switch.cpp
deleted file mode 100644 (file)
index 33d483c..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#include <stdio.h>
-#include <avr/io.h>
-#include <util/delay.h>
-
-#include "switch.hpp"
-#include "../main.hpp"
-
-void Switch::cleanup () {
-   PORTA &= ~((1 << PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0));
-}
-
-
-int8_t Switch::run (uint8_t subtest) {
-   if (subtest == 0) {
-      DDRA &= ~((1 << DDA3) | (1 << DDA2) | (1 << DDA1) | (1 << DDA0));
-      // enable internal pullup resistor
-      PORTA |= (1 << PORTA3) | (1 << PORTA2) | (1 << PORTA1) | (1 << PORTA0);
-      wait(10);
-      return 0;
-
-   } else if (subtest <= 17) {
-      uint8_t bit = (subtest - 1) / 4;
-      switch ((subtest - 1) % 4) {
-         case 0: case 2: {
-            if ((PINA & (1 << bit)) == 0) {
-               printf("Release SW%d (PA%d) ", bit + 1, bit);
-               while ((PINA & (1 << bit)) == 0 && wait(0) == EOF) {}
-               wait(10);
-            }
-            return 0;
-         }
-
-         case 1: {
-            if ((PINA & (1 << bit)) != 0) {
-               printf("Press SW%d (PA%d) ", bit + 1, bit);
-               while ((PINA & (1 << bit)) != 0 && wait(0) == EOF) {}
-               wait(10);
-            }
-            return 0;
-         }
-
-         case 3: {
-            printf("end");
-            return 0;
-         }
-      }
-
-   }
-   
-   return -1;
-}
diff --git a/software/test_2024-07-23/src/units/switch.hpp b/software/test_2024-07-23/src/units/switch.hpp
deleted file mode 100644 (file)
index 8bdcd74..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef SWITCH_HPP
-#define SWITCH_HPP
-
-#include <stdint.h>
-#include "../main.hpp"
-
-class Switch : public TestUnit {
-   public:
-      Switch () {};
-      virtual void cleanup ();            
-      virtual int8_t run (uint8_t subtest);
-      virtual const char *getName () { return "Switch"; }
-};
-
-#endif
\ No newline at end of file
diff --git a/software/test_2024-07-23/src/units/uart1.cpp b/software/test_2024-07-23/src/units/uart1.cpp
deleted file mode 100644 (file)
index 7ef9504..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#include <stdio.h>
-#include <avr/io.h>
-#include <util/delay.h>
-
-#include "uart1.hpp"
-#include "../main.hpp"
-
-int uart1_putchar(char c, FILE *stream) {
-   if (c == '\n') {
-      uart1_putchar('\r', stream);
-   }
-   loop_until_bit_is_set(UCSR1A, UDRE1);
-   UDR1 = c;
-   return 0;
-}
-
-static FILE mystderr = { 0, 0, _FDEV_SETUP_WRITE , 0, 0, uart1_putchar, NULL, 0 };
-
-void Uart1::cleanup () {
-   enabled = 0;
-   UCSR1A = 0;
-   UCSR1B = 0;
-   UCSR1C = 0;
-   UBRR1H = 0;
-   UBRR1L = 0;
-   stderr = NULL;
-}
-
-int8_t Uart1::run (uint8_t subtest) {
-   if (subtest == 0) {
-      // UART1 interface on Nano-644
-      PORTD |= (1 << PD2); // enable RxD1 pullup
-      UCSR1A = (1 << U2X1);
-      UCSR1B = (1 << RXCIE1) | (1 << RXEN1) | (1 <<TXEN1);
-      UCSR1C = (1 << UCSZ11) | ( 1<< UCSZ10);
-      UBRR1H = 0;
-      UBRR1L = F_CPU / 8 / 115200 - 1;
-      stderr = &mystderr;
-      enabled = 1;
-      printf("init");
-
-   } else if (subtest == 1) {
-      do {
-         printf("\n => send text via UART1 now...");
-         fprintf(stderr, "Hello UART1, ECHO-Modus active\n");
-      } while (wait(5000) == EOF);
-
-   } else {
-      printf("end");
-      return -1;
-   }
-   wait(500);
-   return 0;
-}
-
-void Uart1::handleRxByte (uint8_t b) {
-   uart1_putchar(b, stderr);
-}
diff --git a/software/test_2024-07-23/src/units/uart1.hpp b/software/test_2024-07-23/src/units/uart1.hpp
deleted file mode 100644 (file)
index 23c9b7f..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef UART1_HPP
-#define UART1_HPP
-
-#include <stdint.h>
-#include "../main.hpp"
-
-class Uart1 : public TestUnit {
-   public:
-      uint8_t  enabled;
-
-   public:
-      Uart1 () { enabled = 0; }
-      virtual void cleanup ();
-      virtual int8_t run (uint8_t subtest);
-      virtual const char *getName () { return "Uart1"; }
-      void handleRxByte (uint8_t);
-};
-
-#endif
\ No newline at end of file