Merge branch 'norbert-walter:master' into simulation
This commit is contained in:
		
						commit
						3395a63ba1
					
				| 
						 | 
					@ -19,8 +19,8 @@
 | 
				
			||||||
    "flash_mode": "qio",
 | 
					    "flash_mode": "qio",
 | 
				
			||||||
    "hwids": [
 | 
					    "hwids": [
 | 
				
			||||||
      [
 | 
					      [
 | 
				
			||||||
        "0x303A",
 | 
					        "0x1A86",
 | 
				
			||||||
        "0x1001"
 | 
					        "0x7523"
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "mcu": "esp32s3",
 | 
					    "mcu": "esp32s3",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ from datetime import datetime
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
import pprint
 | 
					import pprint
 | 
				
			||||||
from platformio.project.config import ProjectConfig
 | 
					from platformio.project.config import ProjectConfig
 | 
				
			||||||
 | 
					from platformio.project.exception import InvalidProjectConfError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Import("env")
 | 
					Import("env")
 | 
				
			||||||
#print(env.Dump())
 | 
					#print(env.Dump())
 | 
				
			||||||
| 
						 | 
					@ -104,7 +104,17 @@ def writeFileIfChanged(fileName,data):
 | 
				
			||||||
    return True    
 | 
					    return True    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def mergeConfig(base,other):
 | 
					def mergeConfig(base,other):
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        customconfig = env.GetProjectOption("custom_config")
 | 
				
			||||||
 | 
					    except InvalidProjectConfError:
 | 
				
			||||||
 | 
					        customconfig = None
 | 
				
			||||||
    for bdir in other:
 | 
					    for bdir in other:
 | 
				
			||||||
 | 
					        if customconfig and os.path.exists(os.path.join(bdir,customconfig)):
 | 
				
			||||||
 | 
					            cname=os.path.join(bdir,customconfig)
 | 
				
			||||||
 | 
					            print("merge custom config {}".format(cname))
 | 
				
			||||||
 | 
					            with open(cname,'rb') as ah:
 | 
				
			||||||
 | 
					                base += json.load(ah)
 | 
				
			||||||
 | 
					            continue   
 | 
				
			||||||
        cname=os.path.join(bdir,"config.json")
 | 
					        cname=os.path.join(bdir,"config.json")
 | 
				
			||||||
        if os.path.exists(cname):
 | 
					        if os.path.exists(cname):
 | 
				
			||||||
            print("merge config %s"%cname)
 | 
					            print("merge config %s"%cname)
 | 
				
			||||||
| 
						 | 
					@ -274,9 +284,9 @@ class Grove:
 | 
				
			||||||
    def _ss(self,z=False):
 | 
					    def _ss(self,z=False):
 | 
				
			||||||
        if z:
 | 
					        if z:
 | 
				
			||||||
            return self.name
 | 
					            return self.name
 | 
				
			||||||
        return self.name if self.name is not 'Z' else ''
 | 
					        return self.name if self.name != 'Z' else ''
 | 
				
			||||||
    def _suffix(self):
 | 
					    def _suffix(self):
 | 
				
			||||||
        return '_'+self.name if self.name is not 'Z' else ''
 | 
					        return '_'+self.name if self.name != 'Z' else ''
 | 
				
			||||||
    def replace(self,line):
 | 
					    def replace(self,line):
 | 
				
			||||||
        if line is None:
 | 
					        if line is None:
 | 
				
			||||||
            return line
 | 
					            return line
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,518 @@
 | 
				
			||||||
 | 
					print("running extra...")
 | 
				
			||||||
 | 
					import gzip
 | 
				
			||||||
 | 
					import shutil
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import inspect
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
 | 
					import glob
 | 
				
			||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					import pprint
 | 
				
			||||||
 | 
					from platformio.project.config import ProjectConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Import("env")
 | 
				
			||||||
 | 
					#print(env.Dump())
 | 
				
			||||||
 | 
					OWN_FILE="extra_script.py"
 | 
				
			||||||
 | 
					GEN_DIR='lib/generated'
 | 
				
			||||||
 | 
					CFG_FILE='web/config.json'
 | 
				
			||||||
 | 
					XDR_FILE='web/xdrconfig.json'
 | 
				
			||||||
 | 
					INDEXJS="index.js"
 | 
				
			||||||
 | 
					INDEXCSS="index.css"
 | 
				
			||||||
 | 
					CFG_INCLUDE='GwConfigDefinitions.h'
 | 
				
			||||||
 | 
					CFG_INCLUDE_IMPL='GwConfigDefImpl.h'
 | 
				
			||||||
 | 
					XDR_INCLUDE='GwXdrTypeMappings.h'
 | 
				
			||||||
 | 
					TASK_INCLUDE='GwUserTasks.h'
 | 
				
			||||||
 | 
					GROVE_CONFIG="GwM5GroveGen.h"
 | 
				
			||||||
 | 
					GROVE_CONFIG_IN="lib/hardware/GwM5Grove.in"
 | 
				
			||||||
 | 
					EMBEDDED_INCLUDE="GwEmbeddedFiles.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def getEmbeddedFiles(env):
 | 
				
			||||||
 | 
					    rt=[]
 | 
				
			||||||
 | 
					    efiles=env.GetProjectOption("board_build.embed_files")
 | 
				
			||||||
 | 
					    for f in efiles.split("\n"):
 | 
				
			||||||
 | 
					        if f == '':
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        rt.append(f)
 | 
				
			||||||
 | 
					    return rt  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def basePath():
 | 
				
			||||||
 | 
					    #see: https://stackoverflow.com/questions/16771894/python-nameerror-global-name-file-is-not-defined
 | 
				
			||||||
 | 
					    return os.path.dirname(inspect.getfile(lambda: None))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def outPath():
 | 
				
			||||||
 | 
					    return os.path.join(basePath(),GEN_DIR)
 | 
				
			||||||
 | 
					def checkDir():
 | 
				
			||||||
 | 
					    dn=outPath()
 | 
				
			||||||
 | 
					    if not os.path.exists(dn):
 | 
				
			||||||
 | 
					        os.makedirs(dn)
 | 
				
			||||||
 | 
					    if not os.path.isdir(dn):
 | 
				
			||||||
 | 
					        print("unable to create %s"%dn)
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					    return True    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def isCurrent(infile,outfile):
 | 
				
			||||||
 | 
					    if os.path.exists(outfile):
 | 
				
			||||||
 | 
					        otime=os.path.getmtime(outfile)
 | 
				
			||||||
 | 
					        itime=os.path.getmtime(infile)
 | 
				
			||||||
 | 
					        if (otime >= itime):
 | 
				
			||||||
 | 
					            own=os.path.join(basePath(),OWN_FILE)
 | 
				
			||||||
 | 
					            if os.path.exists(own):
 | 
				
			||||||
 | 
					                owntime=os.path.getmtime(own)
 | 
				
			||||||
 | 
					                if owntime > otime:
 | 
				
			||||||
 | 
					                    return False
 | 
				
			||||||
 | 
					            print("%s is newer then %s, no need to recreate"%(outfile,infile))
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					    return False        
 | 
				
			||||||
 | 
					def compressFile(inFile,outfile):
 | 
				
			||||||
 | 
					    if isCurrent(inFile,outfile):
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    print("compressing %s"%inFile)
 | 
				
			||||||
 | 
					    with open(inFile, 'rb') as f_in:
 | 
				
			||||||
 | 
					        with gzip.open(outfile, 'wb') as f_out:
 | 
				
			||||||
 | 
					            shutil.copyfileobj(f_in, f_out)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def generateFile(infile,outfile,callback,inMode='rb',outMode='w'):
 | 
				
			||||||
 | 
					    if isCurrent(infile,outfile):
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    print("creating %s"%outfile)
 | 
				
			||||||
 | 
					    oh=None
 | 
				
			||||||
 | 
					    with open(infile,inMode) as ch:
 | 
				
			||||||
 | 
					        with open(outfile,outMode) as oh:
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                callback(ch,oh,inFile=infile)
 | 
				
			||||||
 | 
					                oh.close()
 | 
				
			||||||
 | 
					            except Exception as e:
 | 
				
			||||||
 | 
					                try:
 | 
				
			||||||
 | 
					                    oh.close()
 | 
				
			||||||
 | 
					                except:
 | 
				
			||||||
 | 
					                    pass
 | 
				
			||||||
 | 
					                os.unlink(outfile)
 | 
				
			||||||
 | 
					                raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def writeFileIfChanged(fileName,data):
 | 
				
			||||||
 | 
					    if os.path.exists(fileName):
 | 
				
			||||||
 | 
					        with open(fileName,"r") as ih:
 | 
				
			||||||
 | 
					            old=ih.read()
 | 
				
			||||||
 | 
					            ih.close()
 | 
				
			||||||
 | 
					            if old == data:
 | 
				
			||||||
 | 
					                return False
 | 
				
			||||||
 | 
					    print("#generating %s"%fileName)
 | 
				
			||||||
 | 
					    with open(fileName,"w") as oh:
 | 
				
			||||||
 | 
					        oh.write(data)
 | 
				
			||||||
 | 
					    return True    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def mergeConfig(base,other):
 | 
				
			||||||
 | 
					    for bdir in other:
 | 
				
			||||||
 | 
					        cname=os.path.join(bdir,"config.json")
 | 
				
			||||||
 | 
					        if os.path.exists(cname):
 | 
				
			||||||
 | 
					            print("merge config %s"%cname)
 | 
				
			||||||
 | 
					            with open(cname,'rb') as ah:
 | 
				
			||||||
 | 
					                merge=json.load(ah)
 | 
				
			||||||
 | 
					                base=base+merge
 | 
				
			||||||
 | 
					    return base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def replaceTexts(data,replacements):
 | 
				
			||||||
 | 
					    if replacements is None:
 | 
				
			||||||
 | 
					        return data
 | 
				
			||||||
 | 
					    if isinstance(data,str):
 | 
				
			||||||
 | 
					        for k,v in replacements.items():
 | 
				
			||||||
 | 
					            data=data.replace("$"+k,str(v))
 | 
				
			||||||
 | 
					        return data
 | 
				
			||||||
 | 
					    if isinstance(data,list):
 | 
				
			||||||
 | 
					        rt=[]
 | 
				
			||||||
 | 
					        for e in data:
 | 
				
			||||||
 | 
					            rt.append(replaceTexts(e,replacements))
 | 
				
			||||||
 | 
					        return rt
 | 
				
			||||||
 | 
					    if isinstance(data,dict):   
 | 
				
			||||||
 | 
					        rt={} 
 | 
				
			||||||
 | 
					        for k,v in data.items():
 | 
				
			||||||
 | 
					            rt[replaceTexts(k,replacements)]=replaceTexts(v,replacements)
 | 
				
			||||||
 | 
					        return rt
 | 
				
			||||||
 | 
					    return data
 | 
				
			||||||
 | 
					def expandConfig(config):
 | 
				
			||||||
 | 
					    rt=[]
 | 
				
			||||||
 | 
					    for item in config:
 | 
				
			||||||
 | 
					        type=item.get('type')
 | 
				
			||||||
 | 
					        if type != 'array':
 | 
				
			||||||
 | 
					            rt.append(item)
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        replacements=item.get('replace')
 | 
				
			||||||
 | 
					        children=item.get('children')
 | 
				
			||||||
 | 
					        name=item.get('name')
 | 
				
			||||||
 | 
					        if name is None:
 | 
				
			||||||
 | 
					            name="#unknown#"
 | 
				
			||||||
 | 
					        if not isinstance(replacements,list):
 | 
				
			||||||
 | 
					            raise Exception("missing replacements at array %s"%name)
 | 
				
			||||||
 | 
					        for replace in replacements:
 | 
				
			||||||
 | 
					            if children is not None:
 | 
				
			||||||
 | 
					                for c in children:
 | 
				
			||||||
 | 
					                    rt.append(replaceTexts(c,replace))
 | 
				
			||||||
 | 
					    return rt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def generateMergedConfig(inFile,outFile,addDirs=[]):
 | 
				
			||||||
 | 
					    if not os.path.exists(inFile):
 | 
				
			||||||
 | 
					        raise Exception("unable to read cfg file %s"%inFile)
 | 
				
			||||||
 | 
					    data=""
 | 
				
			||||||
 | 
					    with open(inFile,'rb') as ch:    
 | 
				
			||||||
 | 
					        config=json.load(ch)
 | 
				
			||||||
 | 
					        config=mergeConfig(config,addDirs)
 | 
				
			||||||
 | 
					        config=expandConfig(config)
 | 
				
			||||||
 | 
					        data=json.dumps(config,indent=2)
 | 
				
			||||||
 | 
					        writeFileIfChanged(outFile,data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def generateCfg(inFile,outFile,impl):
 | 
				
			||||||
 | 
					    if not os.path.exists(inFile):
 | 
				
			||||||
 | 
					        raise Exception("unable to read cfg file %s"%inFile)
 | 
				
			||||||
 | 
					    data=""
 | 
				
			||||||
 | 
					    with open(inFile,'rb') as ch:    
 | 
				
			||||||
 | 
					        config=json.load(ch)     
 | 
				
			||||||
 | 
					        data+="//generated from %s\n"%inFile
 | 
				
			||||||
 | 
					        l=len(config)
 | 
				
			||||||
 | 
					        idx=0
 | 
				
			||||||
 | 
					        if not impl:
 | 
				
			||||||
 | 
					            data+='#include "GwConfigItem.h"\n'
 | 
				
			||||||
 | 
					            data+='class GwConfigDefinitions{\n'
 | 
				
			||||||
 | 
					            data+='  public:\n'
 | 
				
			||||||
 | 
					            data+='  int getNumConfig() const{return %d;}\n'%(l)
 | 
				
			||||||
 | 
					            for item in config:
 | 
				
			||||||
 | 
					                n=item.get('name')
 | 
				
			||||||
 | 
					                if n is None:
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                if len(n) > 15:
 | 
				
			||||||
 | 
					                    raise Exception("%s: config names must be max 15 caracters"%n)
 | 
				
			||||||
 | 
					                data+='  static constexpr const char* %s="%s";\n'%(n,n)
 | 
				
			||||||
 | 
					            data+="};\n"
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            data+='void GwConfigHandler::populateConfigs(GwConfigInterface **config){\n'
 | 
				
			||||||
 | 
					            for item in config:
 | 
				
			||||||
 | 
					                name=item.get('name')
 | 
				
			||||||
 | 
					                if name is None:
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                data+='  configs[%d]='%(idx)
 | 
				
			||||||
 | 
					                idx+=1
 | 
				
			||||||
 | 
					                secret="false";
 | 
				
			||||||
 | 
					                if item.get('type') == 'password':
 | 
				
			||||||
 | 
					                    secret="true"
 | 
				
			||||||
 | 
					                data+="     new GwConfigInterface(%s,\"%s\",%s);\n"%(name,item.get('default'),secret)
 | 
				
			||||||
 | 
					            data+='}\n'  
 | 
				
			||||||
 | 
					    writeFileIfChanged(outFile,data)    
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					def labelFilter(label):
 | 
				
			||||||
 | 
					    return re.sub("[^a-zA-Z0-9]","",re.sub("\([0-9]*\)","",label))    
 | 
				
			||||||
 | 
					def generateXdrMappings(fp,oh,inFile=''):
 | 
				
			||||||
 | 
					    jdoc=json.load(fp)
 | 
				
			||||||
 | 
					    oh.write("static GwXDRTypeMapping* typeMappings[]={\n")
 | 
				
			||||||
 | 
					    first=True
 | 
				
			||||||
 | 
					    for cat in jdoc:
 | 
				
			||||||
 | 
					        item=jdoc[cat]
 | 
				
			||||||
 | 
					        cid=item.get('id')
 | 
				
			||||||
 | 
					        if cid is None:
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        tc=item.get('type')
 | 
				
			||||||
 | 
					        if tc is not None:
 | 
				
			||||||
 | 
					            if first:
 | 
				
			||||||
 | 
					                first=False
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                oh.write(",\n")
 | 
				
			||||||
 | 
					            oh.write("   new GwXDRTypeMapping(%d,0,%d) /*%s*/"%(cid,tc,cat))
 | 
				
			||||||
 | 
					        fields=item.get('fields')
 | 
				
			||||||
 | 
					        if fields is None:
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        idx=0
 | 
				
			||||||
 | 
					        for fe in fields:
 | 
				
			||||||
 | 
					            if not isinstance(fe,dict):
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            tc=fe.get('t')
 | 
				
			||||||
 | 
					            id=fe.get('v')
 | 
				
			||||||
 | 
					            if id is None:
 | 
				
			||||||
 | 
					                id=idx
 | 
				
			||||||
 | 
					            idx+=1
 | 
				
			||||||
 | 
					            l=fe.get('l') or ''
 | 
				
			||||||
 | 
					            if tc is None or id is None:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            if first:
 | 
				
			||||||
 | 
					                first=False
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                oh.write(",\n")
 | 
				
			||||||
 | 
					            oh.write("   new GwXDRTypeMapping(%d,%d,%d) /*%s:%s*/"%(cid,id,tc,cat,l))
 | 
				
			||||||
 | 
					    oh.write("\n")
 | 
				
			||||||
 | 
					    oh.write("};\n")
 | 
				
			||||||
 | 
					    for cat in jdoc:
 | 
				
			||||||
 | 
					        item=jdoc[cat]
 | 
				
			||||||
 | 
					        cid=item.get('id')
 | 
				
			||||||
 | 
					        if cid is None:
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        selectors=item.get('selector')
 | 
				
			||||||
 | 
					        if selectors is not None:
 | 
				
			||||||
 | 
					            for selector in selectors:
 | 
				
			||||||
 | 
					                label=selector.get('l')
 | 
				
			||||||
 | 
					                value=selector.get('v')
 | 
				
			||||||
 | 
					                if label is not None and value is not None:
 | 
				
			||||||
 | 
					                    label=labelFilter(label)
 | 
				
			||||||
 | 
					                    define=("GWXDRSEL_%s_%s"%(cat,label)).upper()
 | 
				
			||||||
 | 
					                    oh.write("    #define %s %s\n"%(define,value))
 | 
				
			||||||
 | 
					        fields=item.get('fields')
 | 
				
			||||||
 | 
					        if fields is not None:
 | 
				
			||||||
 | 
					            idx=0
 | 
				
			||||||
 | 
					            for field in fields:
 | 
				
			||||||
 | 
					                v=field.get('v')
 | 
				
			||||||
 | 
					                if v is None:
 | 
				
			||||||
 | 
					                    v=idx
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    v=int(v)
 | 
				
			||||||
 | 
					                label=field.get('l')
 | 
				
			||||||
 | 
					                if v is not None and label is not None:
 | 
				
			||||||
 | 
					                    define=("GWXDRFIELD_%s_%s"%(cat,labelFilter(label))).upper();
 | 
				
			||||||
 | 
					                    oh.write("    #define %s %s\n"%(define,str(v)))
 | 
				
			||||||
 | 
					                idx+=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Grove:
 | 
				
			||||||
 | 
					    def __init__(self,name) -> None:
 | 
				
			||||||
 | 
					        self.name=name
 | 
				
			||||||
 | 
					    def _ss(self,z=False):
 | 
				
			||||||
 | 
					        if z:
 | 
				
			||||||
 | 
					            return self.name
 | 
				
			||||||
 | 
					        return self.name if self.name is not 'Z' else ''
 | 
				
			||||||
 | 
					    def _suffix(self):
 | 
				
			||||||
 | 
					        return '_'+self.name if self.name is not 'Z' else ''
 | 
				
			||||||
 | 
					    def replace(self,line):
 | 
				
			||||||
 | 
					        if line is None:
 | 
				
			||||||
 | 
					            return line
 | 
				
			||||||
 | 
					        return line.replace('$G$',self._ss()).replace('$Z$',self._ss(True)).replace('$GS$',self._suffix())
 | 
				
			||||||
 | 
					def generateGroveDefs(inh,outh,inFile=''):
 | 
				
			||||||
 | 
					    GROVES=[Grove('Z'),Grove('A'),Grove('B'),Grove('C')]
 | 
				
			||||||
 | 
					    definition=[]
 | 
				
			||||||
 | 
					    started=False
 | 
				
			||||||
 | 
					    def writeConfig():
 | 
				
			||||||
 | 
					        for grove in GROVES:        
 | 
				
			||||||
 | 
					            for cl in definition:
 | 
				
			||||||
 | 
					                outh.write(grove.replace(cl))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for line in inh:
 | 
				
			||||||
 | 
					        if re.match(" *#GROVE",line):
 | 
				
			||||||
 | 
					            started=True
 | 
				
			||||||
 | 
					            if len(definition) > 0:
 | 
				
			||||||
 | 
					                writeConfig()
 | 
				
			||||||
 | 
					            definition=[]
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        if started:
 | 
				
			||||||
 | 
					            definition.append(line)
 | 
				
			||||||
 | 
					    if len(definition) > 0:
 | 
				
			||||||
 | 
					        writeConfig()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					userTaskDirs=[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def getUserTaskDirs():
 | 
				
			||||||
 | 
					    rt=[]
 | 
				
			||||||
 | 
					    taskdirs=glob.glob(os.path.join( basePath(),'lib','*task*'))
 | 
				
			||||||
 | 
					    for task in taskdirs:
 | 
				
			||||||
 | 
					        rt.append(task)
 | 
				
			||||||
 | 
					    return rt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def checkAndAdd(file,names,ilist):
 | 
				
			||||||
 | 
					    if not file.endswith('.h'):
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    match=False
 | 
				
			||||||
 | 
					    for cmp in names:
 | 
				
			||||||
 | 
					        #print("##check %s<->%s"%(f.lower(),cmp))
 | 
				
			||||||
 | 
					        if file.lower() == cmp:
 | 
				
			||||||
 | 
					            match=True
 | 
				
			||||||
 | 
					    if not match:
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    ilist.append(file) 
 | 
				
			||||||
 | 
					def genereateUserTasks(outfile):
 | 
				
			||||||
 | 
					    includes=[]
 | 
				
			||||||
 | 
					    for task in userTaskDirs:
 | 
				
			||||||
 | 
					        #print("##taskdir=%s"%task)
 | 
				
			||||||
 | 
					        base=os.path.basename(task)
 | 
				
			||||||
 | 
					        includeNames=[base.lower()+".h",'gw'+base.lower()+'.h']
 | 
				
			||||||
 | 
					        for f in os.listdir(task):
 | 
				
			||||||
 | 
					            checkAndAdd(f,includeNames,includes)
 | 
				
			||||||
 | 
					    includeData=""
 | 
				
			||||||
 | 
					    for i in includes:
 | 
				
			||||||
 | 
					        print("#task include %s"%i)
 | 
				
			||||||
 | 
					        includeData+="#include <%s>\n"%i
 | 
				
			||||||
 | 
					    writeFileIfChanged(outfile,includeData)            
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def generateEmbedded(elist,outFile):
 | 
				
			||||||
 | 
					    content=""
 | 
				
			||||||
 | 
					    for entry in elist:
 | 
				
			||||||
 | 
					        content+="EMBED_GZ_FILE(\"%s\",%s,\"%s\");\n"%entry
 | 
				
			||||||
 | 
					    writeFileIfChanged(outFile,content)    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def getContentType(fn):
 | 
				
			||||||
 | 
					    if (fn.endswith('.gz')):
 | 
				
			||||||
 | 
					        fn=fn[0:-3]
 | 
				
			||||||
 | 
					    if (fn.endswith('html')):
 | 
				
			||||||
 | 
					        return "text/html"
 | 
				
			||||||
 | 
					    if (fn.endswith('json')):
 | 
				
			||||||
 | 
					        return "application/json"
 | 
				
			||||||
 | 
					    if (fn.endswith('js')):
 | 
				
			||||||
 | 
					        return "text/javascript"
 | 
				
			||||||
 | 
					    if (fn.endswith('css')):    
 | 
				
			||||||
 | 
					        return "text/css"
 | 
				
			||||||
 | 
					    return "application/octet-stream"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def getLibs():
 | 
				
			||||||
 | 
					    base=os.path.join(basePath(),"lib")
 | 
				
			||||||
 | 
					    rt=[]
 | 
				
			||||||
 | 
					    for sd in os.listdir(base):
 | 
				
			||||||
 | 
					        if sd == '..':
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        if sd == '.':
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        fn=os.path.join(base,sd)
 | 
				
			||||||
 | 
					        if os.path.isdir(fn):
 | 
				
			||||||
 | 
					            rt.append(sd)
 | 
				
			||||||
 | 
					    EXTRAS=['generated']
 | 
				
			||||||
 | 
					    for e in EXTRAS:
 | 
				
			||||||
 | 
					        if not e in rt:
 | 
				
			||||||
 | 
					            rt.append(e)
 | 
				
			||||||
 | 
					    return rt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def joinFiles(target,pattern,dirlist):
 | 
				
			||||||
 | 
					    flist=[]
 | 
				
			||||||
 | 
					    for dir in dirlist:
 | 
				
			||||||
 | 
					            fn=os.path.join(dir,pattern)
 | 
				
			||||||
 | 
					            if os.path.exists(fn):
 | 
				
			||||||
 | 
					                flist.append(fn)
 | 
				
			||||||
 | 
					    current=False
 | 
				
			||||||
 | 
					    if os.path.exists(target):
 | 
				
			||||||
 | 
					        current=True
 | 
				
			||||||
 | 
					        for f in flist:
 | 
				
			||||||
 | 
					            if not isCurrent(f,target):
 | 
				
			||||||
 | 
					                current=False
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					    if current:
 | 
				
			||||||
 | 
					        print("%s is up to date"%target)
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    print("creating %s"%target)
 | 
				
			||||||
 | 
					    with gzip.open(target,"wb") as oh:
 | 
				
			||||||
 | 
					        for fn in flist:
 | 
				
			||||||
 | 
					            print("adding %s to %s"%(fn,target))
 | 
				
			||||||
 | 
					            with open(fn,"rb") as rh:
 | 
				
			||||||
 | 
					                shutil.copyfileobj(rh,oh)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OWNLIBS=getLibs()+["FS","WiFi"]
 | 
				
			||||||
 | 
					GLOBAL_INCLUDES=[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def handleDeps(env):
 | 
				
			||||||
 | 
					    #overwrite the GetProjectConfig
 | 
				
			||||||
 | 
					    #to inject all our libs
 | 
				
			||||||
 | 
					    oldGetProjectConfig=env.GetProjectConfig    
 | 
				
			||||||
 | 
					    def GetProjectConfigX(env):        
 | 
				
			||||||
 | 
					        rt=oldGetProjectConfig()
 | 
				
			||||||
 | 
					        cenv="env:"+env['PIOENV']
 | 
				
			||||||
 | 
					        libs=[]
 | 
				
			||||||
 | 
					        for section,options in rt.as_tuple():
 | 
				
			||||||
 | 
					            if section == cenv:
 | 
				
			||||||
 | 
					                for key,values in options:
 | 
				
			||||||
 | 
					                    if key == 'lib_deps':
 | 
				
			||||||
 | 
					                        libs=values
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        mustUpdate=False
 | 
				
			||||||
 | 
					        for lib in OWNLIBS:
 | 
				
			||||||
 | 
					            if not lib in libs:
 | 
				
			||||||
 | 
					                libs.append(lib)
 | 
				
			||||||
 | 
					                mustUpdate=True
 | 
				
			||||||
 | 
					        if mustUpdate:
 | 
				
			||||||
 | 
					            update=[(cenv,[('lib_deps',libs)])]
 | 
				
			||||||
 | 
					            rt.update(update)
 | 
				
			||||||
 | 
					        return rt
 | 
				
			||||||
 | 
					    env.AddMethod(GetProjectConfigX,"GetProjectConfig")
 | 
				
			||||||
 | 
					    #store the list of all includes after we resolved
 | 
				
			||||||
 | 
					    #the dependencies for our main project
 | 
				
			||||||
 | 
					    #we will use them for all compilations afterwards
 | 
				
			||||||
 | 
					    oldLibBuilder=env.ConfigureProjectLibBuilder
 | 
				
			||||||
 | 
					    def ConfigureProjectLibBuilderX(env):
 | 
				
			||||||
 | 
					        global GLOBAL_INCLUDES
 | 
				
			||||||
 | 
					        project=oldLibBuilder()
 | 
				
			||||||
 | 
					        #print("##ConfigureProjectLibBuilderX")
 | 
				
			||||||
 | 
					        #pprint.pprint(project)
 | 
				
			||||||
 | 
					        if project.depbuilders:
 | 
				
			||||||
 | 
					            #print("##depbuilders %s"%",".join(map(lambda x: x.path,project.depbuilders)))
 | 
				
			||||||
 | 
					            for db in project.depbuilders:
 | 
				
			||||||
 | 
					                idirs=db.get_include_dirs()
 | 
				
			||||||
 | 
					                for id in idirs:
 | 
				
			||||||
 | 
					                    if not id in GLOBAL_INCLUDES:
 | 
				
			||||||
 | 
					                        GLOBAL_INCLUDES.append(id)
 | 
				
			||||||
 | 
					        return project
 | 
				
			||||||
 | 
					    env.AddMethod(ConfigureProjectLibBuilderX,"ConfigureProjectLibBuilder")
 | 
				
			||||||
 | 
					    def injectIncludes(env,node):
 | 
				
			||||||
 | 
					        return env.Object(
 | 
				
			||||||
 | 
					            node,
 | 
				
			||||||
 | 
					            CPPPATH=env["CPPPATH"]+GLOBAL_INCLUDES
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    env.AddBuildMiddleware(injectIncludes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def prebuild(env):
 | 
				
			||||||
 | 
					    global userTaskDirs
 | 
				
			||||||
 | 
					    print("#prebuild running")
 | 
				
			||||||
 | 
					    if not checkDir():
 | 
				
			||||||
 | 
					        sys.exit(1)
 | 
				
			||||||
 | 
					    ldf_mode=env.GetProjectOption("lib_ldf_mode")
 | 
				
			||||||
 | 
					    if ldf_mode == 'off':
 | 
				
			||||||
 | 
					        print("##ldf off - own dependency handling")
 | 
				
			||||||
 | 
					        handleDeps(env)
 | 
				
			||||||
 | 
					    userTaskDirs=getUserTaskDirs()
 | 
				
			||||||
 | 
					    mergedConfig=os.path.join(outPath(),os.path.basename(CFG_FILE))
 | 
				
			||||||
 | 
					    generateMergedConfig(os.path.join(basePath(),CFG_FILE),mergedConfig,userTaskDirs)
 | 
				
			||||||
 | 
					    compressFile(mergedConfig,mergedConfig+".gz")
 | 
				
			||||||
 | 
					    generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE),False)
 | 
				
			||||||
 | 
					    generateCfg(mergedConfig,os.path.join(outPath(),CFG_INCLUDE_IMPL),True)
 | 
				
			||||||
 | 
					    joinFiles(os.path.join(outPath(),INDEXJS+".gz"),INDEXJS,["web"]+userTaskDirs)
 | 
				
			||||||
 | 
					    joinFiles(os.path.join(outPath(),INDEXCSS+".gz"),INDEXCSS,["web"]+userTaskDirs)
 | 
				
			||||||
 | 
					    embedded=getEmbeddedFiles(env)
 | 
				
			||||||
 | 
					    filedefs=[]
 | 
				
			||||||
 | 
					    for ef in embedded:
 | 
				
			||||||
 | 
					        print("#checking embedded file %s"%ef)
 | 
				
			||||||
 | 
					        (dn,fn)=os.path.split(ef)
 | 
				
			||||||
 | 
					        pureName=fn
 | 
				
			||||||
 | 
					        if pureName.endswith('.gz'):
 | 
				
			||||||
 | 
					            pureName=pureName[0:-3]
 | 
				
			||||||
 | 
					        ct=getContentType(pureName)
 | 
				
			||||||
 | 
					        usname=ef.replace('/','_').replace('.','_')
 | 
				
			||||||
 | 
					        filedefs.append((pureName,usname,ct))
 | 
				
			||||||
 | 
					        inFile=os.path.join(basePath(),"web",pureName)
 | 
				
			||||||
 | 
					        if os.path.exists(inFile):
 | 
				
			||||||
 | 
					            compressFile(inFile,ef)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            print("#WARNING: infile %s for %s not found"%(inFile,ef))
 | 
				
			||||||
 | 
					    generateEmbedded(filedefs,os.path.join(outPath(),EMBEDDED_INCLUDE))
 | 
				
			||||||
 | 
					    genereateUserTasks(os.path.join(outPath(), TASK_INCLUDE))
 | 
				
			||||||
 | 
					    generateFile(os.path.join(basePath(),XDR_FILE),os.path.join(outPath(),XDR_INCLUDE),generateXdrMappings)
 | 
				
			||||||
 | 
					    generateFile(os.path.join(basePath(),GROVE_CONFIG_IN),os.path.join(outPath(),GROVE_CONFIG),generateGroveDefs,inMode='r')
 | 
				
			||||||
 | 
					    version="dev"+datetime.now().strftime("%Y%m%d")
 | 
				
			||||||
 | 
					    env.Append(CPPDEFINES=[('GWDEVVERSION',version)])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def cleangenerated(source, target, env):
 | 
				
			||||||
 | 
					    od=outPath()
 | 
				
			||||||
 | 
					    if os.path.isdir(od):
 | 
				
			||||||
 | 
					        print("#cleaning up %s"%od)
 | 
				
			||||||
 | 
					        for f in os.listdir(od):
 | 
				
			||||||
 | 
					            if f == "." or f == "..":
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            fn=os.path.join(od,f)
 | 
				
			||||||
 | 
					            os.unlink(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print("#prescript...")
 | 
				
			||||||
 | 
					prebuild(env)
 | 
				
			||||||
 | 
					board="PLATFORM_BOARD_%s"%env["BOARD"].replace("-","_").upper()
 | 
				
			||||||
 | 
					print("Board=#%s#"%board)
 | 
				
			||||||
 | 
					print("BuildFlags=%s"%(" ".join(env["BUILD_FLAGS"])))
 | 
				
			||||||
 | 
					env.Append(
 | 
				
			||||||
 | 
					    LINKFLAGS=[ "-u", "custom_app_desc" ],
 | 
				
			||||||
 | 
					    CPPDEFINES=[(board,"1")]
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					#script does not run on clean yet - maybe in the future
 | 
				
			||||||
 | 
					env.AddPostAction("clean",cleangenerated)
 | 
				
			||||||
| 
						 | 
					@ -141,12 +141,12 @@ void deepSleep(CommonData &common){
 | 
				
			||||||
    getdisplay().print("Sleep Mode");
 | 
					    getdisplay().print("Sleep Mode");
 | 
				
			||||||
    getdisplay().setFont(&Ubuntu_Bold8pt7b);
 | 
					    getdisplay().setFont(&Ubuntu_Bold8pt7b);
 | 
				
			||||||
    getdisplay().setCursor(65, 175);
 | 
					    getdisplay().setCursor(65, 175);
 | 
				
			||||||
    getdisplay().print("For wakeup press key and wait 5s");
 | 
					    getdisplay().print("To wake up press key and wait 5s");
 | 
				
			||||||
    getdisplay().nextPage();                // Update display contents
 | 
					    getdisplay().nextPage();                // Update display contents
 | 
				
			||||||
    getdisplay().powerOff();                // Display power off
 | 
					    getdisplay().powerOff();                // Display power off
 | 
				
			||||||
    setPortPin(OBP_POWER_50, false);        // Power off ePaper display
 | 
					    setPortPin(OBP_POWER_50, false);        // Power off ePaper display
 | 
				
			||||||
    // Stop system
 | 
					    // Stop system
 | 
				
			||||||
    esp_deep_sleep_start();             // Deep Sleep with weakup via touch pin
 | 
					    esp_deep_sleep_start();                 // Deep Sleep with weakup via touch pin
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef BOARD_OBP40S3
 | 
					#ifdef BOARD_OBP40S3
 | 
				
			||||||
| 
						 | 
					@ -166,7 +166,7 @@ void deepSleep(CommonData &common){
 | 
				
			||||||
    getdisplay().print("Sleep Mode");
 | 
					    getdisplay().print("Sleep Mode");
 | 
				
			||||||
    getdisplay().setFont(&Ubuntu_Bold8pt7b);
 | 
					    getdisplay().setFont(&Ubuntu_Bold8pt7b);
 | 
				
			||||||
    getdisplay().setCursor(65, 175);
 | 
					    getdisplay().setCursor(65, 175);
 | 
				
			||||||
    getdisplay().print("For wakeup press wheel and wait 5s");
 | 
					    getdisplay().print("To wake up press wheel and wait 5s");
 | 
				
			||||||
    getdisplay().nextPage();                // Partial update
 | 
					    getdisplay().nextPage();                // Partial update
 | 
				
			||||||
    getdisplay().powerOff();                // Display power off
 | 
					    getdisplay().powerOff();                // Display power off
 | 
				
			||||||
    setPortPin(OBP_POWER_EPD, false);       // Power off ePaper display
 | 
					    setPortPin(OBP_POWER_EPD, false);       // Power off ePaper display
 | 
				
			||||||
| 
						 | 
					@ -341,13 +341,9 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
 | 
				
			||||||
    static unsigned long tcpClTxOld = 0;
 | 
					    static unsigned long tcpClTxOld = 0;
 | 
				
			||||||
    static unsigned long n2kRxOld = 0;
 | 
					    static unsigned long n2kRxOld = 0;
 | 
				
			||||||
    static unsigned long n2kTxOld = 0;
 | 
					    static unsigned long n2kTxOld = 0;
 | 
				
			||||||
    int textcolor = GxEPD_BLACK;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(commonData.config->getBool(commonData.config->statusLine) == true){
 | 
					    if(commonData.config->getBool(commonData.config->statusLine) == true){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Header separator line (optional)
 | 
					 | 
				
			||||||
        // getdisplay().drawLine(0, 19, 399, 19, commonData.fgcolor);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Show status info
 | 
					        // Show status info
 | 
				
			||||||
        getdisplay().setTextColor(commonData.fgcolor);
 | 
					        getdisplay().setTextColor(commonData.fgcolor);
 | 
				
			||||||
        getdisplay().setFont(&Ubuntu_Bold8pt7b);
 | 
					        getdisplay().setFont(&Ubuntu_Bold8pt7b);
 | 
				
			||||||
| 
						 | 
					@ -392,12 +388,36 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
 | 
				
			||||||
            getdisplay().drawXBitmap(166, 1, swipe_bits, swipe_width, swipe_height, commonData.fgcolor);
 | 
					            getdisplay().drawXBitmap(166, 1, swipe_bits, swipe_width, swipe_height, commonData.fgcolor);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef LIPO_ACCU_1200
 | 
				
			||||||
 | 
					        if (commonData.data.BatteryChargeStatus == 1) {
 | 
				
			||||||
 | 
					             getdisplay().drawXBitmap(170, 1, battery_loading_bits, battery_width, battery_height, commonData.fgcolor);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					#ifdef VOLTAGE_SENSOR
 | 
				
			||||||
 | 
					            if (commonData.data.batteryLevelLiPo < 10) {
 | 
				
			||||||
 | 
					                getdisplay().drawXBitmap(170, 1, battery_0_bits, battery_width, battery_height, commonData.fgcolor);
 | 
				
			||||||
 | 
					            } else if (commonData.data.batteryLevelLiPo < 25) {
 | 
				
			||||||
 | 
					                getdisplay().drawXBitmap(170, 1, battery_25_bits, battery_width, battery_height, commonData.fgcolor);
 | 
				
			||||||
 | 
					            } else if (commonData.data.batteryLevelLiPo < 50) {
 | 
				
			||||||
 | 
					                getdisplay().drawXBitmap(170, 1, battery_50_bits, battery_width, battery_height, commonData.fgcolor);
 | 
				
			||||||
 | 
					            } else if (commonData.data.batteryLevelLiPo < 75) {
 | 
				
			||||||
 | 
					                getdisplay().drawXBitmap(170, 1, battery_75_bits, battery_width, battery_height, commonData.fgcolor);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                getdisplay().drawXBitmap(170, 1, battery_100_bits, battery_width, battery_height, commonData.fgcolor);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					#endif // VOLTAGE_SENSOR
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					#endif // LIPO_ACCU_1200
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Heartbeat as dot
 | 
					        // Heartbeat as page number
 | 
				
			||||||
        getdisplay().setTextColor(commonData.fgcolor);
 | 
					        if (heartbeat) {
 | 
				
			||||||
        getdisplay().setFont(&Ubuntu_Bold32pt7b);
 | 
					            getdisplay().setTextColor(commonData.bgcolor);
 | 
				
			||||||
        getdisplay().setCursor(205, 14);
 | 
					            getdisplay().fillRect(201, 0, 23, 19, commonData.fgcolor);
 | 
				
			||||||
        getdisplay().print(heartbeat ? "." : " ");
 | 
					        } else {
 | 
				
			||||||
 | 
					            getdisplay().setTextColor(commonData.fgcolor);
 | 
				
			||||||
 | 
					            getdisplay().drawRect(201, 0, 23, 19, commonData.fgcolor);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        getdisplay().setFont(&Ubuntu_Bold8pt7b);
 | 
				
			||||||
 | 
					        drawTextCenter(211, 9, String(commonData.data.actpage));
 | 
				
			||||||
        heartbeat = !heartbeat;
 | 
					        heartbeat = !heartbeat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Date and time
 | 
					        // Date and time
 | 
				
			||||||
| 
						 | 
					@ -442,17 +462,16 @@ void displayFooter(CommonData &commonData) {
 | 
				
			||||||
        // horizontal elements
 | 
					        // horizontal elements
 | 
				
			||||||
        const uint16_t top = 280;
 | 
					        const uint16_t top = 280;
 | 
				
			||||||
        const uint16_t bottom = 299;
 | 
					        const uint16_t bottom = 299;
 | 
				
			||||||
 | 
					        // horizontal stub lines
 | 
				
			||||||
        getdisplay().drawLine(commonData.keydata[0].x, top, commonData.keydata[0].x+10, top, commonData.fgcolor);
 | 
					        getdisplay().drawLine(commonData.keydata[0].x, top, commonData.keydata[0].x+10, top, commonData.fgcolor);
 | 
				
			||||||
        getdisplay().drawLine(commonData.keydata[1].x-10, top, commonData.keydata[1].x+10, top, commonData.fgcolor);
 | 
					        for (int i = 1; i <= 5; i++) {
 | 
				
			||||||
        getdisplay().drawLine(commonData.keydata[2].x-10, top, commonData.keydata[2].x+10, top, commonData.fgcolor);
 | 
					            getdisplay().drawLine(commonData.keydata[i].x-10, top, commonData.keydata[i].x+10, top, commonData.fgcolor);
 | 
				
			||||||
        getdisplay().drawLine(commonData.keydata[4].x-10, top, commonData.keydata[4].x+10, top, commonData.fgcolor);
 | 
					        }
 | 
				
			||||||
        getdisplay().drawLine(commonData.keydata[5].x-10, top, commonData.keydata[5].x+10, top, commonData.fgcolor);
 | 
					 | 
				
			||||||
        getdisplay().drawLine(commonData.keydata[5].x + commonData.keydata[5].w - 10, top, commonData.keydata[5].x + commonData.keydata[5].w + 1, top, commonData.fgcolor);
 | 
					        getdisplay().drawLine(commonData.keydata[5].x + commonData.keydata[5].w - 10, top, commonData.keydata[5].x + commonData.keydata[5].w + 1, top, commonData.fgcolor);
 | 
				
			||||||
        // vertical key separators
 | 
					        // vertical key separators
 | 
				
			||||||
        getdisplay().drawLine(commonData.keydata[0].x + commonData.keydata[0].w, top, commonData.keydata[0].x + commonData.keydata[0].w, bottom, commonData.fgcolor);
 | 
					        for (int i = 0; i <= 4; i++) {
 | 
				
			||||||
        getdisplay().drawLine(commonData.keydata[1].x + commonData.keydata[1].w, top, commonData.keydata[1].x + commonData.keydata[1].w, bottom, commonData.fgcolor);
 | 
					            getdisplay().drawLine(commonData.keydata[i].x + commonData.keydata[i].w, top, commonData.keydata[i].x + commonData.keydata[i].w, bottom, commonData.fgcolor);
 | 
				
			||||||
        getdisplay().drawLine(commonData.keydata[3].x + commonData.keydata[3].w, top, commonData.keydata[3].x + commonData.keydata[3].w, bottom, commonData.fgcolor);
 | 
					        }
 | 
				
			||||||
        getdisplay().drawLine(commonData.keydata[4].x + commonData.keydata[4].w, top, commonData.keydata[4].x + commonData.keydata[4].w, bottom, commonData.fgcolor);
 | 
					 | 
				
			||||||
        for (int i = 0; i < 6; i++) {
 | 
					        for (int i = 0; i < 6; i++) {
 | 
				
			||||||
            uint16_t x, y;
 | 
					            uint16_t x, y;
 | 
				
			||||||
            if (commonData.keydata[i].label.length() > 0) {
 | 
					            if (commonData.keydata[i].label.length() > 0) {
 | 
				
			||||||
| 
						 | 
					@ -476,9 +495,6 @@ void displayFooter(CommonData &commonData) {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // Current page number in a small box
 | 
					 | 
				
			||||||
        getdisplay().drawRect(190, 280, 23, 19, commonData.fgcolor);
 | 
					 | 
				
			||||||
        drawTextCenter(200, 289, String(commonData.data.actpage));
 | 
					 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        getdisplay().setCursor(65, 295);
 | 
					        getdisplay().setCursor(65, 295);
 | 
				
			||||||
        getdisplay().print("Press 1 and 6 fast to unlock keys");
 | 
					        getdisplay().print("Press 1 and 6 fast to unlock keys");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -100,8 +100,8 @@ void displayFooter(CommonData &commonData);
 | 
				
			||||||
SunData calcSunsetSunrise(GwApi *api, double time, double date, double latitude, double longitude, double timezone); // Calulate sunset and sunrise
 | 
					SunData calcSunsetSunrise(GwApi *api, double time, double date, double latitude, double longitude, double timezone); // Calulate sunset and sunrise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void batteryGraphic(uint x, uint y, float percent, int pcolor, int bcolor); // Battery graphic with fill level
 | 
					void batteryGraphic(uint x, uint y, float percent, int pcolor, int bcolor); // Battery graphic with fill level
 | 
				
			||||||
void solarGraphic(uint x, uint y, int pcolor, int bcolor);                  // Solar graphic with fill level
 | 
					void solarGraphic(uint x, uint y, int pcolor, int bcolor);                  // Solar graphic
 | 
				
			||||||
void generatorGraphic(uint x, uint y, int pcolor, int bcolor);              // Generator graphic with fill level
 | 
					void generatorGraphic(uint x, uint y, int pcolor, int bcolor);              // Generator graphic
 | 
				
			||||||
void startLedTask(GwApi *api);
 | 
					void startLedTask(GwApi *api);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request);
 | 
					void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request);
 | 
				
			||||||
| 
						 | 
					@ -160,6 +160,46 @@ static std::map<String, unsigned char *> iconmap = {
 | 
				
			||||||
    {"AP", ap_bits}
 | 
					    {"AP", ap_bits}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Battery
 | 
				
			||||||
 | 
					#define battery_width 24
 | 
				
			||||||
 | 
					#define battery_height 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static unsigned char battery_0_bits[] = {
 | 
				
			||||||
 | 
					   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0xff, 0xff, 0x1f,
 | 
				
			||||||
 | 
					   0x03, 0x00, 0x18, 0x03, 0x00, 0x78, 0x03, 0x00, 0xf8, 0x03, 0x00, 0xd8,
 | 
				
			||||||
 | 
					   0x03, 0x00, 0xd8, 0x03, 0x00, 0xd8, 0x03, 0x00, 0xf8, 0x03, 0x00, 0x78,
 | 
				
			||||||
 | 
					   0x03, 0x00, 0x18, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0x0f, 0x00, 0x00, 0x00 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static unsigned char battery_25_bits[] = {
 | 
				
			||||||
 | 
					   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0xff, 0xff, 0x1f,
 | 
				
			||||||
 | 
					   0x03, 0x00, 0x18, 0x3b, 0x00, 0x78, 0x3b, 0x00, 0xf8, 0x3b, 0x00, 0xd8,
 | 
				
			||||||
 | 
					   0x3b, 0x00, 0xd8, 0x3b, 0x00, 0xd8, 0x3b, 0x00, 0xf8, 0x3b, 0x00, 0x78,
 | 
				
			||||||
 | 
					   0x03, 0x00, 0x18, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0x0f, 0x00, 0x00, 0x00 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static unsigned char battery_50_bits[] = {
 | 
				
			||||||
 | 
					   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0xff, 0xff, 0x1f,
 | 
				
			||||||
 | 
					   0x03, 0x00, 0x18, 0xbb, 0x03, 0x78, 0xbb, 0x03, 0xf8, 0xbb, 0x03, 0xd8,
 | 
				
			||||||
 | 
					   0xbb, 0x03, 0xd8, 0xbb, 0x03, 0xd8, 0xbb, 0x03, 0xf8, 0xbb, 0x03, 0x78,
 | 
				
			||||||
 | 
					   0x03, 0x00, 0x18, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0x0f, 0x00, 0x00, 0x00 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static unsigned char battery_75_bits[] = {
 | 
				
			||||||
 | 
					   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0xff, 0xff, 0x1f,
 | 
				
			||||||
 | 
					   0x03, 0x00, 0x18, 0xbb, 0x3b, 0x78, 0xbb, 0x3b, 0xf8, 0xbb, 0x3b, 0xd8,
 | 
				
			||||||
 | 
					   0xbb, 0x3b, 0xd8, 0xbb, 0x3b, 0xd8, 0xbb, 0x3b, 0xf8, 0xbb, 0x3b, 0x78,
 | 
				
			||||||
 | 
					   0x03, 0x00, 0x18, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0x0f, 0x00, 0x00, 0x00 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static unsigned char battery_100_bits[] = {
 | 
				
			||||||
 | 
					   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x0f, 0xff, 0xff, 0x1f,
 | 
				
			||||||
 | 
					   0x03, 0x00, 0x18, 0xbb, 0xbb, 0x7b, 0xbb, 0xbb, 0xfb, 0xbb, 0xbb, 0xdb,
 | 
				
			||||||
 | 
					   0xbb, 0xbb, 0xdb, 0xbb, 0xbb, 0xdb, 0xbb, 0xbb, 0xfb, 0xbb, 0xbb, 0x7b,
 | 
				
			||||||
 | 
					   0x03, 0x00, 0x18, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0x0f, 0x00, 0x00, 0x00 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static unsigned char battery_loading_bits[] = {
 | 
				
			||||||
 | 
					   0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xfe, 0xe4, 0x0f, 0xff, 0xec, 0x1f,
 | 
				
			||||||
 | 
					   0x03, 0x08, 0x18, 0x03, 0x18, 0x78, 0x03, 0x30, 0xf8, 0x83, 0x3f, 0xd8,
 | 
				
			||||||
 | 
					   0x03, 0x7f, 0xd8, 0x03, 0x03, 0xd8, 0x03, 0x06, 0xf8, 0x03, 0x04, 0x78,
 | 
				
			||||||
 | 
					   0x03, 0x0c, 0x18, 0xff, 0xcb, 0x1f, 0xfe, 0xd3, 0x0f, 0x00, 0x10, 0x00 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Other symbols
 | 
					// Other symbols
 | 
				
			||||||
#define swipe_width 24
 | 
					#define swipe_width 24
 | 
				
			||||||
#define swipe_height 16
 | 
					#define swipe_height 16
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -277,8 +277,8 @@ void initKeys(CommonData &commonData) {
 | 
				
			||||||
        starttime = millis();   // Start key pressed
 | 
					        starttime = millis();   // Start key pressed
 | 
				
			||||||
        keycodeold = keycode;
 | 
					        keycodeold = keycode;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      // If key pressed longer than 200ms
 | 
					      // If key pressed longer than 100ms
 | 
				
			||||||
      if(millis() > starttime + 200 && keycode == keycodeold) {
 | 
					      if(millis() > starttime + 100 && keycode == keycodeold) {
 | 
				
			||||||
        if (use_syspage and keycode == 3) {
 | 
					        if (use_syspage and keycode == 3) {
 | 
				
			||||||
            keystatus = 12;
 | 
					            keystatus = 12;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -88,8 +88,16 @@ void sensorTask(void *param){
 | 
				
			||||||
    double voffset = (api->getConfig()->getConfigItem(api->getConfig()->vOffset,true)->asString()).toFloat();
 | 
					    double voffset = (api->getConfig()->getConfigItem(api->getConfig()->vOffset,true)->asString()).toFloat();
 | 
				
			||||||
    double vslope = (api->getConfig()->getConfigItem(api->getConfig()->vSlope,true)->asString()).toFloat();
 | 
					    double vslope = (api->getConfig()->getConfigItem(api->getConfig()->vSlope,true)->asString()).toFloat();
 | 
				
			||||||
    if(String(powsensor1) == "off"){
 | 
					    if(String(powsensor1) == "off"){
 | 
				
			||||||
        sensors.batteryVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20;   // Vin = 1/20
 | 
					        #ifdef VOLTAGE_SENSOR
 | 
				
			||||||
        sensors.batteryVoltage = sensors.batteryVoltage * vslope + voffset; // Calibration
 | 
					        float rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2;   // Vin = 1/2 for OBP40
 | 
				
			||||||
 | 
					        #else
 | 
				
			||||||
 | 
					        float rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20;   // Vin = 1/20 for OBP60    
 | 
				
			||||||
 | 
					        #endif
 | 
				
			||||||
 | 
					        sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
 | 
				
			||||||
 | 
					        #ifdef LIPO_ACCU_1200
 | 
				
			||||||
 | 
					        sensors.BatteryChargeStatus = 0;    // Set to discharging
 | 
				
			||||||
 | 
					        sensors.batteryLevelLiPo = 0;       // Level 0...100%
 | 
				
			||||||
 | 
					        #endif
 | 
				
			||||||
        sensors.batteryCurrent = 0;
 | 
					        sensors.batteryCurrent = 0;
 | 
				
			||||||
        sensors.batteryPower = 0;
 | 
					        sensors.batteryPower = 0;
 | 
				
			||||||
        // Fill average arrays with start values
 | 
					        // Fill average arrays with start values
 | 
				
			||||||
| 
						 | 
					@ -459,19 +467,61 @@ void sensorTask(void *param){
 | 
				
			||||||
        // Send supply voltage value all 1s
 | 
					        // Send supply voltage value all 1s
 | 
				
			||||||
        if(millis() > starttime5 + 1000 && String(powsensor1) == "off"){
 | 
					        if(millis() > starttime5 + 1000 && String(powsensor1) == "off"){
 | 
				
			||||||
            starttime5 = millis();
 | 
					            starttime5 = millis();
 | 
				
			||||||
            sensors.batteryVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20;   // Vin = 1/20
 | 
					            #ifdef VOLTAGE_SENSOR
 | 
				
			||||||
            sensors.batteryVoltage = sensors.batteryVoltage * vslope + voffset; // Calibration
 | 
					            float rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2;   // Vin = 1/2 for OBP40
 | 
				
			||||||
 | 
					            #else
 | 
				
			||||||
 | 
					            float rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20;   // Vin = 1/20 for OBP60    
 | 
				
			||||||
 | 
					            #endif
 | 
				
			||||||
 | 
					            sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
 | 
				
			||||||
            // Save new data in average array
 | 
					            // Save new data in average array
 | 
				
			||||||
            batV.reading(int(sensors.batteryVoltage * 100));
 | 
					            batV.reading(int(sensors.batteryVoltage * 100));
 | 
				
			||||||
            // Calculate the average values for different time lines from integer values
 | 
					            // Calculate the average values for different time lines from integer values
 | 
				
			||||||
            sensors.batteryVoltage10 = batV.getAvg(10) / 100.0;
 | 
					            sensors.batteryVoltage10 = batV.getAvg(10) / 100.0;
 | 
				
			||||||
            sensors.batteryVoltage60 = batV.getAvg(60) / 100.0;
 | 
					            sensors.batteryVoltage60 = batV.getAvg(60) / 100.0;
 | 
				
			||||||
            sensors.batteryVoltage300 = batV.getAvg(300) / 100.0;
 | 
					            sensors.batteryVoltage300 = batV.getAvg(300) / 100.0;
 | 
				
			||||||
 | 
					            #if defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR
 | 
				
			||||||
 | 
					            // Polynomfit for LiPo capacity calculation for 3,7V LiPo accus, 0...100%
 | 
				
			||||||
 | 
					            sensors.batteryLevelLiPo = sensors.batteryVoltage60 * 203.8312 -738.1635;
 | 
				
			||||||
 | 
					            // Limiter
 | 
				
			||||||
 | 
					            if(sensors.batteryLevelLiPo > 100){
 | 
				
			||||||
 | 
					                sensors.batteryLevelLiPo = 100;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if(sensors.batteryLevelLiPo < 0){
 | 
				
			||||||
 | 
					                sensors.batteryLevelLiPo = 0;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // Charging detection
 | 
				
			||||||
 | 
					            float deltaV = sensors.batteryVoltage - sensors.batteryVoltage10;
 | 
				
			||||||
 | 
					            // Higher limits for lower voltages
 | 
				
			||||||
 | 
					            if(sensors.batteryVoltage10 < 4.0){
 | 
				
			||||||
 | 
					                if(deltaV > 0.045 && deltaV < 4.15){
 | 
				
			||||||
 | 
					                    sensors.BatteryChargeStatus = 1;    // Charging active
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if(deltaV < -0.04 || deltaV >= 4.15){   // Charging stops by grater than 4,15V
 | 
				
			||||||
 | 
					                    sensors.BatteryChargeStatus = 0;    // Discharging
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // Lower limits for higher voltages
 | 
				
			||||||
 | 
					            else{
 | 
				
			||||||
 | 
					                if(deltaV > 0.03 && deltaV < 4.15){
 | 
				
			||||||
 | 
					                    sensors.BatteryChargeStatus = 1;    // Charging active
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if(deltaV < -0.03 || deltaV >= 4.15){   // Charging stops by grater than 4,15V
 | 
				
			||||||
 | 
					                    sensors.BatteryChargeStatus = 0;    // Discharging
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // Send to NMEA200 bus as instance 10
 | 
				
			||||||
 | 
					            if(!isnan(sensors.batteryVoltage)){
 | 
				
			||||||
 | 
					                SetN2kDCBatStatus(N2kMsg, 10, sensors.batteryVoltage, N2kDoubleNA, N2kDoubleNA, 0);
 | 
				
			||||||
 | 
					                api->sendN2kMessage(N2kMsg);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            #endif
 | 
				
			||||||
 | 
					            #ifdef BOARD_OBP60S3
 | 
				
			||||||
            // Send to NMEA200 bus
 | 
					            // Send to NMEA200 bus
 | 
				
			||||||
            if(!isnan(sensors.batteryVoltage)){
 | 
					            if(!isnan(sensors.batteryVoltage)){
 | 
				
			||||||
                SetN2kDCBatStatus(N2kMsg, 0, sensors.batteryVoltage, N2kDoubleNA, N2kDoubleNA, 1);
 | 
					                SetN2kDCBatStatus(N2kMsg, 0, sensors.batteryVoltage, N2kDoubleNA, N2kDoubleNA, 1);
 | 
				
			||||||
                api->sendN2kMessage(N2kMsg);
 | 
					                api->sendN2kMessage(N2kMsg);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            #endif
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Send data from environment sensor all 2s
 | 
					        // Send data from environment sensor all 2s
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -205,6 +205,18 @@ public:
 | 
				
			||||||
            getdisplay().setCursor(20, 100);
 | 
					            getdisplay().setCursor(20, 100);
 | 
				
			||||||
            getdisplay().print(name1);                           // Value name
 | 
					            getdisplay().print(name1);                           // Value name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            #if defined BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR
 | 
				
			||||||
 | 
					            // Show charge status
 | 
				
			||||||
 | 
					            getdisplay().setFont(&Ubuntu_Bold8pt7b);
 | 
				
			||||||
 | 
					            getdisplay().setCursor(185, 100);
 | 
				
			||||||
 | 
					            if(commonData->data.BatteryChargeStatus == true){
 | 
				
			||||||
 | 
					                getdisplay().print("Charge");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else{
 | 
				
			||||||
 | 
					                getdisplay().print("Discharge");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            #endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Show unit
 | 
					            // Show unit
 | 
				
			||||||
            getdisplay().setFont(&Ubuntu_Bold20pt7b);
 | 
					            getdisplay().setFont(&Ubuntu_Bold20pt7b);
 | 
				
			||||||
            getdisplay().setCursor(270, 100);
 | 
					            getdisplay().setCursor(270, 100);
 | 
				
			||||||
| 
						 | 
					@ -213,7 +225,12 @@ public:
 | 
				
			||||||
            // Show battery type
 | 
					            // Show battery type
 | 
				
			||||||
            getdisplay().setFont(&Ubuntu_Bold8pt7b);
 | 
					            getdisplay().setFont(&Ubuntu_Bold8pt7b);
 | 
				
			||||||
            getdisplay().setCursor(295, 100);
 | 
					            getdisplay().setCursor(295, 100);
 | 
				
			||||||
 | 
					            #ifdef BOARD_OBP60S3
 | 
				
			||||||
            getdisplay().print(batType);
 | 
					            getdisplay().print(batType);
 | 
				
			||||||
 | 
					            #endif
 | 
				
			||||||
 | 
					            #if defined BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR
 | 
				
			||||||
 | 
					            getdisplay().print("LiPo");
 | 
				
			||||||
 | 
					            #endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Show average settings
 | 
					            // Show average settings
 | 
				
			||||||
            printAvg(average, 320, 84, true);
 | 
					            printAvg(average, 320, 84, true);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,8 @@ typedef struct{
 | 
				
			||||||
  double batteryVoltage300 = 0; // Sliding average over 300 values
 | 
					  double batteryVoltage300 = 0; // Sliding average over 300 values
 | 
				
			||||||
  double batteryCurrent300 = 0;
 | 
					  double batteryCurrent300 = 0;
 | 
				
			||||||
  double batteryPower300 = 0;
 | 
					  double batteryPower300 = 0;
 | 
				
			||||||
 | 
					  double batteryLevelLiPo = 0;  // Battery level for OBP40 LiPo accu
 | 
				
			||||||
 | 
					  int BatteryChargeStatus = 0;  // LiPo charge status: 0 = discharge, 1 = loading activ
 | 
				
			||||||
  double solarVoltage = 0;
 | 
					  double solarVoltage = 0;
 | 
				
			||||||
  double solarCurrent = 0;
 | 
					  double solarCurrent = 0;
 | 
				
			||||||
  double solarPower = 0;
 | 
					  double solarPower = 0;
 | 
				
			||||||
| 
						 | 
					@ -93,6 +95,7 @@ typedef struct{
 | 
				
			||||||
  uint16_t fgcolor;
 | 
					  uint16_t fgcolor;
 | 
				
			||||||
  uint16_t bgcolor;
 | 
					  uint16_t bgcolor;
 | 
				
			||||||
  bool keylock = false;
 | 
					  bool keylock = false;
 | 
				
			||||||
 | 
					  String powermode;
 | 
				
			||||||
} CommonData;
 | 
					} CommonData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//a base class that all pages must inherit from
 | 
					//a base class that all pages must inherit from
 | 
				
			||||||
| 
						 | 
					@ -110,7 +113,7 @@ class Page{
 | 
				
			||||||
        commonData->keydata[2].label = "#LEFT";
 | 
					        commonData->keydata[2].label = "#LEFT";
 | 
				
			||||||
        commonData->keydata[3].label = "#RIGHT";
 | 
					        commonData->keydata[3].label = "#RIGHT";
 | 
				
			||||||
        commonData->keydata[4].label = "";
 | 
					        commonData->keydata[4].label = "";
 | 
				
			||||||
        if (commonData->backlight.mode == KEY) {
 | 
					        if ((commonData->backlight.mode == KEY) && !(commonData->powermode == "Min Power")) {
 | 
				
			||||||
            commonData->keydata[5].label = "ILUM";
 | 
					            commonData->keydata[5].label = "ILUM";
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            commonData->keydata[5].label = "";
 | 
					            commonData->keydata[5].label = "";
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -609,10 +609,10 @@
 | 
				
			||||||
        "label": "Undervoltage",
 | 
					        "label": "Undervoltage",
 | 
				
			||||||
        "type": "boolean",
 | 
					        "type": "boolean",
 | 
				
			||||||
        "default": "false",
 | 
					        "default": "false",
 | 
				
			||||||
        "description": "Switch off device if voltage drops below 9V [on|off]",
 | 
					        "description": "Switch off device if LiPo voltage drops below 3.65V [on|off]",
 | 
				
			||||||
        "category": "OBP40 Hardware",
 | 
					        "category": "OBP40 Hardware",
 | 
				
			||||||
        "capabilities": {
 | 
					        "capabilities": {
 | 
				
			||||||
            "obp40": "false"
 | 
					            "obp40":"true"
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -78,7 +78,7 @@ void OBP60Init(GwApi *api){
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #ifdef BOARD_OBP40S3
 | 
					    #ifdef BOARD_OBP40S3
 | 
				
			||||||
    //String sdcard = config->getConfigItem(config->useSDCard, true)->asString();
 | 
					//    String sdcard = config->getConfigItem(config->useSDCard, true)->asString();
 | 
				
			||||||
    String sdcard = "on";
 | 
					    String sdcard = "on";
 | 
				
			||||||
    if (sdcard == "on") {
 | 
					    if (sdcard == "on") {
 | 
				
			||||||
        SPIClass SD_SPI = SPIClass(HSPI);
 | 
					        SPIClass SD_SPI = SPIClass(HSPI);
 | 
				
			||||||
| 
						 | 
					@ -310,12 +310,39 @@ void registerAllPages(PageList &list){
 | 
				
			||||||
// Undervoltage detection for shutdown display
 | 
					// Undervoltage detection for shutdown display
 | 
				
			||||||
void underVoltageDetection(GwApi *api, CommonData &common){
 | 
					void underVoltageDetection(GwApi *api, CommonData &common){
 | 
				
			||||||
    // Read settings
 | 
					    // Read settings
 | 
				
			||||||
    float vslope = uint(api->getConfig()->getConfigItem(api->getConfig()->vSlope,true)->asFloat());
 | 
					    double voffset = (api->getConfig()->getConfigItem(api->getConfig()->vOffset,true)->asString()).toFloat();
 | 
				
			||||||
    float voffset = uint(api->getConfig()->getConfigItem(api->getConfig()->vOffset,true)->asFloat());
 | 
					    double vslope = (api->getConfig()->getConfigItem(api->getConfig()->vSlope,true)->asString()).toFloat();
 | 
				
			||||||
    // Read supply voltage
 | 
					    // Read supply voltage
 | 
				
			||||||
    float actVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20;   // V = 1/20 * Vin
 | 
					    #if defined VOLTAGE_SENSOR && defined LIPO_ACCU_1200
 | 
				
			||||||
    actVoltage = actVoltage * vslope + voffset;
 | 
					    float actVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2;   // Vin = 1/2 for OBP40
 | 
				
			||||||
    if(actVoltage < MIN_VOLTAGE){
 | 
					    float minVoltage = 3.65;  // Absolut minimum volatge for 3,7V LiPo accu
 | 
				
			||||||
 | 
					    #else
 | 
				
			||||||
 | 
					    float actVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20;   // Vin = 1/20 for OBP60
 | 
				
			||||||
 | 
					    float minVoltage = MIN_VOLTAGE;
 | 
				
			||||||
 | 
					    #endif
 | 
				
			||||||
 | 
					    double calVoltage = actVoltage * vslope + voffset;  // Calibration
 | 
				
			||||||
 | 
					    if(calVoltage < minVoltage){
 | 
				
			||||||
 | 
					        #if defined VOLTAGE_SENSOR && defined LIPO_ACCU_1200
 | 
				
			||||||
 | 
					        // Switch off all power lines
 | 
				
			||||||
 | 
					        setPortPin(OBP_BACKLIGHT_LED, false);   // Backlight Off
 | 
				
			||||||
 | 
					        setFlashLED(false);                     // Flash LED Off            
 | 
				
			||||||
 | 
					        buzzer(TONE4, 20);                      // Buzzer tone 4kHz 20ms
 | 
				
			||||||
 | 
					        // Shutdown EInk display
 | 
				
			||||||
 | 
					        getdisplay().setFullWindow();           // Set full Refresh
 | 
				
			||||||
 | 
					        //getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
 | 
				
			||||||
 | 
					        getdisplay().fillScreen(common.bgcolor);// Clear screen
 | 
				
			||||||
 | 
					        getdisplay().setTextColor(common.fgcolor);
 | 
				
			||||||
 | 
					        getdisplay().setFont(&Ubuntu_Bold20pt7b);
 | 
				
			||||||
 | 
					        getdisplay().setCursor(65, 150);
 | 
				
			||||||
 | 
					        getdisplay().print("Undervoltage");
 | 
				
			||||||
 | 
					        getdisplay().setFont(&Ubuntu_Bold8pt7b);
 | 
				
			||||||
 | 
					        getdisplay().setCursor(65, 175);
 | 
				
			||||||
 | 
					        getdisplay().print("Charge battery and restart system");
 | 
				
			||||||
 | 
					        getdisplay().nextPage();                // Partial update
 | 
				
			||||||
 | 
					        getdisplay().powerOff();                // Display power off
 | 
				
			||||||
 | 
					        setPortPin(OBP_POWER_EPD, false);       // Power off ePaper display
 | 
				
			||||||
 | 
					        setPortPin(OBP_POWER_SD, false);        // Power off SD card
 | 
				
			||||||
 | 
					        #else
 | 
				
			||||||
        // Switch off all power lines
 | 
					        // Switch off all power lines
 | 
				
			||||||
        setPortPin(OBP_BACKLIGHT_LED, false);   // Backlight Off
 | 
					        setPortPin(OBP_BACKLIGHT_LED, false);   // Backlight Off
 | 
				
			||||||
        setFlashLED(false);                     // Flash LED Off            
 | 
					        setFlashLED(false);                     // Flash LED Off            
 | 
				
			||||||
| 
						 | 
					@ -323,13 +350,17 @@ void underVoltageDetection(GwApi *api, CommonData &common){
 | 
				
			||||||
        setPortPin(OBP_POWER_50, false);        // Power rail 5.0V Off
 | 
					        setPortPin(OBP_POWER_50, false);        // Power rail 5.0V Off
 | 
				
			||||||
        // Shutdown EInk display
 | 
					        // Shutdown EInk display
 | 
				
			||||||
        getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
 | 
					        getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
 | 
				
			||||||
        getdisplay().fillScreen(common.bgcolor);       // Clear screen
 | 
					        getdisplay().fillScreen(common.bgcolor);// Clear screen
 | 
				
			||||||
        getdisplay().setTextColor(common.fgcolor);
 | 
					        getdisplay().setTextColor(common.fgcolor);
 | 
				
			||||||
        getdisplay().setFont(&Ubuntu_Bold20pt7b);
 | 
					        getdisplay().setFont(&Ubuntu_Bold20pt7b);
 | 
				
			||||||
        getdisplay().setCursor(65, 150);
 | 
					        getdisplay().setCursor(65, 150);
 | 
				
			||||||
        getdisplay().print("Undervoltage");
 | 
					        getdisplay().print("Undervoltage");
 | 
				
			||||||
 | 
					        getdisplay().setFont(&Ubuntu_Bold8pt7b);
 | 
				
			||||||
 | 
					        getdisplay().setCursor(65, 175);
 | 
				
			||||||
 | 
					        getdisplay().print("To wake up repower system");
 | 
				
			||||||
        getdisplay().nextPage();                // Partial update
 | 
					        getdisplay().nextPage();                // Partial update
 | 
				
			||||||
        getdisplay().powerOff();                // Display power off
 | 
					        getdisplay().powerOff();                // Display power off
 | 
				
			||||||
 | 
					        #endif
 | 
				
			||||||
        // Stop system
 | 
					        // Stop system
 | 
				
			||||||
        while(true){
 | 
					        while(true){
 | 
				
			||||||
            esp_deep_sleep_start();             // Deep Sleep without weakup. Weakup only after power cycle (restart).
 | 
					            esp_deep_sleep_start();             // Deep Sleep without weakup. Weakup only after power cycle (restart).
 | 
				
			||||||
| 
						 | 
					@ -382,7 +413,8 @@ void OBP60Task(GwApi *api){
 | 
				
			||||||
    String fastrefresh = api->getConfig()->getConfigItem(api->getConfig()->fastRefresh,true)->asString();
 | 
					    String fastrefresh = api->getConfig()->getConfigItem(api->getConfig()->fastRefresh,true)->asString();
 | 
				
			||||||
    uint fullrefreshtime = uint(api->getConfig()->getConfigItem(api->getConfig()->fullRefreshTime,true)->asInt());
 | 
					    uint fullrefreshtime = uint(api->getConfig()->getConfigItem(api->getConfig()->fullRefreshTime,true)->asInt());
 | 
				
			||||||
    #ifdef BOARD_OBP40S3
 | 
					    #ifdef BOARD_OBP40S3
 | 
				
			||||||
    bool syspage_enabled = config->getBool(config->systemPage);
 | 
					//    bool syspage_enabled = config->getBool(config->systemPage);
 | 
				
			||||||
 | 
					    bool syspage_enabled = true;
 | 
				
			||||||
    #endif
 | 
					    #endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #ifdef DISPLAY_GDEY042T81
 | 
					    #ifdef DISPLAY_GDEY042T81
 | 
				
			||||||
| 
						 | 
					@ -523,6 +555,7 @@ void OBP60Task(GwApi *api){
 | 
				
			||||||
    commonData.backlight.mode = backlightMapping(config->getConfigItem(config->backlight,true)->asString());
 | 
					    commonData.backlight.mode = backlightMapping(config->getConfigItem(config->backlight,true)->asString());
 | 
				
			||||||
    commonData.backlight.color = colorMapping(config->getConfigItem(config->blColor,true)->asString());
 | 
					    commonData.backlight.color = colorMapping(config->getConfigItem(config->blColor,true)->asString());
 | 
				
			||||||
    commonData.backlight.brightness = 2.55 * uint(config->getConfigItem(config->blBrightness,true)->asInt());
 | 
					    commonData.backlight.brightness = 2.55 * uint(config->getConfigItem(config->blBrightness,true)->asInt());
 | 
				
			||||||
 | 
					    commonData.powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode,true)->asString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool uvoltage = api->getConfig()->getConfigItem(api->getConfig()->underVoltage,true)->asBoolean();
 | 
					    bool uvoltage = api->getConfig()->getConfigItem(api->getConfig()->underVoltage,true)->asBoolean();
 | 
				
			||||||
    String cpuspeed = api->getConfig()->getConfigItem(api->getConfig()->cpuSpeed,true)->asString();
 | 
					    String cpuspeed = api->getConfig()->getConfigItem(api->getConfig()->cpuSpeed,true)->asString();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,10 @@
 | 
				
			||||||
#if you want a pio run to only build
 | 
					#if you want a pio run to only build
 | 
				
			||||||
#your special environments you can set this here
 | 
					#your special environments you can set this here
 | 
				
			||||||
#by uncommenting the next line
 | 
					#by uncommenting the next line
 | 
				
			||||||
default_envs = obp60_s3
 | 
					
 | 
				
			||||||
 | 
					default_envs = 
 | 
				
			||||||
 | 
					    obp60_s3
 | 
				
			||||||
 | 
					    obp40_s3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[env:obp60_s3]
 | 
					[env:obp60_s3]
 | 
				
			||||||
platform = espressif32@6.8.1
 | 
					platform = espressif32@6.8.1
 | 
				
			||||||
| 
						 | 
					@ -91,6 +94,8 @@ build_flags=
 | 
				
			||||||
    -D DISABLE_DIAGNOSTIC_OUTPUT    #Disable diagnostic output for GxEPD2 lib
 | 
					    -D DISABLE_DIAGNOSTIC_OUTPUT    #Disable diagnostic output for GxEPD2 lib
 | 
				
			||||||
    -D BOARD_OBP40S3                #Board OBP40 V1.0 with ESP32S3 SKU:DIE07300S (CrowPanel 4.2)
 | 
					    -D BOARD_OBP40S3                #Board OBP40 V1.0 with ESP32S3 SKU:DIE07300S (CrowPanel 4.2)
 | 
				
			||||||
    -D DISPLAY_GDEY042T81           #new E-Ink display from Waveshare, R10 2.2 ohm
 | 
					    -D DISPLAY_GDEY042T81           #new E-Ink display from Waveshare, R10 2.2 ohm
 | 
				
			||||||
 | 
					    -D LIPO_ACCU_1200               #Hardware extension, LiPo accu 3,7V 1200mAh
 | 
				
			||||||
 | 
					    -D VOLTAGE_SENSOR               #Hardware extension, LiPo voltage sensor with two resistors 
 | 
				
			||||||
    ${env.build_flags}
 | 
					    ${env.build_flags}
 | 
				
			||||||
upload_port = /dev/ttyUSB0          #OBP40 download via external USB/Serail converter
 | 
					upload_port = /dev/ttyUSB0          #OBP40 download via external USB/Serail converter
 | 
				
			||||||
upload_protocol = esptool           #firmware upload via USB OTG seriell, by first upload need to set the ESP32-S3 in the upload mode with shortcut GND to Pin27
 | 
					upload_protocol = esptool           #firmware upload via USB OTG seriell, by first upload need to set the ESP32-S3 in the upload mode with shortcut GND to Pin27
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue