MicroLog.MyPower.CZ

… designed for freedom of automation.

Uživatelské nástroje

Nástroje pro tento web


source

Microlog2 Source

Info

Verze 2.04

Kompilace projektu

Pozor: Pro kompilaci je potřeba použít starší verzi Arduino IDE, ideálně verzi 1.0.5

V nových verzích se můžete setkat s chybou

microlog2:897: error: 'WebServer_p' was not declared in this scope

nebo

microlog2:899: error: 'WebServer_p' was not declared in this scope

Notes

  • přidána podpora pro datum a čas, synchronizováno při přenosu na mypower.cz
  • čítač sekund byl změněn na uint32_t
  • mapa eprom byla přesunuta z kódu na wiki

Download

Source

microlog2.ino
// ---[ INFO ]----------------------------------------
 
// Program: MyPower.CZ Microlog2
// License: free for non-commercial use
// Details: info@mypower.cz
 
// ---[ VERSION ]-------------------------------------
 
#define MPWMicroLogVersion     "2.04"
 
// ---[ INCLUDE ]-------------------------------------
 
#include <SPI.h>
#include <EEPROM.h>
#include <Ethernet.h>
#include <utility/w5100.h>
 
// ---[ BUILT-IN CONFIG ]-----------------------------
 
int sendInterval  = 30; // sec default: 120
int sendRetry     = 30; // sec default: 30
byte maxErrors    = 3;  // default: 3
 
byte NETmac[6]    = { 0xF6, 0x88, 0x00, 0x00, 0x00, 0x01 };
//byte NETip[4]     = { 255, 255, 255, 255 }; // = DHCP
byte NETip[4]     = { 192, 168, 1, 188 };  // { 255, 255, 255, 255 }; // = DHCP
byte NETgw[4]     = { 192, 168, 1, 20 };
byte NETmask[4]   = { 255, 255, 255, 0 };
byte NETdns[4]    = { 192, 168, 1, 100 };
 
// ---[ DPIN 22..49 map ]--------------------------
//                    00  01  02  03  04  05  06  07  08  09  10  11  12  13  14  15
//                    [....... LED .....][.not used.][........... RELE .............]
byte DPINmap[16]  = { 40, 42, 44, 46, 48, 0,  0,  0,  22, 23, 24, 25, 26, 27, 28, 29 };
uint16_t DPINstatus    = 0x0000;
//                         <15  08>          <07  >00                          
uint16_t DPINlogicmask = (B00000000 << 8) | B00000011;  // pin/bit  0 = standard logic 0 OFF 1 ON,   1 = negative logic 0 ON 1 OFF
 
// ---[ MODS ]----------------------------------------
 
// mod MyPowerLog
#define modMyPowerLog      1                    // 1=on 0=off
#define modMyPowerHost     "log.mypower.cz"     // log.mypower.cz
#define modMyPowerFVEID    "mojefve1234"        // mojefve1234
 
// mod StausLed
#define modStatusLed       1         // 1=on 0=off
#define modStatusLedPin    41        // default: 40
 
// mod SerialLog
#define modSerialLog       1         // 1=on 0=off
#define modSerialForce     1         
#define modSerialBaudRate  115200    // 9600  ....  115200
 
// mod WebServer
#define modWebServer       1         // 1=on 0=off
#define modWebServerPort   80        // default: 80
 
// mod Display
#define modDisplay         1         // 1=on 0=off
#define modDisplayType     1602      // typ displaye
 
// mod DisplayEmulation
#define modDisplayEmulation     0    // 1=display neni fyzicky pripojen - zobrazeni jen na www rozhrani 
                                     // 0=display pripojen - zobrazeni na fyzickem displayi i na www rozhrani
 
// ---------------------------------------------------
 
byte lxLed=0;
byte lxTime=0;
uint32_t lxSeconds=0;
uint32_t lxSendIndex=0;
byte lxHttpOk=0;
byte lxHttpErrors=0;
 
// ---------------------------------------------------
 
#define dLCDKeyPad1602   1602
 
// ---------------------------------------------------
 
//  TTT GG E   
// 0000 0000
 
#define xpinOPTdefault   B00000000
#define xpinOPTenabled   B00000001
 
#define xpinOPTtypeMask  B01110000
 
#define xpinOPTsample    B00000000
#define xpinOPTavg       B00010000
#define xpinOPTeffective B00100000
#define xpinOPTmin       B00110000
#define xpinOPTmax       B01000000
#define xpinOPTres1      B01010000
#define xpinOPTres2      B01100000
#define xpinOPTdefault   B01110000
 
#define xpinOPTgroupMask B00001100
 
#define xpinOPTsampleG0  B00000000
#define xpinOPTsampleG1  B00000100
#define xpinOPTsampleG2  B00001000
#define xpinOPTsampleG3  B00001100
 
struct tPinConf {
char xname[21];char xunit[6];byte xopt;
signed long xmin,xmax;int xmin10000,xmax10000;
byte chsum;
};
 
struct tDPinConf {
char xname[20];
};
 
#define xComStatusNop               B00000000
#define xComStatusASchanged         B00000001
#define xComStatusDSchanged         B00000010
#define xComStatusIsLastDayInMonth  B00000100
#define xComStatusIsDST             B00001000
 
#define xComStatusXXDaysMonthMask   B00110000
#define xComStatus28DaysMonth       B00000000
#define xComStatus29DaysMonth       B00010000
#define xComStatus30DaysMonth       B00100000
#define xComStatus31DaysMonth       B00110000
 
struct tMpwAccess { 
  byte xComStatus;                    //  flags
  uint16_t mpwremoteDateYear;         //  2013 ...
   uint8_t mpwremoteDateMonth;        //  1-12
   uint8_t mpwremoteDateDay;          //  1-31
   uint8_t mpwremoteDateWeek;         //  1-53
   uint8_t mpwremoteDateDayOfWeek;    //  1 (for Monday) through 7 (for Sunday)
  signed long mpwremoteTimeS;         //  0-86400, -1 - not known
  uint32_t mpwremoteCheckedAtS;       //  local timeS (seconds counter)
  uint32_t mpwlogintimeS;             //  login local timeS 
  uint32_t mpwDeviceId;               //  your device ID on mpw server
  uint32_t mpwAccessId;               //  your access ID on mpw server
} xMpwAccess;
 
struct tMpwDateTime { 
  int xyear, xmonth, xday, xhour, xminute, xsecond; 
  };
 
#define xdcOptBuiltIn      0x00;
#define xdcOptActive       0x01;
 
struct tSampledPin { int pin; byte smpHi8[128]; byte smpLo2[32]; unsigned long smpSqr,smpAvg; int smpMin, smpMax, smpCount; };
struct tPinValue { int smpVal,smpSqr,smpAvg,smpMin,smpMax; unsigned long smpTime; };
struct tSamplerStruct { tPinValue tPinValues[NUM_ANALOG_INPUTS]; tSampledPin tPinSamples[4]; 
  byte tPinTimes[128]; unsigned long tPinMicros; } xSamplerStruct;
 
struct tDeviceConf {
char xfveid[21];
int xsendInterval;
int xsendRetry;
byte xmaxErrors;
byte xdcOpt;
byte chsum;
};
 
struct int64 { word hh,hl,lh,ll; boolean msf; };
 
// ---------------------------------------------------
 
#if modWebServer == 1
EthernetServer server(modWebServerPort);
#endif
 
#if (modDisplay == 1)
byte lcdCurrentPage=0xFF;
byte lcdMaxPage=0;
 
#if (modDisplayType == dLCDKeyPad1602)
struct tDispStats { int xvarscount; int xvarram; };
char _lcdlbuf[17];
char * _lcdweblbuf0=NULL;
char * _lcdweblbuf1=NULL;
tDispStats * _lcdwebdispstats=NULL;
 
#if (modDisplayEmulation == 0)
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 14, 5, 6, 7);
void lcdClear() { lcd.clear(); }
void lcdInit() { lcd.begin(16, 2); lcdClear(); }
#else
void lcdClear() { }
void lcdInit() { lcdClear(); }
#endif
 
void lcdPrintRight(char * str) { int x=16-strlen(str); lcdPrintAt(str,(x<0?0:x)); }
void lcdPrintCenter(char * str) { int x=8-(strlen(str)/2); lcdPrintAt(str,(x<0?0:x)); }
void lcdPrintLeft(char * str) { lcdPrintAt(str,0); }
void lcdPrintAt(char * str,int x) { 
  int ix=strlen(str);for (int i=0;((i+x<sizeof(_lcdlbuf)-1) && (i<ix));i++) if (x>=0) _lcdlbuf[i+x]=str[i]; }
//  int ix=strlen(str);for (int i=0;((i+x<sizeof(_lcdlbuf)-1) && (i<ix));i++) if ((x+i)>=0) _lcdlbuf[i+x]=str[i]; } ??????
void lcdClearLine() { int i; for (i=0;i<sizeof(_lcdlbuf)-1;i++) _lcdlbuf[i]=32; _lcdlbuf[i]=0; }
void lcdPrintLine(int y) { 
  if ((_lcdweblbuf0!=NULL) && (_lcdweblbuf1!=NULL))
    { char * p=_lcdweblbuf0; if (y==1) p=_lcdweblbuf1;strcpy(p,_lcdlbuf); }
  else { 
#if (modDisplayEmulation == 0) 
  lcd.setCursor(0,y); lcd.print(_lcdlbuf); 
#endif
} }
 
#endif
 
#endif
 
 
// -- [SETUP] ----------------------------------------------
 
void setup()
{
#if modStatusLed == 1
pinMode(modStatusLedPin, OUTPUT); SetLiveLed(HIGH);
#endif
 
 
#if ((modSerialLog == 1) || (modSerialForce == 1))
Serial.begin(modSerialBaudRate);
#endif
 
 
#if ((modSerialLog == 1) || (modDisplay == 1))
char sn1[]="MyPower.CZ";
char sn2[]="MicroLOG";
 
#if (modSerialLog == 1)
XLog(sn1);XLog(sn2);XLog(MPWMicroLogVersion);
 
#if (modMyPowerLog == 1)
xMpwAccess.mpwremoteDateYear=0;
xMpwAccess.mpwremoteDateMonth=0;
xMpwAccess.mpwremoteDateDay=0;
xMpwAccess.mpwremoteDateWeek=0;
xMpwAccess.mpwremoteDateDayOfWeek=0;
xMpwAccess.mpwremoteTimeS=-1;
xMpwAccess.mpwremoteCheckedAtS=0;
xMpwAccess.mpwlogintimeS=0;
xMpwAccess.mpwDeviceId=0;
xMpwAccess.mpwAccessId=0;
xMpwAccess.xComStatus=xComStatusNop;
char cx[50]="+mpwlog @ ";strcat(cx,modMyPowerHost);XLog(cx);
#endif
 
#if (modDisplay == 1)
char ccx[50]="";
#if (modDisplayEmulation == 1 )
sprintf(ccx,"+dispemu @ %04d",modDisplayType);
#else
sprintf(ccx,"+disp @ %04d",modDisplayType);
#endif
XLog(ccx);
#endif
 
#endif
 
#if (modDisplay == 1)
lcdInit();lcdCurrentPage=0xFF;
lcdClearLine();lcdPrintCenter(sn1);lcdPrintLine(0);
lcdClearLine();lcdPrintLeft(sn2);lcdPrintRight(MPWMicroLogVersion);lcdPrintLine(1);
#endif
 
#endif
 
DPINResetHW();
pinMode(53, OUTPUT);digitalWrite(53,HIGH);
 
int xret=0;
 
boolean isdhcp=((NETip[0]&NETip[1]&NETip[2]&NETip[3])==255);
 
if (isdhcp)
  {
#if (modSerialLog == 1)
XLog("DHCP?");
#endif
xret=Ethernet.begin(NETmac);
  }
else
  {
  Ethernet.begin(NETmac, NETip, NETdns, NETgw, NETmask);
  xret=1;
  delay(1000);
  }
 
W5100.setRetransmissionTime(0x07FF);
W5100.setRetransmissionCount(2); 
 
if ( (xret == 0) && (isdhcp) ) {
#if modSerialLog == 1
  XLog("No dhcp.");
#endif
  FlashError(4);
  }
else
  {
#if modSerialLog == 1
  char xtext[100];
  char cf[]="%d.%d.%d.%d";
  IPAddress xaddr;
  XLog("IP,MASK,GW,DNS");
  xaddr=Ethernet.localIP();
  for (byte i=0;i<4;i++) {
    if (i==1) xaddr=Ethernet.subnetMask();else
    if (i==2) xaddr=Ethernet.gatewayIP();else
    if (i==3) xaddr=Ethernet.dnsServerIP();   
    sprintf(xtext,cf,xaddr[0],xaddr[1],xaddr[2],xaddr[3]);
    XLog(xtext);
    }
#endif
  }
 
#if modWebServer == 1
server.begin();
#if modSerialLog == 1
char c[30]="";sprintf(c,"+www @ %d",modWebServerPort);XLog(c);
#endif
#endif
 
SamplerReset();
 
#if modSerialLog == 1
XLog("Done.");
#endif
}
 
 
 
// -- [LOOP] ----------------------------------------------
 
void loop()
{
FlashLiveLed();
CalculateSeconds();
#if modMyPowerLog == 1
SendDataToMyPower();
#endif
#if modWebServer == 1
WebServer();
#endif
#if (modDisplay == 1)
#if (modDisplayType == dLCDKeyPad1602)
DisplayDetectButton();
#endif
#endif
SamplerRun();
}
 
void softReset() { asm volatile ("  jmp 0"); } 
 
#if modSerialLog == 1
void XLog(char * xmsg) { Serial.println(xmsg); }
void XLogRam() { Serial.print("> RAM:"); Serial.println(freeRam()); }
void XLog2(char * xmsg,char * xpre) { Serial.print(xpre);Serial.println(xmsg); }
#endif
 
#if (modDisplay == 1)
void CalcDisplayConfEpromSpace(int * xa,int * la) {
* xa=(sizeof(tPinConf)*NUM_ANALOG_INPUTS)+sizeof(tDeviceConf);
* la=2047; }
#endif
 
 
void SetLiveLed(byte xs) { 
#if modStatusLed==1
digitalWrite(modStatusLedPin,xs);
#endif
}
 
void FlashLiveLed() {
unsigned long i=((millis()/100)%10);
byte qlxLed=( ((i==1)) || ((lxHttpOk==2) && ( (i==3) || (i==5) )) )?HIGH:LOW;
if (qlxLed!=lxLed) { SetLiveLed(qlxLed); lxLed=qlxLed; }
}
 
void CalculateSeconds() {
byte smx=0x7F; int qlxTime=((millis()/1000)&smx);
if (qlxTime!=lxTime) { OneSecondActions();
  lxSeconds+=(lxTime>qlxTime)?(qlxTime+smx+1-lxTime):(qlxTime-lxTime); lxTime=qlxTime; }
}
 
void OneSecondActions() {
#if (modDisplay == 1)
DrawDisplayPage();
#endif
}
 
#if modMyPowerLog==1
void SendDataToMyPower()
{ 
boolean canrun=true; 
for (int xp=0;xp<NUM_ANALOG_INPUTS;xp++)
  if (xSamplerStruct.tPinValues[xp].smpTime==0)
    { canrun=false;break; }
if (canrun) SendDataToMyPower_int(); 
}
 
void SendDataToMyPower_int()
{  
char xtext[200]; 
int xsi=sendInterval;
int xsiorig=xsi;
int xsr=sendRetry;
if (lxHttpOk==2) xsi=xsr;
if (xsi<30) xsi=30;
uint32_t qlxSendIndex=lxSeconds;
if ((lxSendIndex+xsi<=qlxSendIndex) || (lxHttpOk==0))
  {
  lxHttpOk=2;
  lxSendIndex=lxSeconds;
  EthernetClient client;  
  SetLiveLed(HIGH);
#if modSerialLog == 1
  XLogRam();XLog("> Connecting");
#endif
  if ( (client.connect(modMyPowerHost, 80)) && (client.connected()) )
    {
#if modSerialLog == 1
    XLog("> Connected");
#endif
    sprintf(xtext,"GET / HTTP/1.0\r\nHost: %s\r\nUser-Agent: MPWmicrolog/%s\r\nCookie: fveid=%s; ",
      modMyPowerHost,MPWMicroLogVersion,modMyPowerFVEID);
    client.write((byte*)xtext,strlen(xtext));  
 
    boolean islogin=(xMpwAccess.mpwlogintimeS==0);
 
if (islogin)
  {
  strcpy(xtext,"cdate=" __DATE__ " " __TIME__ ";");
  for (int i=0;i<strlen(xtext);i++)
    if (xtext[i]==' ') xtext[i]='_';
  strcat(xtext," ");
  client.write((byte*)xtext,strlen(xtext));
  client.write("login=yes; ");
  }
 
      sprintf(xtext,"utime=%lu/%lu; ",millis(),lxSeconds);
      client.write((byte*)xtext,strlen(xtext));
      sprintf(xtext,"AX=VSAMX; ");
      client.write((byte*)xtext,strlen(xtext));
 
    for (int xp=0;xp<NUM_ANALOG_INPUTS;xp++)
      {
 
      tPinValue * qPinValue;
      qPinValue=&xSamplerStruct.tPinValues[xp];
      sprintf(xtext,"A%d=%X,%X,%X,%X,%X; ",xp,
        qPinValue->smpVal,qPinValue->smpSqr,qPinValue->smpAvg,qPinValue->smpMin,qPinValue->smpMax);
      client.write((byte*)xtext,strlen(xtext));
 
if ( (islogin) || ((xMpwAccess.xComStatus&xComStatusASchanged)!=0) )
  {
      tPinConf pconf;            
      boolean isvalid=ReadPinConf(&pconf,xp);            
      if (isvalid)
        { 
        char smin10k[30]="";sprint10k(smin10k,pconf.xmin,pconf.xmin10000);
        char smax10k[30]="";sprint10k(smax10k,pconf.xmax,pconf.xmax10000);
//        sprintf(xtext,"AC%d=%X/%ld.%04d/%ld.%04d/",xp,pconf.xopt,pconf.xmin,pconf.xmin10000,pconf.xmax,pconf.xmax10000);
        sprintf(xtext,"AC%d=%X/%s/%s/",xp,pconf.xopt,smin10k,smax10k);
        client.write((byte*)xtext,strlen(xtext));
        xtext[0]=0;
        char xf[]="%02X";
        char xh[3];
        byte * p;
        p=(byte*)pconf.xname;
        for (int i=0;i<strlen(pconf.xname);i++) { sprintf(xh,xf,p[i]); strcat(xtext,xh); }
        client.write((byte*)xtext,strlen(xtext));
        strcpy(xtext,"/");
        p=(byte*)pconf.xunit;
        for (int i=0;i<strlen(pconf.xunit);i++) { sprintf(xh,xf,p[i]); strcat(xtext,xh); }
        client.write((byte*)xtext,strlen(xtext));
        client.write("; ");
        }              
  }
      }
 
    xMpwAccess.xComStatus&=~xComStatusASchanged;
 
    client.write("\r\n\r\n");
 
    int lbufpos=0;
    boolean ishead=true;
    xtext[0]=0;
    unsigned long xmstart=millis();
    while (client.connected())
      {
      if (millis()-xmstart>3000) { break; }
      if (client.available()) 
        {
        byte qbuf[200];
        int qlen=client.readBytes((char*)qbuf,sizeof(qbuf)-2);
        for (int i=0;i<qlen;i++) {
          char c = qbuf[i];
          if (c=='\n') {
            if (xtext[0]=='\r') ishead=false; else
            if (!ishead) ParseHTTPResult(xtext);
            lbufpos=0;
            xtext[0]=0;
            }
          else {
            if (lbufpos<sizeof(xtext)-2) {
              xtext[lbufpos]=c; lbufpos++; xtext[lbufpos]=0;
              }
            }
          }
        }
      }
    if (lbufpos>0) ParseHTTPResult(xtext);
    client.stop();
    }
  if (lxHttpOk==1) 
    {
#if modSerialLog == 1
    XLog("> OK");
    sprintf(xtext,"> Waiting (%d seconds) ...",xsiorig);
    XLog(xtext);
#endif
    lxHttpErrors=0;
    }
  else 
    {
    lxHttpErrors++;
#if modSerialLog == 1
    sprintf(xtext,"> ERR (%d/%d)",lxHttpErrors,maxErrors);
    XLog(xtext);
#endif
    if (lxHttpErrors>=maxErrors) FlashError(3);
#if modSerialLog == 1
    else
    sprintf(xtext,"> Retry @ %d seconds ...",xsr);
    XLog(xtext);
#endif
    }
  SetLiveLed(LOW);
  }
}
 
void ParseHTTPResult(char * xtext) { 
#if modSerialLog == 1
XLog2(xtext,"# ");
#endif
if (strcmp(xtext,"result:OK")==0) lxHttpOk=1; else
if (strncmp(xtext,"qid:",4)==0) ParseQID(xtext+4);
}
 
void ParseQID(char * xtext) {
if (xMpwAccess.mpwlogintimeS==0) xMpwAccess.mpwlogintimeS=lxSeconds;
char * p=strchr(xtext,'.');
if (p!=NULL)
  {  
  p[0]=0;
  xMpwAccess.mpwDeviceId=atol(xtext);
  p++;
  char * px=strchr(p,'.');
  if (px!=NULL)
    {
    px[0]=0;
    px++;
    xMpwAccess.mpwAccessId=atol(p);
    if (strlen(px)==20)
      {
      char c[30]="";
      strncpy(c,px,4);c[4]=0;xMpwAccess.mpwremoteDateYear=atoi(c);px+=4;
      strncpy(c,px,2);c[2]=0;xMpwAccess.mpwremoteDateMonth=atoi(c);px+=2;
      strncpy(c,px,2);c[2]=0;xMpwAccess.mpwremoteDateDay=atoi(c);px+=2;
      strncpy(c,px,2);c[2]=0;signed long xhour=atoi(c);px+=2;
      strncpy(c,px,2);c[2]=0;signed long xminute=atoi(c);px+=2;
      strncpy(c,px,2);c[2]=0;signed long xsecond=atoi(c);px+=2;
      strncpy(c,px,2);c[2]=0;byte xdaysinmonth=atoi(c);px+=2;
      strncpy(c,px,1);c[1]=0;byte xisdst=atoi(c);px+=1;
      strncpy(c,px,1);c[1]=0;xMpwAccess.mpwremoteDateDayOfWeek=atoi(c);px+=1;
      strncpy(c,px,2);c[2]=0;xMpwAccess.mpwremoteDateWeek=atoi(c);px+=2;
 
      if (xisdst==1)
        xMpwAccess.xComStatus|=xComStatusIsDST;
      else
        xMpwAccess.xComStatus&=~xComStatusIsDST;
 
      if (xdaysinmonth==xMpwAccess.mpwremoteDateDay)
        xMpwAccess.xComStatus|=xComStatusIsLastDayInMonth;
      else
        xMpwAccess.xComStatus&=~xComStatusIsLastDayInMonth;
 
      xMpwAccess.xComStatus&=~xComStatusXXDaysMonthMask;
      if (xdaysinmonth==28) xMpwAccess.xComStatus|=xComStatus28DaysMonth;
      if (xdaysinmonth==29) xMpwAccess.xComStatus|=xComStatus29DaysMonth;
      if (xdaysinmonth==30) xMpwAccess.xComStatus|=xComStatus30DaysMonth;
      if (xdaysinmonth==31) xMpwAccess.xComStatus|=xComStatus31DaysMonth;
 
      xMpwAccess.mpwremoteTimeS=(xhour*3600)+(xminute*60)+(xsecond);
      xMpwAccess.mpwremoteCheckedAtS=lxSeconds;
      }
    }
 
  }
 
/*  
char xstr[50]="";
tMpwDateTime xMpwDateTime;
GetCurrentDateTime(&xMpwDateTime);
FormatDateTimeStr(xstr, "j.n.Y H:i:s",&xMpwDateTime,sizeof(xstr));
Serial.println(xstr);
*/
}
#endif
 
 
 
void FormatDateTimeStr(char * xstr, char * xfmt, struct tMpwDateTime * xMpwDateTime,int xmaxlen)
{
xstr[0]=0;
while (strlen(xfmt)>0)
  {
  char ch=xfmt[0];
  char cout[20]="";
  if (ch=='d') sprintf(cout,"%02d",xMpwDateTime->xday); else
  if (ch=='j') sprintf(cout,"%d",xMpwDateTime->xday); else
  if (ch=='m') sprintf(cout,"%02d",xMpwDateTime->xmonth); else
  if (ch=='n') sprintf(cout,"%d",xMpwDateTime->xmonth); else
  if (ch=='Y') sprintf(cout,"%d",xMpwDateTime->xyear); else
  if (ch=='y') sprintf(cout,"%d",xMpwDateTime->xyear%99); else
  if (ch=='g') sprintf(cout,"%d",((xMpwDateTime->xhour%12)+1)); else
  if (ch=='G') sprintf(cout,"%d",(xMpwDateTime->xhour)); else
  if (ch=='h') sprintf(cout,"%02d",((xMpwDateTime->xhour%12)+1)); else
  if (ch=='H') sprintf(cout,"%02d",(xMpwDateTime->xhour)); else
  if (ch=='i') sprintf(cout,"%02d",(xMpwDateTime->xminute)); else
  if (ch=='s') sprintf(cout,"%02d",(xMpwDateTime->xsecond)); else
    { cout[0]=ch;cout[1]=0; }
 
  xfmt++;
  if (strlen(xstr)+strlen(cout)<xmaxlen) strcat(xstr,cout); else break;
  }
}
 
int GetCurrentDateTime_DaysInMonth()
{
int xret=28;
int xst=xMpwAccess.xComStatus&xComStatusXXDaysMonthMask;
if (xst==xComStatus28DaysMonth) xret=28; else
if (xst==xComStatus29DaysMonth) xret=29; else
if (xst==xComStatus30DaysMonth) xret=30; else
if (xst==xComStatus31DaysMonth) xret=31;
return xret;
}
 
void GetCurrentDateTime(struct tMpwDateTime * xMpwDateTime)
{
if (xMpwAccess.mpwremoteCheckedAtS>0)
  {
  xMpwDateTime->xyear=xMpwAccess.mpwremoteDateYear;
  xMpwDateTime->xmonth=xMpwAccess.mpwremoteDateMonth;
  xMpwDateTime->xday=xMpwAccess.mpwremoteDateDay;
  uint32_t xtime=lxSeconds-xMpwAccess.mpwremoteCheckedAtS+xMpwAccess.mpwremoteTimeS;
  if (xtime>=86400)
    {
    int xdaysover=xtime/86400UL;
    int xtimeover=xtime%86400UL;
 
    if (xdaysover>25) // max 25 days without time sync
      {
      xMpwDateTime->xyear=0;
      xMpwDateTime->xmonth=0;
      xMpwDateTime->xday=0;
      }
    else
      { 
      int xdaysinmonth=GetCurrentDateTime_DaysInMonth();
      for (int i=0;i<xdaysover;i++)
        {
        if (xMpwDateTime->xday<xdaysinmonth)
          xMpwDateTime->xday++;  
        else
          {
          xMpwDateTime->xday=1;
          if (xMpwDateTime->xmonth<12)
            xMpwDateTime->xmonth++;
          else
            {
            xMpwDateTime->xmonth=1;
            xMpwDateTime->xyear++;
            }
          }
        }
      }
 
    xtime=xtimeover;
    }
  xMpwDateTime->xhour=(xtime/3600)%24;
  int xmod=(xtime%3600);
  xMpwDateTime->xminute=(xmod/60)%60;
  xMpwDateTime->xsecond=xmod%60;
  }
else
  {
  uint32_t xtime=lxSeconds;
  xMpwDateTime->xyear=0;
  xMpwDateTime->xmonth=0;
  xMpwDateTime->xday=(xtime/86400UL);
  xMpwDateTime->xhour=((xtime%86400UL)/3600)%24;
  signed long xmod=((xtime%86400L)%3600);
  xMpwDateTime->xminute=(xmod/60)%60;
  xMpwDateTime->xsecond=xmod%60;    
  }
}
 
 
 
 
 
void FlashError(byte xcount)
{
#if modSerialLog == 1
char xtext[50];
sprintf(xtext,"Error %d.",xcount);
XLog(xtext);
XLog("Flashing err (10x) & restarting.");
#endif
xcount*=2;
for (int i=0;i<10;i++) {
  byte x=0;
  for (int i=0;i<xcount;i++) {
    SetLiveLed((!((x++)&1))?HIGH:LOW);
    delay(250);
    }
  delay(1000);
  }
#if modSerialLog == 1
XLog("Reset");
XLog("---");
#endif
delay(1000);
softReset();
}
 
int freeRam() {
extern unsigned int __heap_start;
extern void *__brkval;
int free_memory; int stack_here;
if (__brkval == 0) free_memory = (int) &stack_here - (int) &__heap_start;
else free_memory = (int) &stack_here - (int) __brkval; 
return (free_memory);
}
 
 
byte calcPchsum(byte xpin, struct tPinConf * pconf) {
byte*xconf=(byte*)pconf; byte xqs=pconf->chsum;
pconf->chsum=0; byte chs=0; for (int i=0;i<sizeof(tPinConf);i++) chs+=(xpin+3)+xconf[i]+i;
pconf->chsum=xqs; return chs&0xFF; }
 
boolean ReadPinConf(struct tPinConf * pconf,int xpin)
{
boolean xret=false;
if (xpin<NUM_ANALOG_INPUTS)
  {
  byte * pcx=(byte*)pconf;
  int sp=sizeof(tPinConf);
//  int xs=(xpin*sp);
  int xs=GetEpromPinAddr(xpin);
  if (xs>=0)
    {
    for (int ax=0;ax<sp;ax++) pcx[ax]=EEPROM.read(ax+xs);
    xret=(calcPchsum(xpin, pconf)==pconf->chsum);
    }
  }
return xret;
}
 
int GetEpromPinAddr(int xpin)
{
int xret=-1;
if (xpin<NUM_ANALOG_INPUTS)
  xret=(xpin*sizeof(tPinConf));
return xret;
}
 
 
// -- [WEB SERVER] ----------------------------------------------
 
 
#if modWebServer == 1
 
#define xWEBhome   0x00
#define xWEBcfgp   0x01
#define xWEBcfg    0x02
#define xWEBmpw    0x03
#define xWEBpins   0x04
 
#define xCFGnone   0x00
#define xCFGk      0x10
#define xCFGv      0x20
#define xCFGs      0x30
 
// w = write
// [w]as = analog settings
// [w]ds = digital settings
// [w]dp = display
// [w]ep = eprom
// hw = hwinfo
// dd = display dump
// db = display button press emulation
 
#define xACTnone   0x00
#define xACThw     0x01
#define xACTas     0x02
#define xACTdp     0x03
#define xACTdd     0x04
#define xACTdb     0x05
#define xACTsp     0x06
#define xACTds     0x07
#define xACTep     0x08
 
#define xACTwds    0x0C
#define xACTwep    0x0D
#define xACTwas    0x0E
#define xACTwdp    0x0F
 
#define xKEYnone          0x00
#define xKEYcmd           0x10
#define xKEYpin           0x20
#define xKEYDispButt      0x30
#define xKEYDpin          0x40
 
#define xPinOptNone       0x00
#define xPinOptEnabled    0x01
#define xPinOptName       0x02
#define xPinOptMin        0x03
#define xPinOptMax        0x04
#define xPinOptUnit       0x05
#define xPinOptType       0x06
#define xPinOptGroup      0x07
#define xPinCmdWrite      0xF0
 
#define xDPinOptNone      0x00
#define xDPinOptName      0x02
 
#define xDPinOptSetStatus 0xA0
 
#define xDPinSetStatus    0xE0
 
void WebServer_pf(char * xstr,signed long * xsl,int * xsl10k) {
int im; *xsl=0;*xsl10k=0; char * p=xstr; char * p10k=p;
for (int i=0;i<strlen(xstr);i++) if (p10k[0]=='.') { p10k[0]=0;p10k++;break; } else p10k++;
boolean hm=(p[0]=='-'); if (hm) { p++; } im=strlen(p);
for (int i=0;i<im;i++) { *xsl*=10; if ((p[i]>='0') && (p[i]<='9')) *xsl+=(p[i]-'0'); }
im=strlen(p10k);
for (int i=0;i<4;i++) { *xsl10k*=10; if ((i<im) && ((p10k[i]>='0') && (p10k[i]<='9'))) 
  *xsl10k+=(p10k[i]-'0'); }
if (hm) *xsl=0-*xsl;
if (hm) *xsl10k=0-*xsl10k; // minus bugfix
}
 
void WebServer_wp(byte xpin, struct tPinConf * pconf) {
if (xpin<NUM_ANALOG_INPUTS) {
  xMpwAccess.xComStatus|=xComStatusASchanged;
  byte*xconf=(byte*)pconf; pconf->chsum=calcPchsum(xpin,pconf);
  int ax=GetEpromPinAddr(xpin);
  if (ax>=0)
    for (int ii=0;ii<sizeof(tPinConf);ii++)
      if (EEPROM.read(ax+ii) != xconf[ii])
        EEPROM.write(ax+ii,xconf[ii]);
  }
}
 
void WebServer_wdp(byte xdpin, struct tDPinConf * pconf) {
if (xdpin<16) {
  xMpwAccess.xComStatus|=xComStatusDSchanged;
  DPIN_epWriteConfName(xdpin,pconf->xname);
  }
}
 
byte WebServer_pb(char * xstr) {
byte cb=0; for (int i=0;i<strlen(xstr);i++) { cb*=10; char c=xstr[i];
  if ((c>='0') && (c<='9')) cb+=c-'0'; }
return cb;
}
 
word WebServer_c(char * lbuf,char c,word xmode, byte * datastruct, unsigned long * xparam) {
byte qmode=xmode&0x0F; byte qsubmode=xmode&0xF0; byte qsaction=(xmode>>8)&0x0F; byte wkey=(xmode>>8)&0xF0;
if (qmode==xWEBhome) {
  if (strncmp(lbuf,"GET ",4)==0) {
    char * p=lbuf+5;
    if (strcmp(p,"cfg")==0) qmode=xWEBcfgp; else
    if (strcmp(p,"pin")==0) qmode=xWEBpins; else
    if (strcmp(p,"mpw")==0) qmode=xWEBmpw;
    if (qmode!=xWEBhome) lbuf[0]=0;
    }
  }
else if (qmode==xWEBcfgp) {
  if (c=='?')
    { qsubmode=xCFGk; lbuf[0]=0; }
  else if ((c=='&') || (c==' '))
    { WebServer_p(lbuf,&qsubmode,&wkey,&qsaction,datastruct,xparam); qsubmode=xCFGk; lbuf[0]=0; } 
  else if (c=='=')
    { WebServer_p(lbuf,&qsubmode,&wkey,&qsaction,datastruct,xparam); 
      if (qsubmode!=xCFGs) { qsubmode=xCFGv; strcpy(lbuf,"\n"); } else  { lbuf[0]=0; }  }
  if (c==' ') { qmode=xWEBcfg; WebServer_e(&qsaction,xparam); } else
  if (qsubmode==xCFGv) WebServer_h(lbuf); else
  if (qsubmode==xCFGs) WebServer_s(lbuf,&qsaction,xparam);
  }
return ((qmode|qsubmode)|((qsaction|wkey)<<8));
}
 
void WebServer_e(byte * qsaction, unsigned long  * xparam)
{
#if modDisplay == 1
if (*qsaction==xACTwdp)
  {
  byte chsum=((*xparam)&0xFF);
  int xaddr=((*xparam>>8)&0xFFFF);
  int xa,la;CalcDisplayConfEpromSpace(&xa,&la);
  int raddr=xaddr+xa+1;
  if (raddr<la) if (EEPROM.read(raddr)!=0) EEPROM.write(raddr,0);   
  if (EEPROM.read(la)!=0) EEPROM.write(la,0);
  if (EEPROM.read(xa)!=chsum) EEPROM.write(xa,chsum);
  }
#endif
}
 
unsigned long WebServer_ph(char * lbuf)
{
unsigned long d=0;
for (byte ii=0;ii<8;ii++) {
  char c=lbuf[ii];
  if (c='\0') break;
  d=(d<<4)+(((c>='0') && (c<='9'))?(c-'0'):(((c>='A') && (c<='F'))?(c-'A'+10):0));
  }
return d;
}
 
void WebServer_s(char * lbuf,byte * qsaction,unsigned long * xparam)
{
#if modDisplay == 1
if (*qsaction==xACTwdp)
  {
  if (strlen(lbuf)==2)
    {
    byte d=0;
    for (byte ii=0;ii<2;ii++) {
      char c=lbuf[ii];
      d=(d<<4)+(((c>='0') && (c<='9'))?(c-'0'):(((c>='A') && (c<='F'))?(c-'A'+10):0));
      }
    lbuf[0]=0;
    if (d==0) d=32;
    byte chsum=((*xparam)&0xFF);
    int xaddr=((*xparam>>8)&0xFFFF);
    int xa,la;CalcDisplayConfEpromSpace(&xa,&la);
    int raddr=xaddr+xa+1;
    if (raddr<la)
      {
      if (EEPROM.read(raddr)!=d) EEPROM.write(raddr,d);
      chsum+=((d&0xFF)+(xaddr+1))&0xFF;     
      xaddr++;
      } 
    *xparam=(chsum|((unsigned long)xaddr<<8));
    }
  }
#endif
}
 
void WebServer_h(char * lbuf)
{
int cx=strlen(lbuf);
char * p=lbuf+cx;
while ((p>=lbuf) && (p[0]!='\n')) p--;
if ((p>=lbuf) && (lbuf+cx-p==3)) {
  byte d=0;
  for (byte ii=1;ii<3;ii++) {
    d=d<<4; char c=p[ii];
    if ((c>='0') && (c<='9')) d+=c-'0';
    if ((c>='A') && (c<='F')) d+=c-'A'+10;
    }
  byte pr='\\';
  if (d==pr) { p[0]=pr;p[1]=pr;p++; } 
  if (d==0) { p[0]=pr;p[1]='0';p++; } 
  else p[0]=d;
  p++;p[0]='\n';p[1]=0;
  }
}
 
void WebServer_p(char * xstr,byte * qsubmode,byte * wkey,byte * qsaction, byte * datastruct, 
  unsigned long * xparam)
{
int ix=strlen(xstr);
if (ix>0){ ix--;xstr[ix]=0; }
if (* qsubmode==xCFGk)
  {
 
#if modDisplay == 1   
  if (*qsaction==xACTdb)
    {
    if (strcmp(xstr,"bid")==0) { *wkey=xKEYDispButt; }
    }    
  else
  if (*qsaction==xACTwdp)
    {
    if (strcmp(xstr,"src")==0) { *qsubmode=xCFGs; *xparam=0; }
    }
  else
#endif
  if (*qsaction==xACTwds)
    {
    char * p=xstr;
    if (strcmp(xstr,"DX")==0)
      {
      byte xpinopt=xDPinSetStatus;
      *xparam&=0xFFFFFF00;
      *xparam|=xpinopt;
      }
    else
    if (p[0]=='s')
      {
      p++;
      int xpinindex=atoi(p)&0x0F;
      byte xpinopt=xDPinOptSetStatus;
      *xparam&=0xFFFF0000;
      *xparam|=xpinopt|(xpinindex<<8);
      }
    else
    if (p[0]=='D')
      {
      p++;
      char * px=p;
      for (int ii=1;ii<strlen(xstr);ii++)
        if (p[0]=='.') { p[0]=0;p++;break; } else p++;
      int xpinindex=atoi(px);
      byte xpinopt=xDPinOptNone;
      if (strcmp(p,"nam")==0) xpinopt=xDPinOptName;
      *xparam&=0xFFFF0000;
      *xparam|=xpinopt|(xpinindex<<8);
      }   
    *wkey=xKEYDpin;
    }
  else
  if (*qsaction==xACTwas)
    {
    char * p=xstr;
    if (p[0]=='A')
      {
      p++;
      char * px=p;
      for (int ii=1;ii<strlen(xstr);ii++)
        if (p[0]=='.') { p[0]=0;p++;break; } else p++;
      byte xpinopt=xPinOptNone;
      if (strcmp(p,"ena")==0) xpinopt=xPinOptEnabled; else
      if (strcmp(p,"nam")==0) xpinopt=xPinOptName; else
      if (strcmp(p,"min")==0) xpinopt=xPinOptMin; else
      if (strcmp(p,"max")==0) xpinopt=xPinOptMax; else
      if (strcmp(p,"uni")==0) xpinopt=xPinOptUnit; else
      if (strcmp(p,"typ")==0) xpinopt=xPinOptType; else
      if (strcmp(p,"grp")==0) xpinopt=xPinOptGroup; else
      if (strcmp(p,"~w")==0) xpinopt=xPinCmdWrite;
      *xparam&=0xFFFFFF00;
      *xparam|=xpinopt;
      }   
    *wkey=xKEYpin;
    }
  else
  if (strcmp(xstr,"cmd")==0) *wkey=xKEYcmd; 
  else
    *wkey=xKEYnone;
  }
else
if (* qsubmode==xCFGv)
  {
  if (ix>0){ ix--;xstr[ix]=0; }
 
#if modDisplay == 1   
  if (*wkey==xKEYDispButt)
    {
    DisplayPressButton(xstr);
    }
  else
#endif
 
// ---------------------------------------
 
  if (*wkey==xKEYDpin) 
    {
    byte xpk=((*xparam)&0xFF);
    tDPinConf * pconf;
    pconf=(tDPinConf*)datastruct;
 
if (xpk==xDPinOptName)
  {
  int xpinindex=(((*xparam)&0x0F00)>>8);
  strncpy(pconf->xname,xstr,sizeof(pconf->xname)-1);
  DPIN_epWriteConfName(xpinindex,pconf->xname);
  for (int ii=0;ii<sizeof(tDPinConf);ii++) datastruct[ii]=0;  
  }
else
if (xpk==xDPinSetStatus) {
  DPINstatus=(uint16_t)WebServer_ph(xstr);
  DPINUpdateHW();  
  }
else
if (xpk==xDPinOptSetStatus) {
  int xpinindex=(((*xparam)&0x0F00)>>8);
  uint16_t xpinstatus=(atoi(xstr))&1;
  DPINstatus&=(~(((uint16_t)1)<<xpinindex));
  DPINstatus|=(xpinstatus<<xpinindex);
  DPINUpdateHW();  
  }
 
 
    }   
else
// ---------------------------------------
  if (*wkey==xKEYpin) 
    {
    byte xpk=((*xparam)&0xFF);
    tPinConf * pconf;
    pconf=(tPinConf*)datastruct;
 
if (xpk==xPinOptEnabled) {
  if (strcmp(xstr,"1")==0)
    pconf->xopt|=xpinOPTenabled;
  }
else
if (xpk==xPinOptName)
  {
  strncpy(pconf->xname,xstr,sizeof(pconf->xname)-1);
  pconf->xname[sizeof(pconf->xname)-1]=0;
  }
else
if (xpk==xPinOptMin) {
  signed long xsl=0;int xsl10000=0;
  WebServer_pf(xstr,&xsl,&xsl10000);
  pconf->xmin=xsl;pconf->xmin10000=xsl10000;
  }
else if (xpk==xPinOptMax) {
  signed long xsl=0;int xsl10000=0;
  WebServer_pf(xstr,&xsl,&xsl10000);
  pconf->xmax=xsl;pconf->xmax10000=xsl10000;
  }
else
if (xpk==xPinOptUnit)
  strncpy(pconf->xunit,xstr,sizeof(pconf->xunit)-1);
else
if (xpk==xPinOptType)
  {
  char w=xstr[0];
  pconf->xopt&=~xpinOPTtypeMask;
  if (w=='s') pconf->xopt|=xpinOPTsample; else
  if (w=='a') pconf->xopt|=xpinOPTavg; else
  if (w=='e') pconf->xopt|=xpinOPTeffective; else
  if (w=='m') pconf->xopt|=xpinOPTmin; else
  if (w=='x') pconf->xopt|=xpinOPTmax;
  }
else
if (xpk==xPinOptGroup)
  {
  char w=xstr[0];
  pconf->xopt&=~xpinOPTgroupMask;
  if (w=='0') pconf->xopt|=xpinOPTsampleG0; else
  if (w=='1') pconf->xopt|=xpinOPTsampleG1; else
  if (w=='2') pconf->xopt|=xpinOPTsampleG2; else
  if (w=='3') pconf->xopt|=xpinOPTsampleG3;
  }
else
if (xpk==xPinCmdWrite) {
    WebServer_wp(WebServer_pb(xstr),(tPinConf*)datastruct);
    for (int ii=0;ii<sizeof(tPinConf);ii++) datastruct[ii]=0;
  }
 
    }   
 // --------------------------------------------
  else
  if (*wkey==xKEYcmd) 
    {
    if (strcmp(xstr,"wep")==0) *qsaction=xACTwep;else
    if (strcmp(xstr,"wds")==0) *qsaction=xACTwds;else
    if (strcmp(xstr,"was")==0) *qsaction=xACTwas;else
    if (strcmp(xstr,"wdp")==0) *qsaction=xACTwdp;else
    if (strcmp(xstr,"hw")==0) *qsaction=xACThw;else
    if (strcmp(xstr,"as")==0) *qsaction=xACTas;else
    if (strcmp(xstr,"ds")==0) *qsaction=xACTds;else
    if (strcmp(xstr,"dp")==0) *qsaction=xACTdp; else
    if (strcmp(xstr,"sp")==0) *qsaction=xACTsp; else
    if (strcmp(xstr,"dd")==0) *qsaction=xACTdd; else
    if (strcmp(xstr,"ep")==0) *qsaction=xACTep; else
    if (strcmp(xstr,"db")==0) *qsaction=xACTdb;    
    }
  }
}
 
