From c56c785a1fdddfed93085f93449eed79c8c4679f Mon Sep 17 00:00:00 2001 From: lumapu Date: Mon, 20 Feb 2023 22:10:05 +0100 Subject: [PATCH] basic implementation of HMS/HMT inverters --- src/CHANGES.md | 1 + src/app.cpp | 2 +- src/app.h | 8 + src/config/settings.h | 7 + src/hm/cmt2300a.h | 430 ++++++++++++++++++++++++++++++++++++++++++ src/hm/esp32_3wSpi.h | 138 ++++++++++++++ src/hm/hmPayload.h | 2 +- src/hm/hmRadio.h | 19 +- src/hm/hmsRadio.h | 191 +++++++++++++++++++ src/hm/miPayload.h | 2 +- src/main.cpp | 7 +- src/utils/helper.cpp | 9 + src/utils/helper.h | 3 +- 13 files changed, 800 insertions(+), 19 deletions(-) create mode 100644 src/hm/cmt2300a.h create mode 100644 src/hm/esp32_3wSpi.h create mode 100644 src/hm/hmsRadio.h diff --git a/src/CHANGES.md b/src/CHANGES.md index 447054fa..23ecaa33 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -7,6 +7,7 @@ * added part of mac address to MQTT client ID to seperate multiple ESPs in same network * added dictionary for MQTT to reduce heap-fragmentation * removed `last Alarm` from Live view, because it showed always the same alarm - will change in future +* #671, #650 ## 0.5.88 * MQTT Yield Day zero, next try to fix #671, thx @beegee3 diff --git a/src/app.cpp b/src/app.cpp index d32a5bfa..f609f279 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -115,7 +115,7 @@ void app::loopStandard(void) { DBGPRINT(F("B Ch")); DBGPRINT(String(p->ch)); DBGPRINT(F(" | ")); - mSys.Radio.dumpBuf(p->packet, p->len); + ah::dumpBuf(p->packet, p->len); } mStat.frmCnt++; diff --git a/src/app.h b/src/app.h index 68281492..e65f21b4 100644 --- a/src/app.h +++ b/src/app.h @@ -66,6 +66,10 @@ class app : public IApp, public ah::Scheduler { mSys.Radio.handleIntr(); } + void handleHmsIntr(void) { + //mSys.Radio.handleHmsIntr(); + } + uint32_t getUptime() { return Scheduler::getUptime(); } @@ -160,6 +164,10 @@ class app : public IApp, public ah::Scheduler { return mConfig->nrf.pinIrq; } + uint8_t getHmsIrqPin(void) { + return mConfig->cmt.pinIrq; + } + String getTimeStr(uint32_t offset = 0) { char str[10]; if(0 == mTimestamp) diff --git a/src/config/settings.h b/src/config/settings.h index 527dea72..e42201a5 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -68,6 +68,12 @@ typedef struct { uint8_t amplifierPower; } cfgNrf24_t; +typedef struct { + uint8_t pinCsb; + uint8_t pinFcsb; + uint8_t pinIrq; +} cfgCmt_t; + typedef struct { char addr[NTP_ADDR_LEN]; uint16_t port; @@ -138,6 +144,7 @@ typedef struct { typedef struct { cfgSys_t sys; cfgNrf24_t nrf; + cfgCmt_t cmt; cfgNtp_t ntp; cfgSun_t sun; cfgSerial_t serial; diff --git a/src/hm/cmt2300a.h b/src/hm/cmt2300a.h new file mode 100644 index 00000000..0c6c1641 --- /dev/null +++ b/src/hm/cmt2300a.h @@ -0,0 +1,430 @@ +//----------------------------------------------------------------------------- +// 2023 Ahoy, https://github.com/lumpapu/ahoy +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +//----------------------------------------------------------------------------- + +#ifndef __CMT2300A_H__ +#define __CMT2300A_H__ + +#include "esp32_3wSpi.h" + +// detailed register infos from AN142_CMT2300AW_Quick_Start_Guide-Rev0.8.pdf + +#define CMT2300A_CUS_MODE_CTL 0x60 // [7] go_switch + // [6] go_tx + // [5] go_tfs + // [4] go_sleep + // [3] go_rx + // [2] go_rfs + // [1] go_stby + // [0] n/a + +#define CMT2300A_CUS_MODE_STA 0x61 // [3:0] 0x00 IDLE + // 0x01 SLEEP + // 0x02 STBY + // 0x03 RFS + // 0x04 TFS + // 0x05 RX + // 0x06 TX + // 0x08 UNLOCKED/LOW_VDD + // 0x09 CAL +#define CMT2300A_CUS_EN_CTL 0x62 +#define CMT2300A_CUS_FREQ_CHNL 0x63 + +#define CMT2300A_CUS_IO_SEL 0x65 // [5:4] GPIO3 + // 0x00 CLKO + // 0x01 DOUT / DIN + // 0x02 INT2 + // 0x03 DCLK + // [3:2] GPIO2 + // 0x00 INT1 + // 0x01 INT2 + // 0x02 DOUT / DIN + // 0x03 DCLK + // [1:0] GPIO1 + // 0x00 DOUT / DIN + // 0x01 INT1 + // 0x02 INT2 + // 0x03 DCLK + +#define CMT2300A_CUS_INT1_CTL 0x66 // [4:0] INT1_SEL + // 0x00 RX active + // 0x01 TX active + // 0x02 RSSI VLD + // 0x03 Pream OK + // 0x04 SYNC OK + // 0x05 NODE OK + // 0x06 CRC OK + // 0x07 PKT OK + // 0x08 SL TMO + // 0x09 RX TMO + // 0x0A TX DONE + // 0x0B RX FIFO NMTY + // 0x0C RX FIFO TH + // 0x0D RX FIFO FULL + // 0x0E RX FIFO WBYTE + // 0x0F RX FIFO OVF + // 0x10 TX FIFO NMTY + // 0x11 TX FIFO TH + // 0x12 TX FIFO FULL + // 0x13 STATE IS STBY + // 0x14 STATE IS FS + // 0x15 STATE IS RX + // 0x16 STATE IS TX + // 0x17 LED + // 0x18 TRX ACTIVE + // 0x19 PKT DONE + +#define CMT2300A_CUS_INT2_CTL 0x67 // [4:0] INT2_SEL + +#define CMT2300A_CUS_INT_EN 0x68 // [7] SL TMO EN + // [6] RX TMO EN + // [5] TX DONE EN + // [4] PREAM OK EN + // [3] SYNC_OK EN + // [2] NODE OK EN + // [1] CRC OK EN + // [0] PKT DONE EN + +#define CMT2300A_CUS_FIFO_CTL 0x69 // [7] TX DIN EN + // [6:5] TX DIN SEL + // 0x00 SEL GPIO1 + // 0x01 SEL GPIO2 + // 0x02 SEL GPIO3 + // [4] FIFO AUTO CLR DIS + // [3] FIFO TX RD EN + // [2] FIFO RX TX SEL + // [1] FIFO MERGE EN + // [0] SPI FIFO RD WR SEL + +#define CMT2300A_CUS_INT_CLR1 0x6A // clear interrupts Bank1 +#define CMT2300A_CUS_INT_CLR2 0x6B // clear interrupts Bank2 +#define CMT2300A_CUS_FIFO_CLR 0x6C + +#define CMT2300A_CUS_INT_FLAG 0x6D // [7] LBD FLG + // [6] COL ERR FLG + // [5] PKT ERR FLG + // [4] PREAM OK FLG + // [3] SYNC OK FLG + // [2] NODE OK FLG + // [1] CRC OK FLG + // [0] PKT OK FLG + +#define CMT2300A_CUS_RSSI_DBM 0x70 + +#define CMT2300A_GO_SWITCH 0x80 +#define CMT2300A_GO_TX 0x40 +#define CMT2300A_GO_TFS 0x20 +#define CMT2300A_GO_SLEEP 0x10 +#define CMT2300A_GO_RX 0x08 +#define CMT2300A_GO_RFS 0x04 +#define CMT2300A_GO_STBY 0x02 +#define CMT2300A_GO_EEPROM 0x01 + +#define CMT2300A_STA_IDLE 0x00 +#define CMT2300A_STA_SLEEP 0x01 +#define CMT2300A_STA_STBY 0x02 +#define CMT2300A_STA_RFS 0x03 +#define CMT2300A_STA_TFS 0x04 +#define CMT2300A_STA_RX 0x05 +#define CMT2300A_STA_TX 0x06 +#define CMT2300A_STA_EEPROM 0x07 +#define CMT2300A_STA_ERROR 0x08 +#define CMT2300A_STA_CAL 0x09 + +#define CMT2300A_INT_SEL_TX_DONE 0x0A + +#define CMT2300A_MASK_TX_DONE_FLG 0x08 +#define CMT2300A_MASK_PKT_OK_FLG 0x01 + +// default CMT paramters +static uint8_t cmtConfig[0x60] PROGMEM { + // 0x00 - 0x0f + 0x00, 0x66, 0xEC, 0x1D, 0x70, 0x80, 0x14, 0x08, + 0x91, 0x02, 0x02, 0xD0, 0xAE, 0xE0, 0x35, 0x00, + // 0x10 - 0x1f + 0x00, 0xF4, 0x10, 0xE2, 0x42, 0x20, 0x0C, 0x81, + 0x42, 0xCF, 0xA7, 0x8C, 0x42, 0xC4, 0x4E, 0x1C, + // 0x20 - 0x2f + 0xA6, 0xC9, 0x20, 0x20, 0xD2, 0x35, 0x0C, 0x0A, + 0x9F, 0x4B, 0x29, 0x29, 0xC0, 0x14, 0x05, 0x53, + // 0x30 - 0x3f + 0x10, 0x00, 0xB4, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x12, 0x1E, 0x00, 0xAA, 0x06, 0x00, 0x00, 0x00, + // 0x40 - 0x4f + 0x00, 0xD6, 0xD5, 0xD4, 0x2D, 0x01, 0x1D, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x60, + // 0x50 - 0x5f + 0xFF, 0x00, 0x00, 0x1F, 0x10, 0x70, 0x4D, 0x06, + 0x00, 0x07, 0x50, 0x00, 0x8A, 0x18, 0x3F, 0x7F +}; + +enum {CMT_SUCCESS = 0, CMT_ERR_SWITCH_STATE, CMT_ERR_TX_PENDING, CMT_ERR_REG_VAL}; + +template +class Cmt2300a { + typedef SPI SpiType; + public: + Cmt2300a() {} + + void setup() { + mSpi.setup(); + mTxPending = false; + } + + // call as often as possible + void loop() { + if(mTxPending) { + if(CMT2300A_MASK_TX_DONE_FLG != spi3w.readReg(CMT2300A_CUS_INT_CLR1)) { + if(cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY)) + mTxPending = false; + } + } + } + + inline void swichChannel(bool reset = true, uint8_t start = 0x00, uint8_t end = 0x22) { + if(reset) + mRxTxCh = start; + else if(++mRxTxCh > end) + mRxTxCh = start; + // 0: 868.00MHz + // 1: 868.23MHz + // 2: 868.46MHz + // 3: 868.72MHz + // 4: 868.97MHz + mSpi.writeReg(CMT2300A_CUS_FREQ_CHNL, mRxTxCh); + } + + uint8_t checkRx(uint8_t buf[], uint8_t len, int8_t *rssi) { + if(mTxPending) + return CMT_ERR_TX_PENDING; + + mSpi.readReg(CMT2300A_CUS_INT1_CTL); + mSpi.writeReg(CMT2300A_CUS_INT1_CTL, CMT2300A_INT_SEL_TX_DONE); + + uint8_t tmp = mSpi.readReg(CMT2300A_CUS_INT_CLR1); + if(0x08 == tmp) // first time after TX this reg is 0x08 + mSpi.writeReg(CMT2300A_CUS_INT_CLR1, 0x04); + else + mSpi.writeReg(CMT2300A_CUS_INT_CLR1, 0x00); + + if(0x10 == tmp) + mSpi.writeReg(CMT2300A_CUS_INT_CLR2, 0x10); + else + mSpi.writeReg(CMT2300A_CUS_INT_CLR2, 0x00); + + mSpi.readReg(CMT2300A_CUS_FIFO_CTL); // necessary? -> if 0x02 last was read + // 0x07 last was write + mSpi.writeReg(CMT2300A_CUS_FIFO_CTL, 0x02); + + mSpi.writeReg(CMT2300A_CUS_FIFO_CLR, 0x02); + mSpi.writeReg(0x16, 0x0C); // [4:3]: RSSI_DET_SEL, [2:0]: RSSI_AVG_MODE + + mSpi.writeReg(CMT2300A_CUS_FREQ_CHNL, 0x00); // 863.0 MHz + + if(!cmtSwitchStatus(CMT2300A_GO_RX, CMT2300A_STA_RX)) + return CMT_ERR_SWITCH_STATE; + + uint8_t state = 0x00; + uint16_t timeout = 5000; + for(uint8_t i = 0; i < 52; i++) { + state = mSpi.readReg(CMT2300A_CUS_INT_FLAG); + if(0x00 != state) + break; + } + + if((state & 0x10) == 0x10) { + while(0 == digitalRead(INTR_PIN)) { + usleep(10); + if(0 == --timeout) + break; + } + if(0 != timeout) { + uint16_t timeout2 = 5000; + while(0x18 != (state & 0x18)) { + state = mSpi.readReg(CMT2300A_CUS_INT_FLAG); + if(0 == timeout2--) + break; + } + } + } + + if(0 != digitalRead(INTR_PIN)) { + uint32_t loops = 0; + while((state & 0x1b) != 0x1b) { + state = mSpi.readReg(CMT2300A_CUS_INT_FLAG); + + if((state & 0x20) == 0x20) + return CMT_ERR_REG_VAL; + else if((state & 0x40) == 0x40) + return CMT_ERR_REG_VAL; + if(++loops > 5000) + break; + } + } + + // receive ok (pream, sync, node, crc) + if((state & 0x1b) == 0x1b) { + if(!cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY)) + return CMT_ERR_SWITCH_STATE; + + mSpi.readFifo(buf, len); + *rssi = mSpi.readReg(CMT2300A_CUS_RSSI_DBM) - 128; + + if(!cmtSwitchStatus(CMT2300A_GO_SLEEP, CMT2300A_STA_SLEEP)) + return CMT_ERR_SWITCH_STATE; + } + + if(!cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY)) + return CMT_ERR_SWITCH_STATE; + + return CMT_SUCCESS; + } + + bool tx(uint8_t buf[], uint8_t len) { + if(mTxPending) + return CMT_ERR_TX_PENDING; + + spi3w.writeReg(CMT2300A_CUS_INT1_CTL, CMT2300A_INT_SEL_TX_DONE); + + if(0x00 == spi3w.readReg(CMT2300A_CUS_INT_FLAG)) { + // no data received + spi3w.readReg(CMT2300A_CUS_INT_CLR1); + spi3w.writeReg(CMT2300A_CUS_INT_CLR1, 0x00); + spi3w.writeReg(CMT2300A_CUS_INT_CLR2, 0x00); + + //spi3w.readReg(CMT2300A_CUS_FIFO_CTL); // necessary? + spi3w.writeReg(CMT2300A_CUS_FIFO_CTL, 0x07); + spi3w.writeReg(CMT2300A_CUS_FIFO_CLR, 0x01); + + spi3w.writeReg(0x45, 0x01); + spi3w.writeReg(0x46, len); // payload length + + spi3w.writeFifo(buf, len); + + // send only on base frequency: here 863.0 MHz + spi3w.writeReg(CMT2300A_CUS_FREQ_CHNL, 0x00); + + if(!cmtSwitchStatus(CMT2300A_GO_TX, CMT2300A_STA_TX)) + return CMT_ERR_SWITCH_STATE; + + // wait for tx done + mTxPending = CMT_SUCCESS; + } + else + return CMT_ERR_RX_IN_FIFO; + + return CMT_SUCCESS; + } + + // initialize CMT2300A, returns true on success + bool void reset(void) { + mSpi.writeReg(0x7f, 0xff); // soft reset + delay(30); + + if(cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY)) + return false; + + if(0xAA != mSpi.readReg(0x48)) + mSpi.writeReg(0x48, 0xAA); + //mSpi.readReg(0x48); + mSpi.writeReg(0x4c, 0x00); + + if(0x52 != mSpi.readReg(CMT2300A_CUS_MODE_STA)) + mSpi.writeReg(CMT2300A_CUS_MODE_STA, 0x52); + if(0x20 != mSpi.readReg(0x62)) + mSpi.writeReg(0x62, 0x20); + //mSpi.readReg(0x0D); + mSpi.writeReg(0x0F, 0x00); + + for(uint8_t i = 0; i < 0x60; i++) { + mSpi.writeReg(i, cmtConfig[i]); + } + + if(0x02 != mSpi.readReg(0x09)) + mSpi.writeReg(0x09, 0x02); + + mSpi.writeReg(CMT2300A_CUS_IO_SEL, 0x20); // -> GPIO3_SEL[1:0] = 0x02 + + // interrupt 1 control selection to TX DONE + if(CMT2300A_INT_SEL_TX_DONE != mSpi.readReg(CMT2300A_CUS_INT1_CTL)) + mSpi.writeReg(CMT2300A_CUS_INT1_CTL, CMT2300A_INT_SEL_TX_DONE); + + // select interrupt 2 + if(0x07 != mSpi.readReg(CMT2300A_CUS_INT2_CTL)) + mSpi.writeReg(CMT2300A_CUS_INT2_CTL, 0x07); + + // interrupt enable (TX_DONE, PREAM_OK, SYNC_OK, CRC_OK, PKT_DONE) + mSpi.writeReg(CMT2300A_CUS_INT_EN, 0x3B); + + mSpi.writeReg(0x41, 0x48); + mSpi.writeReg(0x42, 0x5A); + mSpi.writeReg(0x43, 0x48); + mSpi.writeReg(0x44, 0x4D); + mSpi.writeReg(0x64, 0x64); + + if(0x00 == mSpi.readReg(CMT2300A_CUS_FIFO_CTL)) + mSpi.writeReg(CMT2300A_CUS_FIFO_CTL, 0x02); // FIFO_MERGE_EN + + if(!cmtSwitchStatus(CMT2300A_GO_SLEEP, CMT2300A_STA_SLEEP)) + return false; + + delayMicroseconds(95); + + // base frequency 863MHz, with value of CMT2300A_CUS_FREQ_CHNL + // the frequency can be increase in a step size of ~0.24Hz + mSpi.writeReg(0x18, 0x42); + mSpi.writeReg(0x19, 0x6D); + mSpi.writeReg(0x1A, 0x80); + mSpi.writeReg(0x1B, 0x86); + mSpi.writeReg(0x1C, 0x42); + mSpi.writeReg(0x1D, 0x62); + mSpi.writeReg(0x1E, 0x27); + mSpi.writeReg(0x1F, 0x16); + + mSpi.writeReg(0x22, 0x20); + mSpi.writeReg(0x23, 0x20); + mSpi.writeReg(0x24, 0xD2); + mSpi.writeReg(0x25, 0x35); + mSpi.writeReg(0x26, 0x0C); + mSpi.writeReg(0x27, 0x0A); + mSpi.writeReg(0x28, 0x9F); + mSpi.writeReg(0x29, 0x4B); + mSpi.writeReg(0x27, 0x0A); + + if(!cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY)) + return false; + + mSpi.writeReg(0x03, 0x1D); + mSpi.writeReg(0x5C, 0x8A); + mSpi.writeReg(0x5D, 0x18); + + if(!cmtSwitchStatus(CMT2300A_GO_SLEEP, CMT2300A_STA_SLEEP)) + return false; + + if(!cmtSwitchStatus(CMT2300A_GO_STBY, CMT2300A_STA_STBY)) + return false; + + return true; + } + + private: + // CMT state machine, wait for next state, true on success + bool cmtSwitchStatus(uint8_t cmd, uint8_t waitFor, uint16_t cycles = 40) { + mSpi.writeReg(CMT2300A_CUS_MODE_CTL, cmd); + while(cycles--) { + yield(); + delayMicroseconds(10); + if(waitFor == (getChipStatus() & waitFor)) + return true; + } + //Serial.println("status wait for: " + String(waitFor, HEX) + " read: " + String(getChipStatus(), HEX)); + return false; + } + + SpiType mSpi; + bool mTxPending; + uint8_t mRxTxCh; +}; + +#endif /*__CMT2300A_H__*/ diff --git a/src/hm/esp32_3wSpi.h b/src/hm/esp32_3wSpi.h new file mode 100644 index 00000000..41e1bc03 --- /dev/null +++ b/src/hm/esp32_3wSpi.h @@ -0,0 +1,138 @@ +//----------------------------------------------------------------------------- +// 2023 Ahoy, https://github.com/lumpapu/ahoy +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +//----------------------------------------------------------------------------- + +#ifndef __ESP32_3WSPI_H__ +#define __ESP32_3WSPI_H__ + +#include "driver/spi_master.h" +#include "esp_rom_gpio.h" // for esp_rom_gpio_connect_out_signal +#include "Arduino.h" + +#if defined(ESP32) + #define CLK_PIN 18 + #define MOSI_PIN 23 + #define MISO_PIN -1 +#endif + +#define SPI_CLK 1 * 1000 * 1000 // 1MHz + +template +class esp32_3wSpi { + public: + esp32_3wSpi() {} + void setup() { + spi_bus_config_t buscfg = { + .mosi_io_num = MOSI_PIN, + .miso_io_num = MISO_PIN, + .sclk_io_num = CLK_PIN, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = 32, + }; + spi_device_interface_config_t devcfg = { + .command_bits = 0, + .address_bits = 0, + .dummy_bits = 0, + .mode = 0, // SPI mode 0 + .clock_speed_hz = SPI_CLK, // 1 MHz + .spics_io_num = CS_PIN, + .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, + .queue_size = 1, + .pre_cb = NULL, + .post_cb = NULL, + }; + + ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, 0)); + ESP_ERROR_CHECK(spi_bus_add_device(SPI2_HOST, &devcfg, &spi_reg)); + + // FiFo + spi_device_interface_config_t devcfg2 = { + .command_bits = 0, + .address_bits = 0, + .dummy_bits = 0, + .mode = 0, // SPI mode 0 + .cs_ena_pretrans = 2, + .cs_ena_posttrans = (uint8_t)(1 / (SPI_CLK * 10e6 * 2) + 2), // >2 us + .clock_speed_hz = SPI_CLK, // 1 MHz + .spics_io_num = FCS_PIN, + .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, + .queue_size = 1, + .pre_cb = NULL, + .post_cb = NULL, + }; + ESP_ERROR_CHECK(spi_bus_add_device(SPI2_HOST, &devcfg2, &spi_fifo)); + + esp_rom_gpio_connect_out_signal(MOSI_PIN, spi_periph_signal[SPI2_HOST].spid_out, true, false); + delay(100); + + pinMode(INTR_PIN, INPUT); + } + + void writeReg(uint8_t addr, uint8_t reg) { + uint8_t tx_data[2]; + tx_data[0] = ~addr; + tx_data[1] = ~reg; + spi_transaction_t t = { + .length = 2 * 8, + .tx_buffer = &tx_data, + .rx_buffer = NULL + }; + ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); + delayMicroseconds(100); + } + + uint8_t readReg(uint8_t addr) { + uint8_t tx_data, rx_data; + tx_data = ~(addr | 0x80); // negation and MSB high (read command) + spi_transaction_t t = { + .length = 8, + .rxlength = 8, + .tx_buffer = &tx_data, + .rx_buffer = &rx_data + }; + ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); + delayMicroseconds(100); + return rx_data; + } + + void writeFifo(uint8_t buf[], uint8_t len) { + uint8_t tx_data; + + spi_transaction_t t = { + .flags = SPI_TRANS_MODE_OCT, + .length = 8, + .tx_buffer = &tx_data, // reference to write data + .rx_buffer = NULL + }; + + for(uint8_t i = 0; i < len; i++) { + tx_data = ~buf[i]; // negate buffer contents + ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); + delayMicroseconds(4); // > 4 us + } + } + + void readFifo(uint8_t buf[], uint8_t len) { + uint8_t rx_data; + + spi_transaction_t t = { + .length = 8, + .rxlength = 8, + .tx_buffer = NULL, + .rx_buffer = &rx_data + }; + + for(uint8_t i = 0; i < len; i++) { + ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); + delayMicroseconds(4); // > 4 us + buf[i] = rx_data; + } + } + + private: + spi_device_handle_t spi_reg, spi_fifo; +}; + +#endif /*__ESP32_3WSPI_H__*/ diff --git a/src/hm/hmPayload.h b/src/hm/hmPayload.h index 7a05bee3..acfcbd0b 100644 --- a/src/hm/hmPayload.h +++ b/src/hm/hmPayload.h @@ -307,7 +307,7 @@ class HmPayload { DPRINT(DBG_INFO, F("Payload (")); DBGPRINT(String(payloadLen)); DBGPRINT(F("): ")); - mSys->Radio.dumpBuf(payload, payloadLen); + ah::dumpBuf(payload, payloadLen); } if (NULL == rec) { diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index f0236362..47d81166 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -188,7 +188,7 @@ class HmRadio { mTxBuf[cnt++] = ((data[1] ) >> 8) & 0xff; // setting for persistens handlings mTxBuf[cnt++] = ((data[1] ) ) & 0xff; // setting for persistens handling } - sendPacket(invId, cnt, isRetransmit, true); + sendPacket(invId, cnt, isRetransmit); } void prepareDevInformCmd(uint64_t invId, uint8_t cmd, uint32_t ts, uint16_t alarmMesId, bool isRetransmit, uint8_t reqfld=TX_REQ_INFO) { // might not be necessary to add additional arg. @@ -201,21 +201,12 @@ class HmRadio { mTxBuf[18] = (alarmMesId >> 8) & 0xff; mTxBuf[19] = (alarmMesId ) & 0xff; } - sendPacket(invId, 24, isRetransmit, true); + sendPacket(invId, 24, isRetransmit); } void sendCmdPacket(uint64_t invId, uint8_t mid, uint8_t pid, bool isRetransmit) { initPacket(invId, mid, pid); - sendPacket(invId, 10, isRetransmit, false); - } - - void dumpBuf(uint8_t buf[], uint8_t len) { - //DPRINTLN(DBG_VERBOSE, F("hmRadio.h:dumpBuf")); - for(uint8_t i = 0; i < len; i++) { - DHEX(buf[i]); - DBGPRINT(" "); - } - DBGPRINTLN(""); + sendPacket(invId, 10, isRetransmit); } uint8_t getDataRate(void) { @@ -279,7 +270,7 @@ class HmRadio { mTxBuf[9] = pid; } - void sendPacket(uint64_t invId, uint8_t len, bool isRetransmit, bool clear=false) { + void sendPacket(uint64_t invId, uint8_t len, bool isRetransmit) { //DPRINTLN(DBG_VERBOSE, F("hmRadio.h:sendPacket")); //DPRINTLN(DBG_VERBOSE, "sent packet: #" + String(mSendCnt)); @@ -300,7 +291,7 @@ class HmRadio { DBGPRINT("B Ch"); DBGPRINT(String(mRfChLst[mTxChIdx])); DBGPRINT(F(" | ")); - dumpBuf(mTxBuf, len); + ah::dumpBuf(mTxBuf, len); } mNrf24.stopListening(); diff --git a/src/hm/hmsRadio.h b/src/hm/hmsRadio.h new file mode 100644 index 00000000..afe61902 --- /dev/null +++ b/src/hm/hmsRadio.h @@ -0,0 +1,191 @@ +//----------------------------------------------------------------------------- +// 2023 Ahoy, https://github.com/lumpapu/ahoy +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +//----------------------------------------------------------------------------- + +#ifndef __HMS_RADIO_H__ +#define __HMS_RADIO_H__ + +#include "../utils/dbg.h" +#include "cmt2300a.h" + +typedef struct { + int8_t rssi; + uint8_t data[28]; +} hmsPacket_t; + +#define U32_B3(val) ((uint8_t)((val >> 24) & 0xff)) +#define U32_B2(val) ((uint8_t)((val >> 16) & 0xff)) +#define U32_B1(val) ((uint8_t)((val >> 8) & 0xff)) +#define U32_B0(val) ((uint8_t)((val ) & 0xff)) + +template +class HmsRadio { + typedef SPI SpiType; + typedef Cmt2300a CmtType; + public: + HmsRadio() { + mDtuSn = DTU_SN; + } + + void setup(bool genDtuSn = true) { + if(genDtuSn) + generateDtuSn(); + if(!mCmt.resetCMT()) + DPRINTLN(DBG_WARN, F("Initializing CMT2300A failed!")); + + mSendCnt = 0; + mRetransmits = 0 + mSerialDebug = false; + mIvIdChannelSet = NULL; + mIrqRcvd = false; + } + + void loop() { + mCmt.loop(); + if(!mIrqRcvd) + return; + mIrqRcvd = false; + getRx(); + } + + void tickSecond() { + if(NULL != mIvIdChannelSet) + prepareSwitchFreqCmd(mIvIdChannelSet); + } + + void handeIntr(void) { + mIrqRcvd = true; + } + + void enableDebug() { + mSerialDebug = true; + } + + void setIvBackChannel(const uint32_t *ivId) { + mIvIdChannelSet = ivId; + prepareSwitchFreqCmd(); + + } + + void prepareDevInformCmd(const uint32_t *ivId, uint8_t cmd, uint32_t ts, uint16_t alarmMesId, bool isRetransmit, uint8_t reqfld=TX_REQ_INFO) { // might not be necessary to add additional arg. + initPacket(ivId, reqfld, ALL_FRAMES); + mTxBuf[10] = cmd; + mTxBuf[12] = U32_B3(ts); + mTxBuf[13] = U32_B2(ts); + mTxBuf[14] = U32_B1(ts); + mTxBuf[15] = U32_B0(ts); + /*if (cmd == RealTimeRunData_Debug || cmd == AlarmData ) { + mTxBuf[18] = (alarmMesId >> 8) & 0xff; + mTxBuf[19] = (alarmMesId ) & 0xff; + }*/ + mCmt.swichChannel(true); + sendPacket(24, isRetransmit); + } + + inline void prepareSwitchChannelCmd(const uint32_t *ivId, uint8_t freqSel = 0x0c) { + /** freqSel: + * 0x0c: 863.00 MHz + * 0x0d: 863.24 MHz + * 0x0e: 863.48 MHz + * 0x0f: 863.72 MHz + * 0x10: 863.96 MHz + * */ + initPacket(ivId, 0x56, 0x02); + mTxBuf[10] = 0x15; + mTxBuf[11] = 0x21; + mTxBuf[12] = freqSel; + mTxBuf[13] = 0x14; + mCmt.swichChannel(); + sendPacket(14, false); + } + + void sendPacket(uint8_t len, bool isRetransmit) { + if (len > 14) { + uint16_t crc = ah::crc16(&mTxBuf[10], len - 10); + mTxBuf[len++] = (crc >> 8) & 0xff; + mTxBuf[len++] = (crc ) & 0xff; + } + mTxBuf[len] = ah::crc8(mTxBuf, len); + len++; + + if(mSerialDebug) { + DPRINT(DBG_INFO, F("TX ")); + DBGPRINT(String(len)); + DBGPRINT("B Ch"); + DBGPRINT(String(mRfChLst[mTxChIdx])); + DBGPRINT(F(" | ")); + ah::dumpBuf(mTxBuf, len); + } + + uint8_t status = mCmt.tx(mTxBuf, len); + if(CMT_SUCCESS != status) { + DPRINT(DBG_WARN, F("CMT TX failed, code: ")); + DBGPRINTLN(String(status)); + } + + if(isRetransmit) + mRetransmits++; + else + mSendCnt++; + } + + uint32_t mSendCnt; + uint32_t mRetransmits; + std::queue mBufCtrl; + + private: + void initPacket(const uint32_t *ivId, uint8_t mid, uint8_t pid) { + mTxBuf[0] = mid; + mTxBuf[1] = U32_B3(*ivId); + mTxBuf[2] = U32_B2(*ivId); + mTxBuf[3] = U32_B1(*ivId); + mTxBuf[4] = U32_B0(*ivId); + mTxBuf[5] = U32_B3(mDtuSn); + mTxBuf[6] = U32_B2(mDtuSn); + mTxBuf[7] = U32_B1(mDtuSn); + mTxBuf[8] = U32_B0(mDtuSn); + mTxBuf[9] = pid; + memset(&mTxBuf[10], 0x00, 17); + } + + inline void generateDtuSn(void) { + uint32_t chipID = 0; + #ifdef ESP32 + uint64_t MAC = ESP.getEfuseMac(); + chipID = ((MAC >> 8) & 0xFF0000) | ((MAC >> 24) & 0xFF00) | ((MAC >> 40) & 0xFF); + #endif + dtuSn = 0x80000000; // the first digit is an 8 for DTU production year 2022, the rest is filled with the ESP chipID in decimal + for(int i = 0; i < 7; i++) { + dtuSn |= (chipID % 10) << (i * 4); + chipID /= 10; + } + } + + inline void getRx(void) { + hmsPacket_t p; + uint8_t status = mCmt.checkRx(p.data, 28, &p.rssi); + if(CMT_SUCCESS == status) + mBufCtrl.push(p); + if(NULL != mIvIdChannelSet) { + if(U32_B3(*mIvIdChannelSet) != p.data[2]) + return; + if(U32_B2(*mIvIdChannelSet) != p.data[3]) + return; + if(U32_B1(*mIvIdChannelSet) != p.data[4]) + return; + if(U32_B0(*mIvIdChannelSet) != p.data[5]) + return; + *mIvIdChannelSet = NULL; + } + } + + CmtType mCmt; + uint32_t mDtuSn; + uint8_t[27] mTxBuf; + bool mSerialDebug; + uint32_t *mIvIdChannelSet; + bool mIrqRcvd; +}; + +#endif /*__HMS_RADIO_H__*/ diff --git a/src/hm/miPayload.h b/src/hm/miPayload.h index 9d294498..83e273e1 100644 --- a/src/hm/miPayload.h +++ b/src/hm/miPayload.h @@ -194,7 +194,7 @@ class MiPayload { if (mSerialDebug) { DPRINT(DBG_INFO, F("Payload (") + String(payloadLen) + "): "); - mSys->Radio.dumpBuf(payload, payloadLen); + ah::dumpBuf(payload, payloadLen); } if (NULL == rec) { diff --git a/src/main.cpp b/src/main.cpp index c585d0f2..677073f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,13 +15,18 @@ IRAM_ATTR void handleIntr(void) { myApp.handleIntr(); } +//----------------------------------------------------------------------------- +IRAM_ATTR void handleHmsIntr(void) { + myApp.handleHmsIntr(); +} + //----------------------------------------------------------------------------- void setup() { myApp.setup(); - // TODO: move to HmRadio attachInterrupt(digitalPinToInterrupt(myApp.getIrqPin()), handleIntr, FALLING); + attachInterrupt(digitalPinToInterrupt(myApp.getHmsIrqPin()), handleHmsIntr, RISING); } diff --git a/src/utils/helper.cpp b/src/utils/helper.cpp index 97d7418b..95f330e9 100644 --- a/src/utils/helper.cpp +++ b/src/utils/helper.cpp @@ -4,6 +4,7 @@ //----------------------------------------------------------------------------- #include "helper.h" +#include "dbg.h" namespace ah { void ip2Arr(uint8_t ip[], const char *ipStr) { @@ -64,4 +65,12 @@ namespace ah { } return ret; } + + void dumpBuf(uint8_t buf[], uint8_t len) { + for(uint8_t i = 0; i < len; i++) { + DHEX(buf[i]); + DBGPRINT(" "); + } + DBGPRINTLN(""); + } } diff --git a/src/utils/helper.h b/src/utils/helper.h index 179e6078..f7cad96b 100644 --- a/src/utils/helper.h +++ b/src/utils/helper.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://ahoydtu.de +// 2023 Ahoy, https://ahoydtu.de // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -23,6 +23,7 @@ namespace ah { String getDateTimeStr(time_t t); String getTimeStr(time_t t); uint64_t Serial2u64(const char *val); + void dumpBuf(uint8_t buf[], uint8_t len); } #endif /*__HELPER_H__*/