//----------------------------------------------------------------------------- // 2024 Ahoy, https://github.com/lumpapu/ahoy // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed //----------------------------------------------------------------------------- #ifndef __RADIO_H__ #define __RADIO_H__ #define TX_REQ_INFO 0x15 #define TX_REQ_DEVCONTROL 0x51 #define ALL_FRAMES 0x80 #define SINGLE_FRAME 0x81 #include <array> #include <atomic> #include "../utils/dbg.h" #include "../utils/crc.h" #include "../utils/timemonitor.h" enum { IRQ_UNKNOWN = 0, IRQ_OK, IRQ_ERROR }; // forward declaration of class template <class REC_TYP=float> class Inverter; // abstract radio interface class Radio { public: virtual void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit) = 0; virtual bool switchFrequency(Inverter<> *iv, uint32_t fromkHz, uint32_t tokHz) { return true; } virtual bool switchFrequencyCh(Inverter<> *iv, uint8_t fromCh, uint8_t toCh) { return true; } virtual bool isChipConnected(void) const { return false; } virtual uint16_t getBaseFreqMhz() { return 0; } virtual uint16_t getBootFreqMhz() { return 0; } virtual std::pair<uint16_t,uint16_t> getFreqRangeMhz(void) { return std::make_pair(0, 0); } virtual bool loop(void) = 0; Radio() : mTxBuf{} {} void handleIntr(void) { mIrqRcvd = true; mIrqOk = IRQ_OK; } void sendCmdPacket(Inverter<> *iv, uint8_t mid, uint8_t pid, bool isRetransmit, bool appendCrc16=true) { initPacket(getIvId(iv), mid, pid); sendPacket(iv, 10, isRetransmit, appendCrc16); } void prepareDevInformCmd(Inverter<> *iv, 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. if(IV_MI == getIvGen(iv)) { if(*mSerialDebug) { DPRINT(DBG_DEBUG, F("legacy cmd 0x")); DPRINTLN(DBG_DEBUG,String(cmd, HEX)); } sendCmdPacket(iv, cmd, cmd, false, false); return; } if(*mSerialDebug) { DPRINT(DBG_DEBUG, F("prepareDevInformCmd 0x")); DPRINTLN(DBG_DEBUG,String(cmd, HEX)); } initPacket(getIvId(iv), reqfld, ALL_FRAMES); mTxBuf[10] = cmd; CP_U32_LittleEndian(&mTxBuf[12], ts); if (cmd == AlarmData ) { //cmd == RealTimeRunData_Debug || mTxBuf[18] = (alarmMesId >> 8) & 0xff; mTxBuf[19] = (alarmMesId ) & 0xff; } sendPacket(iv, 24, isRetransmit); } uint32_t getDTUSn(void) const { return mDtuSn; } void setExpectedFrames(uint8_t framesExpected) { mFramesExpected = framesExpected; } public: std::queue<packet_t> mBufCtrl; uint8_t mIrqOk = IRQ_UNKNOWN; TimeMonitor mRadioWaitTime = TimeMonitor(0, true); // start as expired (due to code in RESET state) uint8_t mTxRetriesNext = 15; // let heuristics tell us the next reties count (for nRF type radios only) uint8_t mFramesExpected = 0x0c; protected: virtual void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) = 0; virtual uint64_t getIvId(Inverter<> *iv) const = 0; virtual uint8_t getIvGen(Inverter<> *iv) const = 0; void initPacket(uint64_t ivId, uint8_t mid, uint8_t pid) { mTxBuf[0] = mid; CP_U32_BigEndian(&mTxBuf[1], ivId >> 8); CP_U32_LittleEndian(&mTxBuf[5], mDtuSn); mTxBuf[9] = pid; memset(&mTxBuf[10], 0x00, (MAX_RF_PAYLOAD_SIZE-10)); if(IRQ_UNKNOWN == mIrqOk) mIrqOk = IRQ_ERROR; } void updateCrcs(uint8_t *len, bool appendCrc16=true) { // append crc's if (appendCrc16 && ((*len) > 10)) { // crc control data uint16_t crc = ah::crc16(&mTxBuf[10], (*len) - 10); mTxBuf[(*len)++] = (crc >> 8) & 0xff; mTxBuf[(*len)++] = (crc ) & 0xff; } // crc over all mTxBuf[*len] = ah::crc8(mTxBuf.data(), *len); (*len)++; } void generateDtuSn(void) { uint32_t chipID = 0; #ifdef ESP32 chipID = (ESP.getEfuseMac() & 0xffffffff); #else chipID = ESP.getChipId(); #endif mDtuSn = 0; for(int i = 0; i < (7 << 2); i += 4) { uint8_t t = (chipID >> i) & 0x0f; if(t > 0x09) t -= 6; mDtuSn |= (t << i); } mDtuSn |= 0x80000000; // the first digit is an 8 for DTU production year 2022, the rest is filled with the ESP chipID in decimal } protected: uint32_t mDtuSn = 0; std::atomic<bool> mIrqRcvd = false; bool *mSerialDebug = nullptr, *mPrivacyMode = nullptr, *mPrintWholeTrace = nullptr; std::array<uint8_t, MAX_RF_PAYLOAD_SIZE> mTxBuf; }; #endif /*__RADIO_H__*/