void WebServer() {
  EthernetClient client = server.available();
  if (client) {
    boolean cl = true;
    char xln[200]="";
    word xmode=xWEBhome;
    unsigned long xparam=0;
    int cx=sizeof(tPinConf);
    int cxq=sizeof(tDPinConf);if (cx<cxq) cx=cxq;
    byte xDataStruct[cx];
    for (int ii=0;ii<cx;ii++) xDataStruct[ii]=0; 
    int ix=0;
    SetLiveLed(HIGH);
    while (client.connected()) {
      if (client.available()) 
        {
        char c=client.read();
        if (c=='\n') { xln[0]=0; }
        ix=strlen(xln);
        if (ix<sizeof(xln)) { xln[ix]=c;ix++;xln[ix]=0; }
        xmode=WebServer_c(xln,c,xmode,xDataStruct,&xparam);
        byte qmode=xmode&0x0F;
        byte qsubmode=xmode&0xF0; 
        byte qsaction=(xmode>>8)&0x0F; 
        if (c == '\n' && cl) {
          client.write("HTTP/1.1 200 OK\n");
          if (qsaction==xACTep)
            {
            client.write("Content-Disposition: attachment; filename=\"");
            sprintf(xln,"ulog_eprom_%08lX.hex",millis());
            client.write((byte*)xln,strlen(xln));
            client.write("\"\n");
            client.write("Content-Type: text/plain\nConnnection: close\n\n");              
            }
          else
            client.write("Content-Type: text/html\nConnnection: close\n\n");
          if (qmode==xWEBpins) {
            for (int ain = 0; ain < NUM_ANALOG_INPUTS; ain++) {
              if (ain>0) client.write(",");
              sprintf(xln,"A%d:%d",ain,SamplerAnalogRead(ain));
              client.write((byte*)xln,strlen(xln));
              }
            } 
          else
          if (qsaction==xACThw)
            {
            sprintf(xln,"~HW\nt:%lX\nc:%d.",millis(),NUM_ANALOG_INPUTS);
            client.write((byte*)xln,strlen(xln));
            char cq[]="%d.%d.%d.%d.%d.%d.%d.%d";
            sprintf(xln,cq,(int)modStatusLedPin,(int)sendInterval,(int)sendRetry,(int)maxErrors,
            (int)lxHttpErrors,(int)modSerialLog,(int)modDisplay,(modDisplay==1)?(int)modDisplayType:(int)0);
            client.write((byte*)xln,strlen(xln));
            sprintf(xln,".%lu\nf:%s\nh:%s\nx:%d\nr:",(unsigned long)lxSeconds,modMyPowerFVEID,modMyPowerHost,(int)modMyPowerLog);
            client.write((byte*)xln,strlen(xln));
            sprintf(xln,cq,(int)freeRam(),(int)ARDUINO,(int)RAMEND,(int)XRAMEND,
            (int)E2END,(int)FLASHEND,(int)SPM_PAGESIZE,(int)E2PAGESIZE);
            client.write((byte*)xln,strlen(xln));
            sprintf(xln,"\n\nv:%s\nm:%02X%02X%02X%02X%02X%02X\nn:",MPWMicroLogVersion,
            NETmac[0],NETmac[1],NETmac[2],NETmac[3],NETmac[4],NETmac[5],NETmac[6]);
            client.write((byte*)xln,strlen(xln));          
            char cf[]="%d.%d.%d.%d";
            IPAddress xaddr;
            xaddr=Ethernet.localIP();
            for (byte i=0;i<4;i++)
              {
              if (i==1) xaddr=Ethernet.subnetMask();else
              if (i==2) xaddr=Ethernet.gatewayIP();else
              if (i==3) xaddr=Ethernet.dnsServerIP();   
              sprintf(xln,cf,xaddr[0],xaddr[1],xaddr[2],xaddr[3]);
              if (i<3) strcat(xln,"/");
              client.write((byte*)xln,strlen(xln));          
              }
            sprintf(xln,"\nw:%d/%ld\nde:%d",(int)modWebServer,(long)modWebServerPort,(int)modDisplayEmulation);
            client.write((byte*)xln,strlen(xln));
            client.write("\nds:");
            for (int i=0;i<16;i++)
              {
              int qb=DPINmap[i];
              sprintf(xln,"%d",qb);
              if (i<16) strcat(xln,",");
              client.write((byte*)xln,strlen(xln));
              }
            sprintf(xln,"/%04X/%04X\n",DPINstatus,DPINlogicmask);
            client.write((byte*)xln,strlen(xln));          
 
char xstr[30]="";
tMpwDateTime xMpwDateTime;
GetCurrentDateTime(&xMpwDateTime);
FormatDateTimeStr(xstr, "j.n.Y@H:i:s",&xMpwDateTime,sizeof(xstr));
 
 
            sprintf(xln,"mpw:%s/%d.%d.%d.%ld/%lu/%lu/%lu/%lu/%d/%d/%d/%lu\n",xstr,(int)xMpwAccess.mpwremoteDateYear,
              (int)xMpwAccess.mpwremoteDateMonth,
              (int)xMpwAccess.mpwremoteDateDay,
              (signed long)xMpwAccess.mpwremoteTimeS,
              xMpwAccess.mpwremoteCheckedAtS,
              xMpwAccess.mpwlogintimeS,
              xMpwAccess.mpwDeviceId,
              xMpwAccess.mpwAccessId,
              (int)xMpwAccess.xComStatus,
              (int)xMpwAccess.mpwremoteDateWeek,
              (int)xMpwAccess.mpwremoteDateDayOfWeek,
              (unsigned long)(lxSeconds-xMpwAccess.mpwremoteCheckedAtS));
            client.write((byte*)xln,strlen(xln));          
 
            }
          else
// ----------------------------------
          if ((qsaction==xACTds) || (qsaction==xACTwds))
            {
            if (qsaction==xACTwds) client.write("~WDS\n"); else client.write("~DS\n");
            for (int xp=0;xp<16;xp++)
              {
              sprintf(xln,"D%d:%d:",xp,(int)DPINmap[xp]);
              client.write((byte*)xln,strlen(xln));
              char cname[30]="";
              DPIN_epReadConfName(xp,cname,sizeof(cname));
              xln[0]=0;
              char xf[]="%02X";
              char xh[3];
              for (int i=0;i<strlen(cname);i++) {
                int xi=(uint8_t)cname[i];
                sprintf(xh,xf,xi); 
                strcat(xln,xh);
                }
              strcat(xln,"\n");
              client.write((byte*)xln,strlen(xln));
              }
            sprintf(xln,"DX:%04X/%04X\n",DPINstatus,DPINlogicmask);
            client.write((byte*)xln,strlen(xln));
            }
          else
// ----------------------------------
          if ((qsaction==xACTas) || (qsaction==xACTwas))
            {
            if (qsaction==xACTwas) client.write("~WAS\n"); else client.write("~AS\n");
            for (int xp=0;xp<NUM_ANALOG_INPUTS;xp++)
              {
              tPinConf pconf;            
              boolean isvalid=ReadPinConf(&pconf,xp);            
              sprintf(xln,"A%d:",xp);
              client.write((byte*)xln,strlen(xln));
              if (!isvalid)
                strcat(xln,"-");
              else
                { 
                char smin10k[30]="";sprint10k(smin10k,pconf.xmin,pconf.xmin10000);
                char smax10k[30]="";sprint10k(smax10k,pconf.xmax,pconf.xmax10000);
                sprintf(xln,"%X/%s/%s/",pconf.xopt,smin10k,smax10k);
                client.write((byte*)xln,strlen(xln));
                xln[0]=0;
                char xf[]="%02X";
                char xh[3];
                byte * p;
                p=(byte*)pconf.xname;
                for (int i=0;i<strlen(pconf.xname);i++) {
                  sprintf(xh,xf,p[i]); strcat(xln,xh); }
                client.write((byte*)xln,strlen(xln));
                strcpy(xln,"/");
                p=(byte*)pconf.xunit;
                for (int i=0;i<strlen(pconf.xunit);i++) {
                  sprintf(xh,xf,p[i]); strcat(xln,xh); }
                strcat(xln,"/");
                client.write((byte*)xln,strlen(xln));                 
                sprintf(xln,"%d",SamplerAnalogRead(xp));
                }              
              strcat(xln,"\n");
              client.write((byte*)xln,strlen(xln));
              }
            }
          else
          if (qsaction==xACTsp)
            {
            client.write("~SP\n");
            byte qindex=0;
            while (true)
              {
              qindex++;
              byte qpins[4];
              SamplerGet4PinGroup(qpins,&qindex);
              SamplerExec(qpins,&xSamplerStruct,4);
              sprintf(xln,"MS:%lX\nL:%lX\nT:",millis(),xSamplerStruct.tPinMicros);
              client.write((byte*)xln,strlen(xln));
              for (int x=0;x<128;x++)
                {
                sprintf(xln,"%02X",(int)xSamplerStruct.tPinTimes[x]);
                client.write((byte*)xln,strlen(xln));
                }
              client.write("\n");
              for (int p=0;p<4;p++)
                {
                tSampledPin * qSampledPin=&xSamplerStruct.tPinSamples[p];
                int qpin=qSampledPin->pin;
                if (qpin<NUM_ANALOG_INPUTS)
                  {
                  sprintf(xln,"S%d:S=%lu,A=%lu,M=%d,X=%d,C=%d:",qpin,
                    qSampledPin->smpSqr,
                    qSampledPin->smpAvg,
                    qSampledPin->smpMin,
                    qSampledPin->smpMax,
                    qSampledPin->smpCount);
                  client.write((byte*)xln,strlen(xln));
                  for (int x=0;x<128;x++)
                    {
                    sprintf(xln,"%02X",(int)qSampledPin->smpHi8[x]);
                    client.write((byte*)xln,strlen(xln));
                    }
                  client.write("/");
                  for (int x=0;x<32;x++)
                    {
                    sprintf(xln,"%02X",(int)qSampledPin->smpLo2[x]);
                    client.write((byte*)xln,strlen(xln));
                    }
                  client.write("\n");
                  }
                }
              if (qindex==0) break;              
              }
            for (int x=0;x<NUM_ANALOG_INPUTS;x++)
              {
              tPinValue * qPinValue;
              qPinValue=&xSamplerStruct.tPinValues[x];
              sprintf(xln,"A%d:V=%d,S=%d,A=%d,M=%d,X=%d\n",x,
                qPinValue->smpVal,
                qPinValue->smpSqr,
                qPinValue->smpAvg,
                qPinValue->smpMin,
                qPinValue->smpMax);
              client.write((byte*)xln,strlen(xln));
              }
            }
          else
          if ((qsaction==xACTdp) || (qsaction==xACTwdp))
            {
            if (qsaction==xACTwdp)
              client.write("~WDP\n");
            else
              client.write("~DP\n");
#if modDisplay == 1
            sprintf(xln,"T:%d\n",(int)modDisplayType);
            client.write((byte*)xln,strlen(xln));
            int xa,la;
            CalcDisplayConfEpromSpace(&xa,&la);            
            int xs=la-xa;
            sprintf(xln,"B:%d\nD:",xs-1);
            client.write((byte*)xln,strlen(xln));
            byte chsum=0;
            byte rchsum=EEPROM.read(xa);
            for (int i=1;i<xs;i++) 
              {
              int xv=EEPROM.read(xa+i);
              if (xv==0) break;
              chsum+=((xv&0xFF)+i)&0xFF;
              sprintf(xln,"%02X",xv);
              if ((i-1)%10==9) strcat(xln,"\nD:");
              client.write((byte*)xln,strlen(xln));
              }      
            sprintf(xln,"\nR:%02X\nC:%02X",rchsum,chsum);
            client.write((byte*)xln,strlen(xln));
            if (EEPROM.read(la)!=0) EEPROM.write(la,0);
            client.write("\n");
#endif
            }
          else
 
#if ((modDisplay == 1) && (modDisplayType == dLCDKeyPad1602))
          if (qsaction==xACTdd)
            {
            char lcdlbuf0[17]="";
            char lcdlbuf1[17]="";
            _lcdweblbuf0=lcdlbuf0;
            _lcdweblbuf1=lcdlbuf1;
            tDispStats lcdwebdispstats;
            _lcdwebdispstats=&lcdwebdispstats;                     
            DrawDisplayPage();
            client.write("~DD\n");
            sprintf(xln,"S:%d,%d\n",(int)lcdwebdispstats.xvarscount,(int)lcdwebdispstats.xvarram);
            client.write((byte*)xln,strlen(xln));          
            sprintf(xln,"T:%d\nL1:%s\nL2:%s\n",(int)modDisplayType,lcdlbuf0,lcdlbuf1);
            client.write((byte*)xln,strlen(xln));          
            _lcdweblbuf0=NULL;
            _lcdweblbuf1=NULL;
            _lcdwebdispstats=NULL;
            }
          else
#endif
#if (modDisplay == 1)
          if (qsaction==xACTdb)
            {
            client.write("OK");              
            }
          else
#endif         
          if (qsaction==xACTep)
            {
            sprintf(xln,"uLog/%s/eprom.0-%d/@%08lX\n",MPWMicroLogVersion,E2END,millis());
            client.write((byte*)xln,strlen(xln));
            unsigned long xsum=0;
            char s[20]="";
            for (int i=0;i<=E2END;i++)
              {
              if ((i&15)==0) 
                {
                sprintf(xln,"%04X: ",i);
                client.write((byte*)xln,strlen(xln));
                }
              int v=EEPROM.read(i);
              sprintf(xln,"%02X",v);
              int xi=i&15;
              s[xi]=((v>32)&&(v<127))?v:'.';
              s[xi+1]=0;
              client.write((byte*)xln,strlen(xln));          
              xsum=(((xsum>>24)&0xFF)|(xsum<<8))+v+i;
              if ((i&15)==15) 
                {
                sprintf(xln," %s\n",s);
                client.write((byte*)xln,strlen(xln));
                s[0]=0;
                }
              else client.write(" ");
              }
            if (s[0]!=0)
              {
              sprintf(xln," %s\n",s);
              client.write((byte*)xln,strlen(xln));
              s[0]=0;
              }
            sprintf(xln,"S:%08lX",xsum);
            client.write((byte*)xln,strlen(xln));                      
            }
          else
          if (qmode==xWEBcfg)  
            {
            client.write("CFG");
            }
          else
            {
            client.write("<!DOCTYPE HTML>\n<html><head><meta http-equiv=\"Content-Type\" ");
            client.write("content=\"text/html; charset=UTF-8\"><style>");
            client.write("a {color:#FFC000;} body {font-family:sans;font-size:12px;background:#101010;color:white}");
            client.write("</style></head>");
            if (qmode==xWEBhome) client.write("<meta http-equiv=\"refresh\" content=\"5\">");
            client.write("<body>");
            client.write("<b><a href=\"./\">HOME</a> | ");
            client.write("<a href=\"mpw\">TOOLS</a> | ");
            client.write("<a target=\"_blank\" href=\"http://microlog.mypower.cz\">WIKI</a></b><hr>");
            if (qmode==xWEBhome) {
 
 
            for (int x=0;x<NUM_ANALOG_INPUTS;x++)
              {
              tPinValue * qPinValue;
              qPinValue=&xSamplerStruct.tPinValues[x];
              sprintf(xln,"A%d: V=%d, S=%d, A=%d, M=%d, X=%d <br>\n",x,
                qPinValue->smpVal,
                qPinValue->smpSqr,
                qPinValue->smpAvg,
                qPinValue->smpMin,
                qPinValue->smpMax);
              client.write((byte*)xln,strlen(xln));
              }
 
/*              
 
              for (int ain = 0; ain < NUM_ANALOG_INPUTS; ain++) {
                sprintf(xln,"<b>A%d:</b> %d<br>",ain,SamplerAnalogRead(ain));
                client.write((byte*)xln,strlen(xln));
                }
 
*/
              } 
            else
            if (qmode==xWEBmpw) {
              client.write("<script language=\"javascript\" src=\"http://");
              client.write(modMyPowerHost);
              client.write("/microlog-tools?");
              client.write(MPWMicroLogVersion);
              client.write("\"></script>");
              client.write("<div x=\"n\">Connecting to MyPower.CZ ...</div>");
              }
            client.write("<hr><font color=gray size=2><b>powered by ");
            client.write("<a href=\"http://mypower.cz\">MyPower.CZ</a> Microlog</b></font>");
            client.write("</body></html>\n");
            }
          break;
        }
        if (c == '\n') cl = true; else if (c != '\r') cl=false;
        }
      }
    SetLiveLed(LOW);
    delay(1);
    client.stop();
  }
}
#endif
 
 
 
 
// -- [DISPLAY] ----------------------------------------------
 
 
#if (modDisplay == 1)
 
#if (modDisplayType == dLCDKeyPad1602)
 
#define lcdPageSelect   0xFA
#define lcdPageLeft     0xFB
#define lcdPageRight    0xFC
#define lcdPageNone     0xFF
 
struct tDispVar {
char * xname;
signed long xval;
int xval10000;
char * xunit;
};
 
char lcdLastButton=0;
unsigned long lcdLastButtonTime=0;
 
void DisplayDetectButton()
{
char qbutt=0;
int xb=analogRead(0);
if (xb<50) qbutt='R'; else
if (xb<200) qbutt='U'; else
if (xb<400) qbutt='D'; else
if (xb<550) qbutt='L'; else
if (xb<800) qbutt='S';
if ((qbutt!=lcdLastButton) && ((millis()-lcdLastButtonTime)>200))
  {
  if ((lcdLastButton==0) && (qbutt!=0))
    DisplayPressButton(&qbutt);
  lcdLastButton=qbutt;
  lcdLastButtonTime=millis();
  }
 
}
 
void DisplayPressButton(char * xbutt)
{
char p=xbutt[0];
if (p=='X') { delay(1000); softReset(); }
else if (p=='U') { if (lcdCurrentPage==0) lcdCurrentPage=lcdMaxPage; else lcdCurrentPage--; }
else if (p=='D') { if (lcdCurrentPage>=lcdMaxPage) lcdCurrentPage=0; else lcdCurrentPage++; }
else if (p=='R') lcdCurrentPage=lcdPageRight;
else if (p=='L') lcdCurrentPage=lcdPageLeft;
else if (p=='S') lcdCurrentPage=lcdPageSelect;
DrawDisplayPage();
if (lcdCurrentPage>lcdMaxPage) { lcdCurrentPage=0; DrawDisplayPage(); }
}
 
int DisplayGetConfigVarNames(char * xnamebuf,int xbuflen,int xa,int xs)
{
boolean hasvar=false;
char cvn[30]="\n";
strcpy(xnamebuf,cvn);
int xret=0;
for (int i=1;i<=xs;i++)
  {
  char c=EEPROM.read(xa+i);
  if (hasvar)
    {
    if ((c=='.') && ((i<=xs)-2)) { i++;c=EEPROM.read(xa+i);
      if ((c>='0') && (c<='4')) i++;
      c='#';  }
    if ( (DisplayParseVariables_IsVarChar(c)) && (c!='#') )
      {
      int xq=strlen(cvn);
      if (xq<sizeof(cvn)-2) { cvn[xq]=c;xq++;cvn[xq]=0; }
      continue;
      }
    else
      {
      if (cvn[1]!='@')
        {
        strcat(cvn,"\n");
        if (strstr(xnamebuf,cvn)==NULL)
          {
          int qx=(strlen(cvn)-1);
          if (((strlen(xnamebuf)+qx)<(xbuflen-1)) && (qx>1))
            {
            char *p=cvn+1;
            strcat(xnamebuf,p);
            xret++;
            }
          }
        }
      strcpy(cvn,"\n");
      if (c=='#')
        {
        hasvar=false;
        continue;
        }
      }
    }
  if (c==0) break;
  hasvar=(c=='$');
  }
return (xret<1?1:xret);
}
 
struct tDispVar * DisplayFindVarByName(char * xname,byte * xvars, int xmaxvars) {
tDispVar * xret=NULL;
tDispVar * xvar=(tDispVar *)xvars;
for (int i=0;i<xmaxvars;i++) {
  if (strcmp(xvar->xname,xname)==0) { xret=xvar; break; }
  xvar++; }
return xret;
}
 
void DisplayInitConfigVars(char * xnamebuf,byte * xvars, int xmaxvars)
{
char * p=xnamebuf;
if (p[0]=='\n')
  {
  p++;
  tDispVar * xvar=(tDispVar *)xvars;
  char * q=p;
  int ix=strlen(p);
  int qcvars=0;
  for (int i=0;i<ix;i++)
    if (p[i]=='\n') {
      p[i]=0;
      if (q[0]!=0)
        {
        xvar->xname=q;
        xvar->xval=0;
        xvar->xval10000=0;
        xvar->xunit=NULL;    
        qcvars++;
        if (qcvars<xmaxvars) xvar++; else break;
        }
      q=p+1+i;
      }
  }
}
 
void DisplayParseConfig(byte * qWpage)
{
int xa,la;
CalcDisplayConfEpromSpace(&xa,&la);
int xs=la-xa;
byte chsum=0;
byte rchsum=EEPROM.read(xa);
for (int i=1;i<xs;i++) { int xv=EEPROM.read(xa+i);
  if (xv==0) break; chsum+=((xv&0xFF)+i)&0xFF; }      
if (rchsum==chsum) {
  byte xpage=0xFF;
  byte xpageline=0xFF;
  char xln[60]="";
  char xnamebuf[200]="\n";
  int xmaxvars=DisplayGetConfigVarNames(xnamebuf,sizeof(xnamebuf),xa,xs);
  tDispVar xvars[xmaxvars];
  if (_lcdwebdispstats!=NULL) {
    _lcdwebdispstats->xvarscount=xmaxvars;
    _lcdwebdispstats->xvarram=sizeof(xvars);
    }
  DisplayInitConfigVars(xnamebuf,(byte*)xvars, xmaxvars);
  word xflag=lcdPageNone;
  for (int i=1;i<xs;i++) {
    int xv=EEPROM.read(xa+i);
    if ((xv==0) || (xv=='\n')) {
      char * p=xln;
      while ((p[0]!=0) && (p[0]==32)) p++;
      for (int ii=0;p[ii]!=0;ii++) if ((p[ii]=='/') && (p[ii+1]=='/')) { p[ii]=0;break; }
      for (int ii=strlen(p)-1;ii>=0;ii--) if (p[ii]==32) p[ii]=0; else break;
      if (p[0]!=0) DisplayParseConfigL(p,&xpage,&xpageline,qWpage,(byte*)xvars,xmaxvars,&xflag);
      xln[0]=0;
      }
    else 
      { int xls=strlen(xln); if (xls<sizeof(xln)-1) { xln[xls]=xv;xln[xls+1]=0; } }
    if (xv==0) break;
    }
  if (xpage!=0xFF) lcdMaxPage=xpage;
  }
}
 
void DisplayParseConfigL(char * xln,byte * xpage,byte * xpageline,byte * qWpage, byte * xvars, int xmaxvars,word * xflag)
{
if (xln[0]=='#')
  {
  *xflag=lcdPageNone;
  if (xln[1]=='R') *xflag=lcdPageRight; else
  if (xln[1]=='L') *xflag=lcdPageLeft; else
  if (xln[1]=='S') *xflag=lcdPageSelect;
  (*xpage)++;
  (*xpageline)=0xFF;
  }
else
if ((*xpage)==0xFF)
  DisplayParseConfigCmdL(xln, xvars, xmaxvars, xflag);
else
if ((*xpage)!=0xFF)
  {
  if ((*xpageline)==0xFF) (*xpageline)=0; else (*xpageline)++; 
  if (((*qWpage)==(*xpage)) || ((*xflag)==(*qWpage)))
    {
    *qWpage=(*xpage);
    *xflag=lcdPageNone;
    if (*xpageline<2)
      {
      lcdClearLine();
      char * p=xln;
      for (int i=0;i<strlen(xln);i++) if (p[i]=='|') { p+=i;p[0]=0;p++;break; }
      char px[20];
      if (p==xln)
        {       
        DisplayParseVariables(px,xln,sizeof(px),xvars,xmaxvars);
        lcdPrintCenter(px);
        }
      else
        {
        DisplayParseVariables(px,xln,sizeof(px),xvars,xmaxvars);
        lcdPrintLeft(px);
        px[0]=0;
        DisplayParseVariables(px,p,sizeof(px),xvars,xmaxvars);
        lcdPrintRight(px);
        }
      lcdPrintLine(*xpageline);
      }
    }
  }
}
 
#define xVarOpNONE  0x00
#define xVarOpADD   0x01
#define xVarOpSUB   0x02
#define xVarOpMUL   0x03
#define xVarOpDIV   0x04
#define xVarOpEQ    0x05
 
void DisplayParseConfigCmdL(char * xln, byte * xvars, int xmaxvars, word * xflag)
{
byte xop=xVarOpNONE;
tDispVar constVar;
tDispVar * xr=NULL;
tDispVar * xvar=NULL;
int xconstdecs=-1;
boolean hasvar=false;
boolean hasconst=false;
char cvarname[20]="";
for (int i=0;i<=strlen(xln);i++)
  {
  char c=xln[i];
  if ((hasvar) && (!hasconst))
    {
    if (c=='.') { c=xln[i+1]; if ((c>='0') && (c<='9')) { i++; } c='#';  }
    if ( (DisplayParseVariables_IsVarChar(c)) && (c!='#'))
      {
      int xq=strlen(cvarname);
      if (xq<sizeof(cvarname)-1) { cvarname[xq]=c;xq++;cvarname[xq]=0; }
      continue;
      }
    else
      {
      char qunit[10]="";
      constVar.xname=cvarname;
      constVar.xunit=qunit;
      constVar.xval=0;
      constVar.xval10000=0;
      xvar=&constVar;
      if (cvarname[0]=='@')
        {
        char r=cvarname[1];
        char *p=cvarname+2;
        int xpin=0;
        for (int ic=0;ic<strlen(p);ic++)
          { xpin*=10; char e=p[ic]; if ((e>='0') || (e<='9')) xpin+=e-'0'; }
        xvar->xname=cvarname;
        xvar->xunit=qunit;
        xvar->xval=0;
        xvar->xval10000=0;
 
        if ( (xvar->xname[0]=='@') && (xvar->xname[1]=='T') )
          DisplayFillVarByTimeVar(xvar);
        else
          DisplayFillVarByPinValue(xpin,xvar,r);
 
        }
      else
        xvar = DisplayFindVarByName(cvarname,xvars,xmaxvars);
 
      if ((xvar!=NULL) && (xr!=NULL) && (xop!=xVarOpNONE)) {          
        if (xop==xVarOpEQ)
          { xr->xval=xvar->xval; xr->xval10000=xvar->xval10000; }
        else
          DisplayDoVarOp(xr,xvar,xop);
        xop=xVarOpNONE;
        }     
      cvarname[0]=0;
      if (c=='#') { hasvar=false; continue; }
      }
    }
  else
  if ((!hasvar) && (hasconst))
    {
    boolean isnum=((c>='0') && (c<='9'));
    if ((c=='.') && (xconstdecs<0))
      {
      xconstdecs=0;
      continue;
      }
    else
    if ((isnum) && (xvar!=NULL))
      {
      int cn=c-'0';
      if (xconstdecs<0)
        {
        if (xconstdecs>=-9)
          {
          xconstdecs--;
          xvar->xval*=10;
          xvar->xval+=(signed long)cn;
          }
        }
      else
      if (xconstdecs<4)
        {
        xconstdecs++;
        xvar->xval10000*=10;
        xvar->xval10000+=cn;
        }
      continue;
      }
    else
      {
 
      if ((xvar!=NULL) && (xr!=NULL) && (xop!=xVarOpNONE)) {     
        if (xconstdecs>=0) while (xconstdecs<4) { xconstdecs++; xvar->xval10000*=10; }
        if (xop==xVarOpEQ)
          { xr->xval=xvar->xval; xr->xval10000=xvar->xval10000; }
        else
          DisplayDoVarOp(xr,xvar,xop);
        xop=xVarOpNONE;
        }     
 
      xvar=NULL;
      hasconst=false;
      xconstdecs=-1;
      }
    }
 
  if (c==0) break;
  hasvar=(c=='$');
  if (!hasvar) { 
 
    hasconst=((c>='0') && (c<='9'));
    if (hasconst)
      {
      constVar.xval=0;
      constVar.xval10000=0;
      xvar=&constVar;
      i--;
      continue;
      }
 
    if (c!=' ') 
      {      
      byte xlastop=xop;
      if (c=='=') xop=xVarOpEQ; else
      if (c=='+') xop=xVarOpADD; else
      if (c=='-') xop=xVarOpSUB; else
      if (c=='*') xop=xVarOpMUL; else
      if (c=='/') xop=xVarOpDIV;
      if ((xlastop==xVarOpNONE) && (xop==xVarOpEQ) && (xvar!=NULL) && (xr==NULL)) 
        { xr=xvar; xvar=NULL; }
      }
    else
      hasconst=false;
    }
  }
}
 
 
void DisplayDoVarOp(struct tDispVar * xr,struct tDispVar * xvar,byte xop)
{ 
int64 xq1,xq2,xqr;
if (xop==xVarOpMUL) {
  i64x10k2int10kH(&xq1, xr->xval,xr->xval10000);
  i64x10k2int10kH(&xq2, xvar->xval,xvar->xval10000);
} else {
  i64x10k2int10k(&xq1, xr->xval,xr->xval10000);
  i64x10k2int10k(&xq2, xvar->xval,xvar->xval10000);
  }
if (xop==xVarOpADD) i64addS(&xqr,&xq1,&xq2); else
if (xop==xVarOpSUB) i64subS(&xqr,&xq1,&xq2); else
if (xop==xVarOpDIV) { word xmod; i64divW(&xqr, &xmod, &xq1, xvar->xval); } else
if (xop==xVarOpMUL) { int64 xretH; i64mul64(&xretH, &xqr, &xq1, &xq2); }
i64int10k2x10k(&xr->xval,&xr->xval10000,&xqr);
}
 
void PrintComputedPinValue(int xpin,char * xret,int maxunitsize,char xpintype)
{
tDispVar xvar;
DisplayFillVarByPinValue(xpin,&xvar,xpintype);
//signed long xval=xvar.xval;
//int xval10k=xvar.xval10000;
sprint10k(xret,xvar.xval,xvar.xval10000); //minus bugfix
//sprintf(xret,"%ld.%04d",xval,xval10k);
}
 
void DisplayFillVarByPinValue(int xpin,struct tDispVar * xvar,char xpintype)
{
if ((xpin<NUM_ANALOG_INPUTS) && (xvar!=NULL))
  {
  tPinConf pconf;
  byte qpintype=xpinOPTdefault; 
  if ((xpintype=='A') || (xpintype=='a')) qpintype=xpinOPTavg; else
  if ((xpintype=='R') || (xpintype=='r')) qpintype=xpinOPTeffective; else
  if ((xpintype=='S') || (xpintype=='s')) qpintype=xpinOPTsample; else
  if ((xpintype=='M') || (xpintype=='m')) qpintype=xpinOPTmin; else
  if ((xpintype=='X') || (xpintype=='x')) qpintype=xpinOPTmax; 
  int pinval=SamplerAnalogReadType(xpin,qpintype);
  xvar->xval=pinval;
  xvar->xval10000=0;
  if ((xpintype>='A') && (xpintype<='Z'))
    {
    boolean isvalid = ReadPinConf(&pconf,xpin);
    if (isvalid)
      {
      int qxval10000;
      signed long qxval;
      DisplayMapPinValue(pinval,&qxval,&qxval10000,
        pconf.xmin,pconf.xmin10000,pconf.xmax,pconf.xmax10000);
      xvar->xval=qxval;
      xvar->xval10000=qxval10000;
      }
    }
  }
}
 
void DisplayMapPinValue(int pinval,signed long * qxval, int * qxval10k,
      signed long xmin, int xmin10k, signed long xmax, int xmax10k)
{
*qxval10k=0;*qxval=0;int64 qmin,qdif,qx,qml,qmx;i64nul(&qmin);i64nul(&qdif);i64nul(&qx);
i64x10k2int10k(&qmin,xmin,xmin10k);i64x10k2int10k(&qx,xmax,xmax10k);
boolean msf=qmin.msf;i64subS(&qdif,&qx,&qmin);qmin.msf=msf;i64nul(&qx);qx.ll=pinval;
msf=qdif.msf;qdif.msf=false;i64mul64(&qmx,&qml,&qx,&qdif);word xmod;
i64divW(&qmx,&xmod,&qml,1023);qmx.msf=msf;i64addS(&qml,&qmx,&qmin);
i64int10k2x10k(qxval, qxval10k, &qml);
}
 
 
boolean DisplayParseVariables_IsVarChar(char c) {
return ( ((c>='a') && (c<='z')) || ((c>='A') && (c<='Z')) || (c=='_') || 
  (c=='@') || (c=='#') || (c=='.') || ((c>='0') && (c<='9')) ); }
 
