--- /dev/null
+target remote :1234
+layout split
+stepi
+quit
+target remote :1234
+layout split
+stepi
+b *main+9
+quit
--- /dev/null
+.depend
+**/build
+**/dist
+**/sim
--- /dev/null
+{
+ "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
+}
--- /dev/null
+{
+ // 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"
+ }
+ ]
+}
--- /dev/null
+{
+ "[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"
+ ]
+}
--- /dev/null
+{
+ // 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
--- /dev/null
+.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"
--- /dev/null
+# Testprogramm
--- /dev/null
+#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;
+}
--- /dev/null
+// 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
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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;
+}
+
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+
+}
+
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+}
+
--- /dev/null
+#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
--- /dev/null
+#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;
+}
+
--- /dev/null
+#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
--- /dev/null
+#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;
+// }
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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()) {};
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
+
--- /dev/null
+#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
--- /dev/null
+target remote :1234
+layout split
+stepi
+quit
+target remote :1234
+layout split
+stepi
+b *main+9
+quit
--- /dev/null
+.depend
+**/build
+**/dist
+**/sim
--- /dev/null
+{
+ "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
+}
--- /dev/null
+{
+ // 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"
+ }
+ ]
+}
--- /dev/null
+{
+ "[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"
+ ]
+}
--- /dev/null
+{
+ // 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
--- /dev/null
+.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"
--- /dev/null
+# Testprogramm
--- /dev/null
+#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;
+}
--- /dev/null
+// 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
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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;
+}
+
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+
+}
+
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+}
+
--- /dev/null
+#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
--- /dev/null
+#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;
+}
+
--- /dev/null
+#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
--- /dev/null
+#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;
+// }
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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()) {};
+}
+
--- /dev/null
+#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
--- /dev/null
+#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;
+}
+
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
+
+
--- /dev/null
+#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
--- /dev/null
+#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;
+}
+
--- /dev/null
+#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
--- /dev/null
+#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;
+}
+
+
--- /dev/null
+#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
--- /dev/null
+#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;
+}
+
+
--- /dev/null
+#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
--- /dev/null
+#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;
+}
+
+
--- /dev/null
+#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
--- /dev/null
+#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;
+}
+
+
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
+++ /dev/null
-target remote :1234
-layout split
-stepi
-quit
-target remote :1234
-layout split
-stepi
-b *main+9
-quit
+++ /dev/null
-.depend
-**/build
-**/dist
-**/sim
+++ /dev/null
-{
- "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
-}
+++ /dev/null
-{
- // 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"
- }
- ]
-}
+++ /dev/null
-{
- "[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"
- ]
-}
+++ /dev/null
-{
- // 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
+++ /dev/null
-.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"
+++ /dev/null
-# Testprogramm
+++ /dev/null
-#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;
-}
+++ /dev/null
-// 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
+++ /dev/null
-/*
- 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;
-}
+++ /dev/null
-/*
- 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
+++ /dev/null
-/*
- * 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
+++ /dev/null
-#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;
-}
-
+++ /dev/null
-#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
+++ /dev/null
-#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;
- }
-
-}
-
+++ /dev/null
-#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
+++ /dev/null
-#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;
- }
-}
-
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
-
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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()) {};
-}
-
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
-
+++ /dev/null
-#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
+++ /dev/null
-#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;
- }
-}
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
-
-
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
-
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
-
-
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
-
-
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
-
-
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
-
-
+++ /dev/null
-#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
+++ /dev/null
-#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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#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);
-}
+++ /dev/null
-#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