mirror of https://github.com/lumapu/ahoy.git
				
				
			
			
			
				Browse Source
			
			
			
			
				
		https://www.mikrocontroller.net/topic/525778?page=4#7048605 ersetzt NRF24_SendRcv Kommentare dazu: - Projekt jetzt umgenannt in HoyDtuSim (Hoymiles DTU Simulation) -Läuft auf Arduino (bei mir auf Pro Mini) und ESP (Wemos D1 mini), je nachdem wie man kompiliert - Channel hopping für senden und Empfangen (poor man's ...) ist eingebaut und bringt konstante Antworten; obige Erkenntnisse über Kanäle abwärts sind noch nicht eingebaut - da manchmal ein Abbruch der RF-Verbindung vorkam (auch schon oben erwähnt) wird jetzt nach ca 50 Sekunden ohne Empfang das RF-Modul neu initialisiert und es geht problemlos weiter - Definitionen für HM-600 und HM-1200 sind implementiert, andere können anhand der beiden Beispiele sicher leicht impl. werden - Anpassungen sind in der Settings.h zu machenpull/16/head
				 12 changed files with 1959 additions and 0 deletions
			
			
		@ -0,0 +1,158 @@ | 
				
			|||||
 | 
					/*
 | 
				
			||||
 | 
					  CircularBuffer - An Arduino circular buffering library for arbitrary types. | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  Created by Ivo Pullens, Emmission, 2014 -- www.emmission.nl | 
				
			||||
 | 
					   | 
				
			||||
 | 
					  This library is free software; you can redistribute it and/or | 
				
			||||
 | 
					  modify it under the terms of the GNU Lesser General Public | 
				
			||||
 | 
					  License as published by the Free Software Foundation; either | 
				
			||||
 | 
					  version 2.1 of the License, or (at your option) any later version. | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  This library 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 | 
				
			||||
 | 
					  Lesser General Public License for more details. | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  You should have received a copy of the GNU Lesser General Public | 
				
			||||
 | 
					  License along with this library; if not, write to the Free Software | 
				
			||||
 | 
					  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | 
				
			||||
 | 
					*/ | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#ifndef CircularBuffer_h | 
				
			||||
 | 
					#define CircularBuffer_h | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#ifdef ESP8266 | 
				
			||||
 | 
					#define DISABLE_IRQ noInterrupts() | 
				
			||||
 | 
					#define RESTORE_IRQ interrupts() | 
				
			||||
 | 
					#else | 
				
			||||
 | 
					#define DISABLE_IRQ       \ | 
				
			||||
 | 
					  uint8_t sreg = SREG;    \ | 
				
			||||
 | 
					  cli(); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define RESTORE_IRQ        \ | 
				
			||||
 | 
					  SREG = sreg; | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					template <class T> class CircularBuffer | 
				
			||||
 | 
					{ | 
				
			||||
 | 
					  public: | 
				
			||||
 | 
					    /** Constructor
 | 
				
			||||
 | 
					     * @param buffer   Preallocated buffer of at least size records. | 
				
			||||
 | 
					     * @param size     Number of records available in the buffer. | 
				
			||||
 | 
					     */ | 
				
			||||
 | 
					    CircularBuffer(T* buffer, const uint8_t size ) | 
				
			||||
 | 
					      : m_size(size), m_buff(buffer) | 
				
			||||
 | 
					    { | 
				
			||||
 | 
					      clear(); | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    /** Clear all entries in the circular buffer. */ | 
				
			||||
 | 
					    void clear(void) | 
				
			||||
 | 
					    { | 
				
			||||
 | 
					      m_front = 0; | 
				
			||||
 | 
					      m_fill  = 0; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    /** Test if the circular buffer is empty */ | 
				
			||||
 | 
					    inline bool empty(void) const | 
				
			||||
 | 
					    { | 
				
			||||
 | 
					      return !m_fill; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    /** Return the number of records stored in the buffer */ | 
				
			||||
 | 
					    inline uint8_t available(void) const | 
				
			||||
 | 
					    { | 
				
			||||
 | 
					      return m_fill; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    /** Test if the circular buffer is full */ | 
				
			||||
 | 
					    inline bool full(void) const | 
				
			||||
 | 
					    { | 
				
			||||
 | 
					      return m_fill == m_size; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					     | 
				
			||||
 | 
					    /** Aquire record on front of the buffer, for writing.
 | 
				
			||||
 | 
					     * After filling the record, it has to be pushed to actually | 
				
			||||
 | 
					     * add it to the buffer. | 
				
			||||
 | 
					     * @return Pointer to record, or NULL when buffer is full. | 
				
			||||
 | 
					     */ | 
				
			||||
 | 
					    T* getFront(void) const | 
				
			||||
 | 
					    { | 
				
			||||
 | 
					      DISABLE_IRQ; | 
				
			||||
 | 
					      T* f = NULL; | 
				
			||||
 | 
					      if (!full()) | 
				
			||||
 | 
					        f = get(m_front); | 
				
			||||
 | 
					      RESTORE_IRQ; | 
				
			||||
 | 
					      return f; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					     | 
				
			||||
 | 
					    /** Push record to front of the buffer
 | 
				
			||||
 | 
					     * @param record   Record to push. If record was aquired previously (using getFront) its | 
				
			||||
 | 
					     *                 data will not be copied as it is already present in the buffer. | 
				
			||||
 | 
					     * @return True, when record was pushed successfully. | 
				
			||||
 | 
					     */ | 
				
			||||
 | 
					    bool pushFront(T* record) | 
				
			||||
 | 
					    { | 
				
			||||
 | 
					      bool ok = false; | 
				
			||||
 | 
					      DISABLE_IRQ; | 
				
			||||
 | 
					      if (!full()) | 
				
			||||
 | 
					      { | 
				
			||||
 | 
					        T* f = get(m_front); | 
				
			||||
 | 
					        if (f != record) | 
				
			||||
 | 
					          *f = *record; | 
				
			||||
 | 
					        m_front = (m_front+1) % m_size; | 
				
			||||
 | 
					        m_fill++; | 
				
			||||
 | 
					        ok = true; | 
				
			||||
 | 
					      } | 
				
			||||
 | 
					      RESTORE_IRQ; | 
				
			||||
 | 
					      return ok; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    /** Aquire record on back of the buffer, for reading.
 | 
				
			||||
 | 
					     * After reading the record, it has to be pop'ed to actually | 
				
			||||
 | 
					     * remove it from the buffer. | 
				
			||||
 | 
					     * @return Pointer to record, or NULL when buffer is empty. | 
				
			||||
 | 
					     */ | 
				
			||||
 | 
					    T* getBack(void) const | 
				
			||||
 | 
					    { | 
				
			||||
 | 
					      T* b = NULL; | 
				
			||||
 | 
					      DISABLE_IRQ; | 
				
			||||
 | 
					      if (!empty()) | 
				
			||||
 | 
					        b = get(back()); | 
				
			||||
 | 
					      RESTORE_IRQ; | 
				
			||||
 | 
					      return b; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    /** Remove record from back of the buffer.
 | 
				
			||||
 | 
					     * @return True, when record was pop'ed successfully. | 
				
			||||
 | 
					     */ | 
				
			||||
 | 
					    bool popBack(void) | 
				
			||||
 | 
					    { | 
				
			||||
 | 
					      bool ok = false; | 
				
			||||
 | 
					      DISABLE_IRQ; | 
				
			||||
 | 
					      if (!empty()) | 
				
			||||
 | 
					      { | 
				
			||||
 | 
					        m_fill--; | 
				
			||||
 | 
					        ok = true; | 
				
			||||
 | 
					      } | 
				
			||||
 | 
					      RESTORE_IRQ; | 
				
			||||
 | 
					      return ok; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					     | 
				
			||||
 | 
					  protected: | 
				
			||||
 | 
					    inline T * get(const uint8_t idx) const | 
				
			||||
 | 
					    { | 
				
			||||
 | 
					      return &(m_buff[idx]); | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					    inline uint8_t back(void) const | 
				
			||||
 | 
					    { | 
				
			||||
 | 
					      return (m_front - m_fill + m_size) % m_size; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    const uint8_t      m_size;     // Total number of records that can be stored in the buffer.
 | 
				
			||||
 | 
					    T* const           m_buff;     // Ptr to buffer holding all records.
 | 
				
			||||
 | 
					    volatile uint8_t   m_front;    // Index of front element (not pushed yet).
 | 
				
			||||
 | 
					    volatile uint8_t   m_fill;     // Amount of records currently pushed.
 | 
				
			||||
 | 
					}; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#endif // CircularBuffer_h
 | 
				
			||||
@ -0,0 +1,23 @@ | 
				
			|||||
 | 
					#ifndef __DEBUG_H | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define __DEBUG_H | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#ifdef DEBUG | 
				
			||||
 | 
					  #define DEBUG_OUT  Serial | 
				
			||||
 | 
					#else  | 
				
			||||
 | 
					//---
 | 
				
			||||
 | 
					// disable Serial DEBUG output
 | 
				
			||||
 | 
					  #define DEBUG_OUT DummySerial | 
				
			||||
 | 
					  static class { | 
				
			||||
 | 
					  public: | 
				
			||||
 | 
					      void begin(...) {} | 
				
			||||
 | 
					      void print(...) {} | 
				
			||||
 | 
					      void println(...) {} | 
				
			||||
 | 
					      void flush() {} | 
				
			||||
 | 
					      bool available() { return false;} | 
				
			||||
 | 
					      int  readBytes(...) { return 0;} | 
				
			||||
 | 
					      int  printf (...) {return 0;} | 
				
			||||
 | 
					  } DummySerial; | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#endif  | 
				
			||||
@ -0,0 +1,38 @@ | 
				
			|||||
 | 
					#ifndef __HM1200_H | 
				
			||||
 | 
					#define __HM1200_H | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define HM1200 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					const measureDef_t hm1200_measureDef[] = { | 
				
			||||
 | 
					    { IDX_UDC,      UNIT_V,   CH1, CMD01, 14, BYTES2, DIV10   }, | 
				
			||||
 | 
					    { IDX_IDC,      UNIT_A,   CH1, CMD01, 16, BYTES2, DIV100  }, | 
				
			||||
 | 
					    { IDX_PDC,      UNIT_W,   CH1, CMD01, 20, BYTES2, DIV10   }, | 
				
			||||
 | 
					    { IDX_E_TAG,    UNIT_WH,  CH1, CMD02, 16, BYTES2, DIV1    }, | 
				
			||||
 | 
					    { IDX_E_TOTAL,  UNIT_KWH, CH1, CMD01, 24, BYTES4, DIV1000 }, | 
				
			||||
 | 
					    { IDX_UDC,      UNIT_V,   CH2, CMD02, 20, BYTES2, DIV10   }, | 
				
			||||
 | 
					    { IDX_IDC,      UNIT_A,   CH2, CMD01, 18, BYTES2, DIV100  }, | 
				
			||||
 | 
					    { IDX_PDC,      UNIT_W,   CH2, CMD01, 22, BYTES2, DIV10   }, | 
				
			||||
 | 
					    { IDX_E_TAG,    UNIT_WH,  CH2, CMD02, 18, BYTES2, DIV1    }, | 
				
			||||
 | 
					    { IDX_E_TOTAL,  UNIT_KWH, CH2, CMD02, 12, BYTES4, DIV1000 }, | 
				
			||||
 | 
					    { IDX_IDC,      UNIT_A,   CH3, CMD02, 22, BYTES2, DIV100  }, | 
				
			||||
 | 
					    { IDX_PDC,      UNIT_W,   CH3, CMD02, 26, BYTES2, DIV10   }, | 
				
			||||
 | 
					    { IDX_E_TAG,    UNIT_WH,  CH3, CMD03, 22, BYTES2, DIV1    }, | 
				
			||||
 | 
					    { IDX_E_TOTAL,  UNIT_KWH, CH3, CMD03, 14, BYTES4, DIV1000 }, | 
				
			||||
 | 
					    { IDX_IDC,      UNIT_A,   CH4, CMD02, 24, BYTES2, DIV100  }, | 
				
			||||
 | 
					    { IDX_PDC,      UNIT_W,   CH4, CMD03, 12, BYTES2, DIV10   }, | 
				
			||||
 | 
					    { IDX_E_TAG,    UNIT_WH,  CH4, CMD03, 24, BYTES2, DIV1    }, | 
				
			||||
 | 
					    { IDX_E_TOTAL,  UNIT_KWH, CH4, CMD03, 18, BYTES4, DIV1000 }, | 
				
			||||
 | 
					    { IDX_UAC,      UNIT_V,   CH0, CMD03, 26, BYTES2, DIV10   }, | 
				
			||||
 | 
					    { IDX_IPV,      UNIT_A,   CH0, CMD84, 18, BYTES2, DIV100  }, | 
				
			||||
 | 
					    { IDX_PAC,      UNIT_W,   CH0, CMD84, 14, BYTES2, DIV10   }, | 
				
			||||
 | 
					    { IDX_FREQ,     UNIT_HZ,  CH0, CMD84, 12, BYTES2, DIV100  }, | 
				
			||||
 | 
					    { IDX_PERCNT,   UNIT_PCT, CH0, CMD84, 20, BYTES2, DIV10   }, | 
				
			||||
 | 
					    { IDX_WR_TEMP,  UNIT_C,   CH0, CMD84, 22, BYTES2, DIV10   } | 
				
			||||
 | 
					}; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					measureCalc_t hm1200_measureCalc[] = {}; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define HM1200_MEASURE_LIST_LEN  sizeof(hm1200_measureDef)/sizeof(measureDef_t) | 
				
			||||
 | 
					#define HM1200_CALCED_LIST_LEN    0 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#endif | 
				
			||||
@ -0,0 +1,37 @@ | 
				
			|||||
 | 
					#ifndef __HM600_H | 
				
			||||
 | 
					#define __HM600_H | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define HM600 | 
				
			||||
 | 
					#define HM700 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					float calcEheute (float *measure) { return measure[8] + measure[9]; } | 
				
			||||
 | 
					float calcIpv    (float *measure) { return (measure[10] != 0 ? measure[12]/measure[10] : 0); } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					const measureDef_t hm600_measureDef[] = { | 
				
			||||
 | 
					  { IDX_UDC,    CH1, UNIT_V,  CMD01, 14, BYTES2, DIV10}, | 
				
			||||
 | 
					  { IDX_IDC,    CH1, UNIT_A,  CMD01, 16, BYTES2, DIV100}, | 
				
			||||
 | 
					  { IDX_PDC,    CH1, UNIT_W,  CMD01, 18, BYTES2, DIV10}, | 
				
			||||
 | 
					  { IDX_UDC,    CH2, UNIT_V,  CMD01, 20, BYTES2, DIV10}, | 
				
			||||
 | 
					  { IDX_IDC,    CH2, UNIT_A,  CMD01, 22, BYTES2, DIV100}, | 
				
			||||
 | 
					  { IDX_PDC,    CH2, UNIT_W,  CMD01, 24, BYTES2, DIV10}, | 
				
			||||
 | 
					  { IDX_E_WOCHE,CH0, UNIT_WH, CMD02, 12, BYTES2, DIV1}, | 
				
			||||
 | 
					  { IDX_E_TOTAL,CH0, UNIT_WH, CMD02, 14, BYTES4, DIV1}, | 
				
			||||
 | 
					  { IDX_E_TAG,  CH1, UNIT_WH, CMD02, 18, BYTES2, DIV1}, | 
				
			||||
 | 
					  { IDX_E_TAG,  CH2, UNIT_WH, CMD02, 20, BYTES2, DIV1}, | 
				
			||||
 | 
					  { IDX_UAC,    CH0, UNIT_V,  CMD02, 22, BYTES2, DIV10}, | 
				
			||||
 | 
					  { IDX_FREQ,   CH0, UNIT_HZ, CMD02, 24, BYTES2, DIV100}, | 
				
			||||
 | 
					  { IDX_PAC,    CH0, UNIT_W,  CMD02, 26, BYTES2, DIV10}, | 
				
			||||
 | 
					  { IDX_WR_TEMP,CH0, UNIT_C,  CMD83, 18, BYTES2, DIV10}      | 
				
			||||
 | 
					}; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					measureCalc_t hm600_measureCalc[] = { | 
				
			||||
 | 
					  { IDX_E_HEUTE,  UNIT_WH, DIV1,   &calcEheute}, | 
				
			||||
 | 
					  { IDX_IPV,      UNIT_A,  DIV100, &calcIpv} | 
				
			||||
 | 
					}; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define HM600_MEASURE_LIST_LEN  sizeof(hm600_measureDef)/sizeof(measureDef_t) | 
				
			||||
 | 
					#define HM600_CALCED_LIST_LEN   sizeof(hm600_measureCalc)/sizeof(measureCalc_t) | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#endif | 
				
			||||
@ -0,0 +1,605 @@ | 
				
			|||||
 | 
					#include <Arduino.h> | 
				
			||||
 | 
					#include <SPI.h> | 
				
			||||
 | 
					#include "CircularBuffer.h" | 
				
			||||
 | 
					#include <RF24.h> | 
				
			||||
 | 
					#include "printf.h" | 
				
			||||
 | 
					#include <RF24_config.h> | 
				
			||||
 | 
					#include "hm_crc.h" | 
				
			||||
 | 
					#include "hm_packets.h" | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#include "Settings.h"     // Header für Einstellungen | 
				
			||||
 | 
					#include "Debug.h" | 
				
			||||
 | 
					#include "Inverters.h" | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					const char VERSION[] PROGMEM = "0.1.6"; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#ifdef ESP8266 | 
				
			||||
 | 
					  #define DISABLE_EINT noInterrupts() | 
				
			||||
 | 
					  #define ENABLE_EINT  interrupts() | 
				
			||||
 | 
					#else     // für AVR z.B. ProMini oder Nano
 | 
				
			||||
 | 
					  #define DISABLE_EINT EIMSK = 0x00 | 
				
			||||
 | 
					  #define ENABLE_EINT EIMSK = 0x01 | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#ifdef ESP8266 | 
				
			||||
 | 
					#define PACKET_BUFFER_SIZE      (30)  | 
				
			||||
 | 
					#else | 
				
			||||
 | 
					#define PACKET_BUFFER_SIZE      (20)  | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// Startup defaults until user reconfigures it
 | 
				
			||||
 | 
					//#define DEFAULT_RECV_CHANNEL    (3)             // 3 = Default channel for Hoymiles
 | 
				
			||||
 | 
					//#define DEFAULT_SEND_CHANNEL  (75)            // 40 = Default channel for Hoymiles, 61
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					static HM_Packets     hmPackets; | 
				
			||||
 | 
					static uint32_t       tickMillis; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// Set up nRF24L01 radio on SPI bus plus CE/CS pins
 | 
				
			||||
 | 
					// If more than one RF24 unit is used the another CS pin than 10 must be used
 | 
				
			||||
 | 
					// This pin is used hard coded in SPI library
 | 
				
			||||
 | 
					static RF24 Radio (RF1_CE_PIN, RF1_CS_PIN); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					static NRF24_packet_t bufferData[PACKET_BUFFER_SIZE]; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					static CircularBuffer<NRF24_packet_t> packetBuffer(bufferData, sizeof(bufferData) / sizeof(bufferData[0])); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					static Serial_header_t SerialHdr; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define CHECKCRC  1 | 
				
			||||
 | 
					static uint16_t lastCRC; | 
				
			||||
 | 
					static uint16_t crc; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					uint8_t         channels[]            = {3, 23, 40, 61, 75};   //{1, 3, 6, 9, 11, 23, 40, 61, 75}
 | 
				
			||||
 | 
					uint8_t         channelIdx            = 2;                         // fange mit 40 an
 | 
				
			||||
 | 
					uint8_t         DEFAULT_SEND_CHANNEL  = channels[channelIdx];      // = 40
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#if USE_POOR_MAN_CHANNEL_HOPPING_RCV | 
				
			||||
 | 
					uint8_t         rcvChannelIdx         = 0;  | 
				
			||||
 | 
					uint8_t         rcvChannels[]         = {3, 23, 40, 61, 75};   //{1, 3, 6, 9, 11, 23, 40, 61, 75}
 | 
				
			||||
 | 
					uint8_t         DEFAULT_RECV_CHANNEL  = rcvChannels[rcvChannelIdx];      //3;
 | 
				
			||||
 | 
					uint8_t         intvl = 4;          // Zeit für poor man hopping
 | 
				
			||||
 | 
					int             hophop; | 
				
			||||
 | 
					#else | 
				
			||||
 | 
					uint8_t         DEFAULT_RECV_CHANNEL  = 3; | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					boolean         valueChanged          = false; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					static unsigned long timeLastPacket = millis(); | 
				
			||||
 | 
					static unsigned long timeLastIstTagCheck =  millis(); | 
				
			||||
 | 
					static unsigned long timeLastRcvChannelSwitch = millis(); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// Function forward declaration
 | 
				
			||||
 | 
					static void SendPacket(uint64_t dest, uint8_t *buf, uint8_t len); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					static const char BLANK = ' '; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					static boolean istTag = true; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					char CHANNELNAME_BUFFER[15]; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#ifdef ESP8266 | 
				
			||||
 | 
					  #include "wifi.h" | 
				
			||||
 | 
					  #include "ModWebserver.h" | 
				
			||||
 | 
					  #include "Sonne.h" | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					inline static void dumpData(uint8_t *p, int len) { | 
				
			||||
 | 
					//-----------------------------------------------
 | 
				
			||||
 | 
					  while (len > 0){ | 
				
			||||
 | 
					    if (*p < 16) | 
				
			||||
 | 
					      DEBUG_OUT.print(F("0")); | 
				
			||||
 | 
					    DEBUG_OUT.print(*p++, HEX); | 
				
			||||
 | 
					    len--; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  DEBUG_OUT.print(BLANK); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					float extractValue2 (uint8_t *p, int divisor) { | 
				
			||||
 | 
					//-------------------------------------------
 | 
				
			||||
 | 
					  uint16_t b1 = *p++; | 
				
			||||
 | 
					  return ((float) (b1 << 8) + *p) / (float) divisor; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					float extractValue4 (uint8_t *p, int divisor) { | 
				
			||||
 | 
					//-------------------------------------------
 | 
				
			||||
 | 
					  uint32_t ret  = *p++; | 
				
			||||
 | 
					  for (uint8_t i = 1; i <= 3; i++) | 
				
			||||
 | 
					    ret = (ret << 8) + *p++; | 
				
			||||
 | 
					  return (ret / divisor); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void outChannel (uint8_t wr, uint8_t i) { | 
				
			||||
 | 
					//------------------------------------
 | 
				
			||||
 | 
					  DEBUG_OUT.print(getMeasureName(wr, i));  | 
				
			||||
 | 
					  DEBUG_OUT.print(F("\t:"));  | 
				
			||||
 | 
					  DEBUG_OUT.print(getMeasureValue(wr,i));  | 
				
			||||
 | 
					  DEBUG_OUT.println(BLANK);   | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void analyseWords (uint8_t *p) {    // p zeigt auf 01 hinter 2. WR-Adr
 | 
				
			||||
 | 
					//----------------------------------
 | 
				
			||||
 | 
					  //uint16_t val;
 | 
				
			||||
 | 
					  DEBUG_OUT.print (F("analyse words:")); | 
				
			||||
 | 
					  p++; | 
				
			||||
 | 
					  for (int i = 0; i <12;i++) { | 
				
			||||
 | 
					    DEBUG_OUT.print(extractValue2(p,1)); | 
				
			||||
 | 
					    DEBUG_OUT.print(BLANK); | 
				
			||||
 | 
					    p++; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  DEBUG_OUT.println(); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void analyseLongs (uint8_t *p) {    // p zeigt auf 01 hinter 2. WR-Adr
 | 
				
			||||
 | 
					//----------------------------------
 | 
				
			||||
 | 
					  //uint16_t val;
 | 
				
			||||
 | 
					  DEBUG_OUT.print (F("analyse longs:")); | 
				
			||||
 | 
					  p++; | 
				
			||||
 | 
					  for (int i = 0; i <12;i++) { | 
				
			||||
 | 
					    DEBUG_OUT.print(extractValue4(p,1)); | 
				
			||||
 | 
					    DEBUG_OUT.print(BLANK); | 
				
			||||
 | 
					    p++; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  DEBUG_OUT.println(); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void analyse (NRF24_packet_t *p) { | 
				
			||||
 | 
					//------------------------------
 | 
				
			||||
 | 
					  uint8_t wrIdx = findInverter (&p->packet[3]); | 
				
			||||
 | 
					  //DEBUG_OUT.print ("wrIdx="); DEBUG_OUT.println (wrIdx);
 | 
				
			||||
 | 
					  if (wrIdx == 0xFF) return; | 
				
			||||
 | 
					  uint8_t cmd = p->packet[11]; | 
				
			||||
 | 
					  float val = 0; | 
				
			||||
 | 
					  if (cmd == 0x01 || cmd == 0x02 || cmd == 0x83) { | 
				
			||||
 | 
					    const measureDef_t *defs = inverters[wrIdx].measureDef; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    for (uint8_t i = 0; i < inverters[wrIdx].anzMeasures; i++) { | 
				
			||||
 | 
					      if (defs[i].teleId == cmd) { | 
				
			||||
 | 
					        uint8_t pos = defs[i].pos; | 
				
			||||
 | 
					        if (defs[i].bytes == 2)   | 
				
			||||
 | 
					          val = extractValue2 (&p->packet[pos], getDivisor(wrIdx, i) ); | 
				
			||||
 | 
					        else if (defs[i].bytes == 4) | 
				
			||||
 | 
					          val = extractValue4 (&p->packet[pos], getDivisor(wrIdx, i) ); | 
				
			||||
 | 
					        valueChanged = valueChanged ||(val != inverters[wrIdx].values[i]); | 
				
			||||
 | 
					        inverters[wrIdx].values[i] = val; | 
				
			||||
 | 
					      }       | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					    // calculated funstions
 | 
				
			||||
 | 
					    for (uint8_t i = 0; i < inverters[wrIdx].anzMeasureCalculated; i++) { | 
				
			||||
 | 
					      val = inverters[wrIdx].measureCalculated[i].f (inverters[wrIdx].values); | 
				
			||||
 | 
					      int idx = inverters[wrIdx].anzMeasures + i; | 
				
			||||
 | 
					      valueChanged = valueChanged ||(val != inverters[wrIdx].values[idx]); | 
				
			||||
 | 
					      inverters[wrIdx].values[idx] = val; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  else if (cmd == 0x81) { | 
				
			||||
 | 
					    ; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  else { | 
				
			||||
 | 
					    DEBUG_OUT.print (F("---- neues cmd=")); DEBUG_OUT.println(cmd, HEX); | 
				
			||||
 | 
					    analyseWords (&p->packet[11]); | 
				
			||||
 | 
					    analyseLongs (&p->packet[11]); | 
				
			||||
 | 
					    DEBUG_OUT.println(); | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  if (p->packetsLost > 0) { | 
				
			||||
 | 
					    DEBUG_OUT.print(F(" Lost: ")); | 
				
			||||
 | 
					    DEBUG_OUT.println(p->packetsLost); | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#ifdef ESP8266 | 
				
			||||
 | 
					IRAM_ATTR | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
					void handleNrf1Irq() { | 
				
			||||
 | 
					//-------------------------
 | 
				
			||||
 | 
					  static uint8_t lostPacketCount = 0; | 
				
			||||
 | 
					  uint8_t pipe; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  DISABLE_EINT; | 
				
			||||
 | 
					   | 
				
			||||
 | 
					  // Loop until RX buffer(s) contain no more packets.
 | 
				
			||||
 | 
					  while (Radio.available(&pipe)) { | 
				
			||||
 | 
					    if (!packetBuffer.full()) { | 
				
			||||
 | 
					      NRF24_packet_t *p = packetBuffer.getFront(); | 
				
			||||
 | 
					      p->timestamp = micros(); // Micros does not increase in interrupt, but it can be used.
 | 
				
			||||
 | 
					      p->packetsLost = lostPacketCount; | 
				
			||||
 | 
					      p->rcvChannel  = DEFAULT_RECV_CHANNEL; | 
				
			||||
 | 
					      uint8_t packetLen = Radio.getPayloadSize(); | 
				
			||||
 | 
					      if (packetLen > MAX_RF_PAYLOAD_SIZE) | 
				
			||||
 | 
					        packetLen = MAX_RF_PAYLOAD_SIZE; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					      Radio.read(p->packet, packetLen); | 
				
			||||
 | 
					      packetBuffer.pushFront(p); | 
				
			||||
 | 
					      lostPacketCount = 0; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					    else { | 
				
			||||
 | 
					      // Buffer full. Increase lost packet counter.
 | 
				
			||||
 | 
					      bool tx_ok, tx_fail, rx_ready; | 
				
			||||
 | 
					      if (lostPacketCount < 255) | 
				
			||||
 | 
					        lostPacketCount++; | 
				
			||||
 | 
					      // Call 'whatHappened' to reset interrupt status.
 | 
				
			||||
 | 
					      Radio.whatHappened(tx_ok, tx_fail, rx_ready); | 
				
			||||
 | 
					      // Flush buffer to drop the packet.
 | 
				
			||||
 | 
					      Radio.flush_rx(); | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  ENABLE_EINT; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					static void activateConf(void) { | 
				
			||||
 | 
					//-----------------------------
 | 
				
			||||
 | 
					  Radio.begin(); | 
				
			||||
 | 
					  // Disable shockburst for receiving and decode payload manually
 | 
				
			||||
 | 
					  Radio.setAutoAck(false); | 
				
			||||
 | 
					  Radio.setRetries(0, 0); | 
				
			||||
 | 
					  Radio.setChannel(DEFAULT_RECV_CHANNEL); | 
				
			||||
 | 
					  Radio.setDataRate(DEFAULT_RF_DATARATE); | 
				
			||||
 | 
					  Radio.disableCRC(); | 
				
			||||
 | 
					  Radio.setAutoAck(0x00); | 
				
			||||
 | 
					  Radio.setPayloadSize(MAX_RF_PAYLOAD_SIZE); | 
				
			||||
 | 
					  Radio.setAddressWidth(5); | 
				
			||||
 | 
					  Radio.openReadingPipe(1, DTU_RADIO_ID); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  // We want only RX irqs
 | 
				
			||||
 | 
					  Radio.maskIRQ(true, true, false); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  // Use lo PA level, as a higher level will disturb CH340 DEBUG_OUT usb adapter
 | 
				
			||||
 | 
					  Radio.setPALevel(RF24_PA_MAX); | 
				
			||||
 | 
					  Radio.startListening(); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  // Attach interrupt handler to NRF IRQ output. Overwrites any earlier handler.
 | 
				
			||||
 | 
					  attachInterrupt(digitalPinToInterrupt(RF1_IRQ_PIN), handleNrf1Irq, FALLING); // NRF24 Irq pin is active low.
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  // Initialize SerialHdr header's address member to promiscuous address.
 | 
				
			||||
 | 
					  uint64_t addr = DTU_RADIO_ID; | 
				
			||||
 | 
					  for (int8_t i = sizeof(SerialHdr.address) - 1; i >= 0; --i) { | 
				
			||||
 | 
					    SerialHdr.address[i] = addr; | 
				
			||||
 | 
					    addr >>= 8; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  //Radio.printDetails();
 | 
				
			||||
 | 
					  //DEBUG_OUT.println();
 | 
				
			||||
 | 
					  tickMillis = millis() + 200; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define resetRF24() activateConf() | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void setup(void) { | 
				
			||||
 | 
					//--------------
 | 
				
			||||
 | 
					  #ifndef DEBUG | 
				
			||||
 | 
					  #ifndef ESP8266 | 
				
			||||
 | 
					  Serial.begin(SER_BAUDRATE); | 
				
			||||
 | 
					  #endif | 
				
			||||
 | 
					  #endif | 
				
			||||
 | 
					  printf_begin(); | 
				
			||||
 | 
					  DEBUG_OUT.begin(SER_BAUDRATE); | 
				
			||||
 | 
					  DEBUG_OUT.flush(); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  DEBUG_OUT.println(F("-- Hoymiles DTU Simulation --")); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  // Configure nRF IRQ input
 | 
				
			||||
 | 
					  pinMode(RF1_IRQ_PIN, INPUT); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  activateConf(); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#ifdef ESP8266 | 
				
			||||
 | 
					  setupWifi(); | 
				
			||||
 | 
					  setupClock(); | 
				
			||||
 | 
					  setupWebServer(); | 
				
			||||
 | 
					  setupUpdateByOTA(); | 
				
			||||
 | 
					  calcSunUpDown (getNow()); | 
				
			||||
 | 
					  istTag = isDayTime(); | 
				
			||||
 | 
					  DEBUG_OUT.print (F("Es ist ")); DEBUG_OUT.println (istTag?F("Tag"):F("Nacht")); | 
				
			||||
 | 
					  hmPackets.SetUnixTimeStamp (getNow()); | 
				
			||||
 | 
					#else | 
				
			||||
 | 
					  hmPackets.SetUnixTimeStamp(0x62456430); | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  setupInverts(); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					 uint8_t sendBuf[MAX_RF_PAYLOAD_SIZE]; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void isTime2Send () { | 
				
			||||
 | 
					//-----------------
 | 
				
			||||
 | 
					  // Second timer
 | 
				
			||||
 | 
					  static const uint8_t warteZeit = 1; | 
				
			||||
 | 
					  static uint8_t tickSec = 0; | 
				
			||||
 | 
					  if (millis() >= tickMillis) { | 
				
			||||
 | 
					    static uint8_t tel = 0; | 
				
			||||
 | 
					    tickMillis += warteZeit*1000;    //200;
 | 
				
			||||
 | 
					    tickSec++;  | 
				
			||||
 | 
					    | 
				
			||||
 | 
					    if (++tickSec >= 1) {   // 5
 | 
				
			||||
 | 
					      for (uint8_t c=0; c < warteZeit; c++) hmPackets.UnixTimeStampTick(); | 
				
			||||
 | 
					      tickSec = 0; | 
				
			||||
 | 
					    }  | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    int32_t size = 0; | 
				
			||||
 | 
					    uint64_t dest =  0; | 
				
			||||
 | 
					    for (uint8_t wr = 0; wr < anzInv; wr++) { | 
				
			||||
 | 
					      dest = inverters[wr].RadioId; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					      if (tel > 1) | 
				
			||||
 | 
					        tel = 0; | 
				
			||||
 | 
					       | 
				
			||||
 | 
					      if (tel == 0) { | 
				
			||||
 | 
					        #ifdef ESP8266 | 
				
			||||
 | 
					        hmPackets.SetUnixTimeStamp (getNow()); | 
				
			||||
 | 
					        #endif | 
				
			||||
 | 
					        size = hmPackets.GetTimePacket((uint8_t *)&sendBuf, dest >> 8, DTU_RADIO_ID >> 8); | 
				
			||||
 | 
					        //DEBUG_OUT.print ("Timepacket mit cid="); DEBUG_OUT.println(sendBuf[10], HEX);
 | 
				
			||||
 | 
					      } | 
				
			||||
 | 
					      else if (tel <= 1)  | 
				
			||||
 | 
					        size = hmPackets.GetCmdPacket((uint8_t *)&sendBuf, dest >> 8, DTU_RADIO_ID >> 8, 0x15,  0x80 + tel - 1); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					      SendPacket (dest, (uint8_t *)&sendBuf, size); | 
				
			||||
 | 
					    }  // for wr
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    tel++; | 
				
			||||
 | 
					     | 
				
			||||
 | 
					/*    for (uint8_t warte = 0; warte < 2; warte++) {
 | 
				
			||||
 | 
					      delay(1000); | 
				
			||||
 | 
					      hmPackets.UnixTimeStampTick(); | 
				
			||||
 | 
					    }*/  | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void outputPacket(NRF24_packet_t *p, uint8_t payloadLen) { | 
				
			||||
 | 
					//-----------------------------------------------------
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    // Write timestamp, packets lost, address and payload length
 | 
				
			||||
 | 
					    //printf(" %09lu ", SerialHdr.timestamp);
 | 
				
			||||
 | 
					    char _buf[20]; | 
				
			||||
 | 
					    sprintf_P(_buf, PSTR("rcv CH:%d "), p->rcvChannel); | 
				
			||||
 | 
					    DEBUG_OUT.print (_buf); | 
				
			||||
 | 
					    dumpData((uint8_t *)&SerialHdr.packetsLost, sizeof(SerialHdr.packetsLost)); | 
				
			||||
 | 
					    dumpData((uint8_t *)&SerialHdr.address, sizeof(SerialHdr.address)); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    // Trailing bit?!?
 | 
				
			||||
 | 
					    dumpData(&p->packet[0], 2); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    // Payload length from PCF
 | 
				
			||||
 | 
					    dumpData(&payloadLen, sizeof(payloadLen)); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    // Packet control field - PID Packet identification
 | 
				
			||||
 | 
					    uint8_t val = (p->packet[1] >> 1) & 0x03; | 
				
			||||
 | 
					    DEBUG_OUT.print(val); | 
				
			||||
 | 
					    DEBUG_OUT.print(F("  ")); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    if (payloadLen > 9) { | 
				
			||||
 | 
					      dumpData(&p->packet[2], 1); | 
				
			||||
 | 
					      dumpData(&p->packet[3], 4); | 
				
			||||
 | 
					      dumpData(&p->packet[7], 4); | 
				
			||||
 | 
					       | 
				
			||||
 | 
					      uint16_t remain = payloadLen - 2 - 1 - 4 - 4 + 4; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					      if (remain < 32) { | 
				
			||||
 | 
					        dumpData(&p->packet[11], remain); | 
				
			||||
 | 
					        printf_P(PSTR("%04X "), crc); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					        if (((crc >> 8) != p->packet[payloadLen + 2]) || ((crc & 0xFF) != p->packet[payloadLen + 3])) | 
				
			||||
 | 
					          DEBUG_OUT.print(0); | 
				
			||||
 | 
					        else | 
				
			||||
 | 
					          DEBUG_OUT.print(1); | 
				
			||||
 | 
					      } | 
				
			||||
 | 
					      else { | 
				
			||||
 | 
					        DEBUG_OUT.print(F("Ill remain ")); | 
				
			||||
 | 
					        DEBUG_OUT.print(remain); | 
				
			||||
 | 
					      } | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					    else { | 
				
			||||
 | 
					      dumpData(&p->packet[2], payloadLen + 2); | 
				
			||||
 | 
					      printf_P(PSTR("%04X "), crc); | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					    DEBUG_OUT.println();  | 
				
			||||
 | 
					    DEBUG_OUT.flush(); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void writeArduinoInterface() { | 
				
			||||
 | 
					//--------------------------
 | 
				
			||||
 | 
					  if (valueChanged) { | 
				
			||||
 | 
					    for (uint8_t wr = 0; wr < anzInv; wr++) { | 
				
			||||
 | 
					      if (anzInv > 1) { | 
				
			||||
 | 
					        Serial.print(wr); Serial.print('.'); | 
				
			||||
 | 
					      } | 
				
			||||
 | 
					      for (uint8_t i = 0; i < inverters[wr].anzTotalMeasures; i++) { | 
				
			||||
 | 
					        Serial.print(getMeasureName(wr,i));    // Schnittstelle bei Arduino
 | 
				
			||||
 | 
					        Serial.print('=');  | 
				
			||||
 | 
					        Serial.print(getMeasureValue(wr,i), getDigits(wr,i));   // Schnittstelle bei Arduino
 | 
				
			||||
 | 
					        Serial.print (BLANK); | 
				
			||||
 | 
					        Serial.println (getUnit(wr, i)); | 
				
			||||
 | 
					      }  // for i
 | 
				
			||||
 | 
					       | 
				
			||||
 | 
					    }  // for wr
 | 
				
			||||
 | 
					    Serial.println(F("-----------------------")); | 
				
			||||
 | 
					    valueChanged = false; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					boolean doCheckCrc (NRF24_packet_t *p, uint8_t payloadLen) { | 
				
			||||
 | 
					//--------------------------------------------------------
 | 
				
			||||
 | 
					  crc = 0xFFFF; | 
				
			||||
 | 
					  crc = crc16((uint8_t *)&SerialHdr.address, sizeof(SerialHdr.address), crc, 0, BYTES_TO_BITS(sizeof(SerialHdr.address))); | 
				
			||||
 | 
					  // Payload length
 | 
				
			||||
 | 
					  // Add one byte and one bit for 9-bit packet control field
 | 
				
			||||
 | 
					  crc = crc16((uint8_t *)&p->packet[0], sizeof(p->packet), crc, 7, BYTES_TO_BITS(payloadLen + 1) + 1); | 
				
			||||
 | 
					   | 
				
			||||
 | 
					  if (CHECKCRC) { | 
				
			||||
 | 
					    // If CRC is invalid only show lost packets
 | 
				
			||||
 | 
					    if (((crc >> 8) != p->packet[payloadLen + 2]) || ((crc & 0xFF) != p->packet[payloadLen + 3])) { | 
				
			||||
 | 
					      if (p->packetsLost > 0) { | 
				
			||||
 | 
					        DEBUG_OUT.print(F(" Lost: ")); | 
				
			||||
 | 
					        DEBUG_OUT.println(p->packetsLost); | 
				
			||||
 | 
					      } | 
				
			||||
 | 
					      packetBuffer.popBack(); | 
				
			||||
 | 
					      return false; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					   | 
				
			||||
 | 
					    // Dump a decoded packet only once
 | 
				
			||||
 | 
					    if (lastCRC == crc) { | 
				
			||||
 | 
					      packetBuffer.popBack(); | 
				
			||||
 | 
					      return false; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					    lastCRC = crc; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					   | 
				
			||||
 | 
					  // Don't dump mysterious ack packages
 | 
				
			||||
 | 
					  if (payloadLen == 0) { | 
				
			||||
 | 
					      packetBuffer.popBack(); | 
				
			||||
 | 
					      return false; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  return true; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void poorManChannelHopping() { | 
				
			||||
 | 
					//--------------------------
 | 
				
			||||
 | 
					  if (hophop <= 0) return; | 
				
			||||
 | 
					  if (millis() >= timeLastRcvChannelSwitch + intvl) { | 
				
			||||
 | 
					    rcvChannelIdx++; | 
				
			||||
 | 
					    if (rcvChannelIdx >= sizeof(rcvChannels)) | 
				
			||||
 | 
					      rcvChannelIdx = 0; | 
				
			||||
 | 
					    DEFAULT_RECV_CHANNEL  = rcvChannels[rcvChannelIdx];  | 
				
			||||
 | 
					    DISABLE_EINT; | 
				
			||||
 | 
					    Radio.stopListening(); | 
				
			||||
 | 
					    Radio.setChannel (DEFAULT_RECV_CHANNEL); | 
				
			||||
 | 
					    Radio.startListening(); | 
				
			||||
 | 
					    ENABLE_EINT;       | 
				
			||||
 | 
					    timeLastRcvChannelSwitch = millis(); | 
				
			||||
 | 
					    hophop--; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					   | 
				
			||||
 | 
					} | 
				
			||||
 | 
					void loop(void) { | 
				
			||||
 | 
					//=============
 | 
				
			||||
 | 
					  // poor man channel hopping on receive
 | 
				
			||||
 | 
					#if USE_POOR_MAN_CHANNEL_HOPPING_RCV | 
				
			||||
 | 
					  poorManChannelHopping(); | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  if (millis()  > timeLastPacket + 50000UL) { | 
				
			||||
 | 
					    DEBUG_OUT.println (F("Reset RF24")); | 
				
			||||
 | 
					    resetRF24(); | 
				
			||||
 | 
					    timeLastPacket = millis();  | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					   | 
				
			||||
 | 
					  while (!packetBuffer.empty()) { | 
				
			||||
 | 
					    timeLastPacket = millis(); | 
				
			||||
 | 
					    // One or more records present
 | 
				
			||||
 | 
					    NRF24_packet_t *p = packetBuffer.getBack(); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    // Shift payload data due to 9-bit packet control field
 | 
				
			||||
 | 
					    for (int16_t j = sizeof(p->packet) - 1; j >= 0; j--) { | 
				
			||||
 | 
					      if (j > 0) | 
				
			||||
 | 
					        p->packet[j] = (byte)(p->packet[j] >> 7) | (byte)(p->packet[j - 1] << 1); | 
				
			||||
 | 
					      else | 
				
			||||
 | 
					        p->packet[j] = (byte)(p->packet[j] >> 7); | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    SerialHdr.timestamp   = p->timestamp; | 
				
			||||
 | 
					    SerialHdr.packetsLost = p->packetsLost; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    uint8_t payloadLen = ((p->packet[0] & 0x01) << 5) | (p->packet[1] >> 3); | 
				
			||||
 | 
					    // Check CRC
 | 
				
			||||
 | 
					    if (! doCheckCrc(p, payloadLen) ) | 
				
			||||
 | 
					      continue; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    #ifdef DEBUG | 
				
			||||
 | 
					    uint8_t cmd = p->packet[11]; | 
				
			||||
 | 
					    //if (cmd != 0x01 && cmd != 0x02 && cmd != 0x83 && cmd != 0x81)
 | 
				
			||||
 | 
					      outputPacket (p, payloadLen); | 
				
			||||
 | 
					    #endif | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    analyse (p); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    #ifndef ESP8266 | 
				
			||||
 | 
					    writeArduinoInterface(); | 
				
			||||
 | 
					    #endif | 
				
			||||
 | 
					     | 
				
			||||
 | 
					    // Remove record as we're done with it.
 | 
				
			||||
 | 
					    packetBuffer.popBack(); | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  if (istTag)  | 
				
			||||
 | 
					    isTime2Send(); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  #ifdef ESP8266 | 
				
			||||
 | 
					  checkWifi(); | 
				
			||||
 | 
					  webserverHandle();        | 
				
			||||
 | 
					  checkUpdateByOTA(); | 
				
			||||
 | 
					  if (hour() == 0 && minute() == 0) { | 
				
			||||
 | 
					    calcSunUpDown(getNow());   | 
				
			||||
 | 
					    delay (60*1000); | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  if (millis() > timeLastIstTagCheck + 15UL * 60UL * 1000UL) {   // alle 15 Minuten neu berechnen ob noch hell
 | 
				
			||||
 | 
					    istTag = isDayTime(); | 
				
			||||
 | 
					    DEBUG_OUT.print (F("Es ist ")); DEBUG_OUT.println (istTag?F("Tag"):F("Nacht")); | 
				
			||||
 | 
					    timeLastIstTagCheck = millis(); | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  #endif | 
				
			||||
 | 
					/*
 | 
				
			||||
 | 
					  if (millis() > timeLastPacket + 60UL*SECOND) {  // 60 Sekunden
 | 
				
			||||
 | 
					    channelIdx++; | 
				
			||||
 | 
					    if (channelIdx >= sizeof(channels)) channelIdx = 0; | 
				
			||||
 | 
					    DEFAULT_SEND_CHANNEL = channels[channelIdx]; | 
				
			||||
 | 
					    DEBUG_OUT.print (F("\nneuer DEFAULT_SEND_CHANNEL: ")); DEBUG_OUT.println(DEFAULT_SEND_CHANNEL); | 
				
			||||
 | 
					    timeLastPacket = millis(); | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					*/ | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					static void SendPacket(uint64_t dest, uint8_t *buf, uint8_t len) { | 
				
			||||
 | 
					//--------------------------------------------------------------
 | 
				
			||||
 | 
					  //DEBUG_OUT.print (F("Sende: ")); DEBUG_OUT.println (buf[9],  HEX);
 | 
				
			||||
 | 
					  //dumpData (buf, len); DEBUG_OUT.println();
 | 
				
			||||
 | 
					  DISABLE_EINT; | 
				
			||||
 | 
					  Radio.stopListening(); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#ifdef CHANNEL_HOP | 
				
			||||
 | 
					  static uint8_t hop = 0; | 
				
			||||
 | 
					  #if DEBUG_SEND     | 
				
			||||
 | 
					  DEBUG_OUT.print(F("Send... CH")); | 
				
			||||
 | 
					  DEBUG_OUT.println(channels[hop]); | 
				
			||||
 | 
					  #endif   | 
				
			||||
 | 
					  Radio.setChannel(channels[hop++]); | 
				
			||||
 | 
					  if (hop >= sizeof(channels) / sizeof(channels[0])) | 
				
			||||
 | 
					    hop = 0; | 
				
			||||
 | 
					#else | 
				
			||||
 | 
					  Radio.setChannel(DEFAULT_SEND_CHANNEL); | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  Radio.openWritingPipe(dest); | 
				
			||||
 | 
					  Radio.setCRCLength(RF24_CRC_16); | 
				
			||||
 | 
					  Radio.enableDynamicPayloads(); | 
				
			||||
 | 
					  Radio.setAutoAck(true); | 
				
			||||
 | 
					  Radio.setRetries(3, 15); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  bool res = Radio.write(buf, len); | 
				
			||||
 | 
					  // Try to avoid zero payload acks (has no effect)
 | 
				
			||||
 | 
					  Radio.openWritingPipe(DUMMY_RADIO_ID); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  Radio.setAutoAck(false); | 
				
			||||
 | 
					  Radio.setRetries(0, 0); | 
				
			||||
 | 
					  Radio.disableDynamicPayloads(); | 
				
			||||
 | 
					  Radio.setCRCLength(RF24_CRC_DISABLED); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  Radio.setChannel(DEFAULT_RECV_CHANNEL); | 
				
			||||
 | 
					  Radio.startListening(); | 
				
			||||
 | 
					  ENABLE_EINT; | 
				
			||||
 | 
					#if USE_POOR_MAN_CHANNEL_HOPPING_RCV | 
				
			||||
 | 
					  hophop = 5 * sizeof(rcvChannels); | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
					} | 
				
			||||
@ -0,0 +1,283 @@ | 
				
			|||||
 | 
					#ifndef __INVERTERS_H | 
				
			||||
 | 
					#define __INVERTERS_H | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// Ausgabe von Debug Infos auf der seriellen Console
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#include "Settings.h" | 
				
			||||
 | 
					#include "Debug.h" | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					typedef struct _NRF24_packet_t { | 
				
			||||
 | 
					  uint32_t timestamp; | 
				
			||||
 | 
					  uint8_t  packetsLost; | 
				
			||||
 | 
					  uint8_t  rcvChannel; | 
				
			||||
 | 
					  uint8_t  packet[MAX_RF_PAYLOAD_SIZE]; | 
				
			||||
 | 
					} NRF24_packet_t; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					typedef struct _Serial_header_t { | 
				
			||||
 | 
					  unsigned long timestamp; | 
				
			||||
 | 
					  uint8_t  packetsLost; | 
				
			||||
 | 
					  uint8_t  address[RF_MAX_ADDR_WIDTH];    // MSB first, always RF_MAX_ADDR_WIDTH bytes.
 | 
				
			||||
 | 
					} Serial_header_t; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// structs für Inverter und Kanalwerte
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// Liste der Einheiten
 | 
				
			||||
 | 
					enum UNITS {UNIT_V = 0, UNIT_HZ, UNIT_A, UNIT_W,  UNIT_WH, UNIT_C, UNIT_KWH, UNIT_MA, UNIT_PCT}; | 
				
			||||
 | 
					const char* const units[] = {"V", "Hz", "A", "W", "Wh", "°C", "KWh", "mA", "%"}; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// CH0 is default channel (freq, ac, temp)
 | 
				
			||||
 | 
					enum CHANNELS {CH0 = 0, CH1, CH2, CH3, CH4}; | 
				
			||||
 | 
					enum CMDS     {CMD01 = 0x01, CMD02, CMD03, CMD83 = 0x83, CMD84}; | 
				
			||||
 | 
					enum DIVS     {DIV1 = 0, DIV10, DIV100, DIV1000}; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define BYTES2        2 | 
				
			||||
 | 
					#define BYTES4        4 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					const char UDC[]      PROGMEM = "Udc"; | 
				
			||||
 | 
					const char IDC[]      PROGMEM = "Idc"; | 
				
			||||
 | 
					const char PDC[]      PROGMEM = "Pdc"; | 
				
			||||
 | 
					const char E_WOCHE[]  PROGMEM = "E-Woche"; | 
				
			||||
 | 
					const char E_TOTAL[]  PROGMEM = "E-Total"; | 
				
			||||
 | 
					const char E_TAG[]    PROGMEM = "E-Tag"; | 
				
			||||
 | 
					const char UAC[]      PROGMEM = "Uac"; | 
				
			||||
 | 
					const char FREQ[]     PROGMEM = "Freq.ac"; | 
				
			||||
 | 
					const char PAC[]      PROGMEM = "Pac"; | 
				
			||||
 | 
					const char E_HEUTE[]  PROGMEM  = "E-heute"; | 
				
			||||
 | 
					const char IPV[]      PROGMEM  = "Ipv"; | 
				
			||||
 | 
					const char WR_TEMP[]  PROGMEM = "WR-Temp"; | 
				
			||||
 | 
					const char PERCNT[]   PROGMEM = "Pct"; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define IDX_UDC       0 | 
				
			||||
 | 
					#define IDX_IDC       1 | 
				
			||||
 | 
					#define IDX_PDC       2 | 
				
			||||
 | 
					#define IDX_E_WOCHE   3 | 
				
			||||
 | 
					#define IDX_E_TOTAL   4 | 
				
			||||
 | 
					#define IDX_E_TAG     5 | 
				
			||||
 | 
					#define IDX_UAC       6 | 
				
			||||
 | 
					#define IDX_FREQ      7 | 
				
			||||
 | 
					#define IDX_PAC       8 | 
				
			||||
 | 
					#define IDX_E_HEUTE   9 | 
				
			||||
 | 
					#define IDX_IPV      10 | 
				
			||||
 | 
					#define IDX_WR_TEMP  11 | 
				
			||||
 | 
					#define IDX_PERCNT   12     | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					const char* const NAMES[]  | 
				
			||||
 | 
					  = {UDC, IDC, PDC, E_WOCHE, E_TOTAL, E_TAG, UAC, FREQ, PAC, E_HEUTE, IPV, WR_TEMP, PERCNT}; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					typedef float (*calcValueFunc)(float *); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					struct measureDef_t { | 
				
			||||
 | 
					  uint8_t     nameIdx;        //const char* name;           // Zeiger auf den Messwertnamen
 | 
				
			||||
 | 
					  uint8_t     channel;        // 0..4, 
 | 
				
			||||
 | 
					  uint8_t     unitIdx;        // Index in die Liste der Einheiten 'units'
 | 
				
			||||
 | 
					  uint8_t     teleId;         // Telegramm ID, das was hinter der 2. WR Nummer im Telegramm, 02, 03, 83
 | 
				
			||||
 | 
					  uint8_t     pos;            // ab dieser POsition beginnt der Wert (Big Endian)
 | 
				
			||||
 | 
					  uint8_t     bytes;          // Anzahl der Bytes
 | 
				
			||||
 | 
					  uint8_t     digits; | 
				
			||||
 | 
					}; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					struct measureCalc_t { | 
				
			||||
 | 
					  uint8_t     nameIdx;        //const char* name;           // Zeiger auf den Messwertnamen
 | 
				
			||||
 | 
					  uint8_t     unitIdx;        // Index in die Liste der Einheiten 'units'
 | 
				
			||||
 | 
					  uint8_t     digits; | 
				
			||||
 | 
					  calcValueFunc f;            // die Funktion zur Berechnung von Werten, zb Summe von Werten
 | 
				
			||||
 | 
					}; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					struct inverter_t { | 
				
			||||
 | 
					  uint8_t       ID;                       // Inverter-ID = Index
 | 
				
			||||
 | 
					  char          name[20];                 // Name des Inverters zb HM-600.1
 | 
				
			||||
 | 
					  uint64_t      serialNo;                 // dier Seriennummer wie im Barcode auf dem WR, also 1141.....
 | 
				
			||||
 | 
					  uint64_t      RadioId;                  // die gespiegelte (letzte 4 "Bytes") der Seriennummer
 | 
				
			||||
 | 
					  const measureDef_t  *measureDef;              // aus Include HMxxx.h : Liste mit Definitionen der Messwerte, wie Telgramm, offset, länge, ...
 | 
				
			||||
 | 
					  uint8_t       anzMeasures;              // Länge der Liste
 | 
				
			||||
 | 
					  measureCalc_t *measureCalculated;       // Liste mit Defintion für berechnete Werte
 | 
				
			||||
 | 
					  uint8_t       anzMeasureCalculated;     // Länge der Liste
 | 
				
			||||
 | 
					  uint8_t       anzTotalMeasures;         // Gesamtanzahl Messwerte
 | 
				
			||||
 | 
					  float         values[MAX_MEASURE_PER_INV];  // DIE Messewerte
 | 
				
			||||
 | 
					}; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					char _buffer[20]; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					uint8_t anzInv = 0; | 
				
			||||
 | 
					inverter_t inverters[MAX_ANZ_INV]; | 
				
			||||
 | 
					   | 
				
			||||
 | 
					union longlongasbytes { | 
				
			||||
 | 
					  uint64_t ull; | 
				
			||||
 | 
					  uint32_t ul[2]; | 
				
			||||
 | 
					  uint8_t bytes[8];   | 
				
			||||
 | 
					}; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					char *uint64toa (uint64_t s) { | 
				
			||||
 | 
					//--------------------------------  
 | 
				
			||||
 | 
					//0x1141 72607952ULL   
 | 
				
			||||
 | 
					  sprintf(_buffer, "%lX%08lX", (unsigned long)(s>>32), (unsigned long)(s&0xFFFFFFFFULL)); | 
				
			||||
 | 
					  return _buffer; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					uint64_t Serial2RadioID (uint64_t sn) {    | 
				
			||||
 | 
					//----------------------------------
 | 
				
			||||
 | 
					  longlongasbytes llsn; | 
				
			||||
 | 
					  longlongasbytes res; | 
				
			||||
 | 
					  llsn.ull = sn; | 
				
			||||
 | 
					  res.ull = 0; | 
				
			||||
 | 
					  res.bytes[4] = llsn.bytes[0]; | 
				
			||||
 | 
					  res.bytes[3] = llsn.bytes[1]; | 
				
			||||
 | 
					  res.bytes[2] = llsn.bytes[2]; | 
				
			||||
 | 
					  res.bytes[1] = llsn.bytes[3]; | 
				
			||||
 | 
					  res.bytes[0] = 0x01; | 
				
			||||
 | 
					  return res.ull; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void addInverter (uint8_t _ID, const char * _name, uint64_t _serial,  | 
				
			||||
 | 
					                  const measureDef_t * liste, int anzMeasure, | 
				
			||||
 | 
					                  measureCalc_t * calcs, int anzMeasureCalculated) { | 
				
			||||
 | 
					//-------------------------------------------------------------------------------------
 | 
				
			||||
 | 
					  if (anzInv >= MAX_ANZ_INV) { | 
				
			||||
 | 
					    DEBUG_OUT.println(F("ANZ_INV zu klein!")); | 
				
			||||
 | 
					    return; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  inverter_t *p = &(inverters[anzInv]); | 
				
			||||
 | 
					  p->ID                   = _ID; | 
				
			||||
 | 
					  strcpy (p->name, _name); | 
				
			||||
 | 
					  p->serialNo             = _serial; | 
				
			||||
 | 
					  p->RadioId              = Serial2RadioID(_serial); | 
				
			||||
 | 
					  p->measureDef           = liste; | 
				
			||||
 | 
					  p->anzMeasures          = anzMeasure; | 
				
			||||
 | 
					  p->anzMeasureCalculated = anzMeasureCalculated; | 
				
			||||
 | 
					  p->measureCalculated    = calcs; | 
				
			||||
 | 
					  p->anzTotalMeasures     = anzMeasure + anzMeasureCalculated; | 
				
			||||
 | 
					  memset (p->values, 0, sizeof(p->values)); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  DEBUG_OUT.print (F("WR       : "));      DEBUG_OUT.println(anzInv); | 
				
			||||
 | 
					  DEBUG_OUT.print (F("Type     : "));      DEBUG_OUT.println(_name); | 
				
			||||
 | 
					  DEBUG_OUT.print (F("Serial   : "));      DEBUG_OUT.println(uint64toa(_serial)); | 
				
			||||
 | 
					  DEBUG_OUT.print (F("Radio-ID : "));      DEBUG_OUT.println(uint64toa(p->RadioId)); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  anzInv++; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					static uint8_t toggle = 0;           // nur für Test, ob's auch für mehere WR funzt
 | 
				
			||||
 | 
					uint8_t findInverter (uint8_t *fourbytes) { | 
				
			||||
 | 
					//---------------------------------------
 | 
				
			||||
 | 
					  for (uint8_t i = 0; i < anzInv; i++) { | 
				
			||||
 | 
					    longlongasbytes llb; | 
				
			||||
 | 
					    llb.ull = inverters[i].serialNo;   | 
				
			||||
 | 
					    if (llb.bytes[3] == fourbytes[0] &&  | 
				
			||||
 | 
					        llb.bytes[2] == fourbytes[1] &&  | 
				
			||||
 | 
					        llb.bytes[1] == fourbytes[2] && | 
				
			||||
 | 
					        llb.bytes[0] == fourbytes[3] ) | 
				
			||||
 | 
					      { | 
				
			||||
 | 
					        return i; | 
				
			||||
 | 
					        //if (toggle) toggle = 0; else toggle = 1; return toggle;     // Test ob mehr WR  auch geht
 | 
				
			||||
 | 
					      } | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  return 0xFF;      // nicht gefunden
 | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					char * error = {"error"}; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					char *getMeasureName (uint8_t wr, uint8_t i){ | 
				
			||||
 | 
					//------------------------------------------
 | 
				
			||||
 | 
					  inverter_t *p = &(inverters[wr]); | 
				
			||||
 | 
					  if (i >= p->anzTotalMeasures) return error; | 
				
			||||
 | 
					  uint8_t idx, channel = 0; | 
				
			||||
 | 
					  if (i < p->anzMeasures) { | 
				
			||||
 | 
					    idx = p->measureDef[i].nameIdx;   | 
				
			||||
 | 
					    channel = p->measureDef[i].channel; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  else { | 
				
			||||
 | 
					    idx = p->measureCalculated[i - p->anzMeasures].nameIdx;   | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  char tmp[20]; | 
				
			||||
 | 
					  strcpy_P (_buffer, NAMES[idx]); | 
				
			||||
 | 
					  if (channel) { | 
				
			||||
 | 
					    sprintf_P (tmp, PSTR(".CH%d"), channel); | 
				
			||||
 | 
					    strcat(_buffer,tmp); | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  return _buffer; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					const char *getUnit (uint8_t wr, uint8_t i) { | 
				
			||||
 | 
					//------------------------------------------
 | 
				
			||||
 | 
					  inverter_t *p = &(inverters[wr]); | 
				
			||||
 | 
					  if (i >= p->anzTotalMeasures) return error; | 
				
			||||
 | 
					  uint8_t idx; | 
				
			||||
 | 
					  if (i < p->anzMeasures) | 
				
			||||
 | 
					    idx = p->measureDef[i].unitIdx; | 
				
			||||
 | 
					  else | 
				
			||||
 | 
					    idx = p->measureCalculated[i-p->anzMeasures].unitIdx; | 
				
			||||
 | 
					   | 
				
			||||
 | 
					  //strcpy (_buffer, units[i]);
 | 
				
			||||
 | 
					  //return _buffer;
 | 
				
			||||
 | 
					  return units[idx]; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					float getMeasureValue (uint8_t wr, uint8_t i) { | 
				
			||||
 | 
					//------------------------------------------
 | 
				
			||||
 | 
					  if (i >= inverters[wr].anzTotalMeasures) return 0.0; | 
				
			||||
 | 
					  return inverters[wr].values[i]; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					int getDivisor (uint8_t wr, uint8_t i) { | 
				
			||||
 | 
					//------------------------------------
 | 
				
			||||
 | 
					  inverter_t *p = &(inverters[wr]); | 
				
			||||
 | 
					  if (i >= p->anzTotalMeasures) return 1; | 
				
			||||
 | 
					  if (i < p->anzMeasures) { | 
				
			||||
 | 
					    uint8_t digits = p->measureDef[i].digits; | 
				
			||||
 | 
					    if (digits == DIV1) return 1; | 
				
			||||
 | 
					    if (digits == DIV10) return 10; | 
				
			||||
 | 
					    if (digits == DIV100) return 100; | 
				
			||||
 | 
					    if (digits == DIV1000) return 1000; | 
				
			||||
 | 
					    return 1; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  else | 
				
			||||
 | 
					    return p->measureCalculated[i].digits;   | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					uint8_t getDigits (uint8_t wr, uint8_t i) { | 
				
			||||
 | 
					//---------------------------------------  
 | 
				
			||||
 | 
					  inverter_t *p = &(inverters[wr]); | 
				
			||||
 | 
					  if (i >= p->anzTotalMeasures) return 0; | 
				
			||||
 | 
					  if (i < p->anzMeasures)  | 
				
			||||
 | 
					    return p->measureDef[i].digits; | 
				
			||||
 | 
					  else | 
				
			||||
 | 
					    return p->measureCalculated[i-p->anzMeasures].digits;   | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// +++++++++++++++++++++++++++++++++++    Inverter    ++++++++++++++++++++++++++++++++++++++++++++++
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#include "HM600.h"            // für HM-600 und HM-700 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#include "HM1200.h" | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void setupInverts() { | 
				
			||||
 | 
					//-----------------  
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  addInverter (0,"HM-600", 0x114172607952ULL,  | 
				
			||||
 | 
					               hm600_measureDef, HM600_MEASURE_LIST_LEN,     // Tabelle der Messwerte
 | 
				
			||||
 | 
					               hm600_measureCalc, HM600_CALCED_LIST_LEN); // Tabelle berechnete Werte
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					/*
 | 
				
			||||
 | 
					  addInverter (1,"HM-1200", 0x114172607952ULL,  | 
				
			||||
 | 
					               hm1200_measureDef, HM1200_MEASURE_LIST_LEN,     // Tabelle der Messwerte
 | 
				
			||||
 | 
					               hm1200_measureCalc, HM1200_CALCED_LIST_LEN);    // Tabelle berechnete Werte
 | 
				
			||||
 | 
					*/ | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#endif | 
				
			||||
@ -0,0 +1,151 @@ | 
				
			|||||
 | 
					// #################  WebServer #################
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#ifndef __MODWEBSERVER_H | 
				
			||||
 | 
					#define __MODWEBSERVER_H | 
				
			||||
 | 
					#define MODWEBSERVER | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#include <ESP8266WebServer.h> | 
				
			||||
 | 
					#include "Debug.h" | 
				
			||||
 | 
					#include "Settings.h" | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					ESP8266WebServer server (WEBSERVER_PORT); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void returnOK () { | 
				
			||||
 | 
					  //--------------
 | 
				
			||||
 | 
					  server.send(200, F("text/plain"), ""); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void returnFail(String msg) { | 
				
			||||
 | 
					  //-------------------------
 | 
				
			||||
 | 
					  server.send(500, F("text/plain"), msg + "\r\n"); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void handleHelp () { | 
				
			||||
 | 
					//-----------------  
 | 
				
			||||
 | 
					  String out = "<html>"; | 
				
			||||
 | 
					  out += "<body><h2>Hilfe</h2>"; | 
				
			||||
 | 
					  out += "<br><br><table>"; | 
				
			||||
 | 
					  out += "<tr><td>/</td><td>zeigt alle Messwerte in einer Tabelle; refresh alle 10 Sekunden</td></tr>"; | 
				
			||||
 | 
					  out += "<tr><td>/data</td><td>zum Abruf der Messwerte in der Form Name=wert</td></tr>"; | 
				
			||||
 | 
					  out += "<tr><td>:{port+1}/update</td><td>OTA</td></tr>"; | 
				
			||||
 | 
					  out += "<tr><td>/reboot</td><td>startet neu</td></tr>"; | 
				
			||||
 | 
					  out += "</table></body></html>"; | 
				
			||||
 | 
					  server.send (200, "text/html", out); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void handleReboot () { | 
				
			||||
 | 
					  //-------------------
 | 
				
			||||
 | 
					  returnOK (); | 
				
			||||
 | 
					  ESP.reset(); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void handleRoot() { | 
				
			||||
 | 
					  //----------------
 | 
				
			||||
 | 
					  String out = "<html><head><meta http-equiv=\"refresh\" content=\"10\":URL=\"" + server.uri() + "\"></head>"; | 
				
			||||
 | 
					  out += "<body>"; | 
				
			||||
 | 
					  out += "<h2>Hoymiles Micro-Inverters</h2>"; | 
				
			||||
 | 
					  char floatString[20]; | 
				
			||||
 | 
					  char line[100]; | 
				
			||||
 | 
					  for (uint8_t wr = 0; wr < anzInv; wr++) { | 
				
			||||
 | 
					    out += "<h3>" + String(inverters[wr].name) + "</h3>"; | 
				
			||||
 | 
					    out += "<h3>S/N " + String (getSerialNoTxt(wr)) + "</h3>"; | 
				
			||||
 | 
					    out += "<br><br><table border='1'>"; | 
				
			||||
 | 
					    out += "<tr><th>Kanal</th><th>Wert</th><th>Einheit</th></tr>"; | 
				
			||||
 | 
					    for (uint8_t i = 0; i < inverters[wr].anzTotalMeasures; i++) { | 
				
			||||
 | 
					      dtostrf (getMeasureValue(wr, i),1, getDigits(wr,i), floatString); | 
				
			||||
 | 
					      sprintf(line, "<tr><td>%s</td><td>%s</td><td>%s</td></tr>", getMeasureName(wr, i), floatString, getUnit(wr, i)); | 
				
			||||
 | 
					      //DEBUG_OUT.println(line);
 | 
				
			||||
 | 
					      out += String(line);  | 
				
			||||
 | 
					/*        out += "<tr><td>" + getMeasureName(i) + "</td>";
 | 
				
			||||
 | 
					      out += "<td>" +  String(getMeasureValue(i)) + "</td></tr>"; | 
				
			||||
 | 
					      out += "<td>" +  String(getUnit(i)) + "</td></tr>"; */ | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					    out += "</table>"; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  int pos = out.indexOf("°"); | 
				
			||||
 | 
					  do { | 
				
			||||
 | 
					    if (pos>1) { | 
				
			||||
 | 
					      out = out.substring (0, pos) + "°" + out.substring(pos+2); | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					    pos = out.indexOf("°"); | 
				
			||||
 | 
					  } while (pos>1); | 
				
			||||
 | 
					  | 
				
			||||
 | 
					  out += "</body></html>"; | 
				
			||||
 | 
					  server.send (200, "text/html", out); | 
				
			||||
 | 
					  //DEBUG_OUT.println (out);
 | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void handleData () { | 
				
			||||
 | 
					//-----------------
 | 
				
			||||
 | 
					  String out = ""; | 
				
			||||
 | 
					  for (uint8_t wr = 0; wr < anzInv; wr++) { | 
				
			||||
 | 
					    for (int i = 0; i < inverters[wr].anzTotalMeasures; i++) { | 
				
			||||
 | 
					      out += (anzInv <= 1 ? "" : String (wr) + ".") + String(getMeasureName(wr,i)) + '='  | 
				
			||||
 | 
					             + String (getMeasureValue(wr,i)) /*+ ' ' + String(getUnit(wr,i))*/ + '\n'; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  server.send(200, "text/plain", out);   | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void handleNotFound() { | 
				
			||||
 | 
					//--------------------  
 | 
				
			||||
 | 
					  String message  = "URI: "; | 
				
			||||
 | 
					  message += server.uri(); | 
				
			||||
 | 
					  message += "\nMethod: "; | 
				
			||||
 | 
					  message += (server.method() == HTTP_GET) ? "GET" : "POST"; | 
				
			||||
 | 
					  message += "\nArguments: "; | 
				
			||||
 | 
					  message += server.args(); | 
				
			||||
 | 
					  message += "\n"; | 
				
			||||
 | 
					  for (uint8_t i = 0; i < server.args(); i++) { | 
				
			||||
 | 
					    message += " NAME:" + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n"; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  server.send(404, "text/plain", message); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void setupWebServer (void) { | 
				
			||||
 | 
					  //-------------------------
 | 
				
			||||
 | 
					  server.begin(); | 
				
			||||
 | 
					  server.on("/",        handleRoot); | 
				
			||||
 | 
					  server.on("/reboot",  handleReboot); | 
				
			||||
 | 
					  server.on("/data",    handleData); | 
				
			||||
 | 
					  server.on("/help",    handleHelp); | 
				
			||||
 | 
					  //server.onNotFound(handleNotFound);    wegen Spiffs-Dateimanager
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  DEBUG_OUT.println ("[HTTP] installed"); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void webserverHandle() { | 
				
			||||
 | 
					//====================
 | 
				
			||||
 | 
					  server.handleClient();   | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// #################  OTA #################
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#ifdef WITH_OTA | 
				
			||||
 | 
					#include <ESP8266HTTPUpdateServer.h> | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					ESP8266WebServer httpUpdateServer (UPDATESERVER_PORT); | 
				
			||||
 | 
					ESP8266HTTPUpdateServer httpUpdater; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void setupUpdateByOTA () { | 
				
			||||
 | 
					  //------------------------
 | 
				
			||||
 | 
					  httpUpdater.setup (&httpUpdateServer, UPDATESERVER_DIR, UPDATESERVER_USER, UPDATESERVER_PW); | 
				
			||||
 | 
					  httpUpdateServer.begin(); | 
				
			||||
 | 
					  DEBUG_OUT.println ("[OTA] installed"); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void checkUpdateByOTA() { | 
				
			||||
 | 
					//---------------------
 | 
				
			||||
 | 
					  httpUpdateServer.handleClient();   | 
				
			||||
 | 
					} | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#endif | 
				
			||||
@ -0,0 +1,69 @@ | 
				
			|||||
 | 
					#ifndef __SETTINGS_H | 
				
			||||
 | 
					#define __SETTINGS_H | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// Ausgabe von Debug Infos auf der seriellen Console
 | 
				
			||||
 | 
					#define DEBUG | 
				
			||||
 | 
					#define SER_BAUDRATE            (115200) | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#include "Debug.h" | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// Ausgabe was gesendet wird; 0 oder 1 
 | 
				
			||||
 | 
					#define DEBUG_SEND  0    | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// soll zwichen den Sendekanälen 23, 40, 61, 75 ständig gewechselt werden
 | 
				
			||||
 | 
					#define CHANNEL_HOP | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// mit OTA Support, also update der Firmware über WLan mittels IP/update
 | 
				
			||||
 | 
					#define WITH_OTA | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// Hardware configuration
 | 
				
			||||
 | 
					#ifdef ESP8266 | 
				
			||||
 | 
					#define RF1_CE_PIN (D4) | 
				
			||||
 | 
					#define RF1_CS_PIN (D8) | 
				
			||||
 | 
					#define RF1_IRQ_PIN (D3) | 
				
			||||
 | 
					#else | 
				
			||||
 | 
					#define RF1_CE_PIN (9) | 
				
			||||
 | 
					#define RF1_CS_PIN (10) | 
				
			||||
 | 
					#define RF1_IRQ_PIN (2) | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// WR und DTU
 | 
				
			||||
 | 
					#define RF_MAX_ADDR_WIDTH       (5)  | 
				
			||||
 | 
					#define MAX_RF_PAYLOAD_SIZE     (32) | 
				
			||||
 | 
					#define DEFAULT_RF_DATARATE     (RF24_250KBPS)  // Datarate
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define USE_POOR_MAN_CHANNEL_HOPPING_RCV  1     // 0 = not use
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define DUMMY_RADIO_ID          ((uint64_t)0xDEADBEEF01ULL)  | 
				
			||||
 | 
					#define DTU_RADIO_ID            ((uint64_t)0x1234567801ULL) | 
				
			||||
 | 
					#define MAX_ANZ_INV 2             							// <<<<<<<<<<<<<<<<<<<<<<<< anpassen
 | 
				
			||||
 | 
					#define MAX_MEASURE_PER_INV 25    // hier statisch, könnte auch dynamisch erzeugt werden, aber Overhead für dyn. Speicher?
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// Webserver
 | 
				
			||||
 | 
					#define WEBSERVER_PORT          80 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// Time Server
 | 
				
			||||
 | 
					//#define TIMESERVER_NAME "pool.ntp.org"
 | 
				
			||||
 | 
					#define TIMESERVER_NAME "fritz.box"							// <<<<<<<<<<<<<<<<<<<<<<<< anpassen
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#ifdef WITH_OTA | 
				
			||||
 | 
					// OTA Einstellungen
 | 
				
			||||
 | 
					#define UPDATESERVER_PORT   WEBSERVER_PORT+1 | 
				
			||||
 | 
					#define UPDATESERVER_DIR    "/update" | 
				
			||||
 | 
					#define UPDATESERVER_USER   "?????"							// <<<<<<<<<<<<<<<<<<<<<<<< anpassen
 | 
				
			||||
 | 
					#define UPDATESERVER_PW     "?????"							// <<<<<<<<<<<<<<<<<<<<<<<< anpassen
 | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// internes WLan
 | 
				
			||||
 | 
					// PREFIXE dienen dazu, die eigenen WLans (wenn mehrere) von fremden zu unterscheiden
 | 
				
			||||
 | 
					// gehe hier davon aus, dass alle WLans das gleiche Passwort haben. Wenn nicht, dann mehre Passwörter hinterlegen
 | 
				
			||||
 | 
					#define SSID_PREFIX1         "pre1"							// <<<<<<<<<<<<<<<<<<<<<<<< anpassen
 | 
				
			||||
 | 
					#define SSID_PREFIX2         "pre2"							// <<<<<<<<<<<<<<<<<<<<<<<< anpassen
 | 
				
			||||
 | 
					#define SSID_PASSWORD        "?????????????????"			// <<<<<<<<<<<<<<<<<<<<<<<< anpassen
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// zur Berechnung von Sonnenauf- und -untergang
 | 
				
			||||
 | 
					#define  geoBreite  49.2866									// <<<<<<<<<<<<<<<<<<<<<<<< anpassen
 | 
				
			||||
 | 
					#define  geoLaenge  7.3416									// <<<<<<<<<<<<<<<<<<<<<<<< anpassen
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#endif | 
				
			||||
@ -0,0 +1,55 @@ | 
				
			|||||
 | 
					#ifndef __SONNE_H | 
				
			||||
 | 
					#define __SONNE_H | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#include "Settings.h" | 
				
			||||
 | 
					#include "Debug.h" | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					long SunDown, SunUp; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void calcSunUpDown (time_t date) { | 
				
			||||
 | 
					    //SunUpDown res = new SunUpDown();
 | 
				
			||||
 | 
					    boolean isSummerTime = false;   // TODO TimeZone.getDefault().inDaylightTime(new Date(date));
 | 
				
			||||
 | 
					     | 
				
			||||
 | 
					    //- Bogenmass
 | 
				
			||||
 | 
					    double brad = geoBreite / 180.0 * PI; | 
				
			||||
 | 
					    // - Höhe Sonne -50 Bogenmin.
 | 
				
			||||
 | 
					    double h0 = -50.0 / 60.0 / 180.0 * PI; | 
				
			||||
 | 
					    //- Deklination dek, Tag des Jahres d0
 | 
				
			||||
 | 
					    int tage = 30 * month(date) - 30 + day(date);  | 
				
			||||
 | 
					    double dek = 0.40954 * sin (0.0172 * (tage - 79.35)); | 
				
			||||
 | 
					    double zh1 = sin (h0) - sin (brad)  *  sin(dek); | 
				
			||||
 | 
					    double zh2 = cos(brad) * cos(dek); | 
				
			||||
 | 
					    double zd = 12*acos (zh1/zh2) / PI; | 
				
			||||
 | 
					    double zgl = -0.1752 * sin (0.03343 * tage + 0.5474) - 0.134 * sin (0.018234 * tage - 0.1939); | 
				
			||||
 | 
					    //-Sonnenuntergang
 | 
				
			||||
 | 
					    double tsu = 12 + zd - zgl; | 
				
			||||
 | 
					    double su = (tsu + (15.0 - geoLaenge) / 15.0);  | 
				
			||||
 | 
					    int std = (int)su; | 
				
			||||
 | 
					    int minute = (int) ((su - std)*60); | 
				
			||||
 | 
					    if (isSummerTime) std++; | 
				
			||||
 | 
					    SunDown = (100*std + minute) * 100; | 
				
			||||
 | 
					     | 
				
			||||
 | 
					    //- Sonnenaufgang
 | 
				
			||||
 | 
					    double tsa = 12 - zd - zgl; | 
				
			||||
 | 
					    double sa = (tsa + (15.0 - geoLaenge) /15.0);  | 
				
			||||
 | 
					    std = (int) sa; | 
				
			||||
 | 
					    minute = (int) ((sa - std)*60); | 
				
			||||
 | 
					    if (isSummerTime) std++; | 
				
			||||
 | 
					    SunUp = (100*std + minute) * 100; | 
				
			||||
 | 
					    DEBUG_OUT.print(F("Sonnenaufgang  :")); DEBUG_OUT.println(SunUp); | 
				
			||||
 | 
					    DEBUG_OUT.print(F("Sonnenuntergang:")); DEBUG_OUT.println(SunDown); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					boolean isDayTime() { | 
				
			||||
 | 
					//-----------------
 | 
				
			||||
 | 
					// 900 = 15 Minuten, vor Sonnenaufgang und nach -untergang
 | 
				
			||||
 | 
					  const int offset=60*15; | 
				
			||||
 | 
					  time_t no = getNow(); | 
				
			||||
 | 
					  long jetztMinuteU = (100 * hour(no+offset) + minute(no+offset)) * 100; | 
				
			||||
 | 
					  long jetztMinuteO = (100 * hour(no-offset) + minute(no-offset)) * 100; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  return ((jetztMinuteU >= SunUp) &&(jetztMinuteO <= SunDown));   | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#endif | 
				
			||||
@ -0,0 +1,102 @@ | 
				
			|||||
 | 
					#ifndef __HM_CRC_H | 
				
			||||
 | 
					#define __HM_CRC_H | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define BITS_TO_BYTES(x)  (((x)+7)>>3) | 
				
			||||
 | 
					#define BYTES_TO_BITS(x)  ((x)<<3) | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					extern uint16_t crc16_modbus(uint8_t *puchMsg, uint16_t usDataLen); | 
				
			||||
 | 
					extern uint8_t crc8(uint8_t *buf, const uint16_t bufLen); | 
				
			||||
 | 
					extern uint16_t crc16(uint8_t* buf, const uint16_t bufLen, const uint16_t startCRC, const uint16_t startBit, const uint16_t len_bits); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					//#define OUTPUT_DEBUG_INFO
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define CRC8_INIT               0x00 | 
				
			||||
 | 
					#define CRC8_POLY               0x01 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#define CRC16_MODBUS_POLYNOM    0xA001 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					uint8_t crc8(uint8_t buf[], uint16_t len) { | 
				
			||||
 | 
					    uint8_t crc = CRC8_INIT; | 
				
			||||
 | 
					    for(uint8_t i = 0; i < len; i++) { | 
				
			||||
 | 
					        crc ^= buf[i]; | 
				
			||||
 | 
					        for(uint8_t b = 0; b < 8; b ++) { | 
				
			||||
 | 
					            crc = (crc << 1) ^ ((crc & 0x80) ? CRC8_POLY : 0x00); | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					    return crc; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					uint16_t crc16_modbus(uint8_t buf[], uint16_t len) { | 
				
			||||
 | 
					    uint16_t crc = 0xffff; | 
				
			||||
 | 
					    uint8_t lsb; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    for(uint8_t i = 0; i < len; i++) { | 
				
			||||
 | 
					        crc = crc ^ buf[i]; | 
				
			||||
 | 
					        for(int8_t b = 7; b >= 0; b--) { | 
				
			||||
 | 
					            lsb = (crc & 0x0001); | 
				
			||||
 | 
					            if(lsb == 0x01) | 
				
			||||
 | 
					                crc--; | 
				
			||||
 | 
					            crc = crc >> 1; | 
				
			||||
 | 
					            if(lsb == 0x01) | 
				
			||||
 | 
					                crc = crc ^ CRC16_MODBUS_POLYNOM; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    return crc; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// NRF24 CRC16 calculation with poly 0x1021 = (1) 0001 0000 0010 0001 = x^16+x^12+x^5+1
 | 
				
			||||
 | 
					uint16_t crc16(uint8_t *buf, const uint16_t bufLen, const uint16_t startCRC, const uint16_t startBit, const uint16_t len_bits) | 
				
			||||
 | 
					{ | 
				
			||||
 | 
						uint16_t crc = startCRC; | 
				
			||||
 | 
						if ((len_bits > 0) && (len_bits <= BYTES_TO_BITS(bufLen))) | 
				
			||||
 | 
						{ | 
				
			||||
 | 
							// The length of the data might not be a multiple of full bytes.
 | 
				
			||||
 | 
							// Therefore we proceed over the data bit-by-bit (like the NRF24 does) to
 | 
				
			||||
 | 
							// calculate the CRC.
 | 
				
			||||
 | 
							uint16_t data; | 
				
			||||
 | 
							uint8_t byte, shift; | 
				
			||||
 | 
							uint16_t bitoffs = startBit; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
							// Get a new byte for the next 8 bits.
 | 
				
			||||
 | 
							byte = buf[bitoffs >> 3]; | 
				
			||||
 | 
					#ifdef OUTPUT_DEBUG_INFO | 
				
			||||
 | 
							printf_P(PSTR("\nStart CRC %04X, %u bits:"), startCRC, len_bits); | 
				
			||||
 | 
							printf_P(PSTR("\nbyte %02X:"), byte); | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
							while (bitoffs < len_bits + startBit) | 
				
			||||
 | 
							{ | 
				
			||||
 | 
								shift = bitoffs & 7; | 
				
			||||
 | 
								// Shift the active bit to the position of bit 15
 | 
				
			||||
 | 
								data = ((uint16_t)byte) << (8 + shift); | 
				
			||||
 | 
					#ifdef OUTPUT_DEBUG_INFO | 
				
			||||
 | 
								printf_P(PSTR(" bit %u %u,"), shift, data & 0x8000 ? 1 : 0); | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
								// Assure all other bits are 0
 | 
				
			||||
 | 
								data &= 0x8000; | 
				
			||||
 | 
								crc ^= data; | 
				
			||||
 | 
								if (crc & 0x8000) | 
				
			||||
 | 
								{ | 
				
			||||
 | 
									crc = (crc << 1) ^ 0x1021; // 0x1021 = (1) 0001 0000 0010 0001 = x^16+x^12+x^5+1
 | 
				
			||||
 | 
								} | 
				
			||||
 | 
								else | 
				
			||||
 | 
								{ | 
				
			||||
 | 
									crc = (crc << 1); | 
				
			||||
 | 
								} | 
				
			||||
 | 
								++bitoffs; | 
				
			||||
 | 
								if (0 == (bitoffs & 7)) | 
				
			||||
 | 
								{ | 
				
			||||
 | 
									// Get a new byte for the next 8 bits.
 | 
				
			||||
 | 
									byte = buf[bitoffs >> 3]; | 
				
			||||
 | 
					#ifdef OUTPUT_DEBUG_INFO | 
				
			||||
 | 
									printf_P(PSTR("crc %04X:"), crc); | 
				
			||||
 | 
									if (bitoffs < len_bits + startBit) | 
				
			||||
 | 
										printf_P(PSTR("\nbyte %02X:"), byte); | 
				
			||||
 | 
					#endif | 
				
			||||
 | 
								} | 
				
			||||
 | 
							} | 
				
			||||
 | 
						} | 
				
			||||
 | 
						return crc; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#endif | 
				
			||||
@ -0,0 +1,93 @@ | 
				
			|||||
 | 
					#ifndef __HM_PACKETS_H | 
				
			||||
 | 
					#define __HM_PACKETS_H | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#include "hm_crc.h" | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					class HM_Packets | 
				
			||||
 | 
					{ | 
				
			||||
 | 
					private: | 
				
			||||
 | 
						uint32_t unixTimeStamp; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
						void prepareBuffer(uint8_t *buf); | 
				
			||||
 | 
						void copyToBuffer(uint8_t *buf, uint32_t val); | 
				
			||||
 | 
						void copyToBufferBE(uint8_t *buf, uint32_t val); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					public: | 
				
			||||
 | 
						void SetUnixTimeStamp(uint32_t ts); | 
				
			||||
 | 
						void UnixTimeStampTick(); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
						int32_t GetTimePacket(uint8_t *buf, uint32_t wrAdr, uint32_t dtuAdr); | 
				
			||||
 | 
						int32_t GetCmdPacket(uint8_t *buf, uint32_t wrAdr, uint32_t dtuAdr, uint8_t mid, uint8_t cmd); | 
				
			||||
 | 
					}; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void HM_Packets::SetUnixTimeStamp(uint32_t ts) | 
				
			||||
 | 
					{ | 
				
			||||
 | 
						unixTimeStamp = ts; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void HM_Packets::UnixTimeStampTick() | 
				
			||||
 | 
					{ | 
				
			||||
 | 
						unixTimeStamp++; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void HM_Packets::prepareBuffer(uint8_t *buf) | 
				
			||||
 | 
					{ | 
				
			||||
 | 
						// minimal buffer size of 32 bytes is assumed
 | 
				
			||||
 | 
						memset(buf, 0x00, 32); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void HM_Packets::copyToBuffer(uint8_t *buf, uint32_t val) | 
				
			||||
 | 
					{ | 
				
			||||
 | 
						buf[0]= (uint8_t)(val >> 24); | 
				
			||||
 | 
						buf[1]= (uint8_t)(val >> 16); | 
				
			||||
 | 
						buf[2]= (uint8_t)(val >> 8); | 
				
			||||
 | 
						buf[3]= (uint8_t)(val & 0xFF); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void HM_Packets::copyToBufferBE(uint8_t *buf, uint32_t val) | 
				
			||||
 | 
					{ | 
				
			||||
 | 
						memcpy(buf, &val, sizeof(uint32_t)); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					static uint8_t cid = 0; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					int32_t HM_Packets::GetTimePacket(uint8_t *buf, uint32_t wrAdr, uint32_t dtuAdr) | 
				
			||||
 | 
					{ | 
				
			||||
 | 
						prepareBuffer(buf); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
						buf[0] = 0x15; | 
				
			||||
 | 
						copyToBufferBE(&buf[1], wrAdr); | 
				
			||||
 | 
						copyToBufferBE(&buf[5], dtuAdr); | 
				
			||||
 | 
						buf[9] = 0x80; | 
				
			||||
 | 
					  buf[10] = 0x0B;        //0x0B;  0x03 0x11
 | 
				
			||||
 | 
						buf[11] = 0x00; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
						copyToBuffer(&buf[12], unixTimeStamp); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
						buf[19] = 0x05; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
						// CRC16
 | 
				
			||||
 | 
						uint16_t crc16 = crc16_modbus(&buf[10], 14); | 
				
			||||
 | 
						buf[24] = crc16 >> 8; | 
				
			||||
 | 
						buf[25] = crc16 & 0xFF; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
						// crc8
 | 
				
			||||
 | 
						buf[26] = crc8(&buf[0], 26); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
						return 27; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					int32_t HM_Packets::GetCmdPacket(uint8_t *buf, uint32_t wrAdr, uint32_t dtuAdr, uint8_t mid, uint8_t cmd) | 
				
			||||
 | 
					{ | 
				
			||||
 | 
						buf[0] = mid; | 
				
			||||
 | 
						copyToBufferBE(&buf[1], wrAdr); | 
				
			||||
 | 
						copyToBufferBE(&buf[5], dtuAdr); | 
				
			||||
 | 
						buf[9] = cmd; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
						// crc8
 | 
				
			||||
 | 
						buf[10] = crc8(&buf[0], 10); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
						return 11; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#endif | 
				
			||||
@ -0,0 +1,345 @@ | 
				
			|||||
 | 
					#ifndef __WIFI_H | 
				
			||||
 | 
					#define __WIFI_H | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#include "Settings.h" | 
				
			||||
 | 
					#include "Debug.h" | 
				
			||||
 | 
					#include <ESP8266WiFi.h> | 
				
			||||
 | 
					#include <Pinger.h>       // von url=https://www.technologytourist.com    | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					String SSID = "";         // bestes WLan
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// Prototypes
 | 
				
			||||
 | 
					time_t getNow (); | 
				
			||||
 | 
					boolean setupWifi (); | 
				
			||||
 | 
					boolean checkWifi(); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					String findWifi () { | 
				
			||||
 | 
					//----------------
 | 
				
			||||
 | 
					  String ssid; | 
				
			||||
 | 
					  int32_t rssi; | 
				
			||||
 | 
					  uint8_t encryptionType; | 
				
			||||
 | 
					  uint8_t* bssid; | 
				
			||||
 | 
					  int32_t channel; | 
				
			||||
 | 
					  bool hidden; | 
				
			||||
 | 
					  int scanResult; | 
				
			||||
 | 
					   | 
				
			||||
 | 
					  String best_ssid = ""; | 
				
			||||
 | 
					  int32_t best_rssi = -100; | 
				
			||||
 | 
					   | 
				
			||||
 | 
					  DEBUG_OUT.println(F("Starting WiFi scan...")); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  scanResult = WiFi.scanNetworks(/*async=*/false, /*hidden=*/true); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  if (scanResult == 0) { | 
				
			||||
 | 
					    DEBUG_OUT.println(F("keine WLans")); | 
				
			||||
 | 
					  } else if (scanResult > 0) { | 
				
			||||
 | 
					    DEBUG_OUT.printf(PSTR("%d WLans gefunden:\n"), scanResult); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					    // Print unsorted scan results
 | 
				
			||||
 | 
					    for (int8_t i = 0; i < scanResult; i++) { | 
				
			||||
 | 
					      WiFi.getNetworkInfo(i, ssid, encryptionType, rssi, bssid, channel, hidden); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					      DEBUG_OUT.printf(PSTR("  %02d: [CH %02d] [%02X:%02X:%02X:%02X:%02X:%02X] %ddBm %c %c %s\n"), | 
				
			||||
 | 
					                    i, | 
				
			||||
 | 
					                    channel, | 
				
			||||
 | 
					                    bssid[0], bssid[1], bssid[2], | 
				
			||||
 | 
					                    bssid[3], bssid[4], bssid[5], | 
				
			||||
 | 
					                    rssi, | 
				
			||||
 | 
					                    (encryptionType == ENC_TYPE_NONE) ? ' ' : '*', | 
				
			||||
 | 
					                    hidden ? 'H' : 'V', | 
				
			||||
 | 
					                    ssid.c_str()); | 
				
			||||
 | 
					      delay(1); | 
				
			||||
 | 
					      boolean check; | 
				
			||||
 | 
					      #ifdef SSID_PREFIX1 | 
				
			||||
 | 
					      check = ssid.substring(0,strlen(SSID_PREFIX1)).equals(SSID_PREFIX1); | 
				
			||||
 | 
					      #else | 
				
			||||
 | 
					      check = true; | 
				
			||||
 | 
					      #endif     | 
				
			||||
 | 
					      #ifdef SSID_PREFIX2 | 
				
			||||
 | 
					      check = check || ssid.substring(0,strlen(SSID_PREFIX2)).equals(SSID_PREFIX2); | 
				
			||||
 | 
					      #endif | 
				
			||||
 | 
					      if (check) { | 
				
			||||
 | 
					        if (rssi > best_rssi) { | 
				
			||||
 | 
					          best_rssi = rssi; | 
				
			||||
 | 
					          best_ssid = ssid; | 
				
			||||
 | 
					        } | 
				
			||||
 | 
					      } | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					  } else { | 
				
			||||
 | 
					    DEBUG_OUT.printf(PSTR("WiFi scan error %d"), scanResult); | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  if (! best_ssid.equals("")) { | 
				
			||||
 | 
					    SSID = best_ssid; | 
				
			||||
 | 
					    DEBUG_OUT.printf ("Bestes Wifi unter: %s\n", SSID.c_str()); | 
				
			||||
 | 
					    return SSID; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  else | 
				
			||||
 | 
					    return ""; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void IP2string (IPAddress IP, char * buf) { | 
				
			||||
 | 
					  sprintf (buf, "%d.%d.%d.%d", IP[0], IP[1], IP[2], IP[3]); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void connectWifi() { | 
				
			||||
 | 
					//------------------
 | 
				
			||||
 | 
					//  if (SSID.equals(""))
 | 
				
			||||
 | 
					   String s = findWifi(); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  if (!SSID.equals("")) { | 
				
			||||
 | 
					    DEBUG_OUT.print("versuche zu verbinden mit "); DEBUG_OUT.println(SSID); | 
				
			||||
 | 
					    //while (WiFi.status() != WL_CONNECTED) {
 | 
				
			||||
 | 
					    WiFi.begin (SSID, SSID_PASSWORD); | 
				
			||||
 | 
					    int versuche = 20; | 
				
			||||
 | 
					    while (WiFi.status() != WL_CONNECTED && versuche > 0) { | 
				
			||||
 | 
					      delay(1000); | 
				
			||||
 | 
					      versuche--; | 
				
			||||
 | 
					      DEBUG_OUT.print(versuche); DEBUG_OUT.print(' '); | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					    //}
 | 
				
			||||
 | 
					    if (WiFi.status() == WL_CONNECTED) { | 
				
			||||
 | 
					      char buffer[30]; | 
				
			||||
 | 
					      IP2string (WiFi.localIP(), buffer); | 
				
			||||
 | 
					      String out = "\n[WiFi]Verbunden; meine IP:" + String (buffer); | 
				
			||||
 | 
					      DEBUG_OUT.println (out); | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					    else | 
				
			||||
 | 
					      DEBUG_OUT.print("\nkeine Verbindung mit SSID "); DEBUG_OUT.println(SSID); | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					boolean setupWifi () { | 
				
			||||
 | 
					//------------------  
 | 
				
			||||
 | 
					  int count=5; | 
				
			||||
 | 
					  while (count-- && WiFi.status() != WL_CONNECTED) | 
				
			||||
 | 
					    connectWifi();    | 
				
			||||
 | 
					  return (WiFi.status() == WL_CONNECTED); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					Pinger pinger; | 
				
			||||
 | 
					IPAddress ROUTER = IPAddress(192,168,1,1); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					boolean checkWifi() { | 
				
			||||
 | 
					//---------------
 | 
				
			||||
 | 
					  boolean NotConnected = (WiFi.status() != WL_CONNECTED) || !pinger.Ping(ROUTER); | 
				
			||||
 | 
					  if (NotConnected) { | 
				
			||||
 | 
					    setupWifi(); | 
				
			||||
 | 
					    if (WiFi.status() == WL_CONNECTED)  | 
				
			||||
 | 
					      getNow(); | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  return (WiFi.status() == WL_CONNECTED); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// ################  Clock  #################
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#include <WiFiUdp.h> | 
				
			||||
 | 
					#include <TimeLib.h> | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					IPAddress     timeServer; | 
				
			||||
 | 
					unsigned int  localPort = 8888; | 
				
			||||
 | 
					const int     NTP_PACKET_SIZE= 48;            // NTP time stamp is in the first 48 bytes of the message
 | 
				
			||||
 | 
					byte          packetBuf[NTP_PACKET_SIZE];  // Buffer to hold incoming and outgoing packets
 | 
				
			||||
 | 
					const int     timeZone = 1;                   // Central European Time = +1
 | 
				
			||||
 | 
					long          SYNCINTERVALL = 0; | 
				
			||||
 | 
					WiFiUDP Udp;                                  // A UDP instance to let us send and receive packets over UDP
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// prototypes
 | 
				
			||||
 | 
					time_t  getNtpTime (); | 
				
			||||
 | 
					void    sendNTPpacket (IPAddress &address); | 
				
			||||
 | 
					time_t  getNow (); | 
				
			||||
 | 
					char*   getDateTimeStr (time_t no = getNow()); | 
				
			||||
 | 
					time_t  offsetDayLightSaving (uint32_t local_t); | 
				
			||||
 | 
					bool    isDayofDaylightChange (time_t local_t); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void _setSyncInterval (long intervall) { | 
				
			||||
 | 
					//----------------------------------------  
 | 
				
			||||
 | 
					  SYNCINTERVALL = intervall; | 
				
			||||
 | 
					  setSyncInterval (intervall); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					void setupClock() { | 
				
			||||
 | 
					//-----------------
 | 
				
			||||
 | 
					  WiFi.hostByName (TIMESERVER_NAME,timeServer); // at this point the function works
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  Udp.begin(localPort); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  getNtpTime(); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  setSyncProvider (getNtpTime); | 
				
			||||
 | 
					  while(timeStatus()== timeNotSet) | 
				
			||||
 | 
					     delay(1); //
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  _setSyncInterval (SECS_PER_DAY / 2); // Set seconds between re-sync
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					  //lastClock = now();
 | 
				
			||||
 | 
					  //Serial.print("[NTP] get time from NTP server ");
 | 
				
			||||
 | 
					  getNow(); | 
				
			||||
 | 
					  //char buf[20];
 | 
				
			||||
 | 
					  DEBUG_OUT.print ("[NTP] get time from NTP server "); | 
				
			||||
 | 
					  DEBUG_OUT.print (timeServer); | 
				
			||||
 | 
					  //sprintf (buf, ": %02d:%02d:%02d", hour(no), minute(no), second(no));
 | 
				
			||||
 | 
					  DEBUG_OUT.print (": got "); | 
				
			||||
 | 
					  DEBUG_OUT.println (getDateTimeStr()); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					//*-------- NTP code ----------*/
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					time_t getNtpTime() { | 
				
			||||
 | 
					//-------------------
 | 
				
			||||
 | 
					  sendNTPpacket(timeServer); // send an NTP packet to a time server
 | 
				
			||||
 | 
					  //uint32_t beginWait = millis();
 | 
				
			||||
 | 
					  //while (millis() - beginWait < 1500) {
 | 
				
			||||
 | 
					  int versuch = 0; | 
				
			||||
 | 
					  while (versuch < 5) { | 
				
			||||
 | 
					    int wait = 150;      // results in max 1500 ms waitTime
 | 
				
			||||
 | 
					    while (wait--) { | 
				
			||||
 | 
					      int size = Udp.parsePacket(); | 
				
			||||
 | 
					      if (size >= NTP_PACKET_SIZE) { | 
				
			||||
 | 
					        //Serial.println("Receive NTP Response");
 | 
				
			||||
 | 
					        Udp.read(packetBuf, NTP_PACKET_SIZE);  // read packet into the buffer
 | 
				
			||||
 | 
					        unsigned long secsSince1900; | 
				
			||||
 | 
					        // convert four bytes starting at location 40 to a long integer
 | 
				
			||||
 | 
					        secsSince1900 =  (unsigned long)packetBuf[40] << 24; | 
				
			||||
 | 
					        secsSince1900 |= (unsigned long)packetBuf[41] << 16; | 
				
			||||
 | 
					        secsSince1900 |= (unsigned long)packetBuf[42] << 8; | 
				
			||||
 | 
					        secsSince1900 |= (unsigned long)packetBuf[43]; | 
				
			||||
 | 
					        // time_t now = secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
 | 
				
			||||
 | 
					   | 
				
			||||
 | 
					        time_t utc = secsSince1900 - 2208988800UL; | 
				
			||||
 | 
					        time_t now = utc + (timeZone +offsetDayLightSaving(utc)) * SECS_PER_HOUR; | 
				
			||||
 | 
					         | 
				
			||||
 | 
					        if (isDayofDaylightChange (utc) && hour(utc) <= 4) | 
				
			||||
 | 
					          _setSyncInterval (SECS_PER_HOUR); | 
				
			||||
 | 
					        else | 
				
			||||
 | 
					          _setSyncInterval (SECS_PER_DAY / 2); | 
				
			||||
 | 
					        | 
				
			||||
 | 
					        return now; | 
				
			||||
 | 
					      } | 
				
			||||
 | 
					      else | 
				
			||||
 | 
					        delay(10); | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					    versuch++; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  return 0; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// send an NTP request to the time server at the given address
 | 
				
			||||
 | 
					void sendNTPpacket(IPAddress& address) { | 
				
			||||
 | 
					//------------------------------------
 | 
				
			||||
 | 
					  memset(packetBuf, 0, NTP_PACKET_SIZE);  // set all bytes in the buffer to 0
 | 
				
			||||
 | 
					  // Initialize values needed to form NTP request
 | 
				
			||||
 | 
					  packetBuf[0] = B11100011;   // LI, Version, Mode
 | 
				
			||||
 | 
					  packetBuf[1] = 0;     // Stratum
 | 
				
			||||
 | 
					  packetBuf[2] = 6;     // Max Interval between messages in seconds
 | 
				
			||||
 | 
					  packetBuf[3] = 0xEC;  // Clock Precision
 | 
				
			||||
 | 
					  // bytes 4 - 11 are for Root Delay and Dispersion and were set to 0 by memset
 | 
				
			||||
 | 
					  packetBuf[12]  = 49;  // four-byte reference ID identifying
 | 
				
			||||
 | 
					  packetBuf[13]  = 0x4E; | 
				
			||||
 | 
					  packetBuf[14]  = 49; | 
				
			||||
 | 
					  packetBuf[15]  = 52; | 
				
			||||
 | 
					  // send the packet requesting a timestamp:
 | 
				
			||||
 | 
					  Udp.beginPacket(address, 123); //NTP requests are to port 123
 | 
				
			||||
 | 
					  Udp.write(packetBuf,NTP_PACKET_SIZE); | 
				
			||||
 | 
					  Udp.endPacket(); | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					int getTimeTrials = 0; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					bool isValidDateTime (time_t no) { | 
				
			||||
 | 
					  return (year(no) > 2020 && year(no) < 2038);   | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					bool isDayofDaylightChange (time_t local_t) { | 
				
			||||
 | 
					//-----------------------------------------
 | 
				
			||||
 | 
					  int jahr  = year  (local_t); | 
				
			||||
 | 
					  int monat = month (local_t); | 
				
			||||
 | 
					  int tag   = day   (local_t); | 
				
			||||
 | 
					  bool ret = ( (monat ==3 && tag == (31 - (5 * jahr /4 + 4) % 7)) ||  | 
				
			||||
 | 
					               (monat==10 && tag == (31 - (5 * jahr /4 + 1) % 7))); | 
				
			||||
 | 
					  DEBUG_OUT.print ("isDayofDaylightChange="); DEBUG_OUT.println (ret);  | 
				
			||||
 | 
					  return ret; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					// calculates the daylight saving time for middle Europe. Input: Unixtime in UTC (!)
 | 
				
			||||
 | 
					// übernommen von Jurs, see : https://forum.arduino.cc/index.php?topic=172044.msg1278536#msg1278536
 | 
				
			||||
 | 
					time_t offsetDayLightSaving (uint32_t local_t) { | 
				
			||||
 | 
					//--------------------------------------------
 | 
				
			||||
 | 
					  int monat = month (local_t); | 
				
			||||
 | 
					  if (monat < 3 || monat > 10) return 0; // no DSL in Jan, Feb, Nov, Dez
 | 
				
			||||
 | 
					  if (monat > 3 && monat < 10) return 1; // DSL in Apr, May, Jun, Jul, Aug, Sep
 | 
				
			||||
 | 
					  int jahr  = year (local_t); | 
				
			||||
 | 
					  int std   = hour (local_t); | 
				
			||||
 | 
					  //int tag   = day  (local_t);
 | 
				
			||||
 | 
					  int stundenBisHeute = (std + 24 * day(local_t)); | 
				
			||||
 | 
					  if ( (monat == 3  && stundenBisHeute >= (1 + timeZone + 24 * (31 - (5 * jahr /4 + 4) % 7))) ||  | 
				
			||||
 | 
					       (monat == 10 && stundenBisHeute <  (1 + timeZone + 24 * (31 - (5 * jahr /4 + 1) % 7))) ) | 
				
			||||
 | 
					    return 1; | 
				
			||||
 | 
					  else | 
				
			||||
 | 
					    return 0; | 
				
			||||
 | 
					  /*
 | 
				
			||||
 | 
					  int stundenBisWechsel = (1 + 24 * (31 - (5 * year(local_t) / 4 + 4) % 7)); | 
				
			||||
 | 
					  if (monat == 3  && stundenBisHeute >= stundenBisWechsel || monat == 10 && stundenBisHeute <  stundenBisWechsel) | 
				
			||||
 | 
					    return 1; | 
				
			||||
 | 
					  else | 
				
			||||
 | 
					    return 0; | 
				
			||||
 | 
					    */ | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					time_t getNow () { | 
				
			||||
 | 
					//---------------
 | 
				
			||||
 | 
					  time_t jetzt = now(); | 
				
			||||
 | 
					  while (!isValidDateTime(jetzt) && getTimeTrials < 10)  { // ungültig, max 10x probieren
 | 
				
			||||
 | 
					    if (getTimeTrials) { | 
				
			||||
 | 
					      //Serial.print (getTimeTrials);
 | 
				
			||||
 | 
					      //Serial.println(". Versuch für getNtpTime");
 | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					    jetzt = getNtpTime (); | 
				
			||||
 | 
					    if (isValidDateTime(jetzt)) { | 
				
			||||
 | 
					      setTime (jetzt); | 
				
			||||
 | 
					      getTimeTrials = 0; | 
				
			||||
 | 
					    } | 
				
			||||
 | 
					    else | 
				
			||||
 | 
					      getTimeTrials++; | 
				
			||||
 | 
					  } | 
				
			||||
 | 
					  //return jetzt + offsetDayLightSaving(jetzt)*SECS_PER_HOUR;
 | 
				
			||||
 | 
					  return jetzt; | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					char _timestr[24]; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					char* getNowStr (time_t no = getNow()) { | 
				
			||||
 | 
					//------------------------------------
 | 
				
			||||
 | 
					  sprintf (_timestr, "%02d:%02d:%02d", hour(no), minute(no), second(no)); | 
				
			||||
 | 
					  return _timestr;   | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					char* getTimeStr (time_t no = getNow()) { | 
				
			||||
 | 
					//------------------------------------
 | 
				
			||||
 | 
					  return getNowStr (no); | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					char* getDateTimeStr (time_t no) { | 
				
			||||
 | 
					//------------------------------
 | 
				
			||||
 | 
					  sprintf (_timestr, "%04d-%02d-%02d+%02d:%02d:%02d", year(no), month(no), day(no), hour(no), minute(no), second(no)); | 
				
			||||
 | 
					  return _timestr;   | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					char* getDateStr (time_t no) { | 
				
			||||
 | 
					//------------------------------
 | 
				
			||||
 | 
					  sprintf (_timestr, "%04d-%02d-%02d", year(no), month(no), day(no)); | 
				
			||||
 | 
					  return _timestr;   | 
				
			||||
 | 
					} | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					#endif | 
				
			||||
					Loading…
					
					
				
		Reference in new issue