void DisplayParseVariables(char * xdst,char * xsrc, int xmaxlen, byte * xvars,int xmaxvars)
{
int ix=0;
boolean hasvar=false;
char cvarname[20]="";
byte xdecs=0xFF;
for (int i=0;i<=strlen(xsrc);i++)
  {
  char c=xsrc[i];
  if (hasvar)
    {
    if (c=='.') { c=xsrc[i+1];
      if (c=='R') { xdecs=0xFE; i++; }  else
      if ((c>='0') && (c<='9')) { xdecs=c-'0';i++; }
      c='#';  }
    if ( (DisplayParseVariables_IsVarChar(c)) && (c!='#'))
      {
      int xq=strlen(cvarname);
      if (xq<sizeof(cvarname)-1) { cvarname[xq]=c;xq++;cvarname[xq]=0; }
      continue;
      }
    else
      {
      char qunit[10]="";
      tDispVar pinVar;
      pinVar.xname=cvarname;
      pinVar.xunit=qunit;
      pinVar.xval=0;
      pinVar.xval10000=0;
      tDispVar * xvar=&pinVar;
      if (cvarname[0]=='@')
        {
        char r=cvarname[1];
        char *p=cvarname+2;
        int xpin=0;
        for (int ic=0;ic<strlen(p);ic++)
          { xpin*=10; char e=p[ic]; if ((e>='0') || (e<='9')) xpin+=e-'0'; }
        xvar->xname=cvarname;
        xvar->xunit=qunit;
        xvar->xval=0;
        xvar->xval10000=0;       
        DisplayFillVarByPinValue(xpin,xvar,r);
        }
      else
        xvar = DisplayFindVarByName(cvarname,xvars,xmaxvars);
 
      if (xvar!=NULL)
        {
        char pxval[50]="";
        if ( (xvar->xname[0]=='@') && (xvar->xname[1]=='T') )
          {
          DisplayGetValueByTimeVar(pxval,xvar->xname,sizeof(pxval));
          }
        else
          {
          char qsc[]=" kMGT";
          char *xsc=qsc;
          signed long xval=xvar->xval;
          int xval10k=xvar->xval10000;
 
          if (xdecs==0xFE)
            {
            sprintf(pxval,"%ld",xval);
            }
          else
            {
            while (xval>1500)
              {
              if (xsc[1]==0) break;
              int64 xq,xr; word xmod;
              i64x10k2int10k(&xq, xval,xval10k);
              i64divW(&xr, &xmod, &xq, 1000);
              i64int10k2x10k(&xval,&xval10k,&xr);
              xsc++;
              }
            sprint10k(pxval,xval,xval10k); // minus bugfix
    //        sprintf(pxval,"%ld.%04d",xval,xval10k);
            DisplayCorrectDecs(pxval,xdecs);
            if ((xsc[0]!=' ') && (xsc[0]!=0)) { xsc[1]=0; strcat(pxval,xsc); }
            }
          }
        for (int cc=0;cc<strlen(pxval);cc++)
          if (ix<xmaxlen-1) { xdst[ix]=pxval[cc];ix++;xdst[ix]=0; }
        }
      cvarname[0]=0;
      if (c=='#') 
        {
        hasvar=false;
        continue;
        }
      }
    }
  if (c==0) break;
  hasvar=(c=='$');
  if ((!hasvar) && (ix<xmaxlen-1)) { xdst[ix]=c;ix++;xdst[ix]=0; }
  }
}
 
 
void DisplayFillVarByTimeVar(struct tDispVar * xvar)
{
char px[30]="";
DisplayGetValueByTimeVar(px,xvar->xname,sizeof(px));
xvar->xval=atol(px);
xvar->xval10000=0;
}
 
 
void DisplayGetValueByTimeVar(char * xbuf,char * xvarname,int xbufmaxsize)
{
xbuf[0]=0;
if ((xvarname[0]=='@') && (xvarname[1]=='T'))
  {
  if (strlen(xvarname)>=3)
    {
char xfmt[5]="";xfmt[0]=xvarname[2];xfmt[1]=0;
tMpwDateTime xMpwDateTime;GetCurrentDateTime(&xMpwDateTime);
FormatDateTimeStr(xbuf,xfmt,&xMpwDateTime,xbufmaxsize);
    }
  }
}
 
void DisplayCorrectDecs(char * pxval, byte xdecs)
{
int xl=strlen(pxval);
if (xl>0) {
  if (xdecs==0xFF) { for (;;) { xl=strlen(pxval); if ((xl==0) || (pxval[xl-1]!='0')) break; pxval[xl-1]=0; } }
  else { if (xdecs>4) xdecs=4; byte qdecs=4-xdecs;
    for (byte xx=0;xx<qdecs;xx++) { xl=strlen(pxval);if (xl==0) break; pxval[xl-1]=0; } }
  xl=strlen(pxval); if (xl>0) if (pxval[xl-1]=='.') pxval[xl-1]=0;
  }
}
 
void DrawDisplayPage()
{
if (lcdCurrentPage==0xFF)
  {
  lcdClear();
  lcdCurrentPage=0;
  }
DisplayParseConfig(&lcdCurrentPage);
}
 
#endif
 
#endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
// -- [SAMPLER] ----------------------------------------------
 
int SamplerAnalogRead(byte xpin) 
{
return SamplerAnalogReadType(xpin,xpinOPTdefault); 
}
 
 
void SamplerReset() { 
  for (int i=0;i<NUM_ANALOG_INPUTS;i++) 
    xSamplerStruct.tPinValues[i].smpTime=0; 
  }
 
int SamplerAnalogReadType(byte xpin,byte forcextype)
{
int xret=0;
if (xpin<NUM_ANALOG_INPUTS)
  {
  tPinValue * xpinvalue;
  xpinvalue=&xSamplerStruct.tPinValues[xpin];
  byte xtype=SamplerGetPinType(xpin);
  if (forcextype!=xpinOPTdefault) xtype=forcextype;
  if (xtype==xpinOPTavg) xret=xpinvalue->smpAvg;else 
  if (xtype==xpinOPTeffective) xret=xpinvalue->smpSqr;else 
  if (xtype==xpinOPTmin) xret=xpinvalue->smpMin;else 
  if (xtype==xpinOPTmax) xret=xpinvalue->smpMax;else
    xret=xpinvalue->smpVal;
  }
return xret;
}
 
void SamplerRun()
{
byte qpins[4];
SamplerGetNext4PinGroup(qpins); 
SamplerExec(qpins,&xSamplerStruct,4);
}
 
byte SamplerGroupindex=0;
unsigned long Stime=0;
 
void SamplerGetNext4PinGroup(byte * qpins)
{
SamplerGroupindex++;
SamplerGet4PinGroup(qpins,&SamplerGroupindex);
}
 
byte SamplerGetPinType(byte xpin)
{
byte xret=xpinOPTsample;tPinConf pconf;
if (ReadPinConf(&pconf,xpin)) xret=pconf.xopt&xpinOPTtypeMask;
return xret;
}
 
byte SamplerGetPinGroup(byte xpin)
{
byte xret=0;
byte qret=0;
tPinConf pconf;
if (ReadPinConf(&pconf,xpin)) {
  qret=pconf.xopt&xpinOPTgroupMask;
  if (qret==xpinOPTsampleG0) xret=0; else
  if (qret==xpinOPTsampleG1) xret=1; else
  if (qret==xpinOPTsampleG2) xret=2; else
  if (qret==xpinOPTsampleG3) xret=3;
  }
return xret;
}
 
void SamplerGet4PinGroup(byte * qpins,byte * qgroupindex)
{
char pgroups[4][NUM_ANALOG_INPUTS+1];
for (int x=0;x<4;x++) strcpy(pgroups[x],"");
char qch[2]="";qch[1]=0;
for (int x=0;x<NUM_ANALOG_INPUTS;x++) { qch[0]='A'+x; strcat(pgroups[SamplerGetPinGroup(x)],qch); }
char pgroups4[NUM_ANALOG_INPUTS][5];
for (int x=0;x<NUM_ANALOG_INPUTS;x++) strcpy(pgroups4[x],"");
int p4index=0;
for (int x=0;x<4;x++) {
  while (strlen(pgroups[x])>0) {
    strncpy(pgroups4[p4index],pgroups[x],4);
    pgroups4[p4index][4]=0;
    if (strlen(pgroups[x])>4) {
      char * p=(pgroups[x])+4;
      strcpy(pgroups[x],p);
      }
    else
      strcpy(pgroups[x],"");
    p4index++;
    }
  }
for (int p4s=0;p4s<NUM_ANALOG_INPUTS;p4s++) {
  int xl=strlen(pgroups4[p4s]);
  if (xl==4) continue; 
  else {
    int xr=4-xl;
    boolean xfound=false;
    while (!xfound) {
      for (int x=p4s+1;x<NUM_ANALOG_INPUTS;x++)
        if (strlen(pgroups4[x])==xr) {
          strcat(pgroups4[p4s],pgroups4[x]);
          strcpy(pgroups4[x],"");
          xfound=true;
          break;
          }
      if (xfound) break; else if (!xfound) { if (xr>0) xr--; else break; }
      }
    }
  }
byte p4count=0;
for (int x=0;x<NUM_ANALOG_INPUTS;x++) if (strlen(pgroups4[x])==0) { p4count=x;break; }
for (int x=0;x<4;x++) qpins[x]=0xFF;
byte idx=0;
if (p4count>0) {
  idx=*qgroupindex%p4count;
  *qgroupindex=idx;
  char *p=pgroups4[idx];
  int xl=strlen(p);
  if (xl>4) xl=4;
  if (xl!=0)
    for (int x=0;x<xl;x++)
      qpins[x]=(p[x])-'A';
  }
}
 
void SamplerResetPinStruct(int xpin,struct tSampledPin * xSampledPin)
{
xSampledPin->pin=xpin;
for (int x=0;x<sizeof(xSampledPin->smpHi8);x++) xSampledPin->smpHi8[x]=0;
for (int x=0;x<sizeof(xSampledPin->smpLo2);x++) xSampledPin->smpLo2[x]=0;
xSampledPin->smpSqr=0;
xSampledPin->smpAvg=0;
xSampledPin->smpMin=0x3FF;
xSampledPin->smpMax=0x000;
xSampledPin->smpCount=0;
}
 
 
void SamplerExec(byte * pins, struct tSamplerStruct * qSamplerStruct,byte xpinscount)
{
byte * xtimes=qSamplerStruct->tPinTimes;
tSampledPin * xPinSamples=qSamplerStruct->tPinSamples;
unsigned long wqms,wqme;
for (int x=0;x<xpinscount;x++) SamplerResetPinStruct(pins[x],&xPinSamples[x]);
for (int x=0;x<128;x++) xtimes[x]=0;
int qscount=sizeof(xPinSamples[0].smpHi8);
int smpCount=0;
unsigned long xxs,xms,xm1;
unsigned long qms,qm1;
int qsample=0;
int inputpin=0;
tSampledPin * qSampledPin;
int xs3=0;
int xs2=0;
xxs=micros();
xms=0;
qms=0;
while (true) { xm1=micros()-xxs; if ((xm1>>9)!=qms) break; }
xms=xm1;
 
wqms=micros();
for (int x=0;x<qscount;x++)
  {
  xs2=x>>2;
  xs3=(x&0x03)<<1;
  for (int p=0;p<xpinscount;p++)
    {
    qSampledPin=&xPinSamples[p];
    if (qSampledPin->pin<NUM_ANALOG_INPUTS) qsample=analogRead(qSampledPin->pin); else qsample=0;
    qSampledPin->smpHi8[x]=(byte)(qsample>>2);
    qSampledPin->smpLo2[xs2]|=(byte)((qsample&0x03)<<xs3);
    }
  smpCount++;  
  qms=xms>>9;
  while (true) { xm1=micros()-xxs; if ((xm1>>9)!=qms) break; }
  xtimes[x]=(byte)( (xm1-xms) - 400 );
  xms=xm1;
  }
 
wqme=micros()-wqms;
qSamplerStruct->tPinMicros=wqme;
 
for (int x=0;x<qscount;x++)
  {
  xs2=x>>2;
  xs3=(x&0x03)<<1;
  for (int p=0;p<xpinscount;p++)
    {
    qSampledPin=&xPinSamples[p];
    qsample=(((int)qSampledPin->smpHi8[x])<<2)|((qSampledPin->smpLo2[xs2]>>xs3)&0x03);
    qSampledPin->smpAvg+=qsample;
    if (qSampledPin->smpMax<qsample) qSampledPin->smpMax=qsample;
    if (qSampledPin->smpMin>qsample) qSampledPin->smpMin=qsample;
    unsigned long qs=qsample;qs*=qs;
    qSampledPin->smpSqr+=qs;
    }
  }
 
byte qpoint=64;
for (int p=0;p<xpinscount;p++)
  {
  qSampledPin=&xPinSamples[p];
  byte qxpin=qSampledPin->pin;
  if (qxpin<NUM_ANALOG_INPUTS)
    {
    qSampledPin->smpSqr=sqrt(qSampledPin->smpSqr/smpCount);
    qSampledPin->smpAvg=qSampledPin->smpAvg/smpCount;
    qSampledPin->smpCount=smpCount;
    tPinValue * xpinvalue=&qSamplerStruct->tPinValues[qxpin];
    xs3=(qpoint&0x03)<<1;
    xpinvalue->smpVal=(((int)qSampledPin->smpHi8[qpoint])<<2)|((qSampledPin->smpLo2[qpoint>>2]>>xs3)&0x03);;
    xpinvalue->smpSqr=qSampledPin->smpSqr;
    xpinvalue->smpAvg=qSampledPin->smpAvg;
    xpinvalue->smpMin=qSampledPin->smpMin;
    xpinvalue->smpMax=qSampledPin->smpMax;
    xpinvalue->smpTime=millis();
    }
  }
}
 
 
 
 
// -- [MATH10k] -------------------------------------------------
 
boolean isnegative10k(signed long xval, int x10k) { 
  boolean xret=(xval<0);if (xval==0) xret=(x10k<0);return xret; 
}
 
void sprint10k(char * xret, signed long xval, int x10k) {
char xsgn[5]="";if (isnegative10k(xval,x10k)) strcpy(xsgn,"-");
if (xval<0) xval=-xval; if (x10k<0) x10k=-x10k;
sprintf(xret,"%s%ld.%04d",xsgn,xval,x10k);
}
 
// -- [MATH64] -------------------------------------------------
 
void i64print(struct int64 * xval,char * xstr) { 
  char x[200]="";char xm[5]="";if (xval->msf) strcpy(xm,"-");sprintf(x,"--[ %s ] --\n%s%04lX%04lX%04lX%04lX",xstr,xm,
  (long)xval->hh,(long)xval->hl,(long)xval->lh,(long)xval->ll);Serial.println(x); }
 
void i64cpy(struct int64 * xdst,struct int64 * xsrc) { xdst->hh=xsrc->hh;xdst->hl=xsrc->hl;
  xdst->lh=xsrc->lh;xdst->ll=xsrc->ll;xdst->msf=xsrc->msf; }
void i64nul(struct int64 * q1) { q1->hh=0;q1->hl=0;q1->lh=0;q1->ll=0;q1->msf=false; }
void i64shl16(struct int64 * xret) { xret->hh=xret->hl;xret->hl=xret->lh;xret->lh=xret->ll;xret->ll=0;}
int i64cmpS(struct int64 * q1, struct int64 * q2) { for (byte i=0;i<4;i++) {
  word x1=((i==0)?(q1->hh):(i==1?(q1->hl):(i==2?(q1->lh):(i==3?(q1->ll):0))));
  word x2=((i==0)?(q2->hh):(i==1?(q2->hl):(i==2?(q2->lh):(i==3?(q2->ll):0))));
  signed long s1=(signed long)x1; signed long s2=(signed long)x2;
  if (q1->msf) s1=-s1;if (q2->msf) s2=-s2; if (s1<s2) return -1;else if (s1>s2) return 1; }
return 0; }
 
void i64addS(struct int64 * xret, struct int64 * q1, struct int64 * q2) {
i64nul(xret);signed long xx; if ( ((!q1->msf) && (!q2->msf)) || ((q1->msf) && (q2->msf)) ) {
  xx=(signed long)q1->ll+(signed long)q2->ll;xret->ll=(word)(xx&0xFFFF);
  xx=(signed long)q1->lh+(signed long)q2->lh+(signed long)(xx>>16);xret->lh=(word)(xx&0xFFFF);
  xx=(signed long)q1->hl+(signed long)q2->hl+(signed long)(xx>>16);xret->hl=(word)(xx&0xFFFF);
  xx=(signed long)q1->hh+(signed long)q2->hh+(signed long)(xx>>16);xret->hh=(word)(xx&0xFFFF);
  xret->msf=((q1->msf) && (q2->msf)); }
else if ((q1->msf) && (!q2->msf)) { q1->msf=false; i64subS(xret, q2, q1); }
else if ((!q1->msf) && (q2->msf)) { q2->msf=false; i64subS(xret, q1, q2); }
}
 
void i64subS(struct int64 * xret, struct int64 * q1, struct int64 * q2) { 
i64nul(xret); if ((!q1->msf) && (q2->msf)) { q2->msf=false; i64addS(xret, q1, q2); } else
if ((q1->msf) && (!q2->msf)) { q1->msf=false; i64addS(xret, q1, q2); xret->msf=true; } else
if ((q1->msf) && (q2->msf)) { q1->msf=false;q2->msf=false; i64subS(xret, q2, q1); } else 
if ((!q1->msf) && (!q2->msf)) { int xcmp=i64cmpS(q1,q2);
  if (xcmp==0) return; else if (xcmp==-1) { i64subS(xret, q2, q1); xret->msf=true; }
  else { signed long xx,xh,xl;xx=(signed long)q1->ll-(signed long)q2->ll;
    xh=(xx>=0)?0:1;xret->ll=(word)xx;xx=(signed long)q1->lh-(signed long)q2->lh-xh;
    xh=(xx>=0)?0:1;xret->lh=(word)xx;xx=(signed long)q1->hl-(signed long)q2->hl-xh;
    xh=(xx>=0)?0:1;xret->hl=(word)xx;xx=(signed long)q1->hh-(signed long)q2->hh-xh;
    xret->hh=(word)xx; } }
}
 
boolean i64divW(struct int64 * xret, word * xmod, struct int64 * q1, word q2) {
i64nul(xret);*xmod=0; if (q2==0) return false;
else { unsigned long ql;ql=(unsigned long)q1->hh;xret->ll=ql/q2;*xmod=ql%q2;i64shl16(xret);
  ql=(((unsigned long)(*xmod))<<16)+(unsigned long)q1->hl;xret->ll=ql/q2;*xmod=ql%q2;i64shl16(xret);
  ql=(((unsigned long)(*xmod))<<16)+(unsigned long)q1->lh;xret->ll=ql/q2;*xmod=ql%q2;i64shl16(xret);
  ql=(((unsigned long)(*xmod))<<16)+(unsigned long)q1->ll;xret->ll=ql/q2;*xmod=ql%q2;
  return true; } 
}
 
unsigned long i64lo(struct int64 * xint) { return (((unsigned long)xint->lh)<<16)+((unsigned long)xint->ll);}
unsigned long i64hi(struct int64 * xint) { return (((unsigned long)xint->hh)<<16)+((unsigned long)xint->hl);}
 
void i64mul64(struct int64 * xretH, struct int64 * xretL, struct int64 * q1, struct int64 * q2) 
{ i64nul(xretH);i64nul(xretL);unsigned long q1l,q1h,q2l,q2h;
q1l=i64lo(q1);q1h=i64hi(q1);q2l=i64lo(q2);q2h=i64hi(q2);int64 xr1,xr2,xr3,xr4;
i64mul(&xr4, q1h, q2h);i64mul(&xr3, q1h, q2l);i64mul(&xr2, q1l, q2h); i64mul(&xr1, q1l, q2l); 
xretL->ll=xr1.ll;xretL->lh=xr1.lh;unsigned long xr2h=i64hi(&xr2);unsigned long xr3h=i64hi(&xr3);
xr2.hh=0;xr2.hl=0;xr3.hh=0;xr3.hl=0;int64 xr1x;i64nul(&xr1x);
xr1x.lh=xr1.hh;xr1x.ll=xr1.hl;int64 xr23sumx;i64addS(&xr23sumx,&xr2,&xr3);int64 xr23sumL;
i64addS(&xr23sumL,&xr23sumx,&xr1x);xretL->hl=xr23sumL.ll;xretL->hh=xr23sumL.lh;
int64 xr23o;i64nul(&xr23o);xr2.lh=(word)(xr2h>>16);xr2.ll=(word)(xr2h&0xFFFF);
xr3.lh=(word)(xr3h>>16);xr3.ll=(word)(xr3h&0xFFFF);xr23o.ll=xr23sumL.hl;xr23o.lh=xr23sumL.hh;
i64addS(&xr1,&xr4,&xr2);i64addS(&xr4,&xr1,&xr3);i64addS(xretH,&xr4,&xr23o);
}
 
void i64mul(struct int64 * xret, unsigned long q1, unsigned long q2) { 
unsigned long x1,x2,qp,qp1;i64nul(xret);
word all=(word)q1;word alh=(word)(q1>>16);word bll=(word)q2;word blh=(word)(q2>>16);
x1=(unsigned long)all*(unsigned long)bll;xret->ll=(word)(x1);qp=(x1>>16);
x1=(unsigned long)alh*(unsigned long)bll;qp1=(x1>>16);x1&=0xFFFF;
x2=(unsigned long)all*(unsigned long)blh;qp1+=(x2>>16);x2&=0xFFFF;x1+=x2+qp;qp=(x1>>16)+qp1;
xret->lh=(word)(x1);x1=((unsigned long)alh*(unsigned long)blh);
x1+=qp;xret->hl=(word)(x1);xret->hh=(word)(x1>>16);
}
 
void i64int10k2x10k(signed long * xval, int * x10k, struct int64 * xint64) {
boolean msf=xint64->msf;xint64->msf=false;int64 qret;i64nul(&qret);
word xmod=0;i64divW(&qret, &xmod, xint64, 10000);*x10k=xmod;
signed long qval=(((unsigned long)qret.lh)<<16)+(unsigned long)qret.ll;
*xval=msf?(-qval):qval; if (msf) *x10k=-(*x10k); // minus bugfix
xint64->msf=msf;
}
 
void i64x10k2int10k(struct int64 * xret, signed long xval, int x10k) {
int64 q1;int64 q2;i64nul(xret);i64nul(&q1);i64nul(&q2);
/* boolean msf=((xval)<0); */ boolean msf=isnegative10k(xval,x10k); // minus bugfix
if (msf) xval=-(xval);if (x10k<0) x10k=-(x10k);
q2.ll=(x10k)&0xFFFF;i64mul(&q1, (unsigned long)(xval), 10000UL);
i64addS(xret, &q1, &q2);xret->msf=msf;
}
 
void i64x10k2int10kH(struct int64 * xret, signed long xval, int x10k) {
int64 q1;int64 q2;i64nul(xret);i64nul(&q1);i64nul(&q2);
/* boolean msf=((xval)<0); */ boolean msf=isnegative10k(xval,x10k); // minus bugfix
if (msf) xval=-(xval);if (x10k<0) x10k=-(x10k);
q2.ll=(x10k/100)&0xFFFF;i64mul(&q1, (unsigned long)(xval), 100UL);
i64addS(xret, &q1, &q2);xret->msf=msf;
}
 
 
// ---[ Digital Pin Output ]----------------------------------------
 
int DPIN_epGetConfNameAddr(byte xpinindex) { 
return (xpinindex<16)?(2048+(xpinindex*20)):-1;
}
 
void DPIN_epWriteConfName(byte xpinindex,char * xnamebuf ) {
int xaddr=DPIN_epGetConfNameAddr(xpinindex);
if (xaddr>0) 
  {
  byte chsum=(xaddr&0xFF)+xpinindex;
  for (int i=1;i<20;i++) {
    byte ch=xnamebuf[i-1]; 
    chsum+=ch+i+(ch&(0x0F<<2));
    if (EEPROM.read(xaddr+i)!=ch) EEPROM.write(xaddr+i,ch);    
    if (ch==0) break;
    }
  if (EEPROM.read(xaddr)!=chsum) EEPROM.write(xaddr,chsum);    
  }
}
 
void DPIN_epReadConfName(byte xpinindex,char * xnamebuf, int xbufsize ) {
char xbuf[20]="";
int xaddr=DPIN_epGetConfNameAddr(xpinindex);
byte chsum=(xaddr&0xFF)+xpinindex;
if (xaddr>0) 
  {
  for (int i=1;i<20;i++) {
    byte ch=EEPROM.read(xaddr+i);
    chsum+=ch+i+(ch&(0x0F<<2));
    if (ch==0) break;
    xbuf[i-1]=ch;
    xbuf[i]=0;
    }
  if (EEPROM.read(xaddr)==chsum)
    {
    strncpy(xnamebuf,xbuf,xbufsize-1);
    xnamebuf[xbufsize-1]=0;
    }
  else
    xnamebuf[0]=0;
  }
}
 
void DPINUpdateHW() {
uint16_t dstat=DPINstatus^DPINlogicmask;
for (int i=0;i<16;i++) { byte xdpin=DPINmap[i];
  if ((xdpin>=22) && (xdpin<=49)) digitalWrite(xdpin, ((dstat&(1<<i))==0)?LOW:HIGH);
  }
}
 
void DPINResetHW() {
for (int i=0;i<16;i++) { byte xdpin=DPINmap[i];
  if ((xdpin>=22) && (xdpin<=49)) { pinMode(xdpin, OUTPUT); digitalWrite(xdpin, LOW); }
  }
DPINUpdateHW();
}
source.txt · Poslední úprava: 2016/04/22 15:34 autor: mypower_cz