diff --git a/README.md b/README.md index 297c2f1..4d643d3 100644 --- a/README.md +++ b/README.md @@ -183,16 +183,20 @@ pio run -e smart-sensor-d1-mini-esp32 -t upload ``` -### Smart sensor with touch display +### Smart sensor with display +A humidity and temperature sensor with touch display. Supports +GC9A01 240x240 round display and CST816S capacitive touch. -- `smart-sensor-display` - Humidity and temperature sensor with touch display. For generic *ESP32*, - GC9A01 240x240 round display, and CST816S capacitive touch -- `smart-sensor-display-s3` - Like above but with 1.28" round display and integrated ESP32-S3 on board. - +**Generic ESP32** +```bash +pio run -e smart-sensor-display -t upload +``` +**Round display with integrated ESP32-S3** +```bash +pio run -e smart-sensor-display-s3 -t upload +``` ### Shutter control @@ -236,13 +240,10 @@ Just a generic playground project to mess with the library =) pio run -e playground -t upload ``` - - - -`playground-c3` - Same as above but compiling for *ESP32-C3* - - - +**Generic ESP32 C3** +```bash +pio run -e playground-c3 -t upload +``` diff --git a/examples/rf-transceiver/rf-transceiver.cpp b/examples/rf-transceiver/rf-transceiver.cpp index 8636b8b..06ee967 100644 --- a/examples/rf-transceiver/rf-transceiver.cpp +++ b/examples/rf-transceiver/rf-transceiver.cpp @@ -42,15 +42,12 @@ void setup() { homeGenie = HomeGenie::getInstance(); auto miniModule = homeGenie->getDefaultModule(); - // RCSwitch RF Transmitter auto rcsTransmitterConfig = new RCS::RFTransmitterConfig(CONFIG_RCSwitchTransmitterPin); auto rcsTransmitter = new RCS::RFTransmitter(rcsTransmitterConfig); homeGenie->addAPIHandler(new RCSwitchHandler(rcsTransmitter)); // TODO: homeGenie->addIOHandler(new RCS::RFReceiver()); - // TODO: auto propRawData = new ModuleParameter(IOEventPaths::Receiver_RawData); - // TODO: miniModule->properties.add(propRawData); homeGenie->begin(); diff --git a/examples/smart-sensor-display/io/BatterySensor.cpp b/examples/smart-sensor-display/io/BatterySensor.cpp new file mode 100644 index 0000000..982a11f --- /dev/null +++ b/examples/smart-sensor-display/io/BatterySensor.cpp @@ -0,0 +1,53 @@ +/* + * HomeGenie-Mini (c) 2018-2024 G-Labs + * + * + * This file is part of HomeGenie-Mini (HGM). + * + * HomeGenie-Mini is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * HomeGenie-Mini is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with HomeGenie-Mini. If not, see . + * + * + * Authors: + * - Generoso Martello + * + */ + +#include "BatterySensor.h" + +namespace IO { namespace Env { + + void BatterySensor::begin() { + pinMode(sensorPin, INPUT); + } + void BatterySensor::loop() { + float gpioMax = 4096; + float gpioV = 3.3f; + float batteryVMax = 3.7f; + const float conversionFactor = gpioV / gpioMax * 3.0f; + float adv = analogReadMilliVolts(sensorPin); + float v = adv * conversionFactor; //adv * (gpioV / gpioMax) * batteryV; + float batteryLevel = v / batteryVMax * 100; + if (lastBatteryLevel != batteryLevel) { + Logger::info("@%s [%s %.2f]", BATTERY_SENSOR_NS_PREFIX, (IOEventPaths::Status_Battery), batteryLevel); + sendEvent((const uint8_t*)(IOEventPaths::Status_Battery), (float*)&batteryLevel, IOEventDataType::Float); + lastBatteryLevel = batteryLevel; + } + + if (batteryLevel > 100) { + // Charging + Service::PowerManager::setActive(); + } + } + +}} diff --git a/examples/smart-sensor-display/io/BatterySensor.h b/examples/smart-sensor-display/io/BatterySensor.h new file mode 100644 index 0000000..018c33d --- /dev/null +++ b/examples/smart-sensor-display/io/BatterySensor.h @@ -0,0 +1,57 @@ +/* + * HomeGenie-Mini (c) 2018-2024 G-Labs + * + * + * This file is part of HomeGenie-Mini (HGM). + * + * HomeGenie-Mini is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * HomeGenie-Mini is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with HomeGenie-Mini. If not, see . + * + * + * Authors: + * - Generoso Martello + * + */ + +#ifndef HOMEGENIE_MINI_BATTERYSENSOR_H +#define HOMEGENIE_MINI_BATTERYSENSOR_H + +#include + +#include "../configuration.h" + +#define BATTERY_SENSOR_NS_PREFIX "IO::Env::BatterySensor" + +namespace IO { namespace Env { + + class BatterySensor: Task, public IIOEventSender { + public: + BatterySensor(uint8_t analogPin) { + setLoopInterval(5000); // update every 15 seconds + sensorPin = analogPin; + } + void setModule(Module* m) override { + IIOEventSender::setModule(m); + auto statusBattery = new ModuleParameter(IOEventPaths::Status_Battery); + m->properties.add(statusBattery); + } + void begin() override; + void loop() override; + private: + uint8_t sensorPin; + float lastBatteryLevel = 0; + }; + +}} + +#endif //HOMEGENIE_MINI_BATTERYSENSOR_H diff --git a/examples/smart-sensor-display/io/DHTxx.cpp b/examples/smart-sensor-display/io/DHTxx.cpp index 3365b30..951fbf1 100644 --- a/examples/smart-sensor-display/io/DHTxx.cpp +++ b/examples/smart-sensor-display/io/DHTxx.cpp @@ -45,26 +45,31 @@ namespace IO { namespace Env { // signal value changes if (currentData.temperature != t) { Logger::info("@%s [%s %0.2f]", DHTXX_NS_PREFIX, (IOEventPaths::Sensor_Temperature), currentData.temperature); - sendEvent(domain.c_str(), address.c_str(), (const uint8_t*)(IOEventPaths::Sensor_Temperature), (float_t *)¤tData.temperature, SensorTemperature); + sendEvent((const uint8_t*)(IOEventPaths::Sensor_Temperature), (float_t *)¤tData.temperature, SensorTemperature); } if (currentData.humidity != h) { Logger::info("@%s [%s %0.2f]", DHTXX_NS_PREFIX, (IOEventPaths::Sensor_Humidity), currentData.humidity); - sendEvent(domain.c_str(), address.c_str(), (const uint8_t*)(IOEventPaths::Sensor_Humidity), (float_t *)¤tData.humidity, SensorHumidity); + sendEvent((const uint8_t*)(IOEventPaths::Sensor_Humidity), (float_t *)¤tData.humidity, SensorHumidity); } Logger::verbose(" > %s::loop() << END", DHTXX_NS_PREFIX); + setLoopInterval(SENSOR_SAMPLING_RATE); } /// Read temperature and humidity values from one DHTxx. void DHTxx::readSensorData() { - dht->read(); - float h = dht->getHumidity(); - float t = dht->getTemperature(); - if (h != DHT_READ_ERROR && t != DHT_READ_ERROR) { - currentData.temperature = t; - currentData.humidity = h; - } else { + uint8_t attempts = 5; + while (attempts > 0) { + dht->read(); + float h = dht->getHumidity(); + float t = dht->getTemperature(); + if (h != DHT_READ_ERROR && t != DHT_READ_ERROR) { + currentData.temperature = t; + currentData.humidity = h; + break; + } // TODO: report error reading sensor data + attempts--; } } diff --git a/examples/smart-sensor-display/io/DHTxx.h b/examples/smart-sensor-display/io/DHTxx.h index 3f7a9dc..8e1aa54 100644 --- a/examples/smart-sensor-display/io/DHTxx.h +++ b/examples/smart-sensor-display/io/DHTxx.h @@ -57,15 +57,20 @@ namespace IO { namespace Env { class DHTxx : Task, public IIOEventSender { public: DHTxx(uint8_t dhtType) { - setLoopInterval(SENSOR_SAMPLING_RATE); + setLoopInterval(2000); // initial reading delay dht = new DHTNEW(inputPin); dht->setType(dhtType); } + void setModule(Module* m) override { + IIOEventSender::setModule(m); + auto temperature = new ModuleParameter(IOEventPaths::Sensor_Temperature); + m->properties.add(temperature); + auto humidity = new ModuleParameter(IOEventPaths::Sensor_Humidity); + m->properties.add(humidity); + } void begin() override; void loop() override; private: - String domain = IOEventDomains::HomeAutomation_HomeGenie; - String address = CONFIG_BUILTIN_MODULE_ADDRESS; // Set DHTxx pin number uint8_t inputPin = CONFIG_DHTxx_DataPin; // Temperature and humidity sensor diff --git a/examples/smart-sensor-display/io/LightSensor.cpp b/examples/smart-sensor-display/io/LightSensor.cpp index 052c949..2845b35 100644 --- a/examples/smart-sensor-display/io/LightSensor.cpp +++ b/examples/smart-sensor-display/io/LightSensor.cpp @@ -41,7 +41,7 @@ namespace IO { namespace Env { if (lightLevel != currentLevel) { currentLevel = lightLevel; Logger::info("@%s [%s %d]", LIGHTSENSOR_NS_PREFIX, (IOEventPaths::Sensor_Luminance), currentLevel); - sendEvent(domain.c_str(), address.c_str(), (const uint8_t*)(IOEventPaths::Sensor_Luminance), (uint16_t *)¤tLevel, SensorLight); + sendEvent((const uint8_t*)(IOEventPaths::Sensor_Luminance), (uint16_t *)¤tLevel, SensorLight); } } diff --git a/examples/smart-sensor-display/io/LightSensor.h b/examples/smart-sensor-display/io/LightSensor.h index c9de39d..e9a7d8e 100644 --- a/examples/smart-sensor-display/io/LightSensor.h +++ b/examples/smart-sensor-display/io/LightSensor.h @@ -46,13 +46,17 @@ namespace IO { namespace Env { LightSensor() { setLoopInterval(LIGHTSENSOR_SAMPLING_RATE); } + void setModule(Module* m) override { + IIOEventSender::setModule(m); + auto luminance = new ModuleParameter(IOEventPaths::Sensor_Luminance); + m->properties.add(luminance); + } + void begin() override; void loop() override; void setInputPin(uint8_t number); uint16_t getLightLevel(); private: - String domain = IOEventDomains::HomeAutomation_HomeGenie; - String address = CONFIG_BUILTIN_MODULE_ADDRESS; uint8_t inputPin = CONFIG_LightSensorPin; // Analogic input pin A0 (0) uint16_t currentLevel = 0; }; diff --git a/examples/smart-sensor-display/io/MotionSensor.cpp b/examples/smart-sensor-display/io/MotionSensor.cpp new file mode 100644 index 0000000..072dc43 --- /dev/null +++ b/examples/smart-sensor-display/io/MotionSensor.cpp @@ -0,0 +1,30 @@ +// +// Created by gene on 19/02/24. +// + +#include "MotionSensor.h" + +namespace IO { namespace Env { + void MotionSensor::begin() { + pinMode(sensorPin, INPUT_PULLUP); + Logger::info("| ✔ %s (PIN=%d)", MOTION_SENSOR_NS_PREFIX, sensorPin); + } + void MotionSensor::loop() { + int motionValue = digitalRead(sensorPin); + if (motionValue == HIGH && !motionDetected) { + // MOTION DETECTED + motionDetected = true; + Logger::info("@%s [%s %d]", MOTION_SENSOR_NS_PREFIX, (IOEventPaths::Sensor_MotionDetect), motionValue); + sendEvent((const uint8_t*)(IOEventPaths::Sensor_MotionDetect), (int*)&motionValue, IOEventDataType::Number); + PowerManager::setActive(); + } else if (motionValue == LOW && motionDetected) { + // MOTION CLEAR + motionDetected = false; + Logger::info("@%s [%s %d]", MOTION_SENSOR_NS_PREFIX, (IOEventPaths::Sensor_MotionDetect), motionValue); + sendEvent((const uint8_t*)(IOEventPaths::Sensor_MotionDetect), (int*)&motionValue, IOEventDataType::Number); + } else if (motionDetected) { + // MOTION ACTIVE + PowerManager::setActive(); + } + } +}} // Env diff --git a/examples/smart-sensor-display/io/MotionSensor.h b/examples/smart-sensor-display/io/MotionSensor.h new file mode 100644 index 0000000..23d7197 --- /dev/null +++ b/examples/smart-sensor-display/io/MotionSensor.h @@ -0,0 +1,39 @@ +// +// Created by gene on 19/02/24. +// + +#ifndef HOMEGENIE_MINI_MOTIONSENSOR_H +#define HOMEGENIE_MINI_MOTIONSENSOR_H + +#include + +#include "../configuration.h" + +#define MOTION_SENSOR_NS_PREFIX "IO::Env::MotionSensor" + +namespace IO { namespace Env { + + using namespace Service; + + class MotionSensor : Task, public IIOEventSender { + public: + MotionSensor(uint8_t pin) { + setLoopInterval(200); + sensorPin = pin; + } + void setModule(Module* m) override { + IIOEventSender::setModule(m); + auto motionDetect = new ModuleParameter(IOEventPaths::Sensor_MotionDetect); + m->properties.add(motionDetect); + } + void begin() override; + void loop() override; + + private: + uint8_t sensorPin = 0; + bool motionDetected = false; + }; + +}} // Env + +#endif //HOMEGENIE_MINI_MOTIONSENSOR_H diff --git a/examples/smart-sensor-display/io/QMI8658.cpp b/examples/smart-sensor-display/io/QMI8658.cpp new file mode 100644 index 0000000..7e4fdcf --- /dev/null +++ b/examples/smart-sensor-display/io/QMI8658.cpp @@ -0,0 +1,647 @@ + +#include "QMI8658.h" + +#define QMI8658_SLAVE_ADDR_L 0x6a +#define QMI8658_SLAVE_ADDR_H 0x6b +#define QMI8658_printf printf + +#define QMI8658_UINT_MG_DPS + +enum +{ + AXIS_X = 0, + AXIS_Y = 1, + AXIS_Z = 2, + + AXIS_TOTAL +}; + +typedef struct +{ + short sign[AXIS_TOTAL]; + unsigned short map[AXIS_TOTAL]; +} qst_imu_layout; + +static unsigned short acc_lsb_div = 0; +static unsigned short gyro_lsb_div = 0; +static unsigned short ae_q_lsb_div = (1 << 14); +static unsigned short ae_v_lsb_div = (1 << 10); +static unsigned int imu_timestamp = 0; +static struct QMI8658Config QMI8658_config; +static unsigned char QMI8658_slave_addr = QMI8658_SLAVE_ADDR_L; + +void DEV_I2C_Write_Byte(uint8_t addr, uint8_t reg, uint8_t Value) +{ + Wire.beginTransmission(addr); + Wire.write(reg); + Wire.write(Value); + Wire.endTransmission(); +} + +void DEV_I2C_Read_nByte(uint8_t addr, uint8_t reg, uint8_t *pData, uint32_t Len) +{ + Wire.beginTransmission(addr); + Wire.write(reg); + Wire.endTransmission(); + + Wire.requestFrom(addr, Len); + + uint8_t i = 0; + for(i = 0; i < Len; i++) { + pData[i] = Wire.read(); + } + Wire.endTransmission(); +} + +unsigned char QMI8658_write_reg(unsigned char reg, unsigned char value) +{ + unsigned char ret = 0; + unsigned int retry = 0; + + while ((!ret) && (retry++ < 5)) + { + DEV_I2C_Write_Byte(QMI8658_slave_addr, reg, value); + } + return ret; +} + +unsigned char QMI8658_write_regs(unsigned char reg, unsigned char *value, unsigned char len) +{ + int i, ret; + + for (i = 0; i < len; i++) + { + ret = QMI8658_write_reg(reg + i, value[i]); + } + + return ret; +} + +unsigned char QMI8658_read_reg(unsigned char reg, unsigned char *buf, unsigned short len) +{ + unsigned char ret = 0; + unsigned int retry = 0; + DEV_I2C_Read_nByte(QMI8658_slave_addr, reg, buf, len); + + return ret; +} + +#if 0 +static qst_imu_layout imu_map; + +void QMI8658_set_layout(short layout) +{ + if(layout == 0) + { + imu_map.sign[AXIS_X] = 1; + imu_map.sign[AXIS_Y] = 1; + imu_map.sign[AXIS_Z] = 1; + imu_map.map[AXIS_X] = AXIS_X; + imu_map.map[AXIS_Y] = AXIS_Y; + imu_map.map[AXIS_Z] = AXIS_Z; + } + else if(layout == 1) + { + imu_map.sign[AXIS_X] = -1; + imu_map.sign[AXIS_Y] = 1; + imu_map.sign[AXIS_Z] = 1; + imu_map.map[AXIS_X] = AXIS_Y; + imu_map.map[AXIS_Y] = AXIS_X; + imu_map.map[AXIS_Z] = AXIS_Z; + } + else if(layout == 2) + { + imu_map.sign[AXIS_X] = -1; + imu_map.sign[AXIS_Y] = -1; + imu_map.sign[AXIS_Z] = 1; + imu_map.map[AXIS_X] = AXIS_X; + imu_map.map[AXIS_Y] = AXIS_Y; + imu_map.map[AXIS_Z] = AXIS_Z; + } + else if(layout == 3) + { + imu_map.sign[AXIS_X] = 1; + imu_map.sign[AXIS_Y] = -1; + imu_map.sign[AXIS_Z] = 1; + imu_map.map[AXIS_X] = AXIS_Y; + imu_map.map[AXIS_Y] = AXIS_X; + imu_map.map[AXIS_Z] = AXIS_Z; + } + else if(layout == 4) + { + imu_map.sign[AXIS_X] = -1; + imu_map.sign[AXIS_Y] = 1; + imu_map.sign[AXIS_Z] = -1; + imu_map.map[AXIS_X] = AXIS_X; + imu_map.map[AXIS_Y] = AXIS_Y; + imu_map.map[AXIS_Z] = AXIS_Z; + } + else if(layout == 5) + { + imu_map.sign[AXIS_X] = 1; + imu_map.sign[AXIS_Y] = 1; + imu_map.sign[AXIS_Z] = -1; + imu_map.map[AXIS_X] = AXIS_Y; + imu_map.map[AXIS_Y] = AXIS_X; + imu_map.map[AXIS_Z] = AXIS_Z; + } + else if(layout == 6) + { + imu_map.sign[AXIS_X] = 1; + imu_map.sign[AXIS_Y] = -1; + imu_map.sign[AXIS_Z] = -1; + imu_map.map[AXIS_X] = AXIS_X; + imu_map.map[AXIS_Y] = AXIS_Y; + imu_map.map[AXIS_Z] = AXIS_Z; + } + else if(layout == 7) + { + imu_map.sign[AXIS_X] = -1; + imu_map.sign[AXIS_Y] = -1; + imu_map.sign[AXIS_Z] = -1; + imu_map.map[AXIS_X] = AXIS_Y; + imu_map.map[AXIS_Y] = AXIS_X; + imu_map.map[AXIS_Z] = AXIS_Z; + } + else + { + imu_map.sign[AXIS_X] = 1; + imu_map.sign[AXIS_Y] = 1; + imu_map.sign[AXIS_Z] = 1; + imu_map.map[AXIS_X] = AXIS_X; + imu_map.map[AXIS_Y] = AXIS_Y; + imu_map.map[AXIS_Z] = AXIS_Z; + } +} +#endif + +void QMI8658_config_acc(enum QMI8658_AccRange range, enum QMI8658_AccOdr odr, enum QMI8658_LpfConfig lpfEnable, enum QMI8658_StConfig stEnable) +{ + unsigned char ctl_dada; + + switch (range) + { + case QMI8658AccRange_2g: + acc_lsb_div = (1 << 14); + break; + case QMI8658AccRange_4g: + acc_lsb_div = (1 << 13); + break; + case QMI8658AccRange_8g: + acc_lsb_div = (1 << 12); + break; + case QMI8658AccRange_16g: + acc_lsb_div = (1 << 11); + break; + default: + range = QMI8658AccRange_8g; + acc_lsb_div = (1 << 12); + } + if (stEnable == QMI8658St_Enable) + ctl_dada = (unsigned char)range | (unsigned char)odr | 0x80; + else + ctl_dada = (unsigned char)range | (unsigned char)odr; + + QMI8658_write_reg(QMI8658Register_Ctrl2, ctl_dada); + // set LPF & HPF + QMI8658_read_reg(QMI8658Register_Ctrl5, &ctl_dada, 1); + ctl_dada &= 0xf0; + if (lpfEnable == QMI8658Lpf_Enable) + { + ctl_dada |= A_LSP_MODE_3; + ctl_dada |= 0x01; + } + else + { + ctl_dada &= ~0x01; + } + ctl_dada = 0x00; + QMI8658_write_reg(QMI8658Register_Ctrl5, ctl_dada); + // set LPF & HPF +} + +void QMI8658_config_gyro(enum QMI8658_GyrRange range, enum QMI8658_GyrOdr odr, enum QMI8658_LpfConfig lpfEnable, enum QMI8658_StConfig stEnable) +{ + // Set the CTRL3 register to configure dynamic range and ODR + unsigned char ctl_dada; + + // Store the scale factor for use when processing raw data + switch (range) + { + case QMI8658GyrRange_32dps: + gyro_lsb_div = 1024; + break; + case QMI8658GyrRange_64dps: + gyro_lsb_div = 512; + break; + case QMI8658GyrRange_128dps: + gyro_lsb_div = 256; + break; + case QMI8658GyrRange_256dps: + gyro_lsb_div = 128; + break; + case QMI8658GyrRange_512dps: + gyro_lsb_div = 64; + break; + case QMI8658GyrRange_1024dps: + gyro_lsb_div = 32; + break; + case QMI8658GyrRange_2048dps: + gyro_lsb_div = 16; + break; + case QMI8658GyrRange_4096dps: + gyro_lsb_div = 8; + break; + default: + range = QMI8658GyrRange_512dps; + gyro_lsb_div = 64; + break; + } + + if (stEnable == QMI8658St_Enable) + ctl_dada = (unsigned char)range | (unsigned char)odr | 0x80; + else + ctl_dada = (unsigned char)range | (unsigned char)odr; + QMI8658_write_reg(QMI8658Register_Ctrl3, ctl_dada); + + // Conversion from degrees/s to rad/s if necessary + // set LPF & HPF + QMI8658_read_reg(QMI8658Register_Ctrl5, &ctl_dada, 1); + ctl_dada &= 0x0f; + if (lpfEnable == QMI8658Lpf_Enable) + { + ctl_dada |= G_LSP_MODE_3; + ctl_dada |= 0x10; + } + else + { + ctl_dada &= ~0x10; + } + ctl_dada = 0x00; + QMI8658_write_reg(QMI8658Register_Ctrl5, ctl_dada); + // set LPF & HPF +} + +void QMI8658_config_mag(enum QMI8658_MagDev device, enum QMI8658_MagOdr odr) +{ + QMI8658_write_reg(QMI8658Register_Ctrl4, device | odr); +} + +void QMI8658_config_ae(enum QMI8658_AeOdr odr) +{ + // QMI8658_config_acc(QMI8658AccRange_8g, AccOdr_1000Hz, Lpf_Enable, St_Enable); + // QMI8658_config_gyro(QMI8658GyrRange_2048dps, GyrOdr_1000Hz, Lpf_Enable, St_Enable); + QMI8658_config_acc(QMI8658_config.accRange, QMI8658_config.accOdr, QMI8658Lpf_Enable, QMI8658St_Disable); + QMI8658_config_gyro(QMI8658_config.gyrRange, QMI8658_config.gyrOdr, QMI8658Lpf_Enable, QMI8658St_Disable); + QMI8658_config_mag(QMI8658_config.magDev, QMI8658_config.magOdr); + QMI8658_write_reg(QMI8658Register_Ctrl6, odr); +} + +unsigned char QMI8658_readStatus0(void) +{ + unsigned char status[2]; + + QMI8658_read_reg(QMI8658Register_Status0, status, sizeof(status)); + // printf("status[0x%x 0x%x]\n",status[0],status[1]); + + return status[0]; +} +/*! + * \brief Blocking read of data status register 1 (::QMI8658Register_Status1). + * \returns Status byte \see STATUS1 for flag definitions. + */ +unsigned char QMI8658_readStatus1(void) +{ + unsigned char status; + + QMI8658_read_reg(QMI8658Register_Status1, &status, sizeof(status)); + + return status; +} + +float QMI8658_readTemp(void) +{ + unsigned char buf[2]; + short temp = 0; + float temp_f = 0; + + QMI8658_read_reg(QMI8658Register_Tempearture_L, buf, 2); + temp = ((short)buf[1] << 8) | buf[0]; + temp_f = (float)temp / 256.0f; + + return temp_f; +} + +void QMI8658_read_acc_xyz(float acc_xyz[3]) +{ + unsigned char buf_reg[6]; + short raw_acc_xyz[3]; + + QMI8658_read_reg(QMI8658Register_Ax_L, buf_reg, 6); // 0x19, 25 + raw_acc_xyz[0] = (short)((unsigned short)(buf_reg[1] << 8) | (buf_reg[0])); + raw_acc_xyz[1] = (short)((unsigned short)(buf_reg[3] << 8) | (buf_reg[2])); + raw_acc_xyz[2] = (short)((unsigned short)(buf_reg[5] << 8) | (buf_reg[4])); + + acc_xyz[0] = (raw_acc_xyz[0] * ONE_G) / acc_lsb_div; + acc_xyz[1] = (raw_acc_xyz[1] * ONE_G) / acc_lsb_div; + acc_xyz[2] = (raw_acc_xyz[2] * ONE_G) / acc_lsb_div; + + // QMI8658_printf("fis210x acc: %f %f %f\n", acc_xyz[0], acc_xyz[1], acc_xyz[2]); +} + +void QMI8658_read_gyro_xyz(float gyro_xyz[3]) +{ + unsigned char buf_reg[6]; + short raw_gyro_xyz[3]; + + QMI8658_read_reg(QMI8658Register_Gx_L, buf_reg, 6); // 0x1f, 31 + raw_gyro_xyz[0] = (short)((unsigned short)(buf_reg[1] << 8) | (buf_reg[0])); + raw_gyro_xyz[1] = (short)((unsigned short)(buf_reg[3] << 8) | (buf_reg[2])); + raw_gyro_xyz[2] = (short)((unsigned short)(buf_reg[5] << 8) | (buf_reg[4])); + + gyro_xyz[0] = (raw_gyro_xyz[0] * 1.0f) / gyro_lsb_div; + gyro_xyz[1] = (raw_gyro_xyz[1] * 1.0f) / gyro_lsb_div; + gyro_xyz[2] = (raw_gyro_xyz[2] * 1.0f) / gyro_lsb_div; + + // QMI8658_printf("fis210x gyro: %f %f %f\n", gyro_xyz[0], gyro_xyz[1], gyro_xyz[2]); +} + +void QMI8658_read_xyz(float acc[3], float gyro[3], unsigned int *tim_count) +{ + unsigned char buf_reg[12]; + short raw_acc_xyz[3]; + short raw_gyro_xyz[3]; + // float acc_t[3]; + // float gyro_t[3]; + + if (tim_count) + { + unsigned char buf[3]; + unsigned int timestamp; + QMI8658_read_reg(QMI8658Register_Timestamp_L, buf, 3); // 0x18 24 + timestamp = (unsigned int)(((unsigned int)buf[2] << 16) | ((unsigned int)buf[1] << 8) | buf[0]); + if (timestamp > imu_timestamp) + imu_timestamp = timestamp; + else + imu_timestamp = (timestamp + 0x1000000 - imu_timestamp); + + *tim_count = imu_timestamp; + } + + QMI8658_read_reg(QMI8658Register_Ax_L, buf_reg, 12); // 0x19, 25 + raw_acc_xyz[0] = (short)((unsigned short)(buf_reg[1] << 8) | (buf_reg[0])); + raw_acc_xyz[1] = (short)((unsigned short)(buf_reg[3] << 8) | (buf_reg[2])); + raw_acc_xyz[2] = (short)((unsigned short)(buf_reg[5] << 8) | (buf_reg[4])); + + raw_gyro_xyz[0] = (short)((unsigned short)(buf_reg[7] << 8) | (buf_reg[6])); + raw_gyro_xyz[1] = (short)((unsigned short)(buf_reg[9] << 8) | (buf_reg[8])); + raw_gyro_xyz[2] = (short)((unsigned short)(buf_reg[11] << 8) | (buf_reg[10])); + +#if defined(QMI8658_UINT_MG_DPS) + // mg + acc[AXIS_X] = (float)(raw_acc_xyz[AXIS_X] * 1000.0f) / acc_lsb_div; + acc[AXIS_Y] = (float)(raw_acc_xyz[AXIS_Y] * 1000.0f) / acc_lsb_div; + acc[AXIS_Z] = (float)(raw_acc_xyz[AXIS_Z] * 1000.0f) / acc_lsb_div; +#else + // m/s2 + acc[AXIS_X] = (float)(raw_acc_xyz[AXIS_X] * ONE_G) / acc_lsb_div; + acc[AXIS_Y] = (float)(raw_acc_xyz[AXIS_Y] * ONE_G) / acc_lsb_div; + acc[AXIS_Z] = (float)(raw_acc_xyz[AXIS_Z] * ONE_G) / acc_lsb_div; +#endif + // acc[AXIS_X] = imu_map.sign[AXIS_X]*acc_t[imu_map.map[AXIS_X]]; + // acc[AXIS_Y] = imu_map.sign[AXIS_Y]*acc_t[imu_map.map[AXIS_Y]]; + // acc[AXIS_Z] = imu_map.sign[AXIS_Z]*acc_t[imu_map.map[AXIS_Z]]; + +#if defined(QMI8658_UINT_MG_DPS) + // dps + gyro[0] = (float)(raw_gyro_xyz[0] * 1.0f) / gyro_lsb_div; + gyro[1] = (float)(raw_gyro_xyz[1] * 1.0f) / gyro_lsb_div; + gyro[2] = (float)(raw_gyro_xyz[2] * 1.0f) / gyro_lsb_div; +#else + // rad/s + gyro[AXIS_X] = (float)(raw_gyro_xyz[AXIS_X] * 0.01745f) / gyro_lsb_div; // *pi/180 + gyro[AXIS_Y] = (float)(raw_gyro_xyz[AXIS_Y] * 0.01745f) / gyro_lsb_div; + gyro[AXIS_Z] = (float)(raw_gyro_xyz[AXIS_Z] * 0.01745f) / gyro_lsb_div; +#endif + // gyro[AXIS_X] = imu_map.sign[AXIS_X]*gyro_t[imu_map.map[AXIS_X]]; + // gyro[AXIS_Y] = imu_map.sign[AXIS_Y]*gyro_t[imu_map.map[AXIS_Y]]; + // gyro[AXIS_Z] = imu_map.sign[AXIS_Z]*gyro_t[imu_map.map[AXIS_Z]]; +} + +void QMI8658_read_xyz_raw(short raw_acc_xyz[3], short raw_gyro_xyz[3], unsigned int *tim_count) +{ + unsigned char buf_reg[12]; + + if (tim_count) + { + unsigned char buf[3]; + unsigned int timestamp; + QMI8658_read_reg(QMI8658Register_Timestamp_L, buf, 3); // 0x18 24 + timestamp = (unsigned int)(((unsigned int)buf[2] << 16) | ((unsigned int)buf[1] << 8) | buf[0]); + if (timestamp > imu_timestamp) + imu_timestamp = timestamp; + else + imu_timestamp = (timestamp + 0x1000000 - imu_timestamp); + + *tim_count = imu_timestamp; + } + QMI8658_read_reg(QMI8658Register_Ax_L, buf_reg, 12); // 0x19, 25 + + raw_acc_xyz[0] = (short)((unsigned short)(buf_reg[1] << 8) | (buf_reg[0])); + raw_acc_xyz[1] = (short)((unsigned short)(buf_reg[3] << 8) | (buf_reg[2])); + raw_acc_xyz[2] = (short)((unsigned short)(buf_reg[5] << 8) | (buf_reg[4])); + + raw_gyro_xyz[0] = (short)((unsigned short)(buf_reg[7] << 8) | (buf_reg[6])); + raw_gyro_xyz[1] = (short)((unsigned short)(buf_reg[9] << 8) | (buf_reg[8])); + raw_gyro_xyz[2] = (short)((unsigned short)(buf_reg[11] << 8) | (buf_reg[10])); +} + +void QMI8658_read_ae(float quat[4], float velocity[3]) +{ + unsigned char buf_reg[14]; + short raw_q_xyz[4]; + short raw_v_xyz[3]; + + QMI8658_read_reg(QMI8658Register_Q1_L, buf_reg, 14); + raw_q_xyz[0] = (short)((unsigned short)(buf_reg[1] << 8) | (buf_reg[0])); + raw_q_xyz[1] = (short)((unsigned short)(buf_reg[3] << 8) | (buf_reg[2])); + raw_q_xyz[2] = (short)((unsigned short)(buf_reg[5] << 8) | (buf_reg[4])); + raw_q_xyz[3] = (short)((unsigned short)(buf_reg[7] << 8) | (buf_reg[6])); + + raw_v_xyz[1] = (short)((unsigned short)(buf_reg[9] << 8) | (buf_reg[8])); + raw_v_xyz[2] = (short)((unsigned short)(buf_reg[11] << 8) | (buf_reg[10])); + raw_v_xyz[2] = (short)((unsigned short)(buf_reg[13] << 8) | (buf_reg[12])); + + quat[0] = (float)(raw_q_xyz[0] * 1.0f) / ae_q_lsb_div; + quat[1] = (float)(raw_q_xyz[1] * 1.0f) / ae_q_lsb_div; + quat[2] = (float)(raw_q_xyz[2] * 1.0f) / ae_q_lsb_div; + quat[3] = (float)(raw_q_xyz[3] * 1.0f) / ae_q_lsb_div; + + velocity[0] = (float)(raw_v_xyz[0] * 1.0f) / ae_v_lsb_div; + velocity[1] = (float)(raw_v_xyz[1] * 1.0f) / ae_v_lsb_div; + velocity[2] = (float)(raw_v_xyz[2] * 1.0f) / ae_v_lsb_div; +} + +void QMI8658_enableWakeOnMotion(void) +{ + unsigned char womCmd[3]; + enum QMI8658_Interrupt interrupt = QMI8658_Int1; + enum QMI8658_InterruptState initialState = QMI8658State_low; + enum QMI8658_WakeOnMotionThreshold threshold = QMI8658WomThreshold_low; + unsigned char blankingTime = 0x00; + const unsigned char blankingTimeMask = 0x3F; + + QMI8658_enableSensors(QMI8658_CTRL7_DISABLE_ALL); + QMI8658_config_acc(QMI8658AccRange_2g, QMI8658AccOdr_LowPower_21Hz, QMI8658Lpf_Disable, QMI8658St_Disable); + + womCmd[0] = QMI8658Register_Cal1_L; // WoM Threshold: absolute value in mg (with 1mg/LSB resolution) + womCmd[1] = threshold; + womCmd[2] = (unsigned char)interrupt | (unsigned char)initialState | (blankingTime & blankingTimeMask); + QMI8658_write_reg(QMI8658Register_Cal1_L, womCmd[1]); + QMI8658_write_reg(QMI8658Register_Cal1_H, womCmd[2]); + + // QMI8658_doCtrl9Command(Ctrl9_ConfigureWakeOnMotion); + QMI8658_enableSensors(QMI8658_CTRL7_ACC_ENABLE); + // while(1) + //{ + // QMI8658_read_reg(QMI8658Register_Status1,&womCmd[0],1); + // if(womCmd[0]&0x01) + // break; + // } +} + +void QMI8658_disableWakeOnMotion(void) +{ + QMI8658_enableSensors(QMI8658_CTRL7_DISABLE_ALL); + QMI8658_write_reg(QMI8658Register_Cal1_L, 0); + // QMI8658_doCtrl9Command(Ctrl9_ConfigureWakeOnMotion); +} + +void QMI8658_enableSensors(unsigned char enableFlags) +{ + if (enableFlags & QMI8658_CONFIG_AE_ENABLE) + { + enableFlags |= QMI8658_CTRL7_ACC_ENABLE | QMI8658_CTRL7_GYR_ENABLE; + } + + QMI8658_write_reg(QMI8658Register_Ctrl7, enableFlags & QMI8658_CTRL7_ENABLE_MASK); +} + +void QMI8658_Config_apply(struct QMI8658Config const *config) +{ + unsigned char fisSensors = config->inputSelection; + + if (fisSensors & QMI8658_CONFIG_AE_ENABLE) + { + QMI8658_config_ae(config->aeOdr); + } + else + { + if (config->inputSelection & QMI8658_CONFIG_ACC_ENABLE) + { + QMI8658_config_acc(config->accRange, config->accOdr, QMI8658Lpf_Enable, QMI8658St_Disable); + } + if (config->inputSelection & QMI8658_CONFIG_GYR_ENABLE) + { + QMI8658_config_gyro(config->gyrRange, config->gyrOdr, QMI8658Lpf_Enable, QMI8658St_Disable); + } + } + + if (config->inputSelection & QMI8658_CONFIG_MAG_ENABLE) + { + QMI8658_config_mag(config->magDev, config->magOdr); + } + QMI8658_enableSensors(fisSensors); +} + +unsigned char QMI8658_init(void) +{ + unsigned char QMI8658_chip_id = 0x00; + unsigned char QMI8658_revision_id = 0x00; + unsigned char QMI8658_slave[2] = {QMI8658_SLAVE_ADDR_L, QMI8658_SLAVE_ADDR_H}; + unsigned char iCount = 0; + int retry = 0; + + while (iCount < 2) + { + QMI8658_slave_addr = QMI8658_slave[iCount]; + retry = 0; + + while ((QMI8658_chip_id != 0x05) && (retry++ < 5)) + { + + QMI8658_read_reg(QMI8658Register_WhoAmI, &QMI8658_chip_id, 1); + Serial.print("QMI8658Register_WhoAmI = "); + Serial.println(QMI8658_chip_id); +// QMI8658_printf("QMI8658Register_WhoAmI = 0x%x\n", QMI8658_chip_id); + } + if (QMI8658_chip_id == 0x05) + { + break; + } + iCount++; + } + QMI8658_read_reg(QMI8658Register_Revision, &QMI8658_revision_id, 1); + if (QMI8658_chip_id == 0x05) + { + Serial.print("QMI8658_init slave = "); + Serial.println(QMI8658_slave_addr); + Serial.print("nQMI8658Register_WhoAmI = "); + Serial.print(QMI8658_chip_id); + Serial.print(" "); + Serial.println(QMI8658_revision_id); +// QMI8658_printf("QMI8658_init slave=0x%x \r\nQMI8658Register_WhoAmI=0x%x 0x%x\n", QMI8658_slave_addr, QMI8658_chip_id, QMI8658_revision_id); + QMI8658_write_reg(QMI8658Register_Ctrl1, 0x60); + QMI8658_config.inputSelection = QMI8658_CONFIG_ACCGYR_ENABLE; // QMI8658_CONFIG_ACCGYR_ENABLE; + QMI8658_config.accRange = QMI8658AccRange_8g; + QMI8658_config.accOdr = QMI8658AccOdr_1000Hz; + QMI8658_config.gyrRange = QMI8658GyrRange_512dps; // QMI8658GyrRange_2048dps QMI8658GyrRange_1024dps + QMI8658_config.gyrOdr = QMI8658GyrOdr_1000Hz; + QMI8658_config.magOdr = QMI8658MagOdr_125Hz; + QMI8658_config.magDev = MagDev_AKM09918; + QMI8658_config.aeOdr = QMI8658AeOdr_128Hz; + + QMI8658_Config_apply(&QMI8658_config); + if (1) + { + unsigned char read_data = 0x00; + QMI8658_read_reg(QMI8658Register_Ctrl1, &read_data, 1); + Serial.print("QMI8658Register_Ctrl1 = "); + Serial.println(read_data); +// QMI8658_printf("QMI8658Register_Ctrl1=0x%x \n", read_data); + QMI8658_read_reg(QMI8658Register_Ctrl2, &read_data, 1); + Serial.print("QMI8658Register_Ctrl2 = "); + Serial.println(read_data); +// QMI8658_printf("QMI8658Register_Ctrl2=0x%x \n", read_data); + QMI8658_read_reg(QMI8658Register_Ctrl3, &read_data, 1); + Serial.print("QMI8658Register_Ctrl1 = "); + Serial.println(read_data); +// QMI8658_printf("QMI8658Register_Ctrl3=0x%x \n", read_data); + QMI8658_read_reg(QMI8658Register_Ctrl4, &read_data, 1); + Serial.print("QMI8658Register_Ctrl1 = "); + Serial.println(read_data); +// QMI8658_printf("QMI8658Register_Ctrl4=0x%x \n", read_data); + QMI8658_read_reg(QMI8658Register_Ctrl5, &read_data, 1); + Serial.print("QMI8658Register_Ctrl1 = "); + Serial.println(read_data); +// QMI8658_printf("QMI8658Register_Ctrl5=0x%x \n", read_data); + QMI8658_read_reg(QMI8658Register_Ctrl6, &read_data, 1); + Serial.print("QMI8658Register_Ctrl1 = "); + Serial.println(read_data); +// QMI8658_printf("QMI8658Register_Ctrl6=0x%x \n", read_data); + QMI8658_read_reg(QMI8658Register_Ctrl7, &read_data, 1); + Serial.print("QMI8658Register_Ctrl1 = "); + Serial.println(read_data); +// QMI8658_printf("QMI8658Register_Ctrl7=0x%x \n", read_data); + } + // QMI8658_set_layout(2); + return 1; + } + else + { + Serial.println("QMI8658_init fail"); + QMI8658_chip_id = 0; + return 0; + } + // return QMI8658_chip_id; +} diff --git a/examples/smart-sensor-display/io/QMI8658.h b/examples/smart-sensor-display/io/QMI8658.h new file mode 100644 index 0000000..43fbade --- /dev/null +++ b/examples/smart-sensor-display/io/QMI8658.h @@ -0,0 +1,491 @@ +#ifndef QMI8658_H +#define QMI8658_H + +#include "Config.h" +#include "Wire.h" + +#define DEV_SDA_PIN (6) +#define DEV_SCL_PIN (7) + +#ifndef M_PI +#define M_PI (3.14159265358979323846f) +#endif +#ifndef ONE_G +#define ONE_G (9.807f) +#endif + +#define QMI8658_CTRL7_DISABLE_ALL (0x0) +#define QMI8658_CTRL7_ACC_ENABLE (0x1) +#define QMI8658_CTRL7_GYR_ENABLE (0x2) +#define QMI8658_CTRL7_MAG_ENABLE (0x4) +#define QMI8658_CTRL7_AE_ENABLE (0x8) +#define QMI8658_CTRL7_GYR_SNOOZE_ENABLE (0x10) +#define QMI8658_CTRL7_ENABLE_MASK (0xF) + +#define QMI8658_CONFIG_ACC_ENABLE QMI8658_CTRL7_ACC_ENABLE +#define QMI8658_CONFIG_GYR_ENABLE QMI8658_CTRL7_GYR_ENABLE +#define QMI8658_CONFIG_MAG_ENABLE QMI8658_CTRL7_MAG_ENABLE +#define QMI8658_CONFIG_AE_ENABLE QMI8658_CTRL7_AE_ENABLE +#define QMI8658_CONFIG_ACCGYR_ENABLE (QMI8658_CONFIG_ACC_ENABLE | QMI8658_CONFIG_GYR_ENABLE) +#define QMI8658_CONFIG_ACCGYRMAG_ENABLE (QMI8658_CONFIG_ACC_ENABLE | QMI8658_CONFIG_GYR_ENABLE | QMI8658_CONFIG_MAG_ENABLE) +#define QMI8658_CONFIG_AEMAG_ENABLE (QMI8658_CONFIG_AE_ENABLE | QMI8658_CONFIG_MAG_ENABLE) + +#define QMI8658_STATUS1_CMD_DONE (0x01) +#define QMI8658_STATUS1_WAKEUP_EVENT (0x04) + +enum QMI8658Register +{ + /*! \brief FIS device identifier register. */ + QMI8658Register_WhoAmI = 0, // 0 + /*! \brief FIS hardware revision register. */ + QMI8658Register_Revision, // 1 + /*! \brief General and power management modes. */ + QMI8658Register_Ctrl1, // 2 + /*! \brief Accelerometer control. */ + QMI8658Register_Ctrl2, // 3 + /*! \brief Gyroscope control. */ + QMI8658Register_Ctrl3, // 4 + /*! \brief Magnetometer control. */ + QMI8658Register_Ctrl4, // 5 + /*! \brief Data processing settings. */ + QMI8658Register_Ctrl5, // 6 + /*! \brief AttitudeEngine control. */ + QMI8658Register_Ctrl6, // 7 + /*! \brief Sensor enabled status. */ + QMI8658Register_Ctrl7, // 8 + /*! \brief Reserved - do not write. */ + QMI8658Register_Ctrl8, // 9 + /*! \brief Host command register. */ + QMI8658Register_Ctrl9, // 10 + /*! \brief Calibration register 1 most significant byte. */ + QMI8658Register_Cal1_L = 11, + /*! \brief Calibration register 1 least significant byte. */ + QMI8658Register_Cal1_H, + /*! \brief Calibration register 2 most significant byte. */ + QMI8658Register_Cal2_L, + /*! \brief Calibration register 2 least significant byte. */ + QMI8658Register_Cal2_H, + /*! \brief Calibration register 3 most significant byte. */ + QMI8658Register_Cal3_L, + /*! \brief Calibration register 3 least significant byte. */ + QMI8658Register_Cal3_H, + /*! \brief Calibration register 4 most significant byte. */ + QMI8658Register_Cal4_L, + /*! \brief Calibration register 4 least significant byte. */ + QMI8658Register_Cal4_H, + /*! \brief FIFO control register. */ + QMI8658Register_FifoCtrl = 19, + /*! \brief FIFO data register. */ + QMI8658Register_FifoData, // 20 + /*! \brief FIFO status register. */ + QMI8658Register_FifoStatus, // 21 + /*! \brief Output data overrun and availability. */ + QMI8658Register_StatusInt = 45, + /*! \brief Output data overrun and availability. */ + QMI8658Register_Status0, + /*! \brief Miscellaneous status register. */ + QMI8658Register_Status1, + /*! \brief timestamp low. */ + QMI8658Register_Timestamp_L = 48, + /*! \brief timestamp low. */ + QMI8658Register_Timestamp_M, + /*! \brief timestamp low. */ + QMI8658Register_Timestamp_H, + /*! \brief tempearture low. */ + QMI8658Register_Tempearture_L = 51, + /*! \brief tempearture low. */ + QMI8658Register_Tempearture_H, + /*! \brief Accelerometer X axis least significant byte. */ + QMI8658Register_Ax_L = 53, + /*! \brief Accelerometer X axis most significant byte. */ + QMI8658Register_Ax_H, + /*! \brief Accelerometer Y axis least significant byte. */ + QMI8658Register_Ay_L, + /*! \brief Accelerometer Y axis most significant byte. */ + QMI8658Register_Ay_H, + /*! \brief Accelerometer Z axis least significant byte. */ + QMI8658Register_Az_L, + /*! \brief Accelerometer Z axis most significant byte. */ + QMI8658Register_Az_H, + /*! \brief Gyroscope X axis least significant byte. */ + QMI8658Register_Gx_L = 59, + /*! \brief Gyroscope X axis most significant byte. */ + QMI8658Register_Gx_H, + /*! \brief Gyroscope Y axis least significant byte. */ + QMI8658Register_Gy_L, + /*! \brief Gyroscope Y axis most significant byte. */ + QMI8658Register_Gy_H, + /*! \brief Gyroscope Z axis least significant byte. */ + QMI8658Register_Gz_L, + /*! \brief Gyroscope Z axis most significant byte. */ + QMI8658Register_Gz_H, + /*! \brief Magnetometer X axis least significant byte. */ + QMI8658Register_Mx_L = 65, + /*! \brief Magnetometer X axis most significant byte. */ + QMI8658Register_Mx_H, + /*! \brief Magnetometer Y axis least significant byte. */ + QMI8658Register_My_L, + /*! \brief Magnetometer Y axis most significant byte. */ + QMI8658Register_My_H, + /*! \brief Magnetometer Z axis least significant byte. */ + QMI8658Register_Mz_L, + /*! \brief Magnetometer Z axis most significant byte. */ + QMI8658Register_Mz_H, + /*! \brief Quaternion increment W least significant byte. */ + QMI8658Register_Q1_L = 73, + /*! \brief Quaternion increment W most significant byte. */ + QMI8658Register_Q1_H, + /*! \brief Quaternion increment X least significant byte. */ + QMI8658Register_Q2_L, + /*! \brief Quaternion increment X most significant byte. */ + QMI8658Register_Q2_H, + /*! \brief Quaternion increment Y least significant byte. */ + QMI8658Register_Q3_L, + /*! \brief Quaternion increment Y most significant byte. */ + QMI8658Register_Q3_H, + /*! \brief Quaternion increment Z least significant byte. */ + QMI8658Register_Q4_L, + /*! \brief Quaternion increment Z most significant byte. */ + QMI8658Register_Q4_H, + /*! \brief Velocity increment X least significant byte. */ + QMI8658Register_Dvx_L = 81, + /*! \brief Velocity increment X most significant byte. */ + QMI8658Register_Dvx_H, + /*! \brief Velocity increment Y least significant byte. */ + QMI8658Register_Dvy_L, + /*! \brief Velocity increment Y most significant byte. */ + QMI8658Register_Dvy_H, + /*! \brief Velocity increment Z least significant byte. */ + QMI8658Register_Dvz_L, + /*! \brief Velocity increment Z most significant byte. */ + QMI8658Register_Dvz_H, + /*! \brief AttitudeEngine reg1. */ + QMI8658Register_AeReg1 = 87, + /*! \brief AttitudeEngine overflow flags. */ + QMI8658Register_AeOverflow, + + QMI8658Register_I2CM_STATUS = 110 +}; + +enum QMI8658_Ois_Register +{ + /*-----------------------------*/ + /* Setup and Control Registers */ + /*-----------------------------*/ + /*! \brief SPI Endian Selection, and SPI 3/4 Wire */ + QMI8658_OIS_Reg_Ctrl1 = 0x02, // 2 [0x02] -- Dflt: 0x20 + /*! \brief Accelerometer control: ODR, Full Scale, Self Test */ + QMI8658_OIS_Reg_Ctrl2, // 3 [0x03] + /*! \brief Gyroscope control: ODR, Full Scale, Self Test */ + QMI8658_OIS_Reg_Ctrl3, // 4 [0x04] + /*! \brief Sensor Data Processing Settings */ + QMI8658_OIS_Reg_Ctrl5 = 0x06, // 6 [0x06] + /*! \brief Sensor enabled status: Enable Sensors */ + QMI8658_OIS_Reg_Ctrl7 = 0x08, // 8 [0x08] + /*-------------------*/ + /* Status Registers */ + /*-------------------*/ + /*! \brief Sensor Data Availability and Lock Register */ + QMI8658_OIS_Reg_StatusInt = 0x2D, // 45 [0x2D] + /*! \brief Output data overrun and availability */ + QMI8658_OIS_Reg_Status0 = 0x2E, // 46 [0x2E] + + /*-----------------------------------------------------*/ + /* OIS Sensor Data Output Registers. 16-bit 2's complement */ + /*-----------------------------------------------------*/ + /*! \brief Accelerometer X axis least significant byte */ + QMI8658_OIS_Reg_Ax_L = 0x33, // 53 [0x35] + /*! \brief Accelerometer X axis most significant byte */ + QMI8658_OIS_Reg_Ax_H, // 54 [0x36] + /*! \brief Accelerometer Y axis least significant byte */ + QMI8658_OIS_Reg_Ay_L, // 55 [0x37] + /*! \brief Accelerometer Y axis most significant byte */ + QMI8658_OIS_Reg_Ay_H, // 56 [0x38] + /*! \brief Accelerometer Z axis least significant byte */ + QMI8658_OIS_Reg_Az_L, // 57 [0x39] + /*! \brief Accelerometer Z axis most significant byte */ + QMI8658_OIS_Reg_Az_H, // 58 [0x3A] + + /*! \brief Gyroscope X axis least significant byte */ + QMI8658_OIS_Reg_Gx_L = 0x3B, // 59 [0x3B] + /*! \brief Gyroscope X axis most significant byte */ + QMI8658_OIS_Reg_Gx_H, // 60 [0x3C] + /*! \brief Gyroscope Y axis least significant byte */ + QMI8658_OIS_Reg_Gy_L, // 61 [0x3D] + /*! \brief Gyroscope Y axis most significant byte */ + QMI8658_OIS_Reg_Gy_H, // 62 [0x3E] + /*! \brief Gyroscope Z axis least significant byte */ + QMI8658_OIS_Reg_Gz_L, // 63 [0x3F] + /*! \brief Gyroscope Z axis most significant byte */ + QMI8658_OIS_Reg_Gz_H, // 64 [0x40] +}; + +enum QMI8658_Ctrl9Command +{ + QMI8658_Ctrl9_Cmd_NOP = 0X00, + QMI8658_Ctrl9_Cmd_GyroBias = 0X01, + QMI8658_Ctrl9_Cmd_Rqst_Sdi_Mod = 0X03, + QMI8658_Ctrl9_Cmd_WoM_Setting = 0x08, + QMI8658_Ctrl9_Cmd_AccelHostDeltaOffset = 0x09, + QMI8658_Ctrl9_Cmd_GyroHostDeltaOffset = 0x0A, + QMI8658_Ctrl9_Cmd_Dbg_WoM_Data_Enable = 0xF8, + +}; + +enum QMI8658_LpfConfig +{ + QMI8658Lpf_Disable, /*!< \brief Disable low pass filter. */ + QMI8658Lpf_Enable /*!< \brief Enable low pass filter. */ +}; + +enum QMI8658_HpfConfig +{ + QMI8658Hpf_Disable, /*!< \brief Disable high pass filter. */ + QMI8658Hpf_Enable /*!< \brief Enable high pass filter. */ +}; + +enum QMI8658_StConfig +{ + QMI8658St_Disable, /*!< \brief Disable high pass filter. */ + QMI8658St_Enable /*!< \brief Enable high pass filter. */ +}; + +enum QMI8658_LpfMode +{ + A_LSP_MODE_0 = 0x00 << 1, + A_LSP_MODE_1 = 0x01 << 1, + A_LSP_MODE_2 = 0x02 << 1, + A_LSP_MODE_3 = 0x03 << 1, + + G_LSP_MODE_0 = 0x00 << 5, + G_LSP_MODE_1 = 0x01 << 5, + G_LSP_MODE_2 = 0x02 << 5, + G_LSP_MODE_3 = 0x03 << 5 +}; + +enum QMI8658_AccRange +{ + QMI8658AccRange_2g = 0x00 << 4, /*!< \brief +/- 2g range */ + QMI8658AccRange_4g = 0x01 << 4, /*!< \brief +/- 4g range */ + QMI8658AccRange_8g = 0x02 << 4, /*!< \brief +/- 8g range */ + QMI8658AccRange_16g = 0x03 << 4 /*!< \brief +/- 16g range */ +}; + +enum QMI8658_AccOdr +{ + QMI8658AccOdr_8000Hz = 0x00, /*!< \brief High resolution 8000Hz output rate. */ + QMI8658AccOdr_4000Hz = 0x01, /*!< \brief High resolution 4000Hz output rate. */ + QMI8658AccOdr_2000Hz = 0x02, /*!< \brief High resolution 2000Hz output rate. */ + QMI8658AccOdr_1000Hz = 0x03, /*!< \brief High resolution 1000Hz output rate. */ + QMI8658AccOdr_500Hz = 0x04, /*!< \brief High resolution 500Hz output rate. */ + QMI8658AccOdr_250Hz = 0x05, /*!< \brief High resolution 250Hz output rate. */ + QMI8658AccOdr_125Hz = 0x06, /*!< \brief High resolution 125Hz output rate. */ + QMI8658AccOdr_62_5Hz = 0x07, /*!< \brief High resolution 62.5Hz output rate. */ + QMI8658AccOdr_31_25Hz = 0x08, /*!< \brief High resolution 31.25Hz output rate. */ + QMI8658AccOdr_LowPower_128Hz = 0x0c, /*!< \brief Low power 128Hz output rate. */ + QMI8658AccOdr_LowPower_21Hz = 0x0d, /*!< \brief Low power 21Hz output rate. */ + QMI8658AccOdr_LowPower_11Hz = 0x0e, /*!< \brief Low power 11Hz output rate. */ + QMI8658AccOdr_LowPower_3Hz = 0x0f /*!< \brief Low power 3Hz output rate. */ +}; + +enum QMI8658_GyrRange +{ + QMI8658GyrRange_32dps = 0 << 4, /*!< \brief +-32 degrees per second. */ + QMI8658GyrRange_64dps = 1 << 4, /*!< \brief +-64 degrees per second. */ + QMI8658GyrRange_128dps = 2 << 4, /*!< \brief +-128 degrees per second. */ + QMI8658GyrRange_256dps = 3 << 4, /*!< \brief +-256 degrees per second. */ + QMI8658GyrRange_512dps = 4 << 4, /*!< \brief +-512 degrees per second. */ + QMI8658GyrRange_1024dps = 5 << 4, /*!< \brief +-1024 degrees per second. */ + QMI8658GyrRange_2048dps = 6 << 4, /*!< \brief +-2048 degrees per second. */ + QMI8658GyrRange_4096dps = 7 << 4 /*!< \brief +-2560 degrees per second. */ +}; + +/*! + * \brief Gyroscope output rate configuration. + */ +enum QMI8658_GyrOdr +{ + QMI8658GyrOdr_8000Hz = 0x00, /*!< \brief High resolution 8000Hz output rate. */ + QMI8658GyrOdr_4000Hz = 0x01, /*!< \brief High resolution 4000Hz output rate. */ + QMI8658GyrOdr_2000Hz = 0x02, /*!< \brief High resolution 2000Hz output rate. */ + QMI8658GyrOdr_1000Hz = 0x03, /*!< \brief High resolution 1000Hz output rate. */ + QMI8658GyrOdr_500Hz = 0x04, /*!< \brief High resolution 500Hz output rate. */ + QMI8658GyrOdr_250Hz = 0x05, /*!< \brief High resolution 250Hz output rate. */ + QMI8658GyrOdr_125Hz = 0x06, /*!< \brief High resolution 125Hz output rate. */ + QMI8658GyrOdr_62_5Hz = 0x07, /*!< \brief High resolution 62.5Hz output rate. */ + QMI8658GyrOdr_31_25Hz = 0x08 /*!< \brief High resolution 31.25Hz output rate. */ +}; + +enum QMI8658_AeOdr +{ + QMI8658AeOdr_1Hz = 0x00, /*!< \brief 1Hz output rate. */ + QMI8658AeOdr_2Hz = 0x01, /*!< \brief 2Hz output rate. */ + QMI8658AeOdr_4Hz = 0x02, /*!< \brief 4Hz output rate. */ + QMI8658AeOdr_8Hz = 0x03, /*!< \brief 8Hz output rate. */ + QMI8658AeOdr_16Hz = 0x04, /*!< \brief 16Hz output rate. */ + QMI8658AeOdr_32Hz = 0x05, /*!< \brief 32Hz output rate. */ + QMI8658AeOdr_64Hz = 0x06, /*!< \brief 64Hz output rate. */ + QMI8658AeOdr_128Hz = 0x07, /*!< \brief 128Hz output rate. */ + /*! + * \brief Motion on demand mode. + * + * In motion on demand mode the application can trigger AttitudeEngine + * output samples as necessary. This allows the AttitudeEngine to be + * synchronized with external data sources. + * + * When in Motion on Demand mode the application should request new data + * by calling the QMI8658_requestAttitudeEngineData() function. The + * AttitudeEngine will respond with a data ready event (INT2) when the + * data is available to be read. + */ + QMI8658AeOdr_motionOnDemand = 128 +}; + +enum QMI8658_MagOdr +{ + QMI8658MagOdr_1000Hz = 0x00, /*!< \brief 1000Hz output rate. */ + QMI8658MagOdr_500Hz = 0x01, /*!< \brief 500Hz output rate. */ + QMI8658MagOdr_250Hz = 0x02, /*!< \brief 250Hz output rate. */ + QMI8658MagOdr_125Hz = 0x03, /*!< \brief 125Hz output rate. */ + QMI8658MagOdr_62_5Hz = 0x04, /*!< \brief 62.5Hz output rate. */ + QMI8658MagOdr_31_25Hz = 0x05 /*!< \brief 31.25Hz output rate. */ +}; + +enum QMI8658_MagDev +{ + MagDev_AKM09918 = (0 << 3), /*!< \brief AKM09918. */ +}; + +enum QMI8658_AccUnit +{ + QMI8658AccUnit_g, /*!< \brief Accelerometer output in terms of g (9.81m/s^2). */ + QMI8658AccUnit_ms2 /*!< \brief Accelerometer output in terms of m/s^2. */ +}; + +enum QMI8658_GyrUnit +{ + QMI8658GyrUnit_dps, /*!< \brief Gyroscope output in degrees/s. */ + QMI8658GyrUnit_rads /*!< \brief Gyroscope output in rad/s. */ +}; + +struct QMI8658Config +{ + /*! \brief Sensor fusion input selection. */ + unsigned char inputSelection; + /*! \brief Accelerometer dynamic range configuration. */ + enum QMI8658_AccRange accRange; + /*! \brief Accelerometer output rate. */ + enum QMI8658_AccOdr accOdr; + /*! \brief Gyroscope dynamic range configuration. */ + enum QMI8658_GyrRange gyrRange; + /*! \brief Gyroscope output rate. */ + enum QMI8658_GyrOdr gyrOdr; + /*! \brief AttitudeEngine output rate. */ + enum QMI8658_AeOdr aeOdr; + /*! + * \brief Magnetometer output data rate. + * + * \remark This parameter is not used when using an external magnetometer. + * In this case the external magnetometer is sampled at the FIS output + * data rate, or at an integer divisor thereof such that the maximum + * sample rate is not exceeded. + */ + enum QMI8658_MagOdr magOdr; + + /*! + * \brief Magnetometer device to use. + * + * \remark This parameter is not used when using an external magnetometer. + */ + enum QMI8658_MagDev magDev; +}; + +#define QMI8658_SAMPLE_SIZE (3 * sizeof(short)) +#define QMI8658_AE_SAMPLE_SIZE ((4 + 3 + 1) * sizeof(short) + sizeof(unsigned char)) +struct FisImuRawSample +{ + /*! \brief The sample counter of the sample. */ + unsigned char timestamp[3]; + /*! + * \brief Pointer to accelerometer data in the sample buffer. + * + * \c NULL if no accelerometer data is available in the buffer. + */ + unsigned char const *accelerometerData; + /*! + * \brief Pointer to gyroscope data in the sample buffer. + * + * \c NULL if no gyroscope data is available in the buffer. + */ + unsigned char const *gyroscopeData; + /*! + * \brief Pointer to magnetometer data in the sample buffer. + * + * \c NULL if no magnetometer data is available in the buffer. + */ + unsigned char const *magnetometerData; + /*! + * \brief Pointer to AttitudeEngine data in the sample buffer. + * + * \c NULL if no AttitudeEngine data is available in the buffer. + */ + unsigned char const *attitudeEngineData; + /*! \brief Raw sample buffer. */ + unsigned char sampleBuffer[QMI8658_SAMPLE_SIZE + QMI8658_AE_SAMPLE_SIZE]; + /*! \brief Contents of the FIS status 1 register. */ + unsigned char status1; + // unsigned char status0; + // unsigned int durT; +}; + +struct QMI8658_offsetCalibration +{ + enum QMI8658_AccUnit accUnit; + float accOffset[3]; + enum QMI8658_GyrUnit gyrUnit; + float gyrOffset[3]; +}; + +struct QMI8658_sensitivityCalibration +{ + float accSensitivity[3]; + float gyrSensitivity[3]; +}; + +enum QMI8658_Interrupt +{ + /*! \brief FIS INT1 line. */ + QMI8658_Int1 = (0 << 6), + /*! \brief FIS INT2 line. */ + QMI8658_Int2 = (1 << 6) +}; + +enum QMI8658_InterruptState +{ + QMI8658State_high = (1 << 7), /*!< Interrupt high. */ + QMI8658State_low = (0 << 7) /*!< Interrupt low. */ +}; + +enum QMI8658_WakeOnMotionThreshold +{ + QMI8658WomThreshold_high = 128, /*!< High threshold - large motion needed to wake. */ + QMI8658WomThreshold_low = 32 /*!< Low threshold - small motion needed to wake. */ +}; + +extern unsigned char QMI8658_write_reg(unsigned char reg, unsigned char value); +extern unsigned char QMI8658_read_reg(unsigned char reg, unsigned char *buf, unsigned short len); +extern unsigned char QMI8658_init(void); +extern void QMI8658_Config_apply(struct QMI8658Config const *config); +extern void QMI8658_enableSensors(unsigned char enableFlags); +extern void QMI8658_read_acc_xyz(float acc_xyz[3]); +extern void QMI8658_read_gyro_xyz(float gyro_xyz[3]); +extern void QMI8658_read_xyz(float acc[3], float gyro[3], unsigned int *tim_count); +extern void QMI8658_read_xyz_raw(short raw_acc_xyz[3], short raw_gyro_xyz[3], unsigned int *tim_count); +extern void QMI8658_read_ae(float quat[4], float velocity[3]); +extern unsigned char QMI8658_readStatus0(void); +extern unsigned char QMI8658_readStatus1(void); +extern float QMI8658_readTemp(void); +extern void QMI8658_enableWakeOnMotion(void); +extern void QMI8658_disableWakeOnMotion(void); + +#endif diff --git a/examples/smart-sensor-display/smart-sensor-display.cpp b/examples/smart-sensor-display/smart-sensor-display.cpp index 4c9a8a1..c512601 100644 --- a/examples/smart-sensor-display/smart-sensor-display.cpp +++ b/examples/smart-sensor-display/smart-sensor-display.cpp @@ -27,12 +27,12 @@ * */ +#include "configuration.h" #include #include #include - //#include #include //#include @@ -41,7 +41,13 @@ #include "display/activities/SensorValuesActivity.h" #include "io/DHTxx.h" -//#include "io/LightSensor.h" +//#include "../smart-sensor/io/LightSensor.h" +//#include "io/BatterySensor.h" +#include "io/MotionSensor.h" + +// Accelerometer and Gyroscope +//#include "io/QMI8658.h" + using namespace IO::Env; using namespace Service; @@ -57,34 +63,49 @@ ModuleParameter* controlModuleParameter = nullptr; SwitchControlActivity* switchControl; +Dashboard* dashboard; + void setup() { - homeGenie = HomeGenie::getInstance(); + //uint8_t batterySensorPin = 1; + uint8_t motionSensorPin = 16; - miniModule = homeGenie->getDefaultModule(); + PowerManager::setWakeUpGPIO((gpio_num_t)motionSensorPin); - /* - // Light sensor - homeGenie->addIOHandler(new LightSensor()); - auto luminance = new ModuleParameter(IOEventPaths::Sensor_Luminance); - miniModule->properties.add(luminance); - //*/ + homeGenie = HomeGenie::getInstance(); + miniModule = homeGenie->getDefaultModule(); auto roundDisplay = (new UI::Drivers::RoundDisplay())->getDisplay(); - auto dashboard = + dashboard = new Dashboard(roundDisplay); if (Config::isDeviceConfigured()) { + /* + // Battery sensor + auto batterySensor = new BatterySensor(batterySensorPin); + batterySensor->setModule(miniModule); + homeGenie->addIOHandler(batterySensor); + //*/ + + // Motion sensor + auto motionSensor = new MotionSensor(motionSensorPin); + motionSensor->setModule(miniModule); + homeGenie->addIOHandler(motionSensor); + + /* + // Light sensor + auto lightSensor = new LightSensor(); + lightSensor->setModule(miniModule); + homeGenie->addIOHandler(lightSensor); + //*/ + // Temperature and humidity sensor - auto dht = new DHTxx(22); - homeGenie->addIOHandler(dht); - auto temperature = new ModuleParameter(IOEventPaths::Sensor_Temperature); - miniModule->properties.add(temperature); - auto humidity = new ModuleParameter(IOEventPaths::Sensor_Humidity); - miniModule->properties.add(humidity); - - // add custom properties + auto dhtSensor = new DHTxx(22); + dhtSensor->setModule(miniModule); + homeGenie->addIOHandler(dhtSensor); + + // add custom properties to default module controlModuleParameter = new ModuleParameter("RemoteControl.EndPoint", Config::getSetting("ctrl-mod")); miniModule->properties.add(controlModuleParameter); miniModule->properties.add(new ModuleParameter( @@ -92,6 +113,7 @@ void setup() { "module.text:any:switch,light,dimmer,color,shutter:any:uri" // last option can be "uri" or "id" )); + // Add activities to UI auto systemInfo = new SystemInfoActivity(); dashboard->addActivity(systemInfo); @@ -112,6 +134,9 @@ void setup() { } else { + // On devices with default RAM, activating + // Bluetooth will get most of the available RAM, + // so we just start the configuration activity auto systemInfo = new SystemInfoActivity(); dashboard->addActivity(systemInfo); dashboard->setForegroundActivity(systemInfo); @@ -120,17 +145,35 @@ void setup() { homeGenie->begin(); + /* + // Init accel./gyro chip + Wire.begin(DEV_SDA_PIN, DEV_SCL_PIN, 400000); + + QMI8658_enableSensors(QMI8658_CONFIG_AE_ENABLE); + //QMI8658_enableWakeOnMotion(); + + QMI8658_init(); + //*/ } void loop() { homeGenie->loop(); + // check if configuration parameters changed if (controlModuleParameter != nullptr && controlModuleParameter->value != controlModuleUrl) { controlModuleUrl = controlModuleParameter->value; switchControl->setModuleUrl(controlModuleUrl); Config::saveSetting("ctrl-mod", controlModuleUrl); - IO::Logger::info("Control module set to: %s\n", controlModuleUrl.c_str()); + IO::Logger::info("Control module set to: %s", controlModuleUrl.c_str()); } + /* + // Test reading accel/gyro data + float acc[3], gyro[3]; + unsigned int count = 0; + QMI8658_read_xyz(acc, gyro, &count); + Serial.printf("ACC X %.2f Y %.2f Z %.2f ", acc[0], acc[1], acc[2]); + Serial.printf("GYR X %.2f Y %.2f Z %.2f\n", gyro[0], gyro[1], gyro[2]); + //*/ } diff --git a/examples/smart-sensor/io/DS18B20.h b/examples/smart-sensor/io/DS18B20.h index 736bc01..07ad112 100644 --- a/examples/smart-sensor/io/DS18B20.h +++ b/examples/smart-sensor/io/DS18B20.h @@ -50,6 +50,11 @@ namespace IO { namespace Env { DS18B20() { setLoopInterval(DS18B20_SAMPLING_RATE); } + void setModule(Module* m) override { + IIOEventSender::setModule(m); + auto temperature = new ModuleParameter(IOEventPaths::Sensor_Temperature); + m->properties.add(temperature); + } void begin() override; void loop() override; void setInputPin(uint8_t); diff --git a/examples/smart-sensor/io/LightSensor.cpp b/examples/smart-sensor/io/LightSensor.cpp index 052c949..2845b35 100644 --- a/examples/smart-sensor/io/LightSensor.cpp +++ b/examples/smart-sensor/io/LightSensor.cpp @@ -41,7 +41,7 @@ namespace IO { namespace Env { if (lightLevel != currentLevel) { currentLevel = lightLevel; Logger::info("@%s [%s %d]", LIGHTSENSOR_NS_PREFIX, (IOEventPaths::Sensor_Luminance), currentLevel); - sendEvent(domain.c_str(), address.c_str(), (const uint8_t*)(IOEventPaths::Sensor_Luminance), (uint16_t *)¤tLevel, SensorLight); + sendEvent((const uint8_t*)(IOEventPaths::Sensor_Luminance), (uint16_t *)¤tLevel, SensorLight); } } diff --git a/examples/smart-sensor/io/LightSensor.h b/examples/smart-sensor/io/LightSensor.h index c9de39d..e9a7d8e 100644 --- a/examples/smart-sensor/io/LightSensor.h +++ b/examples/smart-sensor/io/LightSensor.h @@ -46,13 +46,17 @@ namespace IO { namespace Env { LightSensor() { setLoopInterval(LIGHTSENSOR_SAMPLING_RATE); } + void setModule(Module* m) override { + IIOEventSender::setModule(m); + auto luminance = new ModuleParameter(IOEventPaths::Sensor_Luminance); + m->properties.add(luminance); + } + void begin() override; void loop() override; void setInputPin(uint8_t number); uint16_t getLightLevel(); private: - String domain = IOEventDomains::HomeAutomation_HomeGenie; - String address = CONFIG_BUILTIN_MODULE_ADDRESS; uint8_t inputPin = CONFIG_LightSensorPin; // Analogic input pin A0 (0) uint16_t currentLevel = 0; }; diff --git a/examples/smart-sensor/smart-sensor.cpp b/examples/smart-sensor/smart-sensor.cpp index cc1969b..60c8f58 100644 --- a/examples/smart-sensor/smart-sensor.cpp +++ b/examples/smart-sensor/smart-sensor.cpp @@ -45,14 +45,14 @@ void setup() { auto miniModule = homeGenie->getDefaultModule(); // Temperature sensor - homeGenie->addIOHandler(new DS18B20()); - auto temperature = new ModuleParameter(IOEventPaths::Sensor_Temperature); - miniModule->properties.add(temperature); + auto temperatureSensor = new DS18B20(); + temperatureSensor->setModule(miniModule); + homeGenie->addIOHandler(temperatureSensor); // Light sensor - homeGenie->addIOHandler(new LightSensor()); - auto luminance = new ModuleParameter(IOEventPaths::Sensor_Luminance); - miniModule->properties.add(luminance); + auto lightSensor = new LightSensor(); + lightSensor->setModule(miniModule); + homeGenie->addIOHandler(lightSensor); homeGenie->begin(); diff --git a/examples/x10-transceiver/io/RFReceiver.cpp b/examples/x10-transceiver/io/RFReceiver.cpp index f983945..b1193dc 100644 --- a/examples/x10-transceiver/io/RFReceiver.cpp +++ b/examples/x10-transceiver/io/RFReceiver.cpp @@ -95,7 +95,7 @@ namespace IO { namespace X10 { if (isStandardCode || isSecurityCode) { messageType = isStandardCode ? (uint8_t) 0x20 : (uint8_t) 0x29; uint8_t data[] = { messageType, byteBuffer[0], byteBuffer[1], (byteBuffer[2]), (byteBuffer[3]) }; - sendEvent(domain.c_str(), address.c_str(), (const uint8_t*)(IOEventPaths::Receiver_RawData), data, IOEventDataType::Undefined); + sendEvent((const uint8_t*)(IOEventPaths::Receiver_RawData), data, IOEventDataType::Undefined); } receivedCount = -1; diff --git a/examples/x10-transceiver/io/RFReceiver.h b/examples/x10-transceiver/io/RFReceiver.h index 6540981..c2d2178 100644 --- a/examples/x10-transceiver/io/RFReceiver.h +++ b/examples/x10-transceiver/io/RFReceiver.h @@ -48,8 +48,6 @@ namespace IO { namespace X10 { void receive(); private: - String domain = IOEventDomains::HomeAutomation_X10; - String address = CONFIG_X10RF_MODULE_ADDRESS; RFReceiverConfig *configuration; // 32-bit RF message decoding volatile uint8_t messageType = 0x00; diff --git a/examples/x10-transceiver/x10-transceiver.cpp b/examples/x10-transceiver/x10-transceiver.cpp index c3c92af..1348beb 100644 --- a/examples/x10-transceiver/x10-transceiver.cpp +++ b/examples/x10-transceiver/x10-transceiver.cpp @@ -51,15 +51,12 @@ void setup() { homeGenie->addAPIHandler(apiHandler); // X10 RF RFReceiver + auto rfModule = apiHandler->getModule(IO::IOEventDomains::HomeAutomation_X10, CONFIG_X10RF_MODULE_ADDRESS); auto x10ReceiverConfig = new X10::RFReceiverConfig(CONFIG_X10RFReceiverPin); auto x10Receiver = new X10::RFReceiver(x10ReceiverConfig); + x10Receiver->setModule(rfModule); homeGenie->addIOHandler(x10Receiver); - auto propRawData = new ModuleParameter(IOEventPaths::Receiver_RawData); - auto propCommand = new ModuleParameter(IOEventPaths::Receiver_Command); - miniModule->properties.add(propRawData); - miniModule->properties.add(propCommand); - homeGenie->begin(); } diff --git a/platformio.ini b/platformio.ini index d20c4b8..c6c92f1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,7 +21,6 @@ lib_deps = ArduinoJson@6.21.4 thijse/ArduinoLog@1.1.1 WebSockets@2.4.1 - OneWire@2.3.8 vortigont/LinkedList@1.5.0 hideakitai/MsgPack adafruit/TINYXML@1.0.3 @@ -66,6 +65,8 @@ build_flags = -Os -I examples -I src build_src_filter = + - + board_build.flash_size = 4MB board_build.partitions = min_spiffs.csv +lib_deps = ${env.lib_deps} + OneWire@2.3.8 [env:smart-sensor-d1-mini-esp32] platform = espressif32@6.5.0 @@ -73,12 +74,16 @@ build_flags = -Os -I examples -I src -D MINI_ESP32 -D CONFIG_ServiceButtonPin=16 build_src_filter = + - + board_build.flash_size = 4MB board_build.partitions = min_spiffs.csv +lib_deps = ${env.lib_deps} + OneWire@2.3.8 [env:smart-sensor-d1-mini] platform = espressif8266@2.6.3 board = d1_mini build_flags = -Os -I examples -I src build_src_filter = + - + +lib_deps = ${env.lib_deps} + OneWire@2.3.8 lib_ignore = ESP32Time ESP32_BleSerial @@ -86,18 +91,17 @@ lib_ignore = [env:smart-sensor-display] platform = espressif32@6.5.0 -build_flags = -Os -I examples -I src +build_flags = -Os -I examples -I src -D CONFIG_ENABLE_POWER_MANAGER build_src_filter = + - + board_build.flash_size = 4MB board_build.partitions = no_ota.csv lib_deps = ${env.lib_deps} - Wire RobTillaart/DHTNew@0.4.19 [env:smart-sensor-display-s3] platform = espressif32@6.5.0 board = esp32-s3-devkitc-1 -build_flags = -Os -I examples -I src -D ESP32_S3 -DESP32S3_DEV -DBOARD_HAS_PSRAM +build_flags = -Os -I examples -I src -D ESP32_S3 -D CONFIG_ENABLE_POWER_MANAGER -DESP32S3_DEV -DBOARD_HAS_PSRAM -D CONFIG_GPIO_OUT={15,17,18} build_src_filter = + - + board_build.mcu = esp32s3 board_build.f_cpu = 240000000L @@ -108,8 +112,7 @@ board_build.flash_mode = qio board_build.partitions = default_16MB.csv board_upload.flash_size = 16MB lib_deps = ${env.lib_deps} - Wire - RobTillaart/DHTNew@0.4.19 + RobTillaart/DHTNew@0.4.19 [env:x10-transceiver] platform = espressif32@6.5.0 diff --git a/src/HomeGenie.cpp b/src/HomeGenie.cpp index db21eaa..c1158a1 100644 --- a/src/HomeGenie.cpp +++ b/src/HomeGenie.cpp @@ -53,7 +53,10 @@ namespace Service { addIOHandler(gpioPort); auto homeGenieHandler = new HomeGenieHandler(gpioPort); addAPIHandler(homeGenieHandler); - +#ifdef CONFIG_ENABLE_POWER_MANAGER + PowerManager::setWakeUpInterval(0); + PowerManager::init(); +#endif Logger::info("+ Starting HomeGenie service"); } @@ -92,6 +95,15 @@ namespace Service { // TODO: sort of system load index could be obtained by measuring time elapsed for `TaskManager::loop()` method TaskManager::loop(); +#ifdef CONFIG_ENABLE_POWER_MANAGER + if (Config::isDeviceConfigured() && PowerManager::canEnterSleepMode()) { + + // TODO: should gracefully stop all activities and drivers + + PowerManager::sleep(); + } +#endif + Logger::verbose(":%s loop() << END", HOMEGENIEMINI_NS_PREFIX); } diff --git a/src/HomeGenie.h b/src/HomeGenie.h index de3ff19..2abeab1 100644 --- a/src/HomeGenie.h +++ b/src/HomeGenie.h @@ -31,9 +31,14 @@ #define HOMEGENIE_MINI_HOMEGENIE_H +#ifdef CONFIG_ENABLE_POWER_MANAGER +#include "PowerManager.h" +#endif + #include "Task.h" #include "TaskManager.h" +#include "data/Module.h" #include "io/gpio/GPIOPort.h" #include "io/IOEventPaths.h" #include "io/IOManager.h" @@ -42,7 +47,6 @@ #include "service/api/APIHandler.h" #include "service/api/HomeGenieHandler.h" #include "service/EventRouter.h" -#include "service/Module.h" #define HOMEGENIEMINI_NS_PREFIX "Service::HomeGenie" @@ -50,6 +54,7 @@ namespace Service { using namespace IO; + using namespace Data; using namespace Net; using namespace Service::API; diff --git a/src/PowerManager.cpp b/src/PowerManager.cpp new file mode 100644 index 0000000..ef2b585 --- /dev/null +++ b/src/PowerManager.cpp @@ -0,0 +1,105 @@ +/* + * HomeGenie-Mini (c) 2018-2024 G-Labs + * + * + * This file is part of HomeGenie-Mini (HGM). + * + * HomeGenie-Mini is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * HomeGenie-Mini is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with HomeGenie-Mini. If not, see . + * + * + * Authors: + * - Generoso Martello + * + */ + +#ifdef ESP32 + +#include "PowerManager.h" + +#if CONFIG_IDF_TARGET_ESP32 + #define THRESHOLD 40 /* Greater the value, more the sensitivity */ +#else //ESP32-S2 and ESP32-S3 + default for other chips (to be adjusted) */ + #define THRESHOLD 5000 /* Lower the value, more the sensitivity */ +#endif + +RTC_DATA_ATTR int wakeUpCount = 0; + +namespace Service { + unsigned long PowerManager::lastUserActivityTs = 0; + unsigned long PowerManager::deepSleepTimeoutMs = 30000; + // wake up sources + unsigned long PowerManager::wakeUpIntervalMs = 0; + gpio_num_t PowerManager::wakeUpGPIO = GPIO_NUM_NC; + touch_pad_t PowerManager::wakeUpTouchPadPin = TOUCH_PAD_NUM0; + + void PowerManager::init() { + + if (wakeUpCount > 0) { + + esp_sleep_wakeup_cause_t wakeup_reason; + wakeup_reason = esp_sleep_get_wakeup_cause(); + switch(wakeup_reason) + { + case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break; + case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break; + case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break; + case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break; + case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break; + default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break; + } + + } else { + + // initial setup + lastUserActivityTs = millis(); + + } + + wakeUpCount++; + } + + bool PowerManager::canEnterSleepMode() { + return (millis() - lastUserActivityTs > deepSleepTimeoutMs); + } + + void PowerManager::sleep() { + + // Configure wake up sources + + // Wakeup on touchpad activity +// TODO: touchSleepWakeUpEnable(wakeUpTouchPadPin, 40); + + // Wakeup on external GPIO high + if (wakeUpGPIO != GPIO_NUM_NC) { +#ifndef ESP32_C3 + esp_sleep_enable_ext0_wakeup(wakeUpGPIO, ESP_EXT1_WAKEUP_ANY_HIGH); +#else + esp_deep_sleep_enable_gpio_wakeup(wakeUpGPIO, ESP_GPIO_WAKEUP_GPIO_HIGH); +#endif + } + + // Wakeup after a given interval + if (wakeUpIntervalMs > 0) { + esp_sleep_enable_timer_wakeup(wakeUpIntervalMs * 1000); + } + + // Enter deep sleep mode + esp_deep_sleep_start(); +// esp_light_sleep_start(); + + } + +} + +#endif \ No newline at end of file diff --git a/src/PowerManager.h b/src/PowerManager.h new file mode 100644 index 0000000..16d5b0a --- /dev/null +++ b/src/PowerManager.h @@ -0,0 +1,65 @@ +/* + * HomeGenie-Mini (c) 2018-2024 G-Labs + * + * + * This file is part of HomeGenie-Mini (HGM). + * + * HomeGenie-Mini is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * HomeGenie-Mini is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with HomeGenie-Mini. If not, see . + * + * + * Authors: + * - Generoso Martello + * + */ + +#ifndef HOMEGENIE_MINI_POWERMANAGER_H +#define HOMEGENIE_MINI_POWERMANAGER_H + +#include "Config.h" + +#ifdef ESP32 + +namespace Service { + + class PowerManager { + private: + static unsigned long lastUserActivityTs; + static unsigned long deepSleepTimeoutMs; + static unsigned long wakeUpIntervalMs; + static gpio_num_t wakeUpGPIO; + static touch_pad_t wakeUpTouchPadPin; + PowerManager() {} + + public: + static void callback() {} + static void init(); + static bool canEnterSleepMode(); + static void sleep(); + static void setActive() { + lastUserActivityTs = millis(); + } + static void setWakeUpInterval(unsigned long ms) { + wakeUpIntervalMs = ms; + } + static void setWakeUpGPIO(gpio_num_t gpio) { + wakeUpGPIO = gpio; + } + + }; + +} + +#endif + +#endif //HOMEGENIE_MINI_POWERMANAGER_H diff --git a/src/service/Module.cpp b/src/data/Module.cpp similarity index 75% rename from src/service/Module.cpp rename to src/data/Module.cpp index 21f2c5a..f30add9 100644 --- a/src/service/Module.cpp +++ b/src/data/Module.cpp @@ -4,6 +4,6 @@ #include "Module.h" -namespace Service { +namespace Data { } \ No newline at end of file diff --git a/src/service/Module.h b/src/data/Module.h similarity index 77% rename from src/service/Module.h rename to src/data/Module.h index e2b037f..10cb790 100644 --- a/src/service/Module.h +++ b/src/data/Module.h @@ -5,12 +5,15 @@ #ifndef HOMEGENIE_MINI_MODULE_H #define HOMEGENIE_MINI_MODULE_H -#include "io/IOEvent.h" -#include "net/NetManager.h" +#include -namespace Service { +#include "Config.h" +#include "net/TimeClient.h" +#include "io/IOEventData.h" - using namespace Net; +namespace Data { + + using namespace IO; enum ModuleTypes { Generic = 0, @@ -26,10 +29,10 @@ namespace Service { String value; String updateTime; void* data; - IO::IOEventDataType dataType; + IOEventDataType dataType; ModuleParameter() { - updateTime = NetManager::getTimeClient().getFormattedDate(); + updateTime = getFormattedDate(); } ModuleParameter(String name): ModuleParameter() { this->name = name; @@ -43,12 +46,16 @@ namespace Service { } void setValue(const char* v) { value = v; - updateTime = NetManager::getTimeClient().getFormattedDate(); + updateTime = getFormattedDate(); } - void setData(void* d, IO::IOEventDataType t) { + void setData(void* d, IOEventDataType t) { data = d; dataType = t; } + private: + String getFormattedDate() { + return Net::TimeClient::getTimeClient().getFormattedDate(); + } }; class Module { @@ -59,7 +66,7 @@ namespace Service { String name; String description; LinkedList properties; - bool setProperty(String pn, String pv, void* data, IO::IOEventDataType dataType) { + bool setProperty(String pn, String pv, void* data, IOEventDataType dataType) { for(int p = 0; p < properties.size(); p++) { auto param = properties.get(p); if (param->is(pn.c_str())) { diff --git a/src/defs.h b/src/defs.h index eedab2f..803ec5c 100644 --- a/src/defs.h +++ b/src/defs.h @@ -61,13 +61,17 @@ #define DISABLE_BLUETOOTH_CLASSIC #define CONFIG_ServiceButtonPin 4 #define CONFIG_StatusLedPin 0 - #define CONFIG_GPIO_OUT {6} + #ifndef CONFIG_GPIO_OUT + #define CONFIG_GPIO_OUT {6} + #endif #elif ESP32_S3 #undef DISABLE_BLUETOOTH_LE #define DISABLE_BLUETOOTH_CLASSIC #define CONFIG_ServiceButtonPin 21 #define CONFIG_StatusLedPin 33 - #define CONFIG_GPIO_OUT {15,16,17,18} + #ifndef CONFIG_GPIO_OUT + #define CONFIG_GPIO_OUT {15,16,17,18} + #endif #else #define DISABLE_BLUETOOTH_LE // #define DISABLE_BLUETOOTH_CLASSIC @@ -103,12 +107,12 @@ #define CONFIG_DISPLAY_DC 8 #define CONFIG_DISPLAY_CS 9 -#define CONFIG_DISPLAY_BL 2 // backlight +#define CONFIG_DISPLAY_BL (2) // backlight #define CONFIG_TOUCH_PORT 1 #define CONFIG_TOUCH_ADDRESS 0x15 -#define CONFIG_TOUCH_INT 5 +#define CONFIG_TOUCH_INT (-1) /* -1 disabled, or pin 5 */ #define CONFIG_TOUCH_SDA 6 #define CONFIG_TOUCH_SCL 7 #define CONFIG_TOUCH_RST 13 @@ -123,12 +127,12 @@ #define CONFIG_DISPLAY_DC 27 #define CONFIG_DISPLAY_CS 14 -#define CONFIG_DISPLAY_BL 32 // backlight +#define CONFIG_DISPLAY_BL (32) // backlight #define CONFIG_TOUCH_PORT 1 #define CONFIG_TOUCH_ADDRESS 0x15 -#define CONFIG_TOUCH_INT 35 +#define CONFIG_TOUCH_INT (-1) /* -1 disabled, or pin 35 */ #define CONFIG_TOUCH_SDA 25 #define CONFIG_TOUCH_SCL 26 #define CONFIG_TOUCH_RST 34 diff --git a/src/io/IOEvent.h b/src/io/IOEvent.h index c88896e..f6ba4cd 100644 --- a/src/io/IOEvent.h +++ b/src/io/IOEvent.h @@ -32,19 +32,14 @@ #include "Config.h" +#include "data/Module.h" +#include "IOEventData.h" + namespace IO { - class IIOEventSender; + using namespace Data; - enum IOEventDataType { - Undefined = 0, - Number, - Float, - UnsignedNumber, - SensorLight, - SensorTemperature, - SensorHumidity - }; + class IIOEventSender; // IIOEventReceiver interface class IIOEventReceiver { @@ -55,22 +50,31 @@ namespace IO { // IIOEventSender interface class IIOEventSender { public: -// const uint8_t* getDomain() { return domain; } -// const uint8_t* getAddress() { return address; } virtual void begin() = 0; void setEventReceiver(IIOEventReceiver *receiver) { eventReceiver = receiver; } + + virtual void setModule(Module* m) { + module = m; + } virtual void sendEvent(const char *domain, const char *address, const uint8_t *eventPath, void *eventData, IOEventDataType dataType) { if (eventReceiver != nullptr) { eventReceiver->onIOEvent(this, domain, address, eventPath, eventData, dataType); lastEventMs = millis(); } }; + virtual void sendEvent(const uint8_t *eventPath, void *eventData, IOEventDataType dataType) { + if (eventReceiver != nullptr && module != nullptr) { + eventReceiver->onIOEvent(this, module->domain.c_str(), module->address.c_str(), eventPath, eventData, dataType); + lastEventMs = millis(); + } + }; protected: IIOEventReceiver *eventReceiver = nullptr; unsigned long lastEventMs = 0; + const Module* module = nullptr; }; } diff --git a/src/io/IOEventData.h b/src/io/IOEventData.h new file mode 100644 index 0000000..a602a2e --- /dev/null +++ b/src/io/IOEventData.h @@ -0,0 +1,42 @@ +/* + * HomeGenie-Mini (c) 2018-2024 G-Labs + * + * + * This file is part of HomeGenie-Mini (HGM). + * + * HomeGenie-Mini is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * HomeGenie-Mini is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with HomeGenie-Mini. If not, see . + * + * + * Authors: + * - Generoso Martello + * + */ + +#ifndef HOMEGENIE_MINI_IOEVENTDATA_H +#define HOMEGENIE_MINI_IOEVENTDATA_H + +namespace IO { + + enum IOEventDataType { + Undefined = 0, + Number, + Float, + UnsignedNumber, + SensorLight, + SensorTemperature, + SensorHumidity + }; +} + +#endif //HOMEGENIE_MINI_IOEVENTDATA_H diff --git a/src/io/IOEventPaths.h b/src/io/IOEventPaths.h index 7489c04..db51f41 100644 --- a/src/io/IOEventPaths.h +++ b/src/io/IOEventPaths.h @@ -38,6 +38,8 @@ namespace IO { const char Sensor_Luminance[] PROGMEM = "Sensor.Luminance"; const char Sensor_Temperature[] PROGMEM = "Sensor.Temperature"; const char Sensor_Humidity[] PROGMEM = "Sensor.Humidity"; + const char Sensor_MotionDetect[] PROGMEM = "Sensor.MotionDetect"; + const char Status_Battery[] PROGMEM = "Status.Battery"; const char System_BytesFree[] PROGMEM = "System.BytesFree"; } } diff --git a/src/io/Logger.cpp b/src/io/Logger.cpp index c47d1b1..c0bebed 100644 --- a/src/io/Logger.cpp +++ b/src/io/Logger.cpp @@ -29,7 +29,7 @@ #include "Logger.h" -#include "net/NetManager.h" +#include "net/TimeClient.h" namespace IO { @@ -112,7 +112,7 @@ namespace IO { } void Logger::timestamp() { - Serial.printf("[%s] ", Net::NetManager::getTimeClient().getFormattedDate().c_str()); + Serial.printf("[%s] ", Net::TimeClient::getTimeClient().getFormattedDate().c_str()); } void Logger::trace(const char *s, ...) { diff --git a/src/net/HTTPServer.cpp b/src/net/HTTPServer.cpp index 369a7d9..fa14e5e 100644 --- a/src/net/HTTPServer.cpp +++ b/src/net/HTTPServer.cpp @@ -164,9 +164,9 @@ namespace Net { void HTTPServer::serverSentEvent(WiFiClient &client, String &domain, String &address, String &event, String &value) { // id: 1548081759906.19 // data: {"Timestamp":"2019-01-21T14:42:39.906194Z","UnixTimestamp":1548081759906.19,"Domain":"HomeAutomation.ZWave","Source":"7","Description":"ZWave Node","Property":"Meter.Watts","Value":0} - String date = Net::NetManager::getTimeClient().getFormattedDate(); - unsigned long epoch = Net::NetManager::getTimeClient().getEpochTime(); - int ms = Net::NetManager::getTimeClient().getMilliseconds(); + String date = Net::TimeClient::getTimeClient().getFormattedDate(); + unsigned long epoch = Net::TimeClient::getTimeClient().getEpochTime(); + int ms = Net::TimeClient::getTimeClient().getMilliseconds(); client.printf("id: %lu\n", millis()); client.printf(R"(data: {"Timestamp":"%s","UnixTimestamp":%lu%03d,"Description":"","Domain":"%s","Source":"%s","Property":"%s","Value":"%s"})", date.c_str(), epoch, ms, domain.c_str(), address.c_str(), event.c_str(), value.c_str()); diff --git a/src/net/NetManager.cpp b/src/net/NetManager.cpp index 64e3b59..82ed594 100644 --- a/src/net/NetManager.cpp +++ b/src/net/NetManager.cpp @@ -33,16 +33,6 @@ namespace Net { using namespace IO; - // Time sync - WiFiUDP ntpUDP; - NTPClient timeClient(ntpUDP); -#ifdef ESP32 - bool rtcTimeSet = (esp_reset_reason() != ESP_RST_POWERON && esp_reset_reason() != ESP_RST_UNKNOWN); -#else - bool rtcTimeSet = false; -#endif - long lastTimeCheck = -100000; - NetManager::NetManager() { // TODO: ... } @@ -125,15 +115,8 @@ namespace Net { mqttServer = new MQTTServer(); mqttServer->begin(); #endif - - // Initialize a NTPClient to get time - timeClient.begin(); - // Set offset time in seconds to adjust for your timezone, for example: - // GMT +1 = 3600 - // GMT +8 = 28800 - // GMT -1 = -3600 - // GMT 0 = 0 - timeClient.setTimeOffset(0); + timeClient = new TimeClient(); + timeClient->begin(); } @@ -144,35 +127,6 @@ namespace Net { webSocket->loop(); } - if (rtcTimeSet) { -#ifdef ESP32 - if (millis() - lastTimeCheck > 60000) { - lastTimeCheck = millis(); - if (!timeClient.isUpdated()) { - // sync TimeClient with RTC - timeClient.setEpochTime(Config::getRTC()->getLocalEpoch()); - Logger::info("| - TimeClient: synced with RTC"); - } - } -#endif - } else if (WiFi.isConnected() && millis() - lastTimeCheck > 60000) { - lastTimeCheck = millis(); - if (!timeClient.isUpdated()) { - if (timeClient.update()) { - // TimeClient synced with NTP -#ifdef ESP32 - Config::getRTC()->setTime(timeClient.getEpochTime(), 0); - rtcTimeSet = true; - Logger::info("| - RTC updated via TimeClient (NTP)"); -#endif - } else { - // NTP Update failed - digitalWrite(Config::StatusLedPin, HIGH); - Logger::warn("| x TimeClient: NTP update failed!"); - } - } - } - Logger::verbose("%s loop() << END", NETMANAGER_LOG_PREFIX); } @@ -200,10 +154,6 @@ namespace Net { return *webSocket; } - NTPClient& NetManager::getTimeClient() { - return timeClient; - } - void NetManager::setRequestHandler(NetRequestHandler* handler) { netRequestHandler = handler; } diff --git a/src/net/NetManager.h b/src/net/NetManager.h index 470177a..92faf62 100644 --- a/src/net/NetManager.h +++ b/src/net/NetManager.h @@ -35,12 +35,9 @@ #else #include #endif -#include #include -#include #include -#include #include #include @@ -56,6 +53,8 @@ #include #endif +#include "TimeClient.h" + #define NETMANAGER_LOG_PREFIX "@Net::NetManager" namespace Net { @@ -148,7 +147,6 @@ namespace Net { MQTTServer& getMQTTServer(); #endif WebSocketsServer& getWebSocketServer(); - static NTPClient& getTimeClient(); void setRequestHandler(NetRequestHandler *); @@ -181,6 +179,8 @@ namespace Net { WebSocketsServer *webSocket; NetRequestHandler* netRequestHandler; + TimeClient* timeClient; + }; } diff --git a/src/net/TimeClient.cpp b/src/net/TimeClient.cpp new file mode 100644 index 0000000..4e63dd9 --- /dev/null +++ b/src/net/TimeClient.cpp @@ -0,0 +1,85 @@ +/* + * HomeGenie-Mini (c) 2018-2024 G-Labs + * + * + * This file is part of HomeGenie-Mini (HGM). + * + * HomeGenie-Mini is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * HomeGenie-Mini is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with HomeGenie-Mini. If not, see . + * + * + * Authors: + * - Generoso Martello + * + * + * Releases: + * - 2019-01-10 Initial release + * + */ + +#include "TimeClient.h" + +namespace Net { + // Time sync + WiFiUDP ntpUDP; + NTPClient timeClient(ntpUDP); + + void TimeClient::begin() { + // Initialize a NTPClient to get time + timeClient.begin(); + // Set offset time in seconds to adjust for your timezone, for example: + // GMT +1 = 3600 + // GMT +8 = 28800 + // GMT -1 = -3600 + // GMT 0 = 0 + timeClient.setTimeOffset(0); + } + + void TimeClient::loop() { + + if (rtcTimeSet) { +#ifdef ESP32 + if (millis() - lastTimeCheck > 60000) { + lastTimeCheck = millis(); + if (!timeClient.isUpdated()) { + // sync TimeClient with RTC + timeClient.setEpochTime(Config::getRTC()->getLocalEpoch()); + Logger::info("| - TimeClient: synced with RTC"); + } + } +#endif + } else if (WiFi.isConnected() && millis() - lastTimeCheck > 60000) { + lastTimeCheck = millis(); + if (!timeClient.isUpdated()) { + if (timeClient.update()) { + // TimeClient synced with NTP +#ifdef ESP32 + Config::getRTC()->setTime(timeClient.getEpochTime(), 0); + rtcTimeSet = true; + Logger::info("| - RTC updated via TimeClient (NTP)"); +#endif + } else { + // NTP Update failed + digitalWrite(Config::StatusLedPin, HIGH); + Logger::warn("| x TimeClient: NTP update failed!"); + } + } + } + + } + + NTPClient& TimeClient::getTimeClient() { + return timeClient; + } + +} diff --git a/src/net/TimeClient.h b/src/net/TimeClient.h new file mode 100644 index 0000000..91daa51 --- /dev/null +++ b/src/net/TimeClient.h @@ -0,0 +1,64 @@ +/* + * HomeGenie-Mini (c) 2018-2024 G-Labs + * + * + * This file is part of HomeGenie-Mini (HGM). + * + * HomeGenie-Mini is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * HomeGenie-Mini is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with HomeGenie-Mini. If not, see . + * + * + * Authors: + * - Generoso Martello + * + */ + +#ifndef HOMEGENIE_MINI_TIMECLIENT_H +#define HOMEGENIE_MINI_TIMECLIENT_H + +#include "Config.h" + +#include +#ifdef ESP8266 +#include +#else +#include +#endif +#include + +#include "io/Logger.h" +#include "Task.h" + +namespace Net { + using namespace IO; + + class TimeClient: Task { + public: + TimeClient() { + setLoopInterval(1000); + } + void begin(); + void loop() override; + static NTPClient& getTimeClient(); + private: +#ifdef ESP32 + bool rtcTimeSet = (esp_reset_reason() != ESP_RST_POWERON && esp_reset_reason() != ESP_RST_UNKNOWN); +#else + bool rtcTimeSet = false; +#endif + long lastTimeCheck = -100000; + }; + +} + +#endif //HOMEGENIE_MINI_TIMECLIENT_H diff --git a/src/service/EventRouter.cpp b/src/service/EventRouter.cpp index 335e340..b40dc15 100644 --- a/src/service/EventRouter.cpp +++ b/src/service/EventRouter.cpp @@ -34,14 +34,16 @@ namespace Service { void EventRouter::loop() { + if (!WiFi.isConnected()) return; + // MQTT & SSE Events Queue (dequeue) for (int i = 0; i < eventsQueue.size(); i++) { - // route event through MQTT + auto m = eventsQueue.pop(); - Logger::verbose(":%s dequeued event >> [domain '%s' address '%s' event '%s']", EVENTROUTER_NS_PREFIX, m.domain.c_str(), m.sender.c_str(), m.event.c_str()); + Logger::info(":%s dequeued event >> [domain '%s' address '%s' event '%s']", EVENTROUTER_NS_PREFIX, m.domain.c_str(), m.sender.c_str(), m.event.c_str()); #ifndef DISABLE_MQTT // MQTT - auto date = NetManager::getTimeClient().getFormattedDate(); + auto date = TimeClient::getTimeClient().getFormattedDate(); auto topic = String(String(CONFIG_SYSTEM_NAME) + "/" + m.domain + "/" + m.sender + "/event"); auto details = Service::HomeGenie::createModuleParameter(m.event.c_str(), m.value.c_str(), date.c_str()); netManager->getMQTTServer().broadcast(&topic, &details); @@ -51,8 +53,8 @@ namespace Service { // WS if (netManager->getWebSocketServer().connectedClients() > 0) { - unsigned long epoch = Net::NetManager::getTimeClient().getEpochTime(); - int ms = Net::NetManager::getTimeClient().getMilliseconds(); + unsigned long epoch = TimeClient::getTimeClient().getEpochTime(); + int ms = TimeClient::getTimeClient().getMilliseconds(); /* // Send as clear text int sz = 1+snprintf(nullptr, 0, R"(data: {"Timestamp":"%s","UnixTimestamp":%lu%03d,"Description":"","Domain":"%s","Source":"%s","Property":"%s","Value":"%s"})", @@ -74,7 +76,7 @@ namespace Service { }; packer.packTimestamp(t); auto epochs = String(epoch) + ms; - packer.packFloat((Net::NetManager::getTimeClient().getEpochTime() * 1000.0f) + ms); + packer.packFloat((TimeClient::getTimeClient().getEpochTime() * 1000.0f) + ms); packer.pack(m.domain.c_str()); packer.pack(m.sender.c_str()); packer.pack(""); diff --git a/src/service/api/APIHandler.h b/src/service/api/APIHandler.h index dac4b02..7d3b03a 100644 --- a/src/service/api/APIHandler.h +++ b/src/service/api/APIHandler.h @@ -31,7 +31,7 @@ #define HOMEGENIE_MINI_APIHANDLER_H #include "io/IOEvent.h" -#include "service/Module.h" +#include "data/Module.h" #include "APIRequest.h" #include "net/NetManager.h" @@ -39,6 +39,7 @@ namespace Service { namespace API { using namespace IO; + using namespace Data; using namespace Net; using namespace Service; diff --git a/src/ui/Dashboard.cpp b/src/ui/Dashboard.cpp index 829a7d4..5078b5f 100644 --- a/src/ui/Dashboard.cpp +++ b/src/ui/Dashboard.cpp @@ -92,6 +92,7 @@ void Dashboard::loop() { activity->pointerMove(tp.x, tp.y); } } + Service::PowerManager::setActive(); } else if (pointerDown) { pointerDown = false; gestureHelper->pointerUp(tp.x, tp.y); @@ -160,25 +161,10 @@ void Dashboard::onPan(PanEvent e) { activity->pan(e); if (activityList.size() > 1 && (e.touchPoint.direction == TOUCH_DIRECTION_LEFT || e.touchPoint.direction == TOUCH_DIRECTION_RIGHT)) { - int nextActivityIndex = 0; - - if (e.touchPoint.shiftX > 0) { - if (currentActivityIndex + 1 < activityList.size()) { - nextActivityIndex = currentActivityIndex + 1; - } - } else { - if (currentActivityIndex - 1 >= 0) { - nextActivityIndex = currentActivityIndex - 1; - } else { - nextActivityIndex = activityList.size() - 1; - } - } - - auto next = activityList.get(nextActivityIndex); + auto next = (e.touchPoint.shiftX > 0) ? getNextActivity() : getPreviousActivity(); if (next != nextActivity && nextActivity != nullptr) { nextActivity->pause(); } - nextActivity = next; nextActivity->resume(); nextActivity->setDrawOffset(e.touchPoint.shiftX + (float)display->width()*(e.touchPoint.shiftX > 0 ? -1 : 1), 0); @@ -195,5 +181,21 @@ void Dashboard::onTouch(PointerEvent e) { void Dashboard::onRelease(PointerEvent e) { } +Activity* Dashboard::getNextActivity() { + int nextActivityIndex = 0; + if (currentActivityIndex + 1 < activityList.size()) { + nextActivityIndex = currentActivityIndex + 1; + } + return activityList.get(nextActivityIndex); +} +Activity* Dashboard::getPreviousActivity() { + unsigned int prevActivityIndex; + if (currentActivityIndex - 1 >= 0) { + prevActivityIndex = currentActivityIndex - 1; + } else { + prevActivityIndex = activityList.size() - 1; + } + return activityList.get(prevActivityIndex); +} #endif // DISABLE_UI diff --git a/src/ui/Dashboard.h b/src/ui/Dashboard.h index 2458197..efa6316 100644 --- a/src/ui/Dashboard.h +++ b/src/ui/Dashboard.h @@ -34,6 +34,7 @@ #include #include "Task.h" +#include "PowerManager.h" #include "io/Logger.h" #include "drivers/RoundDisplay.h" @@ -72,6 +73,8 @@ class Dashboard: public Task, public GestureListener { Activity* getForegroundActivity(); void setForegroundActivity(Activity*); + Activity* getNextActivity(); + Activity* getPreviousActivity(); void addActivity(Activity* activity); void onTap(PointerEvent e) override; diff --git a/src/ui/drivers/RoundDisplay.cpp b/src/ui/drivers/RoundDisplay.cpp index 400b3ba..3e911c9 100644 --- a/src/ui/drivers/RoundDisplay.cpp +++ b/src/ui/drivers/RoundDisplay.cpp @@ -90,8 +90,8 @@ namespace UI { namespace Drivers { cfg.x_max = 239; cfg.y_min = 0; cfg.y_max = 239; - cfg.pin_int = CONFIG_TOUCH_INT; // N.C. - //cfg.bus_shared = true; + cfg.pin_int = CONFIG_TOUCH_INT; + cfg.bus_shared = true; cfg.offset_rotation = 0; // SPI @@ -108,7 +108,7 @@ namespace UI { namespace Drivers { cfg.pin_sda = CONFIG_TOUCH_SDA; cfg.pin_scl = CONFIG_TOUCH_SCL; cfg.pin_rst = CONFIG_TOUCH_RST; - //cfg.freq = 400000; + cfg.freq = 400000; _touch_instance.config(cfg); _panel_instance.setTouch(&_touch_instance);