1
0
mirror of https://github.com/thooge/esp32-nmea2000-obp60.git synced 2025-12-16 15:33:05 +01:00

61 Commits

Author SHA1 Message Date
a8cf34343f Config data for tracker page 2025-09-09 15:32:56 +02:00
c00a0ecbed Prepared page tracker 2025-09-07 08:50:33 +02:00
a16ee74b32 Integrate master branch changes 2025-08-31 14:17:39 +02:00
601d56ee49 Calibration feature switchable as compiler option,
new page code example by page windrose
2025-08-29 19:21:29 +02:00
9a04029cb4 Add webserver options to platformio.ini 2025-08-26 17:45:29 +02:00
22e3ca3875 Switch to current webserver version and some small fixes 2025-08-26 17:17:48 +02:00
8e72537286 Improve task start code for sensors and spiled 2025-08-26 12:23:39 +02:00
494acbf0d0 Work on system page 2025-08-25 20:56:14 +02:00
d7251eeb8a Code cleanup for keyboard task 2025-08-25 20:47:42 +02:00
c4406fd009 Preapared AIS target list in boatdata 2025-08-24 08:32:43 +02:00
0097a1eb1e Active data display in page skyview 2025-08-23 08:45:35 +02:00
c932724473 More work on formatter 2025-08-22 15:17:54 +02:00
672a3843d1 Some code cleanup 2025-08-22 14:31:48 +02:00
1e0acf2a8f Improve and speedup undervoltage detection code 2025-08-22 14:20:55 +02:00
63ae42588f Use placeholder from formatter for missing data text 2025-08-22 14:13:35 +02:00
cba21574cb Move formatValue to formatter class 2025-08-21 12:34:17 +02:00
fbe6c1a9a5 Prepared formatter class for boat values. E.g. for better config handling 2025-08-21 10:34:58 +02:00
ae9334236b Implemented string boatdata value WPName for next waypoint 2025-08-20 21:33:08 +02:00
318a218470 Integrate changes from master 2025-08-19 09:11:51 +02:00
c6df6eac56 Adapt barograph page to current code 2025-08-15 10:46:59 +02:00
4513f9d7b0 Merge branch 'barograph' into extended 2025-08-15 09:46:12 +02:00
2749f25d15 Added page method leavePage() for e.g. cleanup and storage code 2025-08-14 15:11:13 +02:00
992348ce92 Code cleanup 2025-08-14 12:50:18 +02:00
8695d3eeb5 Move config code into page constructors and some code cleanup 2025-08-14 09:12:56 +02:00
54b4954797 Fixed SD card initialization and added some code to system page 2025-08-13 17:41:06 +02:00
1ada6e5a82 Code improvements: commonData, config and logger in page class 2025-08-12 21:35:52 +02:00
15ca3614ba Include uptime in system page 2025-08-12 13:29:36 +02:00
e5ca2a3a3d More work on system page 2025-08-12 09:57:52 +02:00
cf305e9f5a NMEA2000 access for system page 2025-08-06 15:21:29 +02:00
f823dadc6b Moved to c++17 and espressif32 @ 6.9.0, fixed waypoint boatdata code 2025-08-06 14:05:58 +02:00
2d47702627 Added waypoint name to boatdata 2025-08-06 12:03:00 +02:00
15674543b6 Some modifications to avoid AP connection problems 2025-08-06 11:36:05 +02:00
0dd6b7c9cf Preparation for upcoming new pages 2025-08-05 15:31:41 +02:00
cbc0c09d65 Tool for creating JSON from configuration file 2025-08-05 12:07:07 +02:00
1d05c2b5d4 Added INI style device configuration files 2025-08-05 11:21:57 +02:00
38bdc4f577 Switch to xbm image logo 2025-08-05 09:18:54 +02:00
b8a31f2280 Finalize switch to epd pointer instead if getdisplay() 2025-08-05 08:52:45 +02:00
7cff3e62e6 Integrate some SD card code 2025-08-05 08:13:04 +02:00
39966098cc Switched obp60task.cpp to epd pointer 2025-08-04 21:25:01 +02:00
11a061d5a2 Consolidating development hints in README file 2025-08-04 21:13:40 +02:00
4600cb7228 Start switching from getdisplay to epd pointer 2025-08-04 20:57:50 +02:00
e89d1ac62e Merge branch 'clock' into extended 2025-08-04 15:35:13 +02:00
6fb5e01969 Preparation for new anchor page 2025-08-04 15:28:49 +02:00
5c731bc662 Fixed config.json 2025-08-04 14:36:41 +02:00
25c2742f5b Merge branch 'symbols' into extended 2025-08-04 14:14:27 +02:00
6479c7d711 Fixed SkyView: return value needed, enabled for OBP40 2025-08-04 13:47:50 +02:00
e287bfa72e Merge branch 'skyview' into extended 2025-08-04 13:16:57 +02:00
5a652e2c19 Merge branch 'precision' into extended 2025-08-04 12:58:42 +02:00
5081f3a684 Merge branch 'scripts' into extended 2025-08-04 12:57:57 +02:00
7de226b447 Merge branch 'system' into extended 2025-08-04 12:39:54 +02:00
f6e2fa7806 Mark extended firmware and prepare for getdisplay() replacement 2025-08-04 12:37:50 +02:00
d5b0af3b19 Integrated config menu into system page 2025-08-03 21:18:08 +02:00
aed389f3b2 Added code for configuration menu handling 2025-08-01 17:08:01 +02:00
32ba4a64ed Add friendly color names to color class for system page display 2025-07-31 14:23:11 +02:00
thooge
7edd201daa Merge branch 'norbert-walter:master' into system 2025-07-31 13:20:39 +02:00
eb51092b23 Added config option for display precision and formatter code improvements 2025-07-31 12:31:57 +02:00
f4d88f1b8b Code rework at page system. Preparation for config menu. 2025-07-31 11:39:23 +02:00
08cd2ad4b1 Some work on page clock concerning default time source 2025-07-09 15:07:52 +02:00
b50d82eff0 Header switcheable between text and symbol view, symbols added 2025-02-06 17:51:26 +01:00
6ae4e195d6 First template for sky view page 2025-01-21 20:48:43 +01:00
c5e346f4eb First design experiments for barograph 2024-12-21 10:25:53 +01:00
93 changed files with 11615 additions and 8823 deletions

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@
generated/* generated/*
lib/generated lib/generated
webinstall/token.php webinstall/token.php
*~

View File

@@ -205,6 +205,11 @@ def generateCfg(inFile,outFile,impl):
secret="false"; secret="false";
if item.get('type') == 'password': if item.get('type') == 'password':
secret="true" secret="true"
"""
PSRAM Allocator TODO Tests
new (heap_caps_malloc(sizeof(GwConfigInterface), MALLOC_CAP_SPIRAM))
"""
#data+=" new (heap_caps_malloc(sizeof(GwConfigInterface), MALLOC_CAP_SPIRAM)) GwConfigInterface(%s,\"%s\",%s);\n"%(name,item.get('default'),secret)
data+=" new GwConfigInterface(%s,\"%s\",%s);\n"%(name,item.get('default'),secret) data+=" new GwConfigInterface(%s,\"%s\",%s);\n"%(name,item.get('default'),secret)
data+='}\n' data+='}\n'
writeFileIfChanged(outFile,data) writeFileIfChanged(outFile,data)
@@ -501,10 +506,12 @@ def prebuild(env):
genereateUserTasks(os.path.join(outPath(), TASK_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(),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') 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") version = "dev{}{}".format(datetime.now().strftime("%Y%m%d"), "-ext")
env.Append(CPPDEFINES=[('GWDEVVERSION',version)]) env.Append(CPPDEFINES=[('GWDEVVERSION',version)])
def cleangenerated(source, target, env): def cleangenerated(source, target, env):
# TODO source / target order?
print("CLEAN: {} - {}".format(source, target))
od=outPath() od=outPath()
if os.path.isdir(od): if os.path.isdir(od):
print("#cleaning up %s"%od) print("#cleaning up %s"%od)
@@ -514,7 +521,6 @@ def cleangenerated(source, target, env):
fn=os.path.join(od,f) fn=os.path.join(od,f)
os.unlink(f) os.unlink(f)
print("#prescript...") print("#prescript...")
prebuild(env) prebuild(env)
board="PLATFORM_BOARD_%s"%env["BOARD"].replace("-","_").upper() board="PLATFORM_BOARD_%s"%env["BOARD"].replace("-","_").upper()

View File

@@ -1,542 +0,0 @@
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
from platformio.project.exception import InvalidProjectConfError
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):
try:
customconfig = env.GetProjectOption("custom_config")
except InvalidProjectConfError:
customconfig = None
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")
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 != 'Z' else ''
def _suffix(self):
return '_'+self.name if self.name != '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)
#look for extra task scripts and include them here
for taskdir in userTaskDirs:
script = os.path.join(taskdir, "extra_task.py")
if os.path.isfile(script):
taskname = os.path.basename(os.path.normpath(taskdir))
print("#extra task script for '{}'".format(taskname))
with open(script) as fh:
try:
code = compile(fh.read(), taskname, 'exec')
except SyntaxError:
print("#ERROR: script does not compile")
continue
exec(code)

View File

@@ -1,518 +0,0 @@
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)

View File

@@ -2,6 +2,7 @@
#define _GWAPI_H #define _GWAPI_H
#include "GwMessage.h" #include "GwMessage.h"
#include "N2kMsg.h" #include "N2kMsg.h"
#include "Nmea2kTwai.h"
#include "NMEA0183Msg.h" #include "NMEA0183Msg.h"
#include "GWConfig.h" #include "GWConfig.h"
#include "GwBoatData.h" #include "GwBoatData.h"
@@ -23,6 +24,7 @@ class GwApi{
bool formatSet=false; bool formatSet=false;
public: public:
double value=0; double value=0;
String svalue="";
bool valid=false; bool valid=false;
int source=-1; int source=-1;
bool changed=false; //will be set by getBoatDataValues bool changed=false; //will be set by getBoatDataValues
@@ -222,6 +224,7 @@ class GwApi{
* accessing boat data must only be executed from within the main thread * accessing boat data must only be executed from within the main thread
* you need to use the request pattern as shown in GwExampleTask.cpp * you need to use the request pattern as shown in GwExampleTask.cpp
*/ */
virtual Nmea2kTwai *getNMEA2000()=0;
virtual GwBoatData *getBoatData()=0; virtual GwBoatData *getBoatData()=0;
virtual ~GwApi(){} virtual ~GwApi(){}
}; };

View File

@@ -2,11 +2,6 @@
#include <GwJsonDocument.h> #include <GwJsonDocument.h>
#include <ArduinoJson/Json/TextFormatter.hpp> #include <ArduinoJson/Json/TextFormatter.hpp>
#include "GWConfig.h" #include "GWConfig.h"
#define GWTYPE_DOUBLE 1
#define GWTYPE_UINT32 2
#define GWTYPE_UINT16 3
#define GWTYPE_INT16 4
#define GWTYPE_USER 100
class GwBoatItemTypes class GwBoatItemTypes
{ {
@@ -15,7 +10,9 @@ public:
static int getType(const uint16_t &x) { return GWTYPE_UINT16; } static int getType(const uint16_t &x) { return GWTYPE_UINT16; }
static int getType(const int16_t &x) { return GWTYPE_INT16; } static int getType(const int16_t &x) { return GWTYPE_INT16; }
static int getType(const double &x) { return GWTYPE_DOUBLE; } static int getType(const double &x) { return GWTYPE_DOUBLE; }
static int getType(const String &x) { return GWTYPE_STRING; }
static int getType(const GwSatInfoList &x) { return GWTYPE_USER + 1; } static int getType(const GwSatInfoList &x) { return GWTYPE_USER + 1; }
static int getType(const GwAisTargetList &x) { return GWTYPE_USER + 1; }
}; };
bool GwBoatItemBase::isValid(unsigned long now) const bool GwBoatItemBase::isValid(unsigned long now) const
@@ -252,6 +249,10 @@ static void writeToString(GwTextWriter *writer, const int16_t &value)
{ {
writer->writeInteger(value); writer->writeInteger(value);
} }
static void writeToString(GwTextWriter *writer, String value)
{
writer->writeString(value.c_str());
}
static void writeToString(GwTextWriter *writer, GwSatInfoList &value) static void writeToString(GwTextWriter *writer, GwSatInfoList &value)
{ {
writer->writeInteger(value.getNumSats()); writer->writeInteger(value.getNumSats());
@@ -288,6 +289,8 @@ template class GwBoatItem<double>;
template class GwBoatItem<uint32_t>; template class GwBoatItem<uint32_t>;
template class GwBoatItem<uint16_t>; template class GwBoatItem<uint16_t>;
template class GwBoatItem<int16_t>; template class GwBoatItem<int16_t>;
template class GwBoatItem<String>;
void GwSatInfoList::houseKeeping(unsigned long ts) void GwSatInfoList::houseKeeping(unsigned long ts)
{ {
if (ts == 0) if (ts == 0)
@@ -301,6 +304,7 @@ void GwSatInfoList::houseKeeping(unsigned long ts)
}), }),
sats.end()); sats.end());
} }
void GwSatInfoList::update(GwSatInfo entry, unsigned long validTill) void GwSatInfoList::update(GwSatInfo entry, unsigned long validTill)
{ {
entry.validTill = validTill; entry.validTill = validTill;
@@ -343,6 +347,63 @@ void GwBoatDataSatList::toJsonDoc(GwJsonDocument *doc, unsigned long minTime)
GwBoatItem<GwSatInfoList>::toJsonDoc(doc, minTime); GwBoatItem<GwSatInfoList>::toJsonDoc(doc, minTime);
} }
void GwAisTargetList::houseKeeping(unsigned long ts)
{
if (ts == 0) {
ts = millis();
}
targets.erase(
std::remove_if(
targets.begin(),
targets.end(),
[ts, this](const GwAisTarget &target) {
return target.validTill < ts;
}
),
targets.end()
);
}
void GwAisTargetList::update(GwAisTarget target, unsigned long validTill)
{
target.validTill = validTill;
for (auto it = targets.begin(); it != targets.end(); it++) {
if (it->mmsi == target.mmsi) {
*it = target;
houseKeeping();
return;
}
}
houseKeeping();
targets.push_back(target);
}
GwBoatDataAisList::GwBoatDataAisList(String name, String formatInfo, GwBoatItemBase::TOType toType, GwBoatItemMap *map) : GwBoatItem<GwAisTargetList>(name, formatInfo, toType, map) {}
bool GwBoatDataAisList::update(GwAisTarget target, int source)
{
unsigned long now = millis();
if (isValid(now))
{
//priority handling
//sources with lower ids will win
//and we will not overwrite their value
if (lastUpdateSource < source)
{
return false;
}
}
lastUpdateSource = source;
uls(now);
data.update(target, now+invalidTime);
return true;
}
void GwBoatDataAisList::toJsonDoc(GwJsonDocument *doc, unsigned long minTime)
{
data.houseKeeping();
GwBoatItem<GwAisTargetList>::toJsonDoc(doc, minTime);
}
GwBoatData::GwBoatData(GwLog *logger, GwConfigHandler *cfg) GwBoatData::GwBoatData(GwLog *logger, GwConfigHandler *cfg)
{ {
this->logger = logger; this->logger = logger;
@@ -512,6 +573,11 @@ bool convertToJson(const GwSatInfoList &si, JsonVariant &variant)
return variant.set(si.getNumSats()); return variant.set(si.getNumSats());
} }
bool convertToJson(const GwAisTargetList &si, JsonVariant &variant)
{
return variant.set(si.getNumTargets());
}
#ifdef _UNDEF #ifdef _UNDEF
#include <ArduinoJson/Json/TextFormatter.hpp> #include <ArduinoJson/Json/TextFormatter.hpp>

View File

@@ -9,6 +9,13 @@
#define GW_BOAT_VALUE_LEN 32 #define GW_BOAT_VALUE_LEN 32
#define GWSC(name) static constexpr const char* name=#name #define GWSC(name) static constexpr const char* name=#name
#define GWTYPE_DOUBLE 1
#define GWTYPE_UINT32 2
#define GWTYPE_UINT16 3
#define GWTYPE_INT16 4
#define GWTYPE_STRING 5
#define GWTYPE_USER 100
//see https://github.com/wellenvogel/esp32-nmea2000/issues/44 //see https://github.com/wellenvogel/esp32-nmea2000/issues/44
//factor to convert from N2k/SI rad/s to current NMEA rad/min //factor to convert from N2k/SI rad/s to current NMEA rad/min
#define ROT_WA_FACTOR 60 #define ROT_WA_FACTOR 60
@@ -58,6 +65,7 @@ class GwBoatItemBase{
GWSC(formatRot); GWSC(formatRot);
GWSC(formatDate); GWSC(formatDate);
GWSC(formatTime); GWSC(formatTime);
GWSC(formatName);
protected: protected:
int type; int type;
unsigned long lastSet=0; unsigned long lastSet=0;
@@ -92,6 +100,7 @@ class GwBoatItemBase{
virtual int getLastSource(){return lastUpdateSource;} virtual int getLastSource(){return lastUpdateSource;}
virtual void refresh(unsigned long ts=0){uls(ts);} virtual void refresh(unsigned long ts=0){uls(ts);}
virtual double getDoubleValue()=0; virtual double getDoubleValue()=0;
virtual String getStringValue()=0;
String getName(){return name;} String getName(){return name;}
const String & getFormat() const{return format;} const String & getFormat() const{return format;}
virtual void setInvalidTime(GwConfigHandler *cfg); virtual void setInvalidTime(GwConfigHandler *cfg);
@@ -120,7 +129,17 @@ template<class T> class GwBoatItem : public GwBoatItemBase{
if (! isValid(millis())) return defaultv; if (! isValid(millis())) return defaultv;
return data; return data;
} }
virtual double getDoubleValue(){return (double)data;} virtual double getDoubleValue(){
if constexpr (std::is_same<T, String>::value) {
return 0.0; // TODO any better ideas?
} else {
return (double)data;
}
}
virtual String getStringValue(){
return (String)data;
}
virtual void fillString(); virtual void fillString();
virtual void toJsonDoc(GwJsonDocument *doc, unsigned long minTime); virtual void toJsonDoc(GwJsonDocument *doc, unsigned long minTime);
virtual int getLastSource(){return lastUpdateSource;} virtual int getLastSource(){return lastUpdateSource;}
@@ -177,6 +196,55 @@ public:
}; };
class GwAisTarget {
public:
uint32_t mmsi;
char callsign[8];
char name[21];
uint8_t vesseltype;
double lat;
double lon;
float length;
float beam;
float sog;
float cog;
unsigned long validTill;
};
class GwAisTargetList {
public:
static const GwBoatItemBase::TOType toType=GwBoatItemBase::TOType::ais;
std::vector<GwAisTarget> targets;
void houseKeeping(unsigned long ts=0);
void update(GwAisTarget target, unsigned long validTill);
int getNumTargets() const {
return targets.size();
}
GwAisTarget *getAt(int idx){
if (idx >= 0 && idx < targets.size()) return &targets.at(idx);
return NULL;
}
operator double(){ return getNumTargets();}
};
class GwBoatDataAisList : public GwBoatItem<GwAisTargetList> {
public:
GwBoatDataAisList(String name, String formatInfo, GwBoatItemBase::TOType toType, GwBoatItemMap *map = NULL);
bool update(GwAisTarget target, int source);
virtual void toJsonDoc(GwJsonDocument *doc, unsigned long minTime);
GwAisTarget *getAt(int idx) {
if (! isValid()) return NULL;
return data.getAt(idx);
}
int getNumTargets(){
if (! isValid()) return 0;
return data.getNumTargets();
}
virtual double getDoubleValue(){
return (double)(data.getNumTargets());
}
};
class GwBoatItemNameProvider class GwBoatItemNameProvider
{ {
public: public:
@@ -235,7 +303,9 @@ class GwBoatData{
GWBOATDATA(double,XTE,formatXte) // cross track error GWBOATDATA(double,XTE,formatXte) // cross track error
GWBOATDATA(double,WPLat,formatLatitude) // waypoint latitude GWBOATDATA(double,WPLat,formatLatitude) // waypoint latitude
GWBOATDATA(double,WPLon,formatLongitude) // waypoint longitude GWBOATDATA(double,WPLon,formatLongitude) // waypoint longitude
GWBOATDATA(String,WPName,formatName) // waypoint name
GWSPECBOATDATA(GwBoatDataSatList,SatInfo,GwSatInfoList::toType,formatFixed0); GWSPECBOATDATA(GwBoatDataSatList,SatInfo,GwSatInfoList::toType,formatFixed0);
GWSPECBOATDATA(GwBoatDataAisList,AisTarget,GwAisTargetList::toType,formatFixed0);
public: public:
GwBoatData(GwLog *logger, GwConfigHandler *cfg); GwBoatData(GwLog *logger, GwConfigHandler *cfg);
~GwBoatData(); ~GwBoatData();

View File

@@ -79,7 +79,7 @@ GwUpdate::GwUpdate(GwLog *log, GwWebServer *webserver, PasswordChecker ckr)
} }
if (!param->hasError()) if (!param->hasError())
{ {
AsyncWebParameter *hash=request->getParam("_hash"); const AsyncWebParameter *hash=request->getParam("_hash");
if (! hash){ if (! hash){
hash=request->getParam("_hash",true); hash=request->getParam("_hash",true);
} }
@@ -141,4 +141,4 @@ GwUpdate::GwUpdate(GwLog *log, GwWebServer *webserver, PasswordChecker ckr)
} }
} }
}); });
} }

View File

@@ -27,7 +27,7 @@ void sendEmbeddedFile(String name,String contentType,AsyncWebServerRequest *requ
std::map<String,EmbeddedFile*>::iterator it=embeddedFiles.find(name); std::map<String,EmbeddedFile*>::iterator it=embeddedFiles.find(name);
if (it != embeddedFiles.end()){ if (it != embeddedFiles.end()){
EmbeddedFile* found=it->second; EmbeddedFile* found=it->second;
AsyncWebServerResponse *response=request->beginResponse_P(200,contentType,found->start,found->len); AsyncWebServerResponse *response=request->beginResponse(200, contentType, found->start, found->len);
response->addHeader(F("Content-Encoding"), F("gzip")); response->addHeader(F("Content-Encoding"), F("gzip"));
request->send(response); request->send(response);
} }

View File

@@ -355,6 +355,7 @@ private:
AppendN2kRouteWPInfo(n2kMsg,destinationId,rmb.destID,rmb.latitude,rmb.longitude); AppendN2kRouteWPInfo(n2kMsg,destinationId,rmb.destID,rmb.latitude,rmb.longitude);
send(n2kMsg,msg.sourceId); send(n2kMsg,msg.sourceId);
} }
boatData->WPName->update(String(rmb.destID), msg.sourceId);
} }
void convertRMC(const SNMEA0183Msg &msg) void convertRMC(const SNMEA0183Msg &msg)
{ {

View File

@@ -38,7 +38,7 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger)
instance = std::string(config->getString(calInstance, "---").c_str()); instance = std::string(config->getString(calInstance, "---").c_str());
if (instance == "---") { if (instance == "---") {
LOG_DEBUG(GwLog::LOG, "no calibration data for instance no. %d", i + 1); logger->logDebug(GwLog::LOG, "no calibration data for instance no. %d", i + 1);
continue; continue;
} }
calibMap[instance] = { 0.0f, 1.0f, 1.0f, 0.0f, false }; calibMap[instance] = { 0.0f, 1.0f, 1.0f, 0.0f, false };
@@ -101,10 +101,10 @@ void CalibrationDataList::readConfig(GwConfigHandler* config, GwLog* logger)
calibMap[instance].slope = slope; calibMap[instance].slope = slope;
calibMap[instance].smooth = smooth; calibMap[instance].smooth = smooth;
calibMap[instance].isCalibrated = false; calibMap[instance].isCalibrated = false;
LOG_DEBUG(GwLog::LOG, "stored calibration data: %s, offset: %f, slope: %f, smoothing: %f", instance.c_str(), logger->logDebug(GwLog::LOG, "calibration data: %s, offset: %f, slope: %f, smoothing: %f", instance.c_str(),
calibMap[instance].offset, calibMap[instance].slope, calibMap[instance].smooth); calibMap[instance].offset, calibMap[instance].slope, calibMap[instance].smooth);
} }
LOG_DEBUG(GwLog::LOG, "all calibration data read"); logger->logDebug(GwLog::LOG, "all calibration data read");
} }
void CalibrationDataList::calibrateInstance(GwApi::BoatValue* boatDataValue, GwLog* logger) void CalibrationDataList::calibrateInstance(GwApi::BoatValue* boatDataValue, GwLog* logger)
@@ -117,7 +117,7 @@ void CalibrationDataList::calibrateInstance(GwApi::BoatValue* boatDataValue, GwL
std::string format = ""; std::string format = "";
if (calibMap.find(instance) == calibMap.end()) { if (calibMap.find(instance) == calibMap.end()) {
LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s not found in calibration data list", instance.c_str()); logger->logDebug(GwLog::DEBUG, "BoatDataCalibration: %s not in calibration list", instance.c_str());
return; return;
} else if (!boatDataValue->valid) { // no valid boat data value, so we don't want to apply calibration data } else if (!boatDataValue->valid) { // no valid boat data value, so we don't want to apply calibration data
calibMap[instance].isCalibrated = false; calibMap[instance].isCalibrated = false;
@@ -127,7 +127,7 @@ void CalibrationDataList::calibrateInstance(GwApi::BoatValue* boatDataValue, GwL
slope = calibMap[instance].slope; slope = calibMap[instance].slope;
dataValue = boatDataValue->value; dataValue = boatDataValue->value;
format = boatDataValue->getFormat().c_str(); format = boatDataValue->getFormat().c_str();
LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: value: %f, format: %s", instance.c_str(), dataValue, format.c_str()); logger->logDebug(GwLog::DEBUG, "BoatDataCalibration: %s: value: %f, format: %s", instance.c_str(), dataValue, format.c_str());
if (format == "formatWind") { // instance is of type angle if (format == "formatWind") { // instance is of type angle
dataValue = (dataValue * slope) + offset; dataValue = (dataValue * slope) + offset;
@@ -156,7 +156,7 @@ void CalibrationDataList::calibrateInstance(GwApi::BoatValue* boatDataValue, GwL
calibrationData.smoothInstance(boatDataValue, logger); // smooth the boat data value calibrationData.smoothInstance(boatDataValue, logger); // smooth the boat data value
calibMap[instance].value = boatDataValue->value; // store the calibrated + smoothed value in the list calibMap[instance].value = boatDataValue->value; // store the calibrated + smoothed value in the list
LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: Offset: %f, Slope: %f, Result: %f", instance.c_str(), offset, slope, calibMap[instance].value); logger->logDebug(GwLog::DEBUG, "BoatDataCalibration: %s: Offset: %f, Slope: %f, Result: %f", instance.c_str(), offset, slope, calibMap[instance].value);
} }
} }
@@ -173,7 +173,7 @@ void CalibrationDataList::smoothInstance(GwApi::BoatValue* boatDataValue, GwLog*
if (!boatDataValue->valid) { // no valid boat data value, so we don't want to smoothen value if (!boatDataValue->valid) { // no valid boat data value, so we don't want to smoothen value
return; return;
} else if (calibMap.find(instance) == calibMap.end()) { } else if (calibMap.find(instance) == calibMap.end()) {
LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: smooth factor for %s not found in calibration data list", instance.c_str()); logger->logDebug(GwLog::DEBUG, "BoatDataCalibration: smooth factor for %s not found in calibration list", instance.c_str());
return; return;
} else { } else {
smoothFactor = calibMap[instance].smooth; smoothFactor = calibMap[instance].smooth;
@@ -184,9 +184,7 @@ void CalibrationDataList::smoothInstance(GwApi::BoatValue* boatDataValue, GwLog*
} }
lastValue[instance] = dataValue; // store the new value for next cycle; first time, store only the current value and return lastValue[instance] = dataValue; // store the new value for next cycle; first time, store only the current value and return
boatDataValue->value = dataValue; // set the smoothed value to the boat data value boatDataValue->value = dataValue; // set the smoothed value to the boat data value
LOG_DEBUG(GwLog::DEBUG, "BoatDataCalibration: %s: Smoothing factor: %f, Smoothed value: %f", instance.c_str(), smoothFactor, dataValue);
} }
} }
#endif #endif

View File

@@ -3,7 +3,7 @@
#ifndef _BOATDATACALIBRATION_H #ifndef _BOATDATACALIBRATION_H
#define _BOATDATACALIBRATION_H #define _BOATDATACALIBRATION_H
#include "Pagedata.h" #include "GwApi.h"
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@@ -30,4 +30,4 @@ private:
extern CalibrationDataList calibrationData; // this list holds all calibration data extern CalibrationDataList calibrationData; // this list holds all calibration data
#endif #endif

View File

@@ -0,0 +1,204 @@
/*
Menu system for online configuration
*/
#include "ConfigMenu.h"
ConfigMenuItem::ConfigMenuItem(String itemtype, String itemlabel, uint16_t itemval, String itemunit) {
if (! (itemtype == "int" or itemtype == "bool")) {
valtype = "int";
} else {
valtype = itemtype;
}
label = itemlabel;
min = 0;
max = std::numeric_limits<uint16_t>::max();
value = itemval;
unit = itemunit;
}
void ConfigMenuItem::setRange(uint16_t valmin, uint16_t valmax, std::vector<uint16_t> valsteps) {
min = valmin;
max = valmax;
steps = valsteps;
};
bool ConfigMenuItem::checkRange(uint16_t checkval) {
return (checkval >= min) and (checkval <= max);
}
String ConfigMenuItem::getLabel() {
return label;
};
uint16_t ConfigMenuItem::getValue() {
return value;
}
bool ConfigMenuItem::setValue(uint16_t newval) {
if (valtype == "int") {
if (newval >= min and newval <= max) {
value = newval;
return true;
}
return false; // out of range
} else if (valtype == "bool") {
value = (newval != 0) ? 1 : 0;
return true;
}
return false; // invalid type
};
void ConfigMenuItem::incValue() {
// increase value by step
if (valtype == "int") {
if (value + step < max) {
value += step;
} else {
value = max;
}
} else if (valtype == "bool") {
value = !value;
}
};
void ConfigMenuItem::decValue() {
// decrease value by step
if (valtype == "int") {
if (value - step > min) {
value -= step;
} else {
value = min;
}
} else if (valtype == "bool") {
value = !value;
}
};
String ConfigMenuItem::getUnit() {
return unit;
}
uint16_t ConfigMenuItem::getStep() {
return step;
}
void ConfigMenuItem::setStep(uint16_t newstep) {
if (std::find(steps.begin(), steps.end(), newstep) == steps.end()) {
return; // invalid step: not in list of possible steps
}
step = newstep;
}
int8_t ConfigMenuItem::getPos() {
return position;
};
void ConfigMenuItem::setPos(int8_t newpos) {
position = newpos;
};
String ConfigMenuItem::getType() {
return valtype;
}
ConfigMenu::ConfigMenu(String menutitle, uint16_t menu_x, uint16_t menu_y) {
title = menutitle;
x = menu_x;
y = menu_y;
};
ConfigMenuItem* ConfigMenu::addItem(String key, String label, String valtype, uint16_t val, String valunit) {
if (items.find(key) != items.end()) {
// duplicate keys not allowed
return nullptr;
}
ConfigMenuItem *itm = new ConfigMenuItem(valtype, label, val, valunit);
items.insert(std::pair<String, ConfigMenuItem*>(key, itm));
// Append key to index, index starting with 0
int8_t ix = items.size() - 1;
index[ix] = key;
itm->setPos(ix);
return itm;
};
void ConfigMenu::setItemDimension(uint16_t itemwidth, uint16_t itemheight) {
w = itemwidth;
h = itemheight;
};
void ConfigMenu::setItemActive(String key) {
if (items.find(key) != items.end()) {
activeitem = items[key]->getPos();
} else {
activeitem = -1;
}
};
int8_t ConfigMenu::getActiveIndex() {
return activeitem;
}
ConfigMenuItem* ConfigMenu::getActiveItem() {
if (activeitem < 0) {
return nullptr;
}
return items[index[activeitem]];
};
ConfigMenuItem* ConfigMenu::getItemByIndex(uint8_t ix) {
if (ix > index.size() - 1) {
return nullptr;
}
return items[index[ix]];
};
ConfigMenuItem* ConfigMenu::getItemByKey(String key) {
if (items.find(key) == items.end()) {
return nullptr;
}
return items[key];
};
uint8_t ConfigMenu::getItemCount() {
return items.size();
};
void ConfigMenu::goPrev() {
if (activeitem == 0) {
activeitem = items.size() - 1;
} else {
activeitem--;
}
}
void ConfigMenu::goNext() {
if (activeitem == items.size() - 1) {
activeitem = 0;
} else {
activeitem++;
}
}
Point ConfigMenu::getXY() {
return {static_cast<double>(x), static_cast<double>(y)};
}
Rect ConfigMenu::getRect() {
return {static_cast<double>(x), static_cast<double>(y),
static_cast<double>(w), static_cast<double>(h)};
}
Rect ConfigMenu::getItemRect(int8_t index) {
return {static_cast<double>(x), static_cast<double>(y + index * h),
static_cast<double>(w), static_cast<double>(h)};
}
void ConfigMenu::setCallback(void (*callback)()) {
fptrCallback = callback;
}
void ConfigMenu::storeValues() {
if (fptrCallback) {
fptrCallback();
}
}

View File

@@ -0,0 +1,66 @@
#pragma once
#include <Arduino.h>
#include <vector>
#include <map>
#include "Graphics.h" // for Point and Rect
class ConfigMenuItem {
private:
String label;
uint16_t value;
String unit;
String valtype; // "int" | "bool"
uint16_t min;
uint16_t max;
std::vector<uint16_t> steps;
uint16_t step;
int8_t position; // counted fom 0
public:
ConfigMenuItem(String itemtype, String itemlabel, uint16_t itemval, String itemunit);
void setRange(uint16_t valmin, uint16_t valmax, std::vector<uint16_t> steps);
bool checkRange(uint16_t checkval);
String getLabel();
uint16_t getValue();
bool setValue(uint16_t newval);
void incValue();
void decValue();
String getUnit();
uint16_t getStep();
void setStep(uint16_t newstep);
int8_t getPos();
void setPos(int8_t newpos);
String getType();
};
class ConfigMenu {
private:
String title;
std::map <String,ConfigMenuItem*> items;
std::map <uint8_t,String> index;
int8_t activeitem = -1; // refers to position of item
uint16_t x;
uint16_t y;
uint16_t w;
uint16_t h;
void (*fptrCallback)();
public:
ConfigMenu(String title, uint16_t menu_x, uint16_t menu_y);
ConfigMenuItem* addItem(String key, String label, String valtype, uint16_t val, String valunit);
void setItemDimension(uint16_t itemwidth, uint16_t itemheight);
int8_t getActiveIndex();
void setItemActive(String key);
ConfigMenuItem* getActiveItem();
ConfigMenuItem* getItemByIndex(uint8_t index);
ConfigMenuItem* getItemByKey(String key);
uint8_t getItemCount();
void goPrev();
void goNext();
Point getXY();
Rect getRect();
Rect getItemRect(int8_t index);
void setCallback(void (*callback)());
void storeValues();
};

View File

@@ -1,6 +0,0 @@
Craete new page for OBP60
1. Create page under /lib/obp60task/PageXXXX.cpp
2. Set page name in PageXXXX.cpp on file name
3. Register new page in /lib/obp60task/obp60task.cpp line 242 (registerAllPages)
4. Add new page in /lib/obp60task/config.json for each page type or add new page to gen_set.py and run it to auto-generate the relevant section of config.json

View File

@@ -0,0 +1,25 @@
/*
Generic graphics functions
*/
#include <math.h>
#include "Graphics.h"
Point rotatePoint(const Point& origin, const Point& p, double angle) {
// rotate poind around origin by degrees
Point rotated;
double phi = angle * M_PI / 180.0;
double dx = p.x - origin.x;
double dy = p.y - origin.y;
rotated.x = origin.x + cos(phi) * dx - sin(phi) * dy;
rotated.y = origin.y + sin(phi) * dx + cos(phi) * dy;
return rotated;
}
std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle) {
std::vector<Point> rotatedPoints;
for (const auto& p : pts) {
rotatedPoints.push_back(rotatePoint(origin, p, angle));
}
return rotatedPoints;
}

17
lib/obp60task/Graphics.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#include <vector>
struct Point {
double x;
double y;
};
struct Rect {
double x;
double y;
double w;
double h;
};
Point rotatePoint(const Point& origin, const Point& p, double angle);
std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle);

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include "LedSpiTask.h" #include "LedSpiTask.h"
#include "GwHardware.h" #include "GwHardware.h"
@@ -14,6 +15,30 @@ https://controllerstech.com/ws2812-leds-using-spi/
*/ */
String Color::toHex() {
char hexColor[8];
sprintf(hexColor, "#%02X%02X%02X", r, g, b);
return String(hexColor);
}
String Color::toName() {
static std::map<int, String> const names = {
{0xff0000, "Red"},
{0x00ff00, "Green"},
{0x0000ff, "Blue",},
{0xff9900, "Orange"},
{0xffff00, "Yellow"},
{0x3366ff, "Aqua"},
{0xff0066, "Violet"},
{0xffffff, "White"}
};
int color = (r << 16) + (g << 8) + b;
auto it = names.find(color);
if (it == names.end()) {
return toHex();
}
return it->second;
}
static uint8_t mulcolor(uint8_t f1, uint8_t f2){ static uint8_t mulcolor(uint8_t f1, uint8_t f2){
uint16_t rt=f1; uint16_t rt=f1;
@@ -59,12 +84,12 @@ static size_t ledsToBuffer(int numLeds,const Color *leds,uint8_t *buffer){
bool prepareGpio(GwLog *logger, uint8_t pin){ bool prepareGpio(GwLog *logger, uint8_t pin){
esp_err_t err=gpio_set_direction((gpio_num_t)pin,GPIO_MODE_OUTPUT); esp_err_t err=gpio_set_direction((gpio_num_t)pin,GPIO_MODE_OUTPUT);
if (err != ESP_OK){ if (err != ESP_OK){
LOG_DEBUG(GwLog::ERROR,"unable to set gpio mode for %d: %d",pin,(int)err); logger->logDebug(GwLog::ERROR, "unable to set gpio mode for %d: %d", pin, (int)err);
return false; return false;
} }
err=gpio_set_level((gpio_num_t)pin,0); err=gpio_set_level((gpio_num_t)pin,0);
if (err != ESP_OK){ if (err != ESP_OK){
LOG_DEBUG(GwLog::ERROR,"unable to set gpio level for %d: %d",pin,(int)err); logger->logDebug(GwLog::ERROR, "unable to set gpio level for %d: %d", pin, (int)err);
return false; return false;
} }
return true; return true;
@@ -90,8 +115,8 @@ bool prepareSpi(GwLog *logger,spi_host_device_t bus,spi_device_handle_t *device)
}; };
esp_err_t err=spi_bus_initialize(bus,&buscfg,SPI_DMA_CH_AUTO); esp_err_t err=spi_bus_initialize(bus,&buscfg,SPI_DMA_CH_AUTO);
if (err != ESP_OK){ if (err != ESP_OK){
LOG_DEBUG(GwLog::ERROR,"unable to initialize SPI bus %d,mosi=%d, error=%d", logger->logDebug(GwLog::ERROR, "unable to initialize SPI bus %d,mosi=%d, error=%d",
(int)bus,-1,(int)err); (int)bus, -1, (int)err);
return false; return false;
} }
spi_device_interface_config_t devcfg = { spi_device_interface_config_t devcfg = {
@@ -109,16 +134,16 @@ bool prepareSpi(GwLog *logger,spi_host_device_t bus,spi_device_handle_t *device)
}; };
err=spi_bus_add_device(bus,&devcfg,device); err=spi_bus_add_device(bus,&devcfg,device);
if (err != ESP_OK){ if (err != ESP_OK){
LOG_DEBUG(GwLog::ERROR,"unable to add device to SPI bus %d,mosi=%d, error=%d", logger->logDebug(GwLog::ERROR, "unable to add device to SPI bus %d,mosi=%d, error=%d",
(int)bus,-1,(int)err); (int)bus, -1, (int)err);
return false; return false;
} }
//slightly speed up the transactions //slightly speed up the transactions
//as we are the only ones using the bus we can safely acquire it forever //as we are the only ones using the bus we can safely acquire it forever
err=spi_device_acquire_bus(*device,portMAX_DELAY); err=spi_device_acquire_bus(*device,portMAX_DELAY);
if (err != ESP_OK){ if (err != ESP_OK){
LOG_DEBUG(GwLog::ERROR,"unable to acquire SPI bus %d,mosi=%d, error=%d", logger->logDebug(GwLog::ERROR,"unable to acquire SPI bus %d,mosi=%d, error=%d",
(int)bus,-1,(int)err); (int)bus, -1, (int)err);
return false; return false;
} }
return true; return true;
@@ -148,7 +173,7 @@ bool sendToLeds(GwLog *logger, uint8_t pin, int numLeds, Color *leds, spi_host_d
buffer = (uint8_t *)heap_caps_malloc(bufferSize, MALLOC_CAP_DMA|MALLOC_CAP_32BIT); buffer = (uint8_t *)heap_caps_malloc(bufferSize, MALLOC_CAP_DMA|MALLOC_CAP_32BIT);
if (!buffer) if (!buffer)
{ {
LOG_DEBUG(GwLog::ERROR, "unable to allocate %d bytes of DMA buffer", (int)bufferSize); logger->logDebug(GwLog::ERROR, "unable to allocate %d bytes of DMA buffer", (int)bufferSize);
return false; return false;
} }
} }
@@ -169,12 +194,12 @@ bool sendToLeds(GwLog *logger, uint8_t pin, int numLeds, Color *leds, spi_host_d
int64_t end = esp_timer_get_time(); int64_t end = esp_timer_get_time();
if (ret != ESP_OK) if (ret != ESP_OK)
{ {
LOG_DEBUG(GwLog::ERROR, "unable to send led data: %d", (int)ret); logger->logDebug(GwLog::ERROR, "unable to send led data: %d", (int)ret);
rv = false; rv = false;
} }
else else
{ {
LOG_DEBUG(GwLog::DEBUG, "successfully send led data for %d leds, %lld us", numLeds, end - now); logger->logDebug(GwLog::DEBUG, "successfully send led data for %d leds, %lld us", numLeds, end - now);
} }
if (ownsBuffer) if (ownsBuffer)
{ {
@@ -187,10 +212,10 @@ bool sendToLeds(GwLog *logger, uint8_t pin, int numLeds, Color *leds, spi_host_d
void handleSpiLeds(void *param){ void handleSpiLeds(void *param){
LedTaskData *taskData=(LedTaskData*)param; LedTaskData *taskData=(LedTaskData*)param;
GwLog *logger=taskData->api->getLogger(); GwLog *logger=taskData->api->getLogger();
LOG_DEBUG(GwLog::ERROR,"spi led task initialized"); logger->logDebug(GwLog::ERROR, "spi led task initialized");
spi_host_device_t bus=SPI3_HOST; spi_host_device_t bus=SPI3_HOST;
bool spiValid=false; bool spiValid=false;
LOG_DEBUG(GwLog::ERROR,"SpiLed task started"); LOG_DEBUG(GwLog::ERROR, "SpiLed task started");
if (! prepareGpio(logger,OBP_FLASH_LED)){ if (! prepareGpio(logger,OBP_FLASH_LED)){
EXIT_TASK; EXIT_TASK;
@@ -209,15 +234,15 @@ void handleSpiLeds(void *param){
LedInterface newLeds=taskData->getLedData(); LedInterface newLeds=taskData->getLedData();
if (first || current.backlightChanged(newLeds) || current.flasChanged(newLeds)){ if (first || current.backlightChanged(newLeds) || current.flasChanged(newLeds)){
first=false; first=false;
LOG_DEBUG(GwLog::ERROR,"handle SPI leds"); logger->logDebug(GwLog::ERROR, "handle SPI leds");
if (current.backlightChanged(newLeds) || first){ if (current.backlightChanged(newLeds) || first){
LOG_DEBUG(GwLog::ERROR,"setting backlight r=%02d,g=%02d,b=%02d", logger->logDebug(GwLog::ERROR, "setting backlight r=%02d,g=%02d,b=%02d",
newLeds.backlight[0].r,newLeds.backlight[0].g,newLeds.backlight[0].b); newLeds.backlight[0].r,newLeds.backlight[0].g,newLeds.backlight[0].b);
sendToLeds(logger,OBP_BACKLIGHT_LED,newLeds.backlightLen(),newLeds.backlight,bus,device); sendToLeds(logger,OBP_BACKLIGHT_LED,newLeds.backlightLen(),newLeds.backlight,bus,device);
} }
if (current.flasChanged(newLeds) || first){ if (current.flasChanged(newLeds) || first){
LOG_DEBUG(GwLog::ERROR,"setting flashr=%02d,g=%02d,b=%02d", logger->logDebug(GwLog::ERROR, "setting flashr=%02d,g=%02d,b=%02d",
newLeds.flash[0].r,newLeds.flash[0].g,newLeds.flash[0].b); newLeds.flash[0].r,newLeds.flash[0].g,newLeds.flash[0].b);
sendToLeds(logger,OBP_FLASH_LED,newLeds.flashLen(),newLeds.flash,bus,device); sendToLeds(logger,OBP_FLASH_LED,newLeds.flashLen(),newLeds.flash,bus,device);
} }
current=newLeds; current=newLeds;
@@ -227,6 +252,11 @@ void handleSpiLeds(void *param){
vTaskDelete(NULL); vTaskDelete(NULL);
} }
void createSpiLedTask(LedTaskData *param){ void createSpiLedTask(LedTaskData *param) {
xTaskCreate(handleSpiLeds,"handleLeds",4000,param,3,NULL); TaskHandle_t xHandle = NULL;
} GwLog *logger = param->api->getLogger();
esp_err_t err = xTaskCreate(handleSpiLeds, "handleLeds", configMINIMAL_STACK_SIZE + 2048, param, 3, &xHandle);
if (err != pdPASS) {
logger->logDebug(GwLog::ERROR, "Failed to create spiled task! (err=%d)", err);
};
}

View File

@@ -10,7 +10,7 @@ class Color{
uint8_t g; uint8_t g;
uint8_t b; uint8_t b;
Color():r(0),g(0),b(0){} Color():r(0),g(0),b(0){}
Color(uint8_t cr, uint8_t cg,uint8_t cb): Color(uint8_t cr, uint8_t cg, uint8_t cb):
b(cb),g(cg),r(cr){} b(cb),g(cg),r(cr){}
Color(const Color &o):b(o.b),g(o.g),r(o.r){} Color(const Color &o):b(o.b),g(o.g),r(o.r){}
bool equal(const Color &o) const{ bool equal(const Color &o) const{
@@ -22,6 +22,8 @@ class Color{
bool operator != (const Color &other) const{ bool operator != (const Color &other) const{
return ! equal(other); return ! equal(other);
} }
String toHex();
String toName();
}; };
static Color COLOR_GREEN=Color(0,255,0); static Color COLOR_GREEN=Color(0,255,0);

View File

@@ -1,939 +0,0 @@
const unsigned char gImage_Logo_OBP_400x300_sw[15000] = { /* 0X00,0X01,0X90,0X01,0X2C,0X01, */
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X40,
0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X01,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X01,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X03,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0X80,0X00,0X03,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0X80,0X00,0X03,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X80,
0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X3F,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X3F,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X7F,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XFF,0XFF,0X00,0X03,0XFF,0XFF,0XF8,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XFF,0XE0,0X00,0X00,0X1F,0XFF,0XF8,0X00,0X00,0X00,0X07,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0X80,0X00,0X00,0X07,0XFF,0XF8,0X00,0X00,0X00,
0X03,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X0F,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0X00,0X00,0X00,0X03,0XFF,0XF8,0X00,
0X00,0X00,0X01,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X1F,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFE,0X00,0X00,0X00,0X00,0XFF,
0XF8,0X00,0X00,0X00,0X00,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFC,0X00,0X00,0X00,
0X00,0X7F,0XF8,0X00,0X00,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XF8,0X00,
0X00,0X00,0X00,0X7F,0XF8,0X00,0X00,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XF0,0X00,0X00,0X00,0X00,0X3F,0XF8,0X00,0X00,0X00,0X00,0X3F,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XE0,0X00,0X00,0X00,0X00,0X1F,0XF8,0X00,0X1E,0X00,0X00,0X3F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XE0,0X00,0X0F,0XC0,0X00,0X1F,0XF8,0X00,0X1F,0XE0,0X00,0X3F,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X3F,0XF0,0X00,0X0F,0XF8,0X00,0X1F,0XF0,
0X00,0X3F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X01,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X7F,0XF8,0X00,0X0F,0XF8,0X00,
0X1F,0XF8,0X00,0X3F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X03,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0XFF,0XFC,0X00,0X07,
0XF8,0X00,0X1F,0XF8,0X00,0X3F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X80,0X01,0XFF,0XFE,
0X00,0X07,0XF8,0X00,0X1F,0XF8,0X00,0X3F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X80,0X01,
0XFF,0XFE,0X00,0X07,0XF8,0X00,0X1F,0XF8,0X00,0X3F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0X80,0X01,0XFF,0XFF,0X00,0X07,0XF8,0X00,0X1F,0XF8,0X00,0X3F,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0X80,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,0X1F,0XF0,0X00,0X3F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0X80,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,0X1F,0XE0,0X00,0X3F,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0X00,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,0X00,0X00,
0X00,0X3F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X7F,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X00,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,
0X00,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X00,0X03,0XFF,0XFF,0X00,0X03,
0XF8,0X00,0X00,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X00,0X03,0XFF,0XFF,
0X00,0X03,0XF8,0X00,0X00,0X00,0X00,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X00,0X03,
0XFF,0XFF,0X00,0X03,0XF8,0X00,0X00,0X00,0X01,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0X00,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,0X00,0X00,0X03,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0X00,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,0X00,0X00,0X07,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0X00,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,0X00,0X00,0X0F,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0X80,0X03,0XFF,0XFF,0X00,0X03,0XF8,0X00,0X00,0X00,
0X7F,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X80,0X03,0XFF,0XFF,0X00,0X07,0XF8,0X00,
0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X1F,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X80,0X01,0XFF,0XFE,0X00,0X07,
0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X80,0X01,0XFF,0XFE,
0X00,0X07,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0X80,0X00,
0XFF,0XFC,0X00,0X07,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XC0,0X00,0XFF,0XFC,0X00,0X0F,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XC0,0X00,0X3F,0XF8,0X00,0X0F,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XE0,0X00,0X1F,0XE0,0X00,0X1F,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XE0,0X00,0X00,0X00,0X00,0X1F,0XF8,0X00,0X1F,0XFF,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X3F,0XF8,0X00,
0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X07,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X3F,
0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XF8,0X00,0X00,0X00,
0X00,0X7F,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFC,0X00,
0X00,0X00,0X00,0XFF,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XFE,0X00,0X00,0X00,0X01,0XFF,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XFF,0X80,0X00,0X00,0X03,0XFF,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XFF,0XC0,0X00,0X00,0X0F,0XFF,0XF8,0X00,0X1F,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X3F,0XFF,0XF8,0X00,0X1F,0XFF,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFE,0X00,0X01,0XFF,0XFF,0XF8,0X00,
0X3F,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X03,0XFE,0X00,0X1F,0XFF,0XF8,
0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X03,0XFE,0X00,0X0F,
0XFF,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X03,0XFE,
0X00,0X07,0XFF,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,
0X03,0XFE,0X00,0X03,0XFF,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,
0X00,0X00,0X03,0XFE,0X00,0X03,0XFF,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XC0,0X00,0X00,0X00,0X03,0XFE,0X00,0X01,0XFF,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XC0,0X00,0X00,0X00,0X03,0XFE,0X00,0X00,0XFF,0XF0,0X00,0X7F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X03,0XFE,0X00,0X00,0XFF,0XF0,0X00,0X7F,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X03,0XFE,0X00,0X00,0X7F,0XF0,
0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X03,0XFE,0X00,0X00,
0X3F,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,
0X00,0X00,0X3F,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,
0XFF,0XFE,0X00,0X00,0X1F,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,
0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X0F,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X0F,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X07,0XF0,0X00,0X7F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X03,0XF0,0X00,0X7F,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X00,0X03,0XF0,
0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X07,0XFE,0X00,0X00,
0X01,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X07,0XFE,
0X00,0X00,0X00,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,
0X07,0XFE,0X00,0X00,0X00,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,
0X00,0X00,0X07,0XFE,0X00,0X00,0X00,0X70,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XC0,0X00,0X00,0X00,0X07,0XFE,0X00,0X00,0X00,0X30,0X00,0X7F,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XC0,0X00,0X00,0X00,0X07,0XFE,0X00,0X08,0X00,0X30,0X00,0X7F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X07,0XFE,0X00,0X0C,0X00,0X10,0X00,0X7F,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X07,0XFE,0X00,0X0C,0X00,0X00,
0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X07,0XFE,0X00,0X0E,
0X00,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,
0X00,0X0F,0X00,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,
0XFF,0XFE,0X00,0X0F,0X00,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,
0XFF,0XFF,0XFF,0XFE,0X00,0X0F,0X80,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X0F,0XC0,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X0F,0XC0,0X00,0X00,0X7F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X0F,0XE0,0X00,0X00,0X7F,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X0F,0XF0,0X00,
0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X01,0XFF,0XFF,0XFF,0XFE,0X00,0X0F,
0XF8,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0XFF,0XFF,0XFF,0XFE,
0X00,0X0F,0XF8,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,
0X01,0XFE,0X00,0X0F,0XFC,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,
0X00,0X00,0X01,0XFE,0X00,0X0F,0XFE,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XC0,0X00,0X00,0X00,0X01,0XFE,0X00,0X0F,0XFE,0X00,0X00,0X7F,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XC0,0X00,0X00,0X00,0X01,0XFE,0X00,0X0F,0XFF,0X00,0X00,0X7F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X01,0XFE,0X00,0X0F,0XFF,0X80,0X00,0X7F,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X01,0XFE,0X00,0X0F,0XFF,0X80,
0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X01,0XFE,0X00,0X0F,
0XFF,0XC0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X01,0XFE,
0X00,0X0F,0XFF,0XE0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,
0X01,0XFE,0X00,0X0F,0XFF,0XE0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XC0,0X00,
0X00,0X00,0X01,0XFE,0X00,0X1F,0XFF,0XF0,0X00,0X7F,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X7F,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,0X00,0X03,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0X80,
0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0X80,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XC0,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,
0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X40,0X00,0X0F,0XFF,0XE0,0X00,0X00,
0X00,0X00,0X00,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X02,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X03,0XFF,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XC0,0X00,0X0F,0XFF,0XF8,
0X00,0X00,0X00,0X00,0X00,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X0E,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X00,0X00,0X07,0XC0,0X00,0X0F,
0XFF,0XFC,0X00,0X00,0X00,0X00,0X00,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X3E,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XC0,0X00,0X00,0X00,0X00,0X00,0X0F,0XC0,
0X00,0X0F,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,
0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,
0X0F,0XC0,0X00,0X0F,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X7E,0X00,0X00,0X00,0X00,
0X00,0X00,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X1F,0XE0,0X00,0X00,0X00,
0X00,0X00,0X0F,0XC0,0X00,0X0F,0XE0,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X0F,0XE0,0X03,
0XE0,0X00,0X3F,0XE0,0X1F,0XC0,0X00,0X0F,0XE0,0X7F,0X00,0X0E,0X00,0X7E,0X00,0X00,
0X00,0X3F,0X00,0X01,0XFC,0X00,0XFE,0X00,0X3F,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X0F,
0XC0,0X1F,0XFC,0X00,0XFF,0XF8,0X7F,0XFC,0X00,0X0F,0XE0,0X7F,0X1F,0X9F,0X83,0XFF,
0X80,0X7E,0X01,0XFF,0XE0,0X07,0XFF,0X03,0XFF,0XE0,0XFF,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,
0XF8,0X1F,0XC0,0X3F,0XFE,0X03,0XFF,0XFC,0X7F,0XFC,0X00,0X0F,0XE0,0X7F,0X1F,0XFF,
0X07,0XFF,0XE0,0X7E,0X03,0XFF,0XF0,0X1F,0XFF,0XC3,0XFF,0XE1,0XFF,0XF8,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X03,0XFF,0XFF,0X80,0X7F,0XFF,0X83,0XFF,0XFE,0X7F,0XFC,0X00,0X0F,0XE0,0XFF,
0X1F,0XFF,0X0F,0XFF,0XF0,0X7E,0X07,0XFF,0XF8,0X3F,0XFF,0XE3,0XFF,0XE3,0XFF,0XFC,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X03,0XFF,0XFF,0X00,0XFF,0XFF,0X87,0XF8,0XFE,0X7F,0XFC,0X00,0X0F,
0XFF,0XFE,0X1F,0XFE,0X1F,0XFF,0XF0,0X7E,0X0F,0XE3,0XF8,0X3F,0XFF,0XE3,0XFF,0XE3,
0XE1,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0X01,0XFE,0X3F,0XC7,0XF0,0X7E,0X1F,0XC0,
0X00,0X0F,0XFF,0XFE,0X1F,0XF2,0X1F,0XC3,0XF8,0X7E,0X0F,0XC1,0XFC,0X7F,0X87,0XF0,
0XFE,0X07,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XC1,0XFC,0X1F,0XC0,0X00,0XFE,
0X0F,0XC0,0X00,0X0F,0XFF,0XFC,0X1F,0XE0,0X3F,0X83,0XF8,0X7E,0X1F,0XC0,0XFC,0X7F,
0X03,0X80,0XFE,0X03,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XE1,0XFC,0X0F,0XC0,
0X07,0XFE,0X0F,0XC0,0X00,0X0F,0XFF,0XF8,0X1F,0XC0,0X3F,0X81,0XF8,0X7E,0X1F,0XC0,
0XFC,0X7E,0X00,0X00,0XFE,0X03,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X0F,0XE1,0XF8,
0X0F,0XC0,0XFF,0XFE,0X0F,0XC0,0X00,0X0F,0XFF,0XE0,0X1F,0XC0,0X3F,0X81,0XF8,0X7E,
0X1F,0XFF,0XFE,0X7E,0X00,0X00,0XFE,0X01,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X0F,
0XF1,0XF8,0X0F,0XC3,0XFF,0XFE,0X0F,0XC0,0X00,0X0F,0XE0,0X00,0X1F,0XC0,0X3F,0X01,
0XFC,0X7E,0X1F,0XFF,0XFE,0X7E,0X00,0X00,0XFE,0X00,0XFF,0XFC,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,
0XF8,0X0F,0XF1,0XF8,0X0F,0XC7,0XFC,0X7E,0X0F,0XC0,0X00,0X0F,0XE0,0X00,0X1F,0XC0,
0X3F,0X81,0XF8,0X7E,0X1F,0XFF,0XFE,0X7E,0X00,0X00,0XFE,0X00,0X3F,0XFC,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X03,0XF8,0X0F,0XF1,0XFC,0X1F,0XC7,0XF0,0X7E,0X0F,0XC0,0X00,0X0F,0XE0,0X00,
0X1F,0XC0,0X3F,0X81,0XF8,0X7E,0X1F,0XC0,0X00,0X7F,0X03,0XC0,0XFE,0X00,0X01,0XFE,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X03,0XFC,0X3F,0XF1,0XFC,0X1F,0XC7,0XE0,0X7E,0X0F,0XC0,0X00,0X0F,
0XE0,0X00,0X1F,0XC0,0X1F,0X83,0XF8,0X7E,0X1F,0XC0,0X00,0X7F,0X07,0XF0,0XFE,0X00,
0X60,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XE0,0XFF,0X3F,0X87,0XE0,0XFE,0X0F,0XFC,
0X00,0X0F,0XE0,0X00,0X1F,0XC0,0X1F,0XE7,0XF0,0X7E,0X0F,0XE1,0XFC,0X7F,0XCF,0XF0,
0X7F,0XC7,0XF0,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XE0,0XFF,0XFF,0X87,0XFF,0XFE,
0X0F,0XFC,0X00,0X0F,0XE0,0X00,0X1F,0XC0,0X0F,0XFF,0XF0,0X7E,0X0F,0XFF,0XF8,0X3F,
0XFF,0XE0,0X7F,0XC7,0XFF,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XC0,0X7F,0XFF,0X07,
0XFF,0XFE,0X0F,0XFC,0X00,0X0F,0XE0,0X00,0X1F,0XC0,0X07,0XFF,0XE0,0X7E,0X07,0XFF,
0XF0,0X1F,0XFF,0XC0,0X7F,0XE3,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0X80,0X1F,
0XFE,0X03,0XFF,0X3F,0X07,0XFC,0X00,0X0F,0XE0,0X00,0X1F,0XC0,0X03,0XFF,0XC0,0X7E,
0X03,0XFF,0XE0,0X0F,0XFF,0X80,0X3F,0XE1,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XF8,
0X00,0X07,0XF8,0X00,0XFC,0X3F,0X03,0XFC,0X00,0X0F,0XE0,0X00,0X1F,0XC0,0X00,0XFF,
0X00,0X7E,0X00,0XFF,0X80,0X03,0XFE,0X00,0X1F,0XE0,0X7F,0XC0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X03,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFC,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFC,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XE0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,};

View File

@@ -1,939 +0,0 @@
const unsigned char gImage_MFD_OBP60_400x300_sw[15000] = { /* 0X00,0X01,0X90,0X01,0X2C,0X01, */
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XE0,0X3F,
0XC0,0X00,0X00,0X7E,0X00,0X40,0XFC,0X07,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X08,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XE0,0X0F,0XC0,0X00,0X00,0X00,
0X00,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,
0XF0,0X3F,0XC0,0X00,0X00,0X7E,0X01,0XC0,0XFC,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X38,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XF8,0X0F,0XC0,0X00,
0X00,0X00,0X00,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X1F,0XF0,0X7F,0XC0,0X00,0X00,0X7E,0X07,0XC0,0XFC,0X1F,0XF0,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0XF8,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFC,0X0F,
0XC0,0X00,0X00,0X00,0X00,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X1F,0XF0,0X7F,0XC0,0X00,0X00,0X7E,0X0F,0XC0,0XFC,0X3F,0XE0,0X00,
0X00,0X00,0X00,0X00,0X00,0X01,0XF8,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,
0XFE,0X0F,0XC0,0X00,0X00,0X00,0X00,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X1F,0XF0,0X7F,0XC0,0X00,0X00,0X7E,0X0F,0XC0,0X00,0X3F,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X07,0XFF,0XFE,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X80,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XF8,0X7F,0XC0,0X00,0X00,0X7E,0X0F,0XC0,
0X00,0X3F,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XF8,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X07,0XF0,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X80,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XF8,0XFF,0XC3,0XE0,0XF8,0X7E,
0X3F,0XF8,0XFC,0XFF,0XE7,0XC1,0XF0,0XF8,0XF8,0X01,0XFC,0X07,0XFF,0X1F,0X80,0X7F,
0X00,0XF8,0XF8,0X00,0X07,0XF0,0X7F,0X0F,0XC0,0X7F,0X80,0XF9,0XF0,0X1F,0X80,0XFF,
0X83,0XF0,0X3F,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X78,0XF7,0XC3,0XE0,
0XF8,0X7E,0X3F,0XF8,0XFC,0XFF,0XE7,0XC1,0XF0,0XFB,0XFC,0X07,0XFF,0X07,0XFF,0X1F,
0X81,0XFF,0XC0,0XFB,0XFC,0X00,0X07,0XF0,0X3F,0X0F,0XC1,0XFF,0XE0,0XFB,0XFC,0X1F,
0X83,0XFF,0XE1,0XF8,0X3E,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X78,0XF7,
0XC3,0XE0,0XF8,0X7E,0X3F,0XF8,0XFC,0XFF,0XE7,0XC1,0XF0,0XFF,0XFE,0X0F,0XFF,0XC7,
0XFF,0X1F,0X83,0XFF,0XE0,0XFF,0XFE,0X00,0X07,0XF0,0X3F,0X0F,0XC3,0XFF,0XF0,0XFF,
0XFE,0X1F,0X87,0XFF,0XF1,0XF8,0X3E,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,
0X7C,0XF7,0XC3,0XE0,0XF8,0X7E,0X3F,0XF8,0XFC,0XFF,0XE7,0XC1,0XF0,0XFF,0XFE,0X1F,
0XFF,0XC7,0XFF,0X1F,0X87,0XFF,0XF0,0XFF,0XFE,0X00,0X07,0XF0,0X3F,0X8F,0XC3,0XE3,
0XF0,0XFF,0XFE,0X1F,0X87,0XFF,0XF0,0XF8,0X7E,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X1F,0X3D,0XE7,0XC3,0XE0,0XF8,0X7E,0X0F,0XC0,0XFC,0X3F,0X07,0XC1,0XF0,0XFF,
0XFE,0X1F,0X87,0XE1,0XF8,0X1F,0X87,0XE3,0XF0,0XFF,0XFE,0X00,0X07,0XF0,0X3F,0X8F,
0XC3,0XE1,0X80,0XFC,0X7F,0X1F,0X87,0XC1,0XF0,0XFC,0X7C,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X1F,0X3D,0XE7,0XC3,0XE0,0XF8,0X7E,0X0F,0XC0,0XFC,0X3F,0X07,0XC1,
0XF0,0XFC,0X7E,0X3F,0X07,0X01,0XF8,0X1F,0X8F,0XC1,0XF8,0XFC,0X7E,0X00,0X07,0XF0,
0X3F,0X8F,0XC3,0XFC,0X00,0XFC,0X3F,0X1F,0X80,0X07,0XF0,0X7C,0X7C,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X1F,0X3F,0XE7,0XC3,0XE0,0XF8,0X7E,0X0F,0XC0,0XFC,0X3F,
0X07,0XC1,0XF0,0XFC,0X3E,0X3F,0X00,0X01,0XF8,0X1F,0X8F,0XC1,0XF8,0XFC,0X3E,0X00,
0X07,0XF0,0X3F,0X0F,0XC3,0XFF,0XE0,0XF8,0X3F,0X1F,0X80,0X7F,0XF0,0X7C,0XF8,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X3F,0XE7,0XC3,0XE0,0XF8,0X7E,0X0F,0XC0,
0XFC,0X3F,0X07,0XC1,0XF0,0XF8,0X3E,0X3F,0X00,0X01,0XF8,0X1F,0X8F,0XC1,0XF8,0XF8,
0X3E,0X00,0X07,0XF0,0X3F,0X0F,0XC1,0XFF,0XF0,0XF8,0X3F,0X1F,0X83,0XFF,0XF0,0X7C,
0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X1F,0XC7,0XC3,0XE1,0XF8,0X7E,
0X0F,0XC0,0XFC,0X3F,0X07,0XC3,0XF0,0XF8,0X3E,0X3F,0X00,0X01,0XF8,0X1F,0X8F,0XC1,
0XF8,0XF8,0X3E,0X00,0X07,0XF0,0X3F,0X0F,0XC0,0X7F,0XF8,0XF8,0X3F,0X1F,0X87,0XF1,
0XF0,0X3E,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X1F,0XC7,0XC3,0XF1,
0XF8,0X7E,0X0F,0XC0,0XFC,0X3F,0X07,0XE3,0XF0,0XF8,0X3E,0X3F,0X03,0X01,0XF8,0X1F,
0X8F,0XC1,0XF8,0XF8,0X3E,0X00,0X07,0XF0,0X7F,0X0F,0XC0,0X03,0XF8,0XFC,0X3F,0X1F,
0X8F,0XC1,0XF0,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0X1F,0XC7,
0XC3,0XFF,0XF8,0X7E,0X0F,0XC0,0XFC,0X3F,0X07,0XFF,0XF0,0XF8,0X3E,0X1F,0X87,0XE1,
0XF8,0X1F,0X87,0XE3,0XF0,0XF8,0X3E,0X00,0X07,0XFF,0XFE,0X0F,0XC0,0XE0,0XF8,0XFC,
0X7F,0X1F,0X8F,0XC3,0XF0,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,
0X1F,0XC7,0XC3,0XFF,0XF8,0X7E,0X0F,0XF8,0XFC,0X3F,0X07,0XFF,0XF0,0XF8,0X3E,0X1F,
0XFF,0XC1,0XFF,0X1F,0X87,0XFF,0XF0,0XF8,0X3E,0X00,0X07,0XFF,0XFE,0X0F,0XC7,0XE1,
0XF8,0XFF,0XFE,0X1F,0X8F,0XFF,0XF0,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X1F,0X0F,0X87,0XC3,0XFF,0XF8,0X7E,0X0F,0XF8,0XFC,0X3F,0X07,0XFF,0XF0,0XF8,
0X3E,0X0F,0XFF,0XC1,0XFF,0X1F,0X83,0XFF,0XE0,0XF8,0X3E,0X00,0X07,0XFF,0XFC,0X0F,
0XC3,0XFF,0XF0,0XFF,0XFE,0X1F,0X8F,0XFF,0XF0,0X1F,0XE0,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X1F,0X0F,0X87,0XC1,0XFE,0XF8,0X7E,0X07,0XF8,0XFC,0X3F,0X03,0XFD,
0XF0,0XF8,0X3E,0X07,0XFF,0X80,0XFF,0X1F,0X81,0XFF,0XC0,0XF8,0X3E,0X00,0X07,0XFF,
0XF8,0X0F,0XC3,0XFF,0XE0,0XFF,0XFC,0X1F,0X87,0XFD,0XF0,0X0F,0XE0,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X1F,0X0F,0X87,0XC0,0XF8,0XF8,0X7E,0X03,0XF8,0XFC,0X3F,
0X01,0XF1,0XF0,0XF8,0X3E,0X01,0XFE,0X00,0X7F,0X1F,0X80,0X7F,0X00,0XF8,0X3E,0X00,
0X07,0XFF,0XC0,0X0F,0XC0,0X7F,0X80,0XF9,0XF0,0X1F,0X81,0XF0,0XF8,0X0F,0XC0,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XF8,0X00,0X00,0X00,0X00,0X00,0X0F,
0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XF8,0X00,0X00,0X00,0X00,
0X00,0X0F,0XC0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XF8,0X00,0X00,
0X00,0X00,0X01,0XFF,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XF8,
0X00,0X00,0X00,0X00,0X01,0XFF,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0XF8,0X00,0X00,0X00,0X00,0X01,0XFF,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0XF8,0X00,0X00,0X00,0X00,0X00,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XF8,0X01,0XFF,0XFC,0X03,0XFF,0XF0,0X00,0XFC,
0X00,0X3F,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFE,0X01,0XFF,0XFF,0X03,0XFF,0XFC,
0X03,0XFF,0X00,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0X81,0XFF,0XFF,0X83,
0XFF,0XFE,0X07,0XFF,0X81,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XC1,0XFF,
0XFF,0X83,0XFF,0XFE,0X0F,0XFF,0XC3,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,
0XC1,0XFF,0XFF,0XC3,0XFF,0XFE,0X1F,0X8F,0XC3,0XF1,0XF8,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,
0XFE,0X1F,0XE1,0XFC,0X1F,0XC3,0XF0,0X7F,0X1F,0X87,0X83,0XE0,0XF8,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X01,0XFC,0X0F,0XE1,0XFC,0X1F,0X83,0XF0,0X3F,0X1F,0X00,0X07,0XE0,0XF8,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X01,0XF8,0X07,0XE1,0XFC,0X1F,0X83,0XF0,0X3F,0X3F,0X3E,0X07,0XE0,
0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X07,0XF1,0XFF,0XFF,0X03,0XF0,0X7F,0X3F,0X7F,
0X87,0XE0,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X07,0XF1,0XFF,0XFE,0X03,0XFF,0XFE,
0X3F,0XFF,0XC7,0XE0,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X07,0XF1,0XFF,0XFF,0X03,
0XFF,0XFE,0X3F,0XFF,0XC7,0XE0,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X07,0XF1,0XFF,
0XFF,0X83,0XFF,0XFC,0X3F,0X87,0XE7,0XE0,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XF8,0X07,
0XF1,0XFF,0XFF,0XC3,0XFF,0XF8,0X3F,0X07,0XE7,0XE0,0XFC,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,
0XF8,0X07,0XE1,0XFC,0X0F,0XC3,0XFF,0XE0,0X3F,0X03,0XE7,0XE0,0XFC,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X01,0XFC,0X0F,0XE1,0XFC,0X0F,0XE3,0XF0,0X00,0X1F,0X03,0XE3,0XE0,0XFC,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X01,0XFE,0X1F,0XE1,0XFC,0X0F,0XE3,0XF0,0X00,0X1F,0X07,0XE3,0XE0,
0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XC1,0XFF,0XFF,0XC3,0XF0,0X00,0X1F,0X87,
0XE3,0XF1,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XC1,0XFF,0XFF,0XC3,0XF0,0X00,
0X0F,0XFF,0XC1,0XFF,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0X81,0XFF,0XFF,0X83,
0XF0,0X00,0X07,0XFF,0X81,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFE,0X01,0XFF,
0XFF,0X83,0XF0,0X00,0X03,0XFF,0X00,0XFF,0XE0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XF8,
0X01,0XFF,0XFC,0X03,0XF0,0X00,0X00,0XFC,0X00,0X3F,0X80,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X40,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XC0,0X07,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XC0,0X07,
0XFF,0XFC,0X07,0XFF,0X00,0X07,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,
0XC0,0X07,0XFF,0XE0,0X00,0XFF,0X00,0X00,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X0F,0XC0,0X07,0XFF,0XC0,0X00,0X7F,0X00,0X00,0X7F,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X1F,0XC0,0X07,0XFF,0X80,0X00,0X3F,0X00,0X00,0X3F,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X1F,0XC0,0X07,0XFF,0X00,0X00,0X1F,0X00,0X00,0X1F,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XC0,0X07,0XFE,0X00,0XC0,0X0F,0X01,0XE0,
0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XC0,0X07,0XFE,0X03,0XF0,0X0F,
0X01,0XF0,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XC0,0X07,0XFC,0X03,
0XF8,0X0F,0X01,0XF0,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XC0,0X07,
0XFC,0X07,0XF8,0X07,0X01,0XF0,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,
0XC0,0X07,0XFC,0X07,0XFC,0X07,0X01,0XE0,0X1F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X03,0XFF,0XC0,0X07,0XFC,0X07,0XFC,0X07,0X00,0X00,0X3F,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X03,0XFF,0XC0,0X07,0XFC,0X07,0XFC,0X07,0X00,0X00,0X3F,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X07,0XFF,0XC0,0X07,0XFC,0X07,0XFC,0X07,0X00,0X00,0X7F,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XC0,0X07,0XFC,0X07,0XFC,0X07,0X00,0X00,
0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XC0,0X07,0XFC,0X07,0XF8,0X07,
0X01,0XDF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XC0,0X07,0XFC,0X07,
0XF8,0X0F,0X01,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XC0,0X07,
0XFE,0X03,0XF8,0X0F,0X01,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,
0XC0,0X07,0XFE,0X00,0XE0,0X0F,0X01,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X7F,0XFF,0XC0,0X07,0XFF,0X00,0X00,0X1F,0X01,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0XFF,0XFF,0XC0,0X07,0XFF,0X00,0X00,0X3F,0X01,0XFF,0XFF,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X01,0XFF,0XFF,0XC0,0X07,0XFF,0X80,0X00,0X7F,0X01,0XFF,0XFF,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XC0,0X07,0XFF,0XE0,0X00,0XFF,0X01,0XFF,
0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XC0,0X07,0XFF,0XF8,0X03,0XFF,
0X01,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XC0,0X07,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,
0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,
0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X3F,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X3F,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X00,0X00,0X78,
0X0F,0XF0,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X00,
0X00,0X78,0X07,0XF0,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XC0,0X07,
0XFE,0X00,0X00,0X78,0X07,0XF0,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,
0XC0,0X07,0XFE,0X00,0X00,0X78,0X03,0XF0,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,
0XFF,0XFF,0XC0,0X07,0XFE,0X00,0X00,0X78,0X01,0XF0,0X3F,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X07,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X03,0XFF,0XF8,0X01,0XF0,0X3F,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X03,0XFF,0XF8,0X00,0XF0,0X3F,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X03,0XFF,0XF8,0X00,0X70,
0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X00,0X00,0XF8,
0X00,0X70,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X00,
0X00,0XF8,0X00,0X30,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,0XC0,0X07,
0XFE,0X00,0X00,0XF8,0X00,0X10,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7F,0XFF,0XFF,0XFF,
0XC0,0X07,0XFE,0X00,0X00,0XF8,0X00,0X10,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,
0XFF,0XFF,0XC0,0X07,0XFE,0X00,0X00,0XF8,0X08,0X00,0X3F,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,
0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X03,0XFF,0XF8,0X0C,0X00,0X3F,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X03,0XFF,0XF8,0X0E,0X00,0X3F,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X03,0XFF,0XF8,0X0E,0X00,
0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X03,0XFF,0XF8,
0X0F,0X00,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X00,
0X00,0X78,0X0F,0X80,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,
0XFE,0X00,0X00,0X78,0X0F,0X80,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X1F,0XFF,0XFF,0XFF,0XFF,
0XC0,0X07,0XFE,0X00,0X00,0X78,0X0F,0XC0,0X3F,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,
0XFF,0XFF,0XC0,0X07,0XFE,0X00,0X00,0X78,0X0F,0XE0,0X3F,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X3F,
0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFE,0X00,0X00,0X78,0X0F,0XE0,0X3F,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X7F,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X01,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XFF,0XFF,0XFF,0XFF,0XFF,0XC0,0X07,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,
0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFF,0XFF,0XFF,
0XFF,0XFF,0XC0,0X07,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XF0,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XF8,0X00,0X00,0X00,0X20,0X0F,0XE0,
0X00,0X00,0X38,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFC,0X00,0X00,0X00,0X60,
0X0F,0XF8,0X00,0X00,0X38,0X00,0X00,0X01,0X80,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFE,0X00,0X00,
0X00,0XE0,0X0F,0XF8,0X00,0X00,0X00,0X00,0X00,0X03,0X80,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0X0E,
0X06,0X00,0XC0,0XE0,0X0E,0X3C,0X08,0X38,0X00,0X18,0X03,0X03,0X81,0XC0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X07,0X1E,0X1F,0X87,0XFB,0XF8,0X0E,0X3C,0XDC,0XFE,0X38,0X7F,0X0F,0XE7,0XE7,0XF0,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X07,0XFC,0X3F,0XCF,0X3B,0XF8,0X0F,0XF8,0XF9,0XFF,0X38,0XE7,0X1F,0XE7,
0XE6,0X38,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X07,0XFE,0X78,0XE0,0X38,0XE0,0X0F,0XF8,0XE1,0XC7,0X39,0XE3,
0XBC,0X03,0X87,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X07,0X0E,0X70,0XE3,0XF8,0XE0,0X0F,0XE0,0XE1,0XC7,
0X39,0XFF,0XB8,0X03,0X87,0XF0,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0X0F,0X70,0XEF,0X38,0XE0,0X0E,0X00,
0XE1,0XC7,0X39,0XFF,0XB8,0X03,0X81,0XF8,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0X1E,0X39,0XEE,0X38,0XE0,
0X0E,0X00,0XE1,0XC7,0X38,0XE0,0X3C,0XF3,0X80,0X38,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFE,0X3F,0XCF,
0XF8,0XF0,0X0E,0X00,0XE0,0XFF,0X38,0XFF,0X1F,0XE3,0XEF,0X78,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XFC,
0X1F,0X87,0XD8,0XF8,0X0E,0X00,0XE0,0X7C,0X38,0X7E,0X0F,0XC1,0XE7,0XF0,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X38,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X78,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XF8,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X20,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,};

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include <Arduino.h> #include <Arduino.h>
@@ -24,6 +25,7 @@
#include "fonts/Ubuntu_Bold20pt8b.h" #include "fonts/Ubuntu_Bold20pt8b.h"
#include "fonts/Ubuntu_Bold32pt8b.h" #include "fonts/Ubuntu_Bold32pt8b.h"
#include "fonts/Atari16px8b.h" // Key label font #include "fonts/Atari16px8b.h" // Key label font
#include "fonts/Atari6px8b.h" // Very small (6x6) font
// E-Ink Display // E-Ink Display
#define GxEPD_WIDTH 400 // Display width #define GxEPD_WIDTH 400 // Display width
@@ -32,30 +34,20 @@
#ifdef DISPLAY_GDEW042T2 #ifdef DISPLAY_GDEW042T2
// Set display type and SPI pins for display // Set display type and SPI pins for display
GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> display(GxEPD2_420(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398) GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> display(GxEPD2_420(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398)
// Export display in new funktion
GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> & getdisplay(){return display;}
#endif #endif
#ifdef DISPLAY_GDEY042T81 #ifdef DISPLAY_GDEY042T81
// Set display type and SPI pins for display // Set display type and SPI pins for display
GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> display(GxEPD2_420_GDEY042T81(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398) GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> display(GxEPD2_420_GDEY042T81(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398)
// Export display in new funktion
GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> & getdisplay(){return display;}
#endif #endif
#ifdef DISPLAY_GYE042A87 #ifdef DISPLAY_GYE042A87
// Set display type and SPI pins for display // Set display type and SPI pins for display
GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> display(GxEPD2_420_GYE042A87(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398) GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> display(GxEPD2_420_GYE042A87(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398)
// Export display in new funktion
GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> & getdisplay(){return display;}
#endif #endif
#ifdef DISPLAY_SE0420NQ04 #ifdef DISPLAY_SE0420NQ04
// Set display type and SPI pins for display // Set display type and SPI pins for display
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> display(GxEPD2_420_SE0420NQ04(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398) GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> display(GxEPD2_420_SE0420NQ04(OBP_SPI_CS, OBP_SPI_DC, OBP_SPI_RST, OBP_SPI_BUSY)); // GDEW042T2 400x300, UC8176 (IL0398)
// Export display in new funktion
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay(){return display;}
#endif #endif
gxepd2display *epd = &display;
// Horter I2C moduls // Horter I2C moduls
PCF8574 pcf8574_Out(PCF8574_I2C_ADDR1); // First digital output modul PCF8574 from Horter PCF8574 pcf8574_Out(PCF8574_I2C_ADDR1); // First digital output modul PCF8574 from Horter
@@ -64,7 +56,14 @@ PCF8574 pcf8574_Out(PCF8574_I2C_ADDR1); // First digital output modul PCF8574 fr
Adafruit_FRAM_I2C fram; Adafruit_FRAM_I2C fram;
bool hasFRAM = false; bool hasFRAM = false;
// SD Card
#ifdef BOARD_OBP40S3
sdmmc_card_t *sdcard;
#endif
bool hasSDCard = false;
// Global vars // Global vars
bool heartbeat = false; // Heartbeat indicator with two different states
bool blinkingLED = false; // Enable / disable blinking flash LED bool blinkingLED = false; // Enable / disable blinking flash LED
bool statusLED = false; // Actual status of flash LED on/off bool statusLED = false; // Actual status of flash LED on/off
bool statusBacklightLED = false;// Actual status of flash LED on/off bool statusBacklightLED = false;// Actual status of flash LED on/off
@@ -78,16 +77,19 @@ LedTaskData *ledTaskData=nullptr;
void hardwareInit(GwApi *api) void hardwareInit(GwApi *api)
{ {
GwLog *logger = api->getLogger();
GwConfigHandler *config = api->getConfig();
Wire.begin(); Wire.begin();
// Init PCF8574 digital outputs // Init PCF8574 digital outputs
Wire.setClock(I2C_SPEED); // Set I2C clock on 10 kHz Wire.setClock(I2C_SPEED); // Set I2C clock as defined
if(pcf8574_Out.begin()){ // Initialize PCF8574 if(pcf8574_Out.begin()){ // Initialize PCF8574
pcf8574_Out.write8(255); // Clear all outputs pcf8574_Out.write8(255); // Clear all outputs
} }
fram = Adafruit_FRAM_I2C(); fram = Adafruit_FRAM_I2C();
if (esp_reset_reason() == ESP_RST_POWERON) { if (esp_reset_reason() == ESP_RST_POWERON) {
// help initialize FRAM // help initialize FRAM
api->getLogger()->logDebug(GwLog::LOG,"Delaying I2C init for 250ms due to cold boot"); logger->logDebug(GwLog::LOG, "Delaying I2C init for 250ms due to cold boot");
delay(250); delay(250);
} }
// FRAM (e.g. MB85RC256V) // FRAM (e.g. MB85RC256V)
@@ -99,11 +101,88 @@ void hardwareInit(GwApi *api)
// Boot counter // Boot counter
uint8_t framcounter = fram.read(0x0000); uint8_t framcounter = fram.read(0x0000);
fram.write(0x0000, framcounter+1); fram.write(0x0000, framcounter+1);
api->getLogger()->logDebug(GwLog::LOG,"FRAM detected: 0x%04x/0x%04x (counter=%d)", manufacturerID, productID, framcounter); logger->logDebug(GwLog::LOG, "FRAM detected: 0x%04x/0x%04x (counter=%d)", manufacturerID, productID, framcounter);
} }
else { else {
hasFRAM = false; hasFRAM = false;
api->getLogger()->logDebug(GwLog::LOG,"NO FRAM detected"); logger->logDebug(GwLog::LOG, "NO FRAM detected");
}
// SD Card
hasSDCard = false;
#ifdef BOARD_OBP40S3
if (config->getBool(config->useSDCard)) {
esp_err_t ret;
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
host.slot = SPI3_HOST;
logger->logDebug(GwLog::DEBUG, "SDSPI_HOST: max_freq_khz=%d" , host.max_freq_khz);
spi_bus_config_t bus_cfg = {
.mosi_io_num = SD_SPI_MOSI,
.miso_io_num = SD_SPI_MISO,
.sclk_io_num = SD_SPI_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4000,
};
ret = spi_bus_initialize((spi_host_device_t) host.slot, &bus_cfg, SDSPI_DEFAULT_DMA);
if (ret != ESP_OK) {
logger->logDebug(GwLog::ERROR, "Failed to initialize SPI bus for SD card");
} else {
sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
slot_config.gpio_cs = SD_SPI_CS;
slot_config.host_id = (spi_host_device_t) host.slot;
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = false,
.max_files = 5,
.allocation_unit_size = 16 * 1024
};
ret = esp_vfs_fat_sdspi_mount(MOUNT_POINT, &host, &slot_config, &mount_config, &sdcard);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
logger->logDebug(GwLog::ERROR, "Failed to mount SD card filesystem");
} else {
// ret == 263 could be not powered up yet
logger->logDebug(GwLog::ERROR, "Failed to initialize SD card (error #%d)", ret);
}
} else {
logger->logDebug(GwLog::LOG, "SD card filesystem mounted at '%s'", MOUNT_POINT);
hasSDCard = true;
}
}
if (hasSDCard) {
// read some stats
String features = "";
if (sdcard->is_mem) features += "MEM "; // Memory card
if (sdcard->is_sdio) features += "IO "; // IO Card
if (sdcard->is_mmc) features += "MMC "; // MMC Card
if (sdcard->is_ddr) features += "DDR ";
// if (sdcard->is_uhs1) features += "UHS-1 ";
// ext_csd. Extended information
// uint8_t rev, uint8_t power_class
logger->logDebug(GwLog::LOG, "SD card features: %s", features);
logger->logDebug(GwLog::LOG, "SD card size: %lluMB", ((uint64_t) sdcard->csd.capacity) * sdcard->csd.sector_size / (1024 * 1024));
}
}
#endif
}
void powerInit(String powermode) {
// Max Power | Only 5.0V | Min Power
if (powermode == "Max Power" || powermode == "Only 5.0V") {
#ifdef HARDWARE_V21
setPortPin(OBP_POWER_50, true); // Power on 5.0V rail
#endif
#ifdef BOARD_OBP40S3
setPortPin(OBP_POWER_EPD, true);// Power on ePaper display
setPortPin(OBP_POWER_SD, true); // Power on SD card
#endif
} else { // Min Power
#ifdef HARDWARE_V21
setPortPin(OBP_POWER_50, false); // Power off 5.0V rail
#endif
#ifdef BOARD_OBP40S3
setPortPin(OBP_POWER_EPD, false);// Power off ePaper display
setPortPin(OBP_POWER_SD, false); // Power off SD card
#endif
} }
} }
@@ -134,17 +213,17 @@ void deepSleep(CommonData &common){
setFlashLED(false); // Flash LED Off setFlashLED(false); // Flash LED Off
buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms
// Shutdown EInk display // Shutdown EInk display
getdisplay().setFullWindow(); // Set full Refresh epd->setFullWindow(); // Set full Refresh
getdisplay().fillScreen(common.bgcolor); // Clear screen epd->fillScreen(common.bgcolor); // Clear screen
getdisplay().setTextColor(common.fgcolor); epd->setTextColor(common.fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(85, 150); epd->setCursor(85, 150);
getdisplay().print("Sleep Mode"); epd->print("Sleep Mode");
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(65, 175); epd->setCursor(65, 175);
getdisplay().print("To wake up press key and wait 5s"); epd->print("To wake up press key and wait 5s");
getdisplay().nextPage(); // Update display contents epd->nextPage(); // Update display contents
getdisplay().powerOff(); // Display power off epd->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
@@ -158,18 +237,18 @@ void deepSleep(CommonData &common){
setPortPin(OBP_BACKLIGHT_LED, false); // Backlight Off setPortPin(OBP_BACKLIGHT_LED, false); // Backlight Off
setFlashLED(false); // Flash LED Off setFlashLED(false); // Flash LED Off
// Shutdown EInk display // Shutdown EInk display
getdisplay().setFullWindow(); // Set full Refresh epd->setFullWindow(); // Set full Refresh
//getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update //epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
getdisplay().fillScreen(common.bgcolor); // Clear screen epd->fillScreen(common.bgcolor); // Clear screen
getdisplay().setTextColor(common.fgcolor); epd->setTextColor(common.fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(85, 150); epd->setCursor(85, 150);
getdisplay().print("Sleep Mode"); epd->print("Sleep Mode");
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(65, 175); epd->setCursor(65, 175);
getdisplay().print("To wake up press wheel and wait 5s"); epd->print("To wake up press wheel and wait 5s");
getdisplay().nextPage(); // Partial update epd->nextPage(); // Partial update
getdisplay().powerOff(); // Display power off epd->powerOff(); // Display power off
setPortPin(OBP_POWER_EPD, false); // Power off ePaper display setPortPin(OBP_POWER_EPD, false); // Power off ePaper display
setPortPin(OBP_POWER_SD, false); // Power off SD card setPortPin(OBP_POWER_SD, false); // Power off SD card
// Stop system // Stop system
@@ -214,31 +293,31 @@ void setBacklightLED(uint brightness, const Color &color){
ledTaskData->setLedData(current); ledTaskData->setLedData(current);
} }
void toggleBacklightLED(uint brightness, const Color &color){ void toggleBacklightLED(uint brightness, const Color &color) {
if (ledTaskData == nullptr) return; if (ledTaskData == nullptr) return;
statusBacklightLED = !statusBacklightLED; statusBacklightLED = !statusBacklightLED;
Color nv=setBrightness(statusBacklightLED?color:COLOR_BLACK,brightness); Color nv = setBrightness(statusBacklightLED ? color : COLOR_BLACK, brightness);
LedInterface current=ledTaskData->getLedData(); LedInterface current = ledTaskData->getLedData();
current.setBacklight(nv); current.setBacklight(nv);
ledTaskData->setLedData(current); ledTaskData->setLedData(current);
} }
void setFlashLED(bool status){ void setFlashLED(bool status) {
if (ledTaskData == nullptr) return; if (ledTaskData == nullptr) return;
Color c=status?COLOR_RED:COLOR_BLACK; Color c = status ? COLOR_RED : COLOR_BLACK;
LedInterface current=ledTaskData->getLedData(); LedInterface current = ledTaskData->getLedData();
current.setFlash(c); current.setFlash(c);
ledTaskData->setLedData(current); ledTaskData->setLedData(current);
} }
void blinkingFlashLED(){ void blinkingFlashLED() {
if(blinkingLED == true){ if (blinkingLED == true) {
statusLED = !statusLED; // Toggle LED for each run statusLED = !statusLED; // Toggle LED for each run
setFlashLED(statusLED); setFlashLED(statusLED);
} }
} }
void setBlinkingLED(bool status){ void setBlinkingLED(bool status) {
blinkingLED = status; blinkingLED = status;
} }
@@ -268,36 +347,29 @@ void setBuzzerPower(uint power){
buzzerpower = power; buzzerpower = power;
} }
// Delete xdr prefix from string // Delete xdr prefix from string and optional limit length
String xdrDelete(String input){ String xdrDelete(String input, uint8_t maxlen) {
if(input.substring(0,3) == "xdr"){ if (input.substring(0, 3) == "xdr") {
input = input.substring(3, input.length()); input = input.substring(3, input.length());
} }
if (maxlen > 0) {
return input.substring(0, maxlen);
}
return input; return input;
} }
Point rotatePoint(const Point& origin, const Point& p, double angle) {
// rotate poind around origin by degrees
Point rotated;
double phi = angle * M_PI / 180.0;
double dx = p.x - origin.x;
double dy = p.y - origin.y;
rotated.x = origin.x + cos(phi) * dx - sin(phi) * dy;
rotated.y = origin.y + sin(phi) * dx + cos(phi) * dy;
return rotated;
}
std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle) {
std::vector<Point> rotatedPoints;
for (const auto& p : pts) {
rotatedPoints.push_back(rotatePoint(origin, p, angle));
}
return rotatedPoints;
}
void fillPoly4(const std::vector<Point>& p4, uint16_t color) { void fillPoly4(const std::vector<Point>& p4, uint16_t color) {
getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[1].x, p4[1].y, p4[2].x, p4[2].y, color); epd->fillTriangle(p4[0].x, p4[0].y, p4[1].x, p4[1].y, p4[2].x, p4[2].y, color);
getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[2].x, p4[2].y, p4[3].x, p4[3].y, color); epd->fillTriangle(p4[0].x, p4[0].y, p4[2].x, p4[2].y, p4[3].x, p4[3].y, color);
}
void drawPoly(const std::vector<Point>& points, uint16_t color) {
size_t polysize = points.size();
for (size_t i = 0; i < polysize - 1; i++) {
epd->drawLine(points[i].x, points[i].y, points[i+1].x, points[i+1].y, color);
}
// close path
epd->drawLine(points[polysize-1].x, points[polysize-1].y, points[0].x, points[0].y, color);
} }
// Split string into words, whitespace separated // Split string into words, whitespace separated
@@ -347,34 +419,51 @@ std::vector<String> wordwrap(String &line, uint16_t maxwidth) {
void drawTextCenter(int16_t cx, int16_t cy, String text) { void drawTextCenter(int16_t cx, int16_t cy, String text) {
int16_t x1, y1; int16_t x1, y1;
uint16_t w, h; uint16_t w, h;
getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h); epd->getTextBounds(text, 0, 150, &x1, &y1, &w, &h);
getdisplay().setCursor(cx - w / 2, cy + h / 2); epd->setCursor(cx - w / 2, cy + h / 2);
getdisplay().print(text); epd->print(text);
} }
// Draw right aligned text // Draw right aligned text
void drawTextRalign(int16_t x, int16_t y, String text) { void drawTextRalign(int16_t x, int16_t y, String text) {
int16_t x1, y1; int16_t x1, y1;
uint16_t w, h; uint16_t w, h;
getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h); epd->getTextBounds(text, 0, 150, &x1, &y1, &w, &h);
getdisplay().setCursor(x - w, y); epd->setCursor(x - w, y);
getdisplay().print(text); epd->print(text);
}
// Draw text inside box, normal or inverted
void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border) {
if (inverted) {
epd->fillRect(box.x, box.y, box.w, box.h, fg);
epd->setTextColor(bg);
} else {
if (border) {
epd->fillRect(box.x + 1, box.y + 1, box.w - 2, box.h - 2, bg);
epd->drawRect(box.x, box.y, box.w, box.h, fg);
}
epd->setTextColor(fg);
}
uint16_t border_offset = box.h / 4; // 25% of box height
epd->setCursor(box.x + border_offset, box.y + box.h - border_offset);
epd->print(text);
epd->setTextColor(fg);
} }
// Show a triangle for trend direction high (x, y is the left edge) // Show a triangle for trend direction high (x, y is the left edge)
void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color){ void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color){
getdisplay().fillTriangle(x, y, x+size*2, y, x+size, y-size*2, color); epd->fillTriangle(x, y, x+size*2, y, x+size, y-size*2, color);
} }
// Show a triangle for trend direction low (x, y is the left edge) // Show a triangle for trend direction low (x, y is the left edge)
void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color){ void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color){
getdisplay().fillTriangle(x, y, x+size*2, y, x+size, y+size*2, color); epd->fillTriangle(x, y, x+size*2, y, x+size, y+size*2, color);
} }
// Show header informations // Show header informations
void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatValue *time, GwApi::BoatValue *hdop){ void displayHeader(CommonData &commonData, bool symbolmode, GwApi::BoatValue *date, GwApi::BoatValue *time, GwApi::BoatValue *hdop){
static bool heartbeat = false;
static unsigned long usbRxOld = 0; static unsigned long usbRxOld = 0;
static unsigned long usbTxOld = 0; static unsigned long usbTxOld = 0;
static unsigned long serRxOld = 0; static unsigned long serRxOld = 0;
@@ -386,31 +475,65 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
static unsigned long n2kRxOld = 0; static unsigned long n2kRxOld = 0;
static unsigned long n2kTxOld = 0; static unsigned long n2kTxOld = 0;
uint16_t symbol_x = 2;
static const uint16_t symbol_offset = 20;
// TODO invert and get rid of the if
if(commonData.config->getBool(commonData.config->statusLine) == true){ if(commonData.config->getBool(commonData.config->statusLine) == true){
// Show status info // Show status info
getdisplay().setTextColor(commonData.fgcolor); epd->setTextColor(commonData.fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(0, 15); epd->setCursor(0, 15);
if(commonData.status.wifiApOn){ if (commonData.status.wifiApOn) {
getdisplay().print(" AP "); if (symbolmode) {
epd->drawXBitmap(symbol_x, 1, iconmap["AP"], icon_width, icon_height, commonData.fgcolor);
symbol_x += symbol_offset;
} else {
epd->print(" AP ");
}
} }
// If receive new telegram data then display bus name // If receive new telegram data then display bus name
if(commonData.status.tcpClRx != tcpClRxOld || commonData.status.tcpClTx != tcpClTxOld || commonData.status.tcpSerRx != tcpSerRxOld || commonData.status.tcpSerTx != tcpSerTxOld){ if(commonData.status.tcpClRx != tcpClRxOld || commonData.status.tcpClTx != tcpClTxOld || commonData.status.tcpSerRx != tcpSerRxOld || commonData.status.tcpSerTx != tcpSerTxOld){
getdisplay().print("TCP "); if (symbolmode) {
epd->drawXBitmap(symbol_x, 1, iconmap["TCP"], icon_width, icon_height, commonData.fgcolor);
symbol_x += symbol_offset;
} else {
epd->print("TCP ");
}
} }
if(commonData.status.n2kRx != n2kRxOld || commonData.status.n2kTx != n2kTxOld){ if(commonData.status.n2kRx != n2kRxOld || commonData.status.n2kTx != n2kTxOld){
getdisplay().print("N2K "); if (symbolmode) {
epd->drawXBitmap(symbol_x, 1, iconmap["N2K"], icon_width, icon_height, commonData.fgcolor);
symbol_x += symbol_offset;
} else {
epd->print("N2K ");
}
} }
if(commonData.status.serRx != serRxOld || commonData.status.serTx != serTxOld){ if(commonData.status.serRx != serRxOld || commonData.status.serTx != serTxOld){
getdisplay().print("183 "); if (symbolmode) {
epd->drawXBitmap(symbol_x, 1, iconmap["0183"], icon_width, icon_height, commonData.fgcolor);
symbol_x += symbol_offset;
} else {
epd->print("183 ");
}
} }
if(commonData.status.usbRx != usbRxOld || commonData.status.usbTx != usbTxOld){ if(commonData.status.usbRx != usbRxOld || commonData.status.usbTx != usbTxOld){
getdisplay().print("USB "); if (symbolmode) {
epd->drawXBitmap(symbol_x, 1, iconmap["USB"], icon_width, icon_height, commonData.fgcolor);
symbol_x += symbol_offset;
} else {
epd->print("USB ");
}
} }
double gpshdop = formatValue(hdop, commonData).value; double gpshdop = commonData.fmt->formatValue(hdop, commonData).value;
if(commonData.config->getString(commonData.config->useGPS) != "off" && gpshdop <= commonData.config->getInt(commonData.config->hdopAccuracy) && hdop->valid == true){ if(commonData.config->getString(commonData.config->useGPS) != "off" && gpshdop <= commonData.config->getInt(commonData.config->hdopAccuracy) && hdop->valid == true){
getdisplay().print("GPS"); if (symbolmode) {
epd->drawXBitmap(symbol_x, 1, iconmap["GPS"], icon_width, icon_height, commonData.fgcolor);
symbol_x += symbol_offset;
} else {
epd->print("GPS");
}
} }
// Save old telegram counter // Save old telegram counter
tcpClRxOld = commonData.status.tcpClRx; tcpClRxOld = commonData.status.tcpClRx;
@@ -427,26 +550,26 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
#ifdef HARDWARE_V21 #ifdef HARDWARE_V21
// Display key lock status // Display key lock status
if (commonData.keylock) { if (commonData.keylock) {
getdisplay().drawXBitmap(170, 1, lock_bits, icon_width, icon_height, commonData.fgcolor); epd->drawXBitmap(170, 1, lock_bits, icon_width, icon_height, commonData.fgcolor);
} else { } else {
getdisplay().drawXBitmap(166, 1, swipe_bits, swipe_width, swipe_height, commonData.fgcolor); epd->drawXBitmap(166, 1, swipe_bits, swipe_width, swipe_height, commonData.fgcolor);
} }
#endif #endif
#ifdef LIPO_ACCU_1200 #ifdef LIPO_ACCU_1200
if (commonData.data.BatteryChargeStatus == 1) { if (commonData.data.BatteryChargeStatus == 1) {
getdisplay().drawXBitmap(170, 1, battery_loading_bits, battery_width, battery_height, commonData.fgcolor); epd->drawXBitmap(170, 1, battery_loading_bits, battery_width, battery_height, commonData.fgcolor);
} else { } else {
#ifdef VOLTAGE_SENSOR #ifdef VOLTAGE_SENSOR
if (commonData.data.batteryLevelLiPo < 10) { if (commonData.data.batteryLevelLiPo < 10) {
getdisplay().drawXBitmap(170, 1, battery_0_bits, battery_width, battery_height, commonData.fgcolor); epd->drawXBitmap(170, 1, battery_0_bits, battery_width, battery_height, commonData.fgcolor);
} else if (commonData.data.batteryLevelLiPo < 25) { } else if (commonData.data.batteryLevelLiPo < 25) {
getdisplay().drawXBitmap(170, 1, battery_25_bits, battery_width, battery_height, commonData.fgcolor); epd->drawXBitmap(170, 1, battery_25_bits, battery_width, battery_height, commonData.fgcolor);
} else if (commonData.data.batteryLevelLiPo < 50) { } else if (commonData.data.batteryLevelLiPo < 50) {
getdisplay().drawXBitmap(170, 1, battery_50_bits, battery_width, battery_height, commonData.fgcolor); epd->drawXBitmap(170, 1, battery_50_bits, battery_width, battery_height, commonData.fgcolor);
} else if (commonData.data.batteryLevelLiPo < 75) { } else if (commonData.data.batteryLevelLiPo < 75) {
getdisplay().drawXBitmap(170, 1, battery_75_bits, battery_width, battery_height, commonData.fgcolor); epd->drawXBitmap(170, 1, battery_75_bits, battery_width, battery_height, commonData.fgcolor);
} else { } else {
getdisplay().drawXBitmap(170, 1, battery_100_bits, battery_width, battery_height, commonData.fgcolor); epd->drawXBitmap(170, 1, battery_100_bits, battery_width, battery_height, commonData.fgcolor);
} }
#endif // VOLTAGE_SENSOR #endif // VOLTAGE_SENSOR
} }
@@ -454,33 +577,33 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
// Heartbeat as page number // Heartbeat as page number
if (heartbeat) { if (heartbeat) {
getdisplay().setTextColor(commonData.bgcolor); epd->setTextColor(commonData.bgcolor);
getdisplay().fillRect(201, 0, 23, 19, commonData.fgcolor); epd->fillRect(201, 0, 23, 19, commonData.fgcolor);
} else { } else {
getdisplay().setTextColor(commonData.fgcolor); epd->setTextColor(commonData.fgcolor);
getdisplay().drawRect(201, 0, 23, 19, commonData.fgcolor); epd->drawRect(201, 0, 23, 19, commonData.fgcolor);
} }
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
drawTextCenter(211, 9, String(commonData.data.actpage)); drawTextCenter(211, 9, String(commonData.data.actpage));
heartbeat = !heartbeat; heartbeat = !heartbeat;
// Date and time // Date and time
String fmttype = commonData.config->getString(commonData.config->dateFormat); fmtDate fmttype = commonData.fmt->getDateFormat(commonData.config->getString(commonData.config->dateFormat));
String timesource = commonData.config->getString(commonData.config->timeSource); String timesource = commonData.config->getString(commonData.config->timeSource);
double tz = commonData.config->getString(commonData.config->timeZone).toDouble(); double tz = commonData.config->getString(commonData.config->timeZone).toDouble();
getdisplay().setTextColor(commonData.fgcolor); epd->setTextColor(commonData.fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(230, 15); epd->setCursor(230, 15);
if (timesource == "RTC" or timesource == "iRTC") { if (timesource == "RTC" or timesource == "iRTC") {
// TODO take DST into account // TODO take DST into account
if (commonData.data.rtcValid) { if (commonData.data.rtcValid) {
time_t tv = mktime(&commonData.data.rtcTime) + (int)(tz * 3600); time_t tv = mktime(&commonData.data.rtcTime) + (int)(tz * 3600);
struct tm *local_tm = localtime(&tv); struct tm *local_tm = localtime(&tv);
getdisplay().print(formatTime('m', local_tm->tm_hour, local_tm->tm_min, 0)); epd->print(formatTime(fmtTime::MMHH, local_tm->tm_hour, local_tm->tm_min, 0));
getdisplay().print(" "); epd->print(" ");
getdisplay().print(formatDate(fmttype, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday)); epd->print(formatDate(fmttype, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday));
getdisplay().print(" "); epd->print(" ");
getdisplay().print(tz == 0 ? "UTC" : "LOT"); epd->print(tz == 0 ? "UTC" : "LOT");
} else { } else {
drawTextRalign(396, 15, "RTC invalid"); drawTextRalign(396, 15, "RTC invalid");
} }
@@ -488,18 +611,18 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
else if (timesource == "GPS") { else if (timesource == "GPS") {
// Show date and time if date present // Show date and time if date present
if(date->valid == true){ if(date->valid == true){
String acttime = formatValue(time, commonData).svalue; String acttime = commonData.fmt->formatValue(time, commonData).svalue;
acttime = acttime.substring(0, 5); acttime = acttime.substring(0, 5);
String actdate = formatValue(date, commonData).svalue; String actdate = commonData.fmt->formatValue(date, commonData).svalue;
getdisplay().print(acttime); epd->print(acttime);
getdisplay().print(" "); epd->print(" ");
getdisplay().print(actdate); epd->print(actdate);
getdisplay().print(" "); epd->print(" ");
getdisplay().print(tz == 0 ? "UTC" : "LOT"); epd->print(tz == 0 ? "UTC" : "LOT");
} }
else{ else{
if(commonData.config->getBool(commonData.config->useSimuData) == true){ if(commonData.config->getBool(commonData.config->useSimuData) == true){
getdisplay().print("12:00 01.01.2024 LOT"); epd->print("12:00 01.01.2024 LOT");
} }
else{ else{
drawTextRalign(396, 15, "No GPS data"); drawTextRalign(396, 15, "No GPS data");
@@ -507,15 +630,15 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
} }
} // timesource == "GPS" } // timesource == "GPS"
else { else {
getdisplay().print("No time source"); epd->print("No time source");
} }
} }
} }
void displayFooter(CommonData &commonData) { void displayFooter(CommonData &commonData) {
getdisplay().setFont(&Atari16px); epd->setFont(&Atari16px);
getdisplay().setTextColor(commonData.fgcolor); epd->setTextColor(commonData.fgcolor);
#ifdef HARDWARE_V21 #ifdef HARDWARE_V21
// Frame around key icon area // Frame around key icon area
@@ -524,14 +647,14 @@ void displayFooter(CommonData &commonData) {
const uint16_t top = 280; const uint16_t top = 280;
const uint16_t bottom = 299; const uint16_t bottom = 299;
// horizontal stub lines // horizontal stub lines
getdisplay().drawLine(commonData.keydata[0].x, top, commonData.keydata[0].x+10, top, commonData.fgcolor); epd->drawLine(commonData.keydata[0].x, top, commonData.keydata[0].x+10, top, commonData.fgcolor);
for (int i = 1; i <= 5; i++) { for (int i = 1; i <= 5; i++) {
getdisplay().drawLine(commonData.keydata[i].x-10, top, commonData.keydata[i].x+10, top, commonData.fgcolor); epd->drawLine(commonData.keydata[i].x-10, top, commonData.keydata[i].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); epd->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
for (int i = 0; i <= 4; i++) { for (int i = 0; i <= 4; i++) {
getdisplay().drawLine(commonData.keydata[i].x + commonData.keydata[i].w, top, commonData.keydata[i].x + commonData.keydata[i].w, bottom, commonData.fgcolor); epd->drawLine(commonData.keydata[i].x + commonData.keydata[i].w, top, commonData.keydata[i].x + commonData.keydata[i].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;
@@ -542,7 +665,7 @@ void displayFooter(CommonData &commonData) {
if (iconmap.find(icon_name) != iconmap.end()) { if (iconmap.find(icon_name) != iconmap.end()) {
x = commonData.keydata[i].x + (commonData.keydata[i].w - icon_width) / 2; x = commonData.keydata[i].x + (commonData.keydata[i].w - icon_width) / 2;
y = commonData.keydata[i].y + (commonData.keydata[i].h - icon_height) / 2; y = commonData.keydata[i].y + (commonData.keydata[i].h - icon_height) / 2;
getdisplay().drawXBitmap(x, y, iconmap[icon_name], icon_width, icon_height, commonData.fgcolor); epd->drawXBitmap(x, y, iconmap[icon_name], icon_width, icon_height, commonData.fgcolor);
} else { } else {
// icon is missing, use name instead // icon is missing, use name instead
x = commonData.keydata[i].x + commonData.keydata[i].w / 2; x = commonData.keydata[i].x + commonData.keydata[i].w / 2;
@@ -557,8 +680,8 @@ void displayFooter(CommonData &commonData) {
} }
} }
} else { } else {
getdisplay().setCursor(65, 295); epd->setCursor(65, 295);
getdisplay().print("Press 1 and 6 fast to unlock keys"); epd->print("Press 1 and 6 fast to unlock keys");
} }
#endif #endif
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
@@ -569,21 +692,21 @@ void displayFooter(CommonData &commonData) {
uint16_t x0 = (GxEPD_WIDTH - w) / 2 + r * 2; uint16_t x0 = (GxEPD_WIDTH - w) / 2 + r * 2;
for (int i = 0; i < commonData.data.maxpage; i++) { for (int i = 0; i < commonData.data.maxpage; i++) {
if (i == (commonData.data.actpage - 1)) { if (i == (commonData.data.actpage - 1)) {
getdisplay().fillCircle(x0 + i * (r * 2 + space), 290, r, commonData.fgcolor); epd->fillCircle(x0 + i * (r * 2 + space), 290, r, commonData.fgcolor);
} else { } else {
getdisplay().drawCircle(x0 + i * (r * 2 + space), 290, r, commonData.fgcolor); epd->drawCircle(x0 + i * (r * 2 + space), 290, r, commonData.fgcolor);
} }
} }
// key indicators // key indicators
// left side = top key "menu" // left side = top key "menu"
getdisplay().drawLine(0, 280, 10, 280, commonData.fgcolor); epd->drawLine(0, 280, 10, 280, commonData.fgcolor);
getdisplay().drawLine(55, 280, 65, 280, commonData.fgcolor); epd->drawLine(55, 280, 65, 280, commonData.fgcolor);
getdisplay().drawLine(65, 280, 65, 299, commonData.fgcolor); epd->drawLine(65, 280, 65, 299, commonData.fgcolor);
drawTextCenter(33, 291, commonData.keydata[0].label); drawTextCenter(33, 291, commonData.keydata[0].label);
// right side = bottom key "exit" // right side = bottom key "exit"
getdisplay().drawLine(390, 280, 399, 280, commonData.fgcolor); epd->drawLine(390, 280, 399, 280, commonData.fgcolor);
getdisplay().drawLine(335, 280, 345, 280, commonData.fgcolor); epd->drawLine(335, 280, 345, 280, commonData.fgcolor);
getdisplay().drawLine(335, 280, 335, 399, commonData.fgcolor); epd->drawLine(335, 280, 335, 399, commonData.fgcolor);
drawTextCenter(366, 291, commonData.keydata[1].label); drawTextCenter(366, 291, commonData.keydata[1].label);
#endif #endif
} }
@@ -596,31 +719,31 @@ void displayAlarm(CommonData &commonData) {
const uint16_t w = 300; const uint16_t w = 300;
const uint16_t h = 150; const uint16_t h = 150;
getdisplay().setFont(&Atari16px); epd->setFont(&Atari16px);
getdisplay().setTextColor(commonData.fgcolor); epd->setTextColor(commonData.fgcolor);
// overlay // overlay
getdisplay().drawRect(x, y, w, h, commonData.fgcolor); epd->drawRect(x, y, w, h, commonData.fgcolor);
getdisplay().fillRect(x + 1, y + 1, w - 1, h - 1, commonData.bgcolor); epd->fillRect(x + 1, y + 1, w - 1, h - 1, commonData.bgcolor);
getdisplay().drawRect(x + 3, y + 3, w - 5, h - 5, commonData.fgcolor); epd->drawRect(x + 3, y + 3, w - 5, h - 5, commonData.fgcolor);
// exclamation icon in left top corner // exclamation icon in left top corner
getdisplay().drawXBitmap(x + 16, y + 16, exclamation_bits, exclamation_width, exclamation_height, commonData.fgcolor); epd->drawXBitmap(x + 16, y + 16, exclamation_bits, exclamation_width, exclamation_height, commonData.fgcolor);
// title // title
getdisplay().setCursor(x + 64, y + 30); epd->setCursor(x + 64, y + 30);
getdisplay().print("A L A R M"); epd->print("A L A R M");
getdisplay().setCursor(x + 64, y + 48); epd->setCursor(x + 64, y + 48);
getdisplay().print("#" + commonData.alarm.id); epd->print("#" + commonData.alarm.id);
getdisplay().print(" from "); epd->print(" from ");
getdisplay().print(commonData.alarm.source); epd->print(commonData.alarm.source);
// message, but maximum 4 lines // message, but maximum 4 lines
std::vector<String> lines = wordwrap (commonData.alarm.message, w - 16 - 8 / 8); std::vector<String> lines = wordwrap (commonData.alarm.message, w - 16 - 8 / 8);
int n = 0; int n = 0;
for (const auto& l : lines) { for (const auto& l : lines) {
getdisplay().setCursor(x + 16, y + 80 + n); epd->setCursor(x + 16, y + 80 + n);
getdisplay().print(l); epd->print(l);
n += 16; n += 16;
if (n > 64) { if (n > 64) {
break; break;
@@ -713,20 +836,20 @@ void batteryGraphic(uint x, uint y, float percent, int pcolor, int bcolor){
} }
// Battery corpus 100x80 with fill level // Battery corpus 100x80 with fill level
int level = int((100.0 - percent) * (80-(2*t)) / 100.0); int level = int((100.0 - percent) * (80-(2*t)) / 100.0);
getdisplay().fillRect(xb, yb, 100, 80, pcolor); epd->fillRect(xb, yb, 100, 80, pcolor);
if(percent < 99){ if(percent < 99){
getdisplay().fillRect(xb+t, yb+t, 100-(2*t), level, bcolor); epd->fillRect(xb+t, yb+t, 100-(2*t), level, bcolor);
} }
// Plus pol 20x15 // Plus pol 20x15
int xp = xb + 20; int xp = xb + 20;
int yp = yb - 15 + t; int yp = yb - 15 + t;
getdisplay().fillRect(xp, yp, 20, 15, pcolor); epd->fillRect(xp, yp, 20, 15, pcolor);
getdisplay().fillRect(xp+t, yp+t, 20-(2*t), 15-(2*t), bcolor); epd->fillRect(xp+t, yp+t, 20-(2*t), 15-(2*t), bcolor);
// Minus pol 20x15 // Minus pol 20x15
int xm = xb + 60; int xm = xb + 60;
int ym = yb -15 + t; int ym = yb -15 + t;
getdisplay().fillRect(xm, ym, 20, 15, pcolor); epd->fillRect(xm, ym, 20, 15, pcolor);
getdisplay().fillRect(xm+t, ym+t, 20-(2*t), 15-(2*t), bcolor); epd->fillRect(xm+t, ym+t, 20-(2*t), 15-(2*t), bcolor);
} }
// Solar graphic with fill level // Solar graphic with fill level
@@ -738,17 +861,17 @@ void solarGraphic(uint x, uint y, int pcolor, int bcolor){
int percent = 0; int percent = 0;
// Solar corpus 100x80 // Solar corpus 100x80
int level = int((100.0 - percent) * (80-(2*t)) / 100.0); int level = int((100.0 - percent) * (80-(2*t)) / 100.0);
getdisplay().fillRect(xb, yb, 100, 80, pcolor); epd->fillRect(xb, yb, 100, 80, pcolor);
if(percent < 99){ if(percent < 99){
getdisplay().fillRect(xb+t, yb+t, 100-(2*t), level, bcolor); epd->fillRect(xb+t, yb+t, 100-(2*t), level, bcolor);
} }
// Draw horizontel lines // Draw horizontel lines
getdisplay().fillRect(xb, yb+28-t, 100, t, pcolor); epd->fillRect(xb, yb+28-t, 100, t, pcolor);
getdisplay().fillRect(xb, yb+54-t, 100, t, pcolor); epd->fillRect(xb, yb+54-t, 100, t, pcolor);
// Draw vertical lines // Draw vertical lines
getdisplay().fillRect(xb+19+t, yb, t, 80, pcolor); epd->fillRect(xb+19+t, yb, t, 80, pcolor);
getdisplay().fillRect(xb+39+2*t, yb, t, 80, pcolor); epd->fillRect(xb+39+2*t, yb, t, 80, pcolor);
getdisplay().fillRect(xb+59+3*t, yb, t, 80, pcolor); epd->fillRect(xb+59+3*t, yb, t, 80, pcolor);
} }
@@ -760,13 +883,13 @@ void generatorGraphic(uint x, uint y, int pcolor, int bcolor){
int t = 4; // Line thickness int t = 4; // Line thickness
// Generator corpus with radius 45 // Generator corpus with radius 45
getdisplay().fillCircle(xb, yb, 45, pcolor); epd->fillCircle(xb, yb, 45, pcolor);
getdisplay().fillCircle(xb, yb, 41, bcolor); epd->fillCircle(xb, yb, 41, bcolor);
// Insert G // Insert G
getdisplay().setTextColor(pcolor); epd->setTextColor(pcolor);
getdisplay().setFont(&Ubuntu_Bold32pt8b); epd->setFont(&Ubuntu_Bold32pt8b);
getdisplay().setCursor(xb-22, yb+20); epd->setCursor(xb-22, yb+20);
getdisplay().print("G"); epd->print("G");
} }
// Function to handle HTTP image request // Function to handle HTTP image request
@@ -780,7 +903,7 @@ void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUM
logger->logDebug(GwLog::LOG,"handle image request [%s]: %s", imgformat, filename); logger->logDebug(GwLog::LOG,"handle image request [%s]: %s", imgformat, filename);
uint8_t *fb = getdisplay().getBuffer(); // EPD framebuffer uint8_t *fb = epd->getBuffer(); // EPD framebuffer
std::vector<uint8_t> imageBuffer; // image in webserver transferbuffer std::vector<uint8_t> imageBuffer; // image in webserver transferbuffer
String mimetype; String mimetype;
@@ -803,7 +926,7 @@ void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUM
createPBM(fb, &imageBuffer, GxEPD_WIDTH, GxEPD_HEIGHT); createPBM(fb, &imageBuffer, GxEPD_WIDTH, GxEPD_HEIGHT);
} }
AsyncWebServerResponse *response = request->beginResponse_P(200, mimetype, (const uint8_t*)imageBuffer.data(), imageBuffer.size()); AsyncWebServerResponse *response = request->beginResponse(200, mimetype, (const uint8_t*)imageBuffer.data(), imageBuffer.size());
response->addHeader("Content-Disposition", "inline; filename=" + filename); response->addHeader("Content-Disposition", "inline; filename=" + filename);
request->send(response); request->send(response);

View File

@@ -1,12 +1,21 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef _OBP60EXTENSIONPORT_H #ifndef _OBP60EXTENSIONPORT_H
#define _OBP60EXTENSIONPORT_H #define _OBP60EXTENSIONPORT_H
#include <Arduino.h> #include <Arduino.h>
#include "OBP60Hardware.h" #include "OBP60Hardware.h"
#include "OBP60Formatter.h"
#include "LedSpiTask.h" #include "LedSpiTask.h"
#include "Graphics.h"
#include <GxEPD2_BW.h> // E-paper lib V2 #include <GxEPD2_BW.h> // E-paper lib V2
#include <Adafruit_FRAM_I2C.h> // I2C FRAM #include <Adafruit_FRAM_I2C.h> // I2C FRAM
#ifdef BOARD_OBP40S3
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#define MOUNT_POINT "/sdcard"
#endif
// FRAM address reservations 32kB: 0x0000 - 0x7FFF // FRAM address reservations 32kB: 0x0000 - 0x7FFF
// 0x0000 - 0x03ff: single variables // 0x0000 - 0x03ff: single variables
#define FRAM_PAGE_NO 0x0002 #define FRAM_PAGE_NO 0x0002
@@ -15,6 +24,7 @@
#define FRAM_VOLTAGE_AVG 0x000A #define FRAM_VOLTAGE_AVG 0x000A
#define FRAM_VOLTAGE_TREND 0x000B #define FRAM_VOLTAGE_TREND 0x000B
#define FRAM_VOLTAGE_MODE 0x000C #define FRAM_VOLTAGE_MODE 0x000C
// Wind page
#define FRAM_WIND_SIZE 0x000D #define FRAM_WIND_SIZE 0x000D
#define FRAM_WIND_SRC 0x000E #define FRAM_WIND_SRC 0x000E
#define FRAM_WIND_MODE 0x000F #define FRAM_WIND_MODE 0x000F
@@ -24,6 +34,12 @@
extern Adafruit_FRAM_I2C fram; extern Adafruit_FRAM_I2C fram;
extern bool hasFRAM; extern bool hasFRAM;
extern bool hasSDCard;
#ifdef BOARD_OBP40S3
extern sdmmc_card_t *sdcard;
#endif
extern bool heartbeat;
// Fonts declarations for display (#includes see OBP60Extensions.cpp) // Fonts declarations for display (#includes see OBP60Extensions.cpp)
extern const GFXfont DSEG7Classic_BoldItalic16pt7b; extern const GFXfont DSEG7Classic_BoldItalic16pt7b;
@@ -39,42 +55,37 @@ extern const GFXfont Ubuntu_Bold16pt8b;
extern const GFXfont Ubuntu_Bold20pt8b; extern const GFXfont Ubuntu_Bold20pt8b;
extern const GFXfont Ubuntu_Bold32pt8b; extern const GFXfont Ubuntu_Bold32pt8b;
extern const GFXfont Atari16px; extern const GFXfont Atari16px;
extern const GFXfont Atari6px;
// Global functions // Global functions
#ifdef DISPLAY_GDEW042T2 #ifdef DISPLAY_GDEW042T2
GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> & getdisplay(); typedef GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> gxepd2display;
#endif #endif
#ifdef DISPLAY_GDEY042T81 #ifdef DISPLAY_GDEY042T81
GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> & getdisplay(); typedef GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> gxepd2display;
#endif #endif
#ifdef DISPLAY_GYE042A87 #ifdef DISPLAY_GYE042A87
GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> & getdisplay(); typedef GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> gxepd2display;
#endif #endif
#ifdef DISPLAY_SE0420NQ04 #ifdef DISPLAY_SE0420NQ04
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay(); typedef GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> gxepd2display;
#endif #endif
extern gxepd2display *epd;
// Page display return values // Page display return values
#define PAGE_OK 0 // all ok, do nothing #define PAGE_OK 0 // all ok, do nothing
#define PAGE_UPDATE 1 // page wants display to update #define PAGE_UPDATE 1 // page wants display to update
#define PAGE_HIBERNATE 2 // page wants displey to hibernate #define PAGE_HIBERNATE 2 // page wants displey to hibernate
struct Point {
double x;
double y;
};
Point rotatePoint(const Point& origin, const Point& p, double angle);
std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle);
void fillPoly4(const std::vector<Point>& p4, uint16_t color); void fillPoly4(const std::vector<Point>& p4, uint16_t color);
void drawPoly(const std::vector<Point>& points, uint16_t color);
void deepSleep(CommonData &common); void deepSleep(CommonData &common);
uint8_t getLastPage(); uint8_t getLastPage();
void hardwareInit(GwApi *api); void hardwareInit(GwApi *api);
void powerInit(String powermode);
void setPortPin(uint pin, bool value); // Set port pin for extension port void setPortPin(uint pin, bool value); // Set port pin for extension port
@@ -92,15 +103,16 @@ void setBlinkingLED(bool on); // Set blinking flash LED active
void buzzer(uint frequency, uint duration); // Buzzer function void buzzer(uint frequency, uint duration); // Buzzer function
void setBuzzerPower(uint power); // Set buzzer power void setBuzzerPower(uint power); // Set buzzer power
String xdrDelete(String input); // Delete xdr prefix from string String xdrDelete(String input, uint8_t maxlen = 0); // Delete xdr prefix from string and optional limit length
void drawTextCenter(int16_t cx, int16_t cy, String text); void drawTextCenter(int16_t cx, int16_t cy, String text);
void drawTextRalign(int16_t x, int16_t y, String text); void drawTextRalign(int16_t x, int16_t y, String text);
void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border);
void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color); void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color);
void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color); void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color);
void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatValue *time, GwApi::BoatValue *hdop); // Draw display header void displayHeader(CommonData &commonData, bool symbolmode, GwApi::BoatValue *date, GwApi::BoatValue *time, GwApi::BoatValue *hdop); // Draw display header
void displayFooter(CommonData &commonData); void displayFooter(CommonData &commonData);
void displayAlarm(CommonData &commonData); void displayAlarm(CommonData &commonData);
@@ -148,24 +160,62 @@ static unsigned char fram_bits[] PROGMEM = {
0xff, 0xff, 0xf8, 0x1f, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f,
0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f }; 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f };
static unsigned char ap_bits[] PROGMEM = { // Header symbols
static unsigned char ap_bits[] PROGMEM= {
0xe0, 0x03, 0x18, 0x0c, 0x04, 0x10, 0xc2, 0x21, 0x30, 0x06, 0x08, 0x08, 0xe0, 0x03, 0x18, 0x0c, 0x04, 0x10, 0xc2, 0x21, 0x30, 0x06, 0x08, 0x08,
0xc0, 0x01, 0x20, 0x02, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x20, 0x02, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x01, 0xc0, 0x01,
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 };
static unsigned char dish_bits[] PROGMEM = { static unsigned char gps_bits[] PROGMEM = {
0x3c, 0x00, 0x42, 0x18, 0xfa, 0x1b, 0x02, 0x04, 0x02, 0x0a, 0x02, 0x09, 0x3c, 0x00, 0x42, 0x18, 0xfa, 0x1b, 0x02, 0x04, 0x02, 0x0a, 0x02, 0x09,
0x82, 0x08, 0x06, 0x0a, 0x0e, 0x1b, 0x9c, 0x2b, 0x38, 0x2b, 0x74, 0x20, 0x82, 0x08, 0x06, 0x0a, 0x0e, 0x1b, 0x9c, 0x2b, 0x38, 0x2b, 0x74, 0x20,
0xec, 0x1f, 0x1c, 0x00, 0xf4, 0x00, 0xfe, 0x03 }; 0xec, 0x1f, 0x1c, 0x00, 0xf4, 0x00, 0xfe, 0x03 };
static unsigned char nmea_bits[] PROGMEM = {
0x00, 0x00, 0x22, 0x21, 0x26, 0x33, 0x26, 0x33, 0x2a, 0x2d, 0x32, 0x2d,
0x32, 0x21, 0x22, 0x21, 0x00, 0x00, 0x3c, 0x0c, 0x04, 0x0c, 0x04, 0x12,
0x3c, 0x12, 0x04, 0x1e, 0x04, 0x21, 0x3c, 0x21 };
static unsigned char n2k_bits[] PROGMEM = {
0xe0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x02, 0x40, 0x32, 0x4c, 0x31, 0x8c,
0x01, 0x80, 0x81, 0x81, 0x81, 0x81, 0x01, 0x80, 0x31, 0x8c, 0x32, 0x4c,
0x02, 0x40, 0x04, 0x20, 0x98, 0x19, 0xe0, 0x07 };
static unsigned char tcp_bits[] PROGMEM = {
0x00, 0x00, 0xe0, 0x03, 0x20, 0x02, 0x20, 0x02, 0x20, 0x02, 0xe0, 0x03,
0x80, 0x00, 0x80, 0x00, 0xff, 0xff, 0x08, 0x10, 0x08, 0x10, 0x3e, 0x7c,
0x22, 0x44, 0x22, 0x44, 0x22, 0x44, 0x3e, 0x7c };
static unsigned char usb_bits[] PROGMEM = {
0x00, 0x00, 0x92, 0x39, 0x52, 0x4a, 0x52, 0x48, 0x92, 0x39, 0x12, 0x4a,
0x52, 0x4a, 0x8c, 0x39, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0x04, 0x20,
0xf4, 0x2f, 0x04, 0x20, 0xf8, 0x1f, 0x00, 0x00 };
static unsigned char sdcard_bits[] PROGMEM = {
0xf8, 0x07, 0x0c, 0x08, 0x04, 0x08, 0xc4, 0x09, 0x24, 0x1a, 0xe4, 0x13,
0x04, 0x20, 0x24, 0x21, 0xa4, 0x12, 0x44, 0x12, 0x04, 0x20, 0x04, 0x20,
0xc4, 0x23, 0x34, 0x2c, 0xd8, 0x1b, 0x00, 0x00 };
static unsigned char bluetooth_bits[] PROGMEM = {
0x00, 0x00, 0x22, 0x21, 0x26, 0x33, 0x26, 0x33, 0x2a, 0x2d, 0x32, 0x2d,
0x32, 0x21, 0x22, 0x21, 0x00, 0x00, 0x3c, 0x0c, 0x04, 0x0c, 0x04, 0x12,
0x3c, 0x12, 0x04, 0x1e, 0x04, 0x21, 0x3c, 0x21 };
static std::map<String, unsigned char *> iconmap = { static std::map<String, unsigned char *> iconmap = {
{"LEFT", left_bits}, {"LEFT", left_bits},
{"RIGHT", right_bits}, {"RIGHT", right_bits},
{"LOCK", lock_bits}, {"LOCK", lock_bits},
{"PLUS", plus_bits}, {"PLUS", plus_bits},
{"MINUS", minus_bits}, {"MINUS", minus_bits},
{"DISH", dish_bits}, {"GPS", gps_bits},
{"AP", ap_bits} {"AP", ap_bits},
{"0183", nmea_bits},
{"N2K", n2k_bits},
{"TCP", tcp_bits},
{"USB", usb_bits},
{"SDCARD", sdcard_bits},
{"BLUE", bluetooth_bits}
}; };
// Battery // Battery

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,172 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef _OBP60FORMATTER_H
#define _OBP60FORMATTER_H
#include "GwApi.h"
#include "Pagedata.h"
#include <unordered_map>
/*
XDR types
A Angular displacement
C Temperature
D Linear displacement
F Frequency
G Generic
H Humidity
I Current
L Salinity
N Force
P Pressure
R Flow
S Switch or valve
T Tachometer
U Voltage
V Volume
XDR units
A Ampere
B Bar
C Celsius
D Degrees
H Hertz
I Liter per second?
M Meter / Cubic meter
N Newton
P Percent
R RPM
V Volt
*/
enum class fmtType {
// Formatter names as defined in BoatItemBase
COURSE,
KNOTS,
WIND,
LATITUDE,
LONGITUDE,
XTE,
FIXED0,
DEPTH,
DOP, // dilution of precision
ROT,
DATE,
TIME,
NAME,
kelvinToC, // TODO not a format but conversion
mtr2nm, // TODO not a format but conversion
// XDR Formatter names
XDR_PP, // pressure percent
XDR_PB, // pressure bar
XDR_UV, // voltage volt
XDR_IA, // current ampere
XDR_CK, // temperature kelvin
XDR_CC, // temperature celsius
XDR_HP, // humidity percent
XDR_VP, // volume percent
XDR_VM, // volume cubic meters
XDR_RI, // flow liter per second?
XDR_G, // generic
XDR_AP, // angle percent
XDR_AD, // angle degrees
XDR_TR // tachometer rpm
};
// Hint: String is not supported
static std::unordered_map<const char*, fmtType> formatMap PROGMEM = {
{"formatCourse", fmtType::COURSE},
{"formatKnots", fmtType::KNOTS},
{"formatWind", fmtType::WIND},
{"formatLatitude", fmtType::LATITUDE},
{"formatLongitude", fmtType::LONGITUDE},
{"formatXte", fmtType::XTE},
{"formatFixed0", fmtType::FIXED0},
{"formatDepth", fmtType::DEPTH},
{"formatDop", fmtType::DOP},
{"formatRot", fmtType::ROT},
{"formatDate", fmtType::DATE},
{"formatTime", fmtType::TIME},
{"formatName", fmtType::NAME},
{"kelvinToC", fmtType::kelvinToC},
{"mtr2nm", fmtType::mtr2nm},
{"formatXdr:P:P", fmtType::XDR_PP},
{"formatXdr:P:B", fmtType::XDR_PB},
{"formatXdr:U:V", fmtType::XDR_UV},
{"formatXdr:I:A", fmtType::XDR_IA},
{"formatXdr:C:K", fmtType::XDR_CK},
{"formatXdr:C:C", fmtType::XDR_CC},
{"formatXdr:H:P", fmtType::XDR_HP},
{"formatXdr:V:P", fmtType::XDR_VP},
{"formatXdr:V:M", fmtType::XDR_VM},
{"formatXdr:R:I", fmtType::XDR_RI},
{"formatXdr:G:", fmtType::XDR_G},
{"formatXdr:A:P", fmtType::XDR_AP},
{"formatXdr:A:D", fmtType::XDR_AD},
{"formatXdr:T:R", fmtType::XDR_TR}
};
// Possible formats as scoped enums
enum class fmtDate {DE, GB, US, ISO};
enum class fmtTime {MMHH, MMHHSS};
enum class fmtLength {METER, FEET, FATHOM, CABLE};
enum class fmtDepth {METER, FEET, FATHOM};
enum class fmtWind {KMH, MS, KN, BFT};
enum class fmtCourse {DEG, RAD};
enum class fmtRot {DEGS, RADS};
enum class fmtXte {M, KM, NM, CABLE};
enum class fmtPress {PA, BAR};
enum class fmtTemp {KELVIN, CELSUIS, FAHRENHEIT};
// Conversion factors
#define CONV_M_FT 3.2808399 // meter too feet
#define CONV_M_FM 0.5468 // meter to fathom
#define CONV_M_CBL 0.0053961182483768 // meter to cable
#define CONV_CBL_FT 608 // cable to feet
#define CONV_FM_FT 6 // fathom to feet
// Structure for formatted boat values
typedef struct {
double value;
String svalue;
String unit;
} FormattedData;
// Formatter for boat values
class Formatter {
private:
String stimeZone = "0";
double timeZone = 0.0; // [UTC -14.00...+12.00]
String lengthFormat = "m"; // [m|ft]
String distanceFormat = "nm"; // [m|km|nm]
String speedFormat = "kn"; // [m/s|km/h|kn]
String windspeedFormat = "kn"; // [m/s|km/h|kn|bft]
String tempFormat = "C"; // [K|°C|°F]
String dateFormat = "ISO"; // [DE|GB|US|ISO]
fmtDate dateFmt;
bool usesimudata = false; // [on|off]
String precision = "2"; // [1|2]
const char* fmt_dec_1;
const char* fmt_dec_10;
const char* fmt_dec_100;
public:
Formatter(GwConfigHandler *config);
fmtType stringToFormat(const char* formatStr);
fmtDate getDateFormat(String sformat);
fmtTime getTimeFormat(String sformat);
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata);
String placeholder = "---";
};
// Standard format functions without class and overhead
String formatDate(fmtDate fmttype, uint16_t year, uint8_t month, uint8_t day);
String formatTime(fmtTime fmttype, uint8_t hour, uint8_t minute, uint8_t second);
String formatLatitude(double lat);
String formatLongitude(double lon);
#endif

View File

@@ -82,7 +82,7 @@
// Direction pin for RS485 NMEA0183 // Direction pin for RS485 NMEA0183
#define OBP_DIRECTION_PIN 8 #define OBP_DIRECTION_PIN 8
// I2C // I2C
#define I2C_SPEED 10000UL // 10kHz clock speed on I2C bus #define I2C_SPEED 100000UL // 100kHz clock speed on I2C bus
#define OBP_I2C_SDA 21 #define OBP_I2C_SDA 21
#define OBP_I2C_SCL 38 #define OBP_I2C_SCL 38
// DS1388 RTC // DS1388 RTC
@@ -120,10 +120,10 @@
#define SHOW_TIME 6000 // Show time in [ms] for logo and WiFi QR code #define SHOW_TIME 6000 // Show time in [ms] for logo and WiFi QR code
#define FULL_REFRESH_TIME 600 // Refresh cycle time in [s][600...3600] for full display update (very important healcy function) #define FULL_REFRESH_TIME 600 // Refresh cycle time in [s][600...3600] for full display update (very important healcy function)
// SPI SD-Card // SPI SD-Card
#define SD_SPI_CS 10 #define SD_SPI_CS GPIO_NUM_10
#define SD_SPI_MOSI 40 #define SD_SPI_MOSI GPIO_NUM_40
#define SD_SPI_CLK 39 #define SD_SPI_CLK GPIO_NUM_39
#define SD_SPI_MISO 13 #define SD_SPI_MISO GPIO_NUM_13
// GPS (NEO-6M, NEO-M8N, ATGM336H) // GPS (NEO-6M, NEO-M8N, ATGM336H)
#define OBP_GPS_RX 19 #define OBP_GPS_RX 19

View File

@@ -26,20 +26,20 @@ void qrWiFi(String ssid, String passwd, uint16_t fgcolor, uint16_t bgcolor){
// Each horizontal module // Each horizontal module
for (uint8_t x = 0; x < qrcode.size; x++) { for (uint8_t x = 0; x < qrcode.size; x++) {
if(qrcode_getModule(&qrcode, x, y)){ if(qrcode_getModule(&qrcode, x, y)){
getdisplay().fillRect(box_x, box_y, box_s, box_s, fgcolor); epd->fillRect(box_x, box_y, box_s, box_s, fgcolor);
} else { } else {
getdisplay().fillRect(box_x, box_y, box_s, box_s, bgcolor); epd->fillRect(box_x, box_y, box_s, box_s, bgcolor);
} }
box_x = box_x + box_s; box_x = box_x + box_s;
} }
box_y = box_y + box_s; box_y = box_y + box_s;
box_x = init_x; box_x = init_x;
} }
getdisplay().setFont(&Ubuntu_Bold32pt8b); epd->setFont(&Ubuntu_Bold32pt8b);
getdisplay().setTextColor(fgcolor); epd->setTextColor(fgcolor);
getdisplay().setCursor(140, 285); epd->setCursor(140, 285);
getdisplay().print("WiFi"); epd->print("WiFi");
getdisplay().nextPage(); // Full Refresh epd->nextPage(); // Full Refresh
} }
#endif #endif

View File

@@ -1,5 +1,147 @@
#include "OBPDataOperations.h" #include "OBPDataOperations.h"
// --- Class HstryBuf ---------------
// Init history buffers for selected boat data
void HstryBuf::init(BoatValueList* boatValues, GwLog *log) {
logger = log;
int hstryUpdFreq = 1000; // Update frequency for history buffers in ms
int hstryMinVal = 0; // Minimum value for these history buffers
twdHstryMax = 6283; // Max value for wind direction (TWD, AWD) in rad [0...2*PI], shifted by 1000 for 3 decimals
twsHstryMax = 65000; // Max value for wind speed (TWS, AWS) in m/s [0..65], shifted by 1000 for 3 decimals
awdHstryMax = twdHstryMax;
awsHstryMax = twsHstryMax;
twdHstryMin = hstryMinVal;
twsHstryMin = hstryMinVal;
awdHstryMin = hstryMinVal;
awsHstryMin = hstryMinVal;
const double DBL_MAX = std::numeric_limits<double>::max();
// Initialize history buffers with meta data
hstryBufList.twdHstry->setMetaData("TWD", "formatCourse", hstryUpdFreq, hstryMinVal, twdHstryMax);
hstryBufList.twsHstry->setMetaData("TWS", "formatKnots", hstryUpdFreq, hstryMinVal, twsHstryMax);
hstryBufList.awdHstry->setMetaData("AWD", "formatCourse", hstryUpdFreq, hstryMinVal, twdHstryMax);
hstryBufList.awsHstry->setMetaData("AWS", "formatKnots", hstryUpdFreq, hstryMinVal, twsHstryMax);
// create boat values for history data types, if they don't exist yet
twdBVal = boatValues->findValueOrCreate(hstryBufList.twdHstry->getName());
twsBVal = boatValues->findValueOrCreate(hstryBufList.twsHstry->getName());
twaBVal = boatValues->findValueOrCreate("TWA");
awdBVal = boatValues->findValueOrCreate(hstryBufList.awdHstry->getName());
awsBVal = boatValues->findValueOrCreate(hstryBufList.awsHstry->getName());
if (!awdBVal->valid) { // AWD usually does not exist
awdBVal->setFormat(hstryBufList.awdHstry->getFormat());
awdBVal->value = DBL_MAX;
}
// collect boat values for true wind calculation
awaBVal = boatValues->findValueOrCreate("AWA");
hdtBVal = boatValues->findValueOrCreate("HDT");
hdmBVal = boatValues->findValueOrCreate("HDM");
varBVal = boatValues->findValueOrCreate("VAR");
cogBVal = boatValues->findValueOrCreate("COG");
sogBVal = boatValues->findValueOrCreate("SOG");
}
// Handle history buffers for TWD, TWS, AWD, AWS
//void HstryBuf::handleHstryBuf(GwApi* api, BoatValueList* boatValues, bool useSimuData) {
void HstryBuf::handleHstryBuf(bool useSimuData) {
static int16_t twd = 20; //initial value only relevant if we use simulation data
static uint16_t tws = 20; //initial value only relevant if we use simulation data
static double awd, aws, hdt = 20; //initial value only relevant if we use simulation data
GwApi::BoatValue *calBVal; // temp variable just for data calibration -> we don't want to calibrate the original data here
LOG_DEBUG(GwLog::DEBUG,"obp60task handleHstryBuf: TWD_isValid? %d, twdBVal: %.1f, twaBVal: %.1f, twsBVal: %.1f", twdBVal->valid, twdBVal->value * RAD_TO_DEG,
twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852);
if (twdBVal->valid) {
calBVal = new GwApi::BoatValue("TWD"); // temporary solution for calibration of history buffer values
calBVal->setFormat(twdBVal->getFormat());
calBVal->value = twdBVal->value;
calBVal->valid = twdBVal->valid;
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
twd = static_cast<int16_t>(std::round(calBVal->value * 1000.0));
if (twd >= twdHstryMin && twd <= twdHstryMax) {
hstryBufList.twdHstry->add(twd);
}
delete calBVal;
calBVal = nullptr;
} else if (useSimuData) {
twd += random(-20, 20);
twd = WindUtils::to360(twd);
hstryBufList.twdHstry->add(static_cast<int16_t>(DegToRad(twd) * 1000.0));
}
if (twsBVal->valid) {
calBVal = new GwApi::BoatValue("TWS"); // temporary solution for calibration of history buffer values
calBVal->setFormat(twsBVal->getFormat());
calBVal->value = twsBVal->value;
calBVal->valid = twsBVal->valid;
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
tws = static_cast<uint16_t>(std::round(calBVal->value * 1000));
if (tws >= twsHstryMin && tws <= twsHstryMax) {
hstryBufList.twsHstry->add(tws);
}
delete calBVal;
calBVal = nullptr;
} else if (useSimuData) {
tws += random(-5000, 5000); // TWS value in m/s; expands to 3 decimals
tws = constrain(tws, 0, 25000); // Limit TWS to [0..25] m/s
hstryBufList.twsHstry->add(tws);
}
if (awaBVal->valid) {
if (hdtBVal->valid) {
hdt = hdtBVal->value; // Use HDT if available
} else {
hdt = WindUtils::calcHDT(&hdmBVal->value, &varBVal->value, &cogBVal->value, &sogBVal->value);
}
awd = awaBVal->value + hdt;
awd = WindUtils::to2PI(awd);
calBVal = new GwApi::BoatValue("AWD"); // temporary solution for calibration of history buffer values
calBVal->value = awd;
calBVal->setFormat(awdBVal->getFormat());
calBVal->valid = true;
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
awdBVal->value = calBVal->value;
awdBVal->valid = true;
awd = std::round(calBVal->value * 1000.0);
if (awd >= awdHstryMin && awd <= awdHstryMax) {
hstryBufList.awdHstry->add(static_cast<int16_t>(awd));
}
delete calBVal;
calBVal = nullptr;
} else if (useSimuData) {
awd += random(-20, 20);
awd = WindUtils::to360(awd);
hstryBufList.awdHstry->add(static_cast<int16_t>(DegToRad(awd) * 1000.0));
}
if (awsBVal->valid) {
calBVal = new GwApi::BoatValue("AWS"); // temporary solution for calibration of history buffer values
calBVal->setFormat(awsBVal->getFormat());
calBVal->value = awsBVal->value;
calBVal->valid = awsBVal->valid;
calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated
aws = std::round(calBVal->value * 1000);
if (aws >= awsHstryMin && aws <= awsHstryMax) {
hstryBufList.awsHstry->add(static_cast<uint16_t>(aws));
}
delete calBVal;
calBVal = nullptr;
} else if (useSimuData) {
aws += random(-5000, 5000); // TWS value in m/s; expands to 1 decimal
aws = constrain(aws, 0, 25000); // Limit TWS to [0..25] m/s
hstryBufList.awsHstry->add(aws);
}
}
// --- Class HstryBuf ---------------
// --- Class WindUtils --------------
double WindUtils::to2PI(double a) double WindUtils::to2PI(double a)
{ {
a = fmod(a, 2 * M_PI); a = fmod(a, 2 * M_PI);
@@ -68,13 +210,28 @@ void WindUtils::calcTwdSA(const double* AWA, const double* AWS,
double awd = *AWA + *HDT; double awd = *AWA + *HDT;
awd = to2PI(awd); awd = to2PI(awd);
double stw = -*STW; double stw = -*STW;
// Serial.println("\ncalcTwdSA: AWA: " + String(*AWA) + ", AWS: " + String(*AWS) + ", CTW: " + String(*CTW) + ", STW: " + String(*STW) + ", HDT: " + String(*HDT));
addPolar(&awd, AWS, CTW, &stw, TWD, TWS); addPolar(&awd, AWS, CTW, &stw, TWD, TWS);
// Normalize TWD and TWA to 0-360° // Normalize TWD and TWA to 0-360°
*TWD = to2PI(*TWD); *TWD = to2PI(*TWD);
*TWA = toPI(*TWD - *HDT); *TWA = toPI(*TWD - *HDT);
// Serial.println("calcTwdSA: TWD: " + String(*TWD) + ", TWS: " + String(*TWS)); }
double WindUtils::calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal)
{
double hdt;
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
if (*hdmVal != DBL_MAX) {
hdt = *hdmVal + (*varVal != DBL_MAX ? *varVal : 0.0); // Use corrected HDM if HDT is not available (or just HDM if VAR is not available)
hdt = to2PI(hdt);
} else if (*cogVal != DBL_MAX && *sogVal >= minSogVal) {
hdt = *cogVal; // Use COG as fallback if HDT and HDM are not available, and SOG is not data noise
} else {
hdt = DBL_MAX; // Cannot calculate HDT without valid HDM or HDM+VAR or COG
}
return hdt;
} }
bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal, bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
@@ -83,38 +240,32 @@ bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
{ {
double stw, hdt, ctw; double stw, hdt, ctw;
double twd, tws, twa; double twd, tws, twa;
static const double DBL_MIN = std::numeric_limits<double>::lowest(); double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
if (*hdtVal != DBL_MIN) { if (*hdtVal != DBL_MAX) {
hdt = *hdtVal; // Use HDT if available hdt = *hdtVal; // Use HDT if available
} else { } else {
if (*hdmVal != DBL_MIN && *varVal != DBL_MIN) { hdt = calcHDT(hdmVal, varVal, cogVal, sogVal);
hdt = *hdmVal + *varVal; // Use corrected HDM if HDT is not available
hdt = to2PI(hdt);
} else if (*cogVal != DBL_MIN) {
hdt = *cogVal; // Use COG as fallback if HDT and HDM are not available
} else {
return false; // Cannot calculate without valid HDT or HDM+VAR or COG
}
} }
if (*cogVal != DBL_MIN) { if (*cogVal != DBL_MAX && *sogVal >= minSogVal) { // if SOG is data noise, we don't trust COG
ctw = *cogVal; // Use COG as CTW if available
// ctw = *cogVal + ((*cogVal - hdt) / 2); // Estimate CTW from COG ctw = *cogVal; // Use COG for CTW if available
} else { } else {
ctw = hdt; // 2nd approximation for CTW; hdt must exist if we reach this part of the code ctw = hdt; // 2nd approximation for CTW; hdt must exist if we reach this part of the code
} }
if (*stwVal != DBL_MIN) { if (*stwVal != DBL_MAX) {
stw = *stwVal; // Use STW if available stw = *stwVal; // Use STW if available
} else if (*sogVal != DBL_MIN) { } else if (*sogVal != DBL_MAX) {
stw = *sogVal; stw = *sogVal;
} else { } else {
// If STW and SOG are not available, we cannot calculate true wind // If STW and SOG are not available, we cannot calculate true wind
return false; return false;
} }
// Serial.println("\ncalcTrueWind: HDT: " + String(hdt) + ", CTW: " + String(ctw) + ", STW: " + String(stw));
if ((*awaVal == DBL_MIN) || (*awsVal == DBL_MIN)) { if ((*awaVal == DBL_MAX) || (*awsVal == DBL_MAX)) {
// Cannot calculate true wind without valid AWA, AWS; other checks are done earlier // Cannot calculate true wind without valid AWA, AWS; other checks are done earlier
return false; return false;
} else { } else {
@@ -127,31 +278,45 @@ bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
} }
} }
void HstryBuf::fillWndBufSimData(tBoatHstryData& hstryBufs) // Calculate true wind data and add to obp60task boat data list
// Fill most part of TWD and TWS history buffer with simulated data bool WindUtils::addTrueWind(GwApi* api, BoatValueList* boatValues, GwLog* log) {
{
double value = 20.0; GwLog* logger = log;
int16_t value2 = 0;
for (int i = 0; i < 900; i++) { double awaVal, awsVal, cogVal, stwVal, sogVal, hdtVal, hdmVal, varVal;
value += random(-20, 20); double twd, tws, twa;
value = WindUtils::to360(value); bool isCalculated = false;
value2 = static_cast<int16_t>(value * DEG_TO_RAD * 1000);
hstryBufs.twdHstry->add(value2); awaVal = awaBVal->valid ? awaBVal->value : DBL_MAX;
awsVal = awsBVal->valid ? awsBVal->value : DBL_MAX;
cogVal = cogBVal->valid ? cogBVal->value : DBL_MAX;
stwVal = stwBVal->valid ? stwBVal->value : DBL_MAX;
sogVal = sogBVal->valid ? sogBVal->value : DBL_MAX;
hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MAX;
hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MAX;
varVal = varBVal->valid ? varBVal->value : DBL_MAX;
LOG_DEBUG(GwLog::DEBUG,"obp60task addTrueWind: AWA %.1f, AWS %.1f, COG %.1f, STW %.1f, SOG %.2f, HDT %.1f, HDM %.1f, VAR %.1f", awaBVal->value * RAD_TO_DEG, awsBVal->value * 3.6 / 1.852,
cogBVal->value * RAD_TO_DEG, stwBVal->value * 3.6 / 1.852, sogBVal->value * 3.6 / 1.852, hdtBVal->value * RAD_TO_DEG, hdmBVal->value * RAD_TO_DEG, varBVal->value * RAD_TO_DEG);
isCalculated = calcTrueWind(&awaVal, &awsVal, &cogVal, &stwVal, &sogVal, &hdtVal, &hdmVal, &varVal, &twd, &tws, &twa);
if (isCalculated) { // Replace values only, if successfully calculated and not already available
if (!twdBVal->valid) {
twdBVal->value = twd;
twdBVal->valid = true;
}
if (!twsBVal->valid) {
twsBVal->value = tws;
twsBVal->valid = true;
}
if (!twaBVal->valid) {
twaBVal->value = twa;
twaBVal->valid = true;
}
} }
LOG_DEBUG(GwLog::DEBUG,"obp60task addTrueWind: isCalculated %d, TWD %.1f, TWA %.1f, TWS %.1f", isCalculated, twdBVal->value * RAD_TO_DEG,
twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852);
return isCalculated;
} }
// --- Class WindUtils --------------
/* double genTwdSimDat()
{
simTwd += random(-20, 20);
if (simTwd < 0.0)
simTwd += 360.0;
if (simTwd >= 360.0)
simTwd -= 360.0;
int16_t z = static_cast<int16_t>(DegToRad(simTwd) * 1000.0);
pageData.boatHstry.twdHstry->add(z); // Fill the buffer with some test data
simTws += random(-200, 150) / 10.0; // TWS value in knots
simTws = constrain(simTws, 0.0f, 50.0f); // Ensure TWS is between 0 and 50 knots
twsValue = simTws;
}*/

View File

@@ -1,36 +1,90 @@
#pragma once #pragma once
#include "GwApi.h" #include <N2kMessages.h>
#include "OBPRingBuffer.h" #include "OBPRingBuffer.h"
#include <Arduino.h> #include "BoatDataCalibration.h" // Functions lib for data instance calibration
#include "obp60task.h"
#include <math.h> #include <math.h>
typedef struct { typedef struct {
RingBuffer<int16_t>* twdHstry; RingBuffer<int16_t>* twdHstry;
RingBuffer<int16_t>* twsHstry; RingBuffer<uint16_t>* twsHstry;
RingBuffer<int16_t>* awdHstry;
RingBuffer<uint16_t>* awsHstry;
} tBoatHstryData; // Holds pointers to all history buffers for boat data } tBoatHstryData; // Holds pointers to all history buffers for boat data
class HstryBuf { class HstryBuf {
private:
GwLog *logger;
RingBuffer<int16_t> twdHstry; // Circular buffer to store true wind direction values
RingBuffer<uint16_t> twsHstry; // Circular buffer to store true wind speed values (TWS)
RingBuffer<int16_t> awdHstry; // Circular buffer to store apparant wind direction values
RingBuffer<uint16_t> awsHstry; // Circular buffer to store apparant xwind speed values (AWS)
int16_t twdHstryMin; // Min value for wind direction (TWD) in history buffer
int16_t twdHstryMax; // Max value for wind direction (TWD) in history buffer
uint16_t twsHstryMin;
uint16_t twsHstryMax;
int16_t awdHstryMin;
int16_t awdHstryMax;
uint16_t awsHstryMin;
uint16_t awsHstryMax;
// boat values for buffers and for true wind calculation
GwApi::BoatValue *twdBVal, *twsBVal, *twaBVal, *awdBVal, *awsBVal;
GwApi::BoatValue *awaBVal, *hdtBVal, *hdmBVal, *varBVal, *cogBVal, *sogBVal;
public: public:
void fillWndBufSimData(tBoatHstryData& hstryBufs); // Fill most part of the TWD and TWS history buffer with simulated data tBoatHstryData hstryBufList;
HstryBuf(){
hstryBufList = {&twdHstry, &twsHstry, &awdHstry, &awsHstry}; // Generate history buffers of zero size
};
HstryBuf(int size) {
hstryBufList = {&twdHstry, &twsHstry, &awdHstry, &awsHstry};
hstryBufList.twdHstry->resize(960); // store 960 TWD values for 16 minutes history
hstryBufList.twsHstry->resize(960);
hstryBufList.awdHstry->resize(960);
hstryBufList.awsHstry->resize(960);
};
void init(BoatValueList* boatValues, GwLog *log);
void handleHstryBuf(bool useSimuData);
}; };
class WindUtils { class WindUtils {
private:
GwApi::BoatValue *twdBVal, *twsBVal, *twaBVal;
GwApi::BoatValue *awaBVal, *awsBVal, *cogBVal, *stwBVal, *sogBVal, *hdtBVal, *hdmBVal, *varBVal;
static constexpr double DBL_MAX = std::numeric_limits<double>::max();
public: public:
WindUtils(BoatValueList* boatValues){
twdBVal = boatValues->findValueOrCreate("TWD");
twsBVal = boatValues->findValueOrCreate("TWS");
twaBVal = boatValues->findValueOrCreate("TWA");
awaBVal = boatValues->findValueOrCreate("AWA");
awsBVal = boatValues->findValueOrCreate("AWS");
cogBVal = boatValues->findValueOrCreate("COG");
stwBVal = boatValues->findValueOrCreate("STW");
sogBVal = boatValues->findValueOrCreate("SOG");
hdtBVal = boatValues->findValueOrCreate("HDT");
hdmBVal = boatValues->findValueOrCreate("HDM");
varBVal = boatValues->findValueOrCreate("VAR");
};
static double to2PI(double a); static double to2PI(double a);
static double toPI(double a); static double toPI(double a);
static double to360(double a); static double to360(double a);
static double to180(double a); static double to180(double a);
static void toCart(const double* phi, const double* r, double* x, double* y); void toCart(const double* phi, const double* r, double* x, double* y);
static void toPol(const double* x, const double* y, double* phi, double* r); void toPol(const double* x, const double* y, double* phi, double* r);
static void addPolar(const double* phi1, const double* r1, void addPolar(const double* phi1, const double* r1,
const double* phi2, const double* r2, const double* phi2, const double* r2,
double* phi, double* r); double* phi, double* r);
static void calcTwdSA(const double* AWA, const double* AWS, void calcTwdSA(const double* AWA, const double* AWS,
const double* CTW, const double* STW, const double* HDT, const double* CTW, const double* STW, const double* HDT,
double* TWD, double* TWS, double* TWA); double* TWD, double* TWS, double* TWA);
static bool calcTrueWind(const double* awaVal, const double* awsVal, static double calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal);
bool calcTrueWind(const double* awaVal, const double* awsVal,
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal, const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal); const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal);
bool addTrueWind(GwApi* api, BoatValueList* boatValues, GwLog *log);
}; };

View File

@@ -1,9 +1,10 @@
#ifndef _OBP60FUNCTIONS_H // SPDX-License-Identifier: GPL-2.0-or-later
#define _OBP60FUNCTIONS_H #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include <Arduino.h> #include <Arduino.h>
#include "OBP60Hardware.h" #include "OBP60Hardware.h"
#include "OBP60Extensions.h" // for buzzer
#include "OBPKeyboardTask.h"
// Global vars // Global vars
// Touch keypad over ESP32 touch sensor inputs // Touch keypad over ESP32 touch sensor inputs
@@ -58,10 +59,10 @@ void initKeys(CommonData &commonData) {
commonData.keydata[5].h = height; commonData.keydata[5].h = height;
} }
#ifdef HARDWARE_V21 #ifdef HARDWARE_V21
// Keypad functions for original OBP60 hardware // Keypad functions for original OBP60 hardware
int readKeypad(GwLog* logger, uint thSensitivity, bool use_syspage) { int readKeypad(GwLog* logger, uint thSensitivity) {
// Touch sensor values // Touch sensor values
// 35000 - Not touched // 35000 - Not touched
// 50000 - Light toched with fingertip // 50000 - Light toched with fingertip
@@ -233,35 +234,35 @@ void initKeys(CommonData &commonData) {
keycodeold2 = keycode2; keycodeold2 = keycode2;
return keystatus; return keystatus;
} }
#endif #endif
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
int readSensorpads(){ int readSensorpads(){
// Read key code // Read key code
if(digitalRead(UP) == LOW){ if (digitalRead(UP) == LOW) {
keycode = 10; // Left swipe keycode = 10; // Left swipe
}
else if(digitalRead(DOWN) == LOW){
keycode = 9; // Right swipe
}
else if(digitalRead(CONF) == LOW){
keycode = 3; // Key 3
}
else if(digitalRead(MENUE) == LOW){
keycode = 1; // Key 1
}
else if(digitalRead(EXIT) == LOW){
keycode = 2; // Key 2
}
else{
keycode = 0; // No key activ
}
return keycode;
} }
else if (digitalRead(DOWN) == LOW) {
keycode = 9; // Right swipe
}
else if (digitalRead(CONF) == LOW) {
keycode = 3; // Key 3
}
else if (digitalRead(MENUE) == LOW) {
keycode = 1; // Key 1
}
else if (digitalRead(EXIT) == LOW) {
keycode = 2; // Key 2
}
else {
keycode = 0; // No key activ
}
return keycode;
}
// Keypad functions for OBP60 clone (thSensitivity is inactiv) // Keypad functions for OBP60 clone (thSensitivity is inactiv)
int readKeypad(GwLog* logger, uint thSensitivity, bool use_syspage) { int readKeypad(GwLog* logger, uint thSensitivity, bool use_syspage) {
pinMode(UP, INPUT); pinMode(UP, INPUT);
pinMode(DOWN, INPUT); pinMode(DOWN, INPUT);
pinMode(CONF, INPUT); pinMode(CONF, INPUT);
@@ -273,31 +274,68 @@ void initKeys(CommonData &commonData) {
// Detect key // Detect key
if (keycode > 0 ){ if (keycode > 0 ){
if(keycode != keycodeold){ if(keycode != keycodeold){
starttime = millis(); // Start key pressed starttime = millis(); // Start key pressed
keycodeold = keycode; keycodeold = keycode;
} }
// If key pressed longer than 100ms // If key pressed longer than 100ms
if(millis() > starttime + 100 && 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 {
keystatus = keycode; keystatus = keycode;
}
// Copy keycode
keycodeold = keycode;
// 100% Task-CPU RLY?
while(readSensorpads() > 0){} // Wait for pad release
delay(keydelay);
} }
// Copy keycode
keycodeold = keycode;
while(readSensorpads() > 0){} // Wait for pad release
delay(keydelay);
}
} }
else{ else {
keycode = 0; keycode = 0;
keycodeold = 0; keycodeold = 0;
keystatus = 0; keystatus = 0;
} }
return keystatus; return keystatus;
} }
#endif #endif
void keyboardTask(void *param) {
// params needed:
// queue
// logger
// sensitivity
// use_syspage for deep sleep activation
KbTaskData *data = (KbTaskData *)param;
int keycode = 0;
data->logger->logDebug(GwLog::LOG, "Start keyboard task");
while (true) {
#ifdef BOARD_OBP40S3
keycode = readKeypad(data->logger, data->sensitivity, data->use_syspage);
#else
keycode = readKeypad(data->logger, data->sensitivity);
#endif
//send a key event
if (keycode != 0) {
xQueueSend(data->queue, &keycode, 0);
data->logger->logDebug(GwLog::LOG,"kbtask: send keycode: %d", keycode);
}
delay(20); // 50Hz update rate (20ms)
}
vTaskDelete(NULL);
}
void createKeyboardTask(KbTaskData *param) {
TaskHandle_t xHandle = NULL;
if (xTaskCreate(keyboardTask, "keyboard", configMINIMAL_STACK_SIZE + 1024, param, configMAX_PRIORITIES-1, &xHandle) != pdPASS) {
param->logger->logDebug(GwLog::ERROR, "Failed to create keyboard task!");
};
}
#endif #endif

View File

@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "GwLog.h"
#include "Pagedata.h"
typedef struct {
QueueHandle_t queue;
GwLog* logger = nullptr;
uint sensitivity = 100;
#ifdef BOARD_OBP40S3
bool use_syspage = true;
#endif
} KbTaskData;
void initKeys(CommonData &commonData);
void createKeyboardTask(KbTaskData *param);

View File

@@ -9,29 +9,34 @@
template <typename T> template <typename T>
class RingBuffer { class RingBuffer {
private: private:
mutable SemaphoreHandle_t bufLocker; std::vector<T> buffer; // THE buffer vector
std::vector<T> buffer;
size_t capacity; size_t capacity;
size_t head; // Points to the next insertion position size_t head; // Points to the next insertion position
size_t first; // Points to the first (oldest) valid element size_t first; // Points to the first (oldest) valid element
size_t last; // Points to the last (newest) valid element size_t last; // Points to the last (newest) valid element
size_t count; // Number of valid elements currently in buffer size_t count; // Number of valid elements currently in buffer
bool is_Full; // Indicates that all buffer elements are used and ringing is in use bool is_Full; // Indicates that all buffer elements are used and ringing is in use
T MIN_VAL; // lowest possible value of buffer T MIN_VAL; // lowest possible value of buffer of type <T>
T MAX_VAL; // highest possible value of buffer of type <T> T MAX_VAL; // highest possible value of buffer of type <T> -> indicates invalid value in buffer
mutable SemaphoreHandle_t bufLocker;
// metadata for buffer // metadata for buffer
String dataName; // Name of boat data in buffer String dataName; // Name of boat data in buffer
String dataFmt; // Format of boat data in buffer String dataFmt; // Format of boat data in buffer
int updFreq; // Update frequency in milliseconds int updFreq; // Update frequency in milliseconds
T smallest; // Value range of buffer: smallest value T smallest; // Value range of buffer: smallest value; needs to be => MIN_VAL
T largest; // Value range of buffer: biggest value T largest; // Value range of buffer: biggest value; needs to be < MAX_VAL, since MAX_VAL indicates invalid entries
void initCommon();
public: public:
RingBuffer();
RingBuffer(size_t size); RingBuffer(size_t size);
void setMetaData(String name, String format, int updateFrequency, T minValue, T maxValue); // Set meta data for buffer void setMetaData(String name, String format, int updateFrequency, T minValue, T maxValue); // Set meta data for buffer
bool getMetaData(String& name, String& format, int& updateFrequency, T& minValue, T& maxValue); // Get meta data of buffer bool getMetaData(String& name, String& format, int& updateFrequency, T& minValue, T& maxValue); // Get meta data of buffer
bool getMetaData(String& name, String& format);
String getName() const; // Get buffer name String getName() const; // Get buffer name
String getFormat() const; // Get buffer data format
void add(const T& value); // Add a new value to buffer void add(const T& value); // Add a new value to buffer
T get(size_t index) const; // Get value at specific position (0-based index from oldest to newest) T get(size_t index) const; // Get value at specific position (0-based index from oldest to newest)
T getFirst() const; // Get the first (oldest) value in buffer T getFirst() const; // Get the first (oldest) value in buffer
@@ -50,9 +55,10 @@ public:
size_t getLastIdx() const; // Get the index of newest value in buffer size_t getLastIdx() const; // Get the index of newest value in buffer
bool isEmpty() const; // Check if buffer is empty bool isEmpty() const; // Check if buffer is empty
bool isFull() const; // Check if buffer is full bool isFull() const; // Check if buffer is full
T getMinVal() const; // Get lowest possible value for buffer; used for initialized buffer data T getMinVal() const; // Get lowest possible value for buffer
T getMaxVal() const; // Get highest possible value for buffer T getMaxVal() const; // Get highest possible value for buffer; used for unset/invalid buffer data
void clear(); // Clear buffer void clear(); // Clear buffer
void resize(size_t size); // Delete buffer and set new size
T operator[](size_t index) const; // Operator[] for convenient access (same as get()) T operator[](size_t index) const; // Operator[] for convenient access (same as get())
std::vector<T> getAllValues() const; // Get all current values as a vector std::vector<T> getAllValues() const; // Get all current values as a vector
}; };

View File

@@ -1,5 +1,30 @@
#include "OBPRingBuffer.h" #include "OBPRingBuffer.h"
template <typename T>
void RingBuffer<T>::initCommon() {
MIN_VAL = std::numeric_limits<T>::lowest();
MAX_VAL = std::numeric_limits<T>::max();
dataName = "";
dataFmt = "";
updFreq = -1;
smallest = MIN_VAL;
largest = MAX_VAL;
bufLocker = xSemaphoreCreateMutex();
}
template <typename T>
RingBuffer<T>::RingBuffer()
: capacity(0)
, head(0)
, first(0)
, last(0)
, count(0)
, is_Full(false)
{
initCommon();
// <buffer> stays empty
}
template <typename T> template <typename T>
RingBuffer<T>::RingBuffer(size_t size) RingBuffer<T>::RingBuffer(size_t size)
: capacity(size) : capacity(size)
@@ -9,23 +34,8 @@ RingBuffer<T>::RingBuffer(size_t size)
, count(0) , count(0)
, is_Full(false) , is_Full(false)
{ {
bufLocker = xSemaphoreCreateMutex(); initCommon();
buffer.resize(size, MAX_VAL); // MAX_VAL indicate invalid values
if (size == 0) {
// return false;
}
MIN_VAL = std::numeric_limits<T>::lowest();
MAX_VAL = std::numeric_limits<T>::max();
dataName = "";
dataFmt = "";
updFreq = -1;
smallest = MIN_VAL;
largest = MAX_VAL;
buffer.resize(size, MIN_VAL);
// return true;
} }
// Specify meta data of buffer content // Specify meta data of buffer content
@@ -57,6 +67,20 @@ bool RingBuffer<T>::getMetaData(String& name, String& format, int& updateFrequen
return true; return true;
} }
// Get meta data of buffer content
template <typename T>
bool RingBuffer<T>::getMetaData(String& name, String& format)
{
if (dataName == "" || dataFmt == "") {
return false; // Meta data not set
}
GWSYNCHRONIZED(&bufLocker);
name = dataName;
format = dataFmt;
return true;
}
// Get buffer name // Get buffer name
template <typename T> template <typename T>
String RingBuffer<T>::getName() const String RingBuffer<T>::getName() const
@@ -64,13 +88,20 @@ String RingBuffer<T>::getName() const
return dataName; return dataName;
} }
// Get buffer data format
template <typename T>
String RingBuffer<T>::getFormat() const
{
return dataFmt;
}
// Add a new value to buffer // Add a new value to buffer
template <typename T> template <typename T>
void RingBuffer<T>::add(const T& value) void RingBuffer<T>::add(const T& value)
{ {
GWSYNCHRONIZED(&bufLocker); GWSYNCHRONIZED(&bufLocker);
if (value < smallest || value > largest) { if (value < smallest || value > largest) {
buffer[head] = MIN_VAL; // Store MIN_VAL if value is out of range buffer[head] = MAX_VAL; // Store MAX_VAL if value is out of range
} else { } else {
buffer[head] = value; buffer[head] = value;
} }
@@ -94,7 +125,7 @@ T RingBuffer<T>::get(size_t index) const
{ {
GWSYNCHRONIZED(&bufLocker); GWSYNCHRONIZED(&bufLocker);
if (isEmpty() || index < 0 || index >= count) { if (isEmpty() || index < 0 || index >= count) {
return MIN_VAL; return MAX_VAL;
} }
size_t realIndex = (first + index) % capacity; size_t realIndex = (first + index) % capacity;
@@ -113,7 +144,7 @@ template <typename T>
T RingBuffer<T>::getFirst() const T RingBuffer<T>::getFirst() const
{ {
if (isEmpty()) { if (isEmpty()) {
return MIN_VAL; return MAX_VAL;
} }
return get(0); return get(0);
} }
@@ -123,7 +154,7 @@ template <typename T>
T RingBuffer<T>::getLast() const T RingBuffer<T>::getLast() const
{ {
if (isEmpty()) { if (isEmpty()) {
return MIN_VAL; return MAX_VAL;
} }
return get(count - 1); return get(count - 1);
} }
@@ -133,14 +164,14 @@ template <typename T>
T RingBuffer<T>::getMin() const T RingBuffer<T>::getMin() const
{ {
if (isEmpty()) { if (isEmpty()) {
return MIN_VAL; return MAX_VAL;
} }
T minVal = MAX_VAL; T minVal = MAX_VAL;
T value; T value;
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
value = get(i); value = get(i);
if (value < minVal && value != MIN_VAL) { if (value < minVal && value != MAX_VAL) {
minVal = value; minVal = value;
} }
} }
@@ -152,7 +183,7 @@ template <typename T>
T RingBuffer<T>::getMin(size_t amount) const T RingBuffer<T>::getMin(size_t amount) const
{ {
if (isEmpty() || amount <= 0) { if (isEmpty() || amount <= 0) {
return MIN_VAL; return MAX_VAL;
} }
if (amount > count) if (amount > count)
amount = count; amount = count;
@@ -161,7 +192,7 @@ T RingBuffer<T>::getMin(size_t amount) const
T value; T value;
for (size_t i = 0; i < amount; i++) { for (size_t i = 0; i < amount; i++) {
value = get(count - 1 - i); value = get(count - 1 - i);
if (value < minVal && value != MIN_VAL) { if (value < minVal && value != MAX_VAL) {
minVal = value; minVal = value;
} }
} }
@@ -173,14 +204,14 @@ template <typename T>
T RingBuffer<T>::getMax() const T RingBuffer<T>::getMax() const
{ {
if (isEmpty()) { if (isEmpty()) {
return MIN_VAL; return MAX_VAL;
} }
T maxVal = MIN_VAL; T maxVal = MIN_VAL;
T value; T value;
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
value = get(i); value = get(i);
if (value > maxVal && value != MIN_VAL) { if (value > maxVal && value != MAX_VAL) {
maxVal = value; maxVal = value;
} }
} }
@@ -192,7 +223,7 @@ template <typename T>
T RingBuffer<T>::getMax(size_t amount) const T RingBuffer<T>::getMax(size_t amount) const
{ {
if (isEmpty() || amount <= 0) { if (isEmpty() || amount <= 0) {
return MIN_VAL; return MAX_VAL;
} }
if (amount > count) if (amount > count)
amount = count; amount = count;
@@ -201,7 +232,7 @@ T RingBuffer<T>::getMax(size_t amount) const
T value; T value;
for (size_t i = 0; i < amount; i++) { for (size_t i = 0; i < amount; i++) {
value = get(count - 1 - i); value = get(count - 1 - i);
if (value > maxVal && value != MIN_VAL) { if (value > maxVal && value != MAX_VAL) {
maxVal = value; maxVal = value;
} }
} }
@@ -213,7 +244,7 @@ template <typename T>
T RingBuffer<T>::getMid() const T RingBuffer<T>::getMid() const
{ {
if (isEmpty()) { if (isEmpty()) {
return MIN_VAL; return MAX_VAL;
} }
return (getMin() + getMax()) / static_cast<T>(2); return (getMin() + getMax()) / static_cast<T>(2);
@@ -224,7 +255,7 @@ template <typename T>
T RingBuffer<T>::getMid(size_t amount) const T RingBuffer<T>::getMid(size_t amount) const
{ {
if (isEmpty() || amount <= 0) { if (isEmpty() || amount <= 0) {
return MIN_VAL; return MAX_VAL;
} }
if (amount > count) if (amount > count)
@@ -238,7 +269,7 @@ template <typename T>
T RingBuffer<T>::getMedian() const T RingBuffer<T>::getMedian() const
{ {
if (isEmpty()) { if (isEmpty()) {
return MIN_VAL; return MAX_VAL;
} }
// Create a temporary vector with current valid elements // Create a temporary vector with current valid elements
@@ -267,7 +298,7 @@ template <typename T>
T RingBuffer<T>::getMedian(size_t amount) const T RingBuffer<T>::getMedian(size_t amount) const
{ {
if (isEmpty() || amount <= 0) { if (isEmpty() || amount <= 0) {
return MIN_VAL; return MAX_VAL;
} }
if (amount > count) if (amount > count)
amount = count; amount = count;
@@ -335,14 +366,14 @@ bool RingBuffer<T>::isFull() const
return is_Full; return is_Full;
} }
// Get lowest possible value for buffer; used for non-set buffer data // Get lowest possible value for buffer
template <typename T> template <typename T>
T RingBuffer<T>::getMinVal() const T RingBuffer<T>::getMinVal() const
{ {
return MIN_VAL; return MIN_VAL;
} }
// Get highest possible value for buffer // Get highest possible value for buffer; used for unset/invalid buffer data
template <typename T> template <typename T>
T RingBuffer<T>::getMaxVal() const T RingBuffer<T>::getMaxVal() const
{ {
@@ -361,6 +392,22 @@ void RingBuffer<T>::clear()
is_Full = false; is_Full = false;
} }
// Delete buffer and set new size
template <typename T>
void RingBuffer<T>::resize(size_t newSize)
{
GWSYNCHRONIZED(&bufLocker);
capacity = newSize;
head = 0;
first = 0;
last = 0;
count = 0;
is_Full = false;
buffer.clear();
buffer.resize(newSize, MAX_VAL);
}
// Get all current values as a vector // Get all current values as a vector
template <typename T> template <typename T>
std::vector<T> RingBuffer<T>::getAllValues() const std::vector<T> RingBuffer<T>::getAllValues() const

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include <Adafruit_Sensor.h> // Adafruit Lib for sensors #include <Adafruit_Sensor.h> // Adafruit Lib for sensors
#include <Adafruit_BME280.h> // Adafruit Lib for BME280 #include <Adafruit_BME280.h> // Adafruit Lib for BME280
@@ -90,16 +91,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"){
#ifdef VOLTAGE_SENSOR #ifdef VOLTAGE_SENSOR
float rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2; // Vin = 1/2 for OBP40 float rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2; // Vin = 1/2 for OBP40
#else #else
float rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 for OBP60 float rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 for OBP60
#endif #endif
sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
#ifdef LIPO_ACCU_1200 #ifdef LIPO_ACCU_1200
sensors.BatteryChargeStatus = 0; // Set to discharging sensors.BatteryChargeStatus = 0; // Set to discharging
sensors.batteryLevelLiPo = 0; // Level 0...100% sensors.batteryLevelLiPo = 0; // Level 0...100%
#endif #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
@@ -499,24 +500,24 @@ void sensorTask(void *param){
if(millis() > starttime5 + 1000 && String(powsensor1) == "off"){ if(millis() > starttime5 + 1000 && String(powsensor1) == "off"){
starttime5 = millis(); starttime5 = millis();
float rawVoltage = 0; // Default value float rawVoltage = 0; // Default value
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
sensors.batteryVoltage = 0; // If no sensor then zero voltage sensors.batteryVoltage = 0; // If no sensor then zero voltage
#endif #endif
#if defined(BOARD_OBP40S3) && defined(VOLTAGE_SENSOR) #if defined(BOARD_OBP40S3) && defined(VOLTAGE_SENSOR)
rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2; // Vin = 1/2 for OBP40 rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2; // Vin = 1/2 for OBP40
sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
#endif #endif
#ifdef BOARD_OBP60S3 #ifdef BOARD_OBP60S3
rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 for OBP60 rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 for OBP60
sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
#endif #endif
// 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 BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR #if BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR
// Polynomfit for LiPo capacity calculation for 3,7V LiPo accus, 0...100% // Polynomfit for LiPo capacity calculation for 3,7V LiPo accus, 0...100%
sensors.batteryLevelLiPo = sensors.batteryVoltage60 * 203.8312 -738.1635; sensors.batteryLevelLiPo = sensors.batteryVoltage60 * 203.8312 -738.1635;
// Limiter // Limiter
@@ -555,19 +556,24 @@ void sensorTask(void *param){
SetN2kDCBatStatus(N2kMsg, 10, sensors.batteryVoltage, N2kDoubleNA, N2kDoubleNA, 0); SetN2kDCBatStatus(N2kMsg, 10, sensors.batteryVoltage, N2kDoubleNA, N2kDoubleNA, 0);
api->sendN2kMessage(N2kMsg); api->sendN2kMessage(N2kMsg);
} }
#endif #endif
#ifdef BOARD_OBP60S3 #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 #endif
} }
// Send data from environment sensor all 2s // Send data from environment sensor all 2s
if(millis() > starttime6 + 2000){ if(millis() > starttime6 + 2000){
starttime6 = millis(); starttime6 = millis();
// DEBUG
UBaseType_t stackfree = uxTaskGetStackHighWaterMark(NULL);
api->getLogger()->logDebug(GwLog::LOG, "obpSensortask Stack=%d", stackfree);
unsigned char TempSource = 2; // Inside temperature unsigned char TempSource = 2; // Inside temperature
unsigned char PressureSource = 0; // Atmospheric pressure unsigned char PressureSource = 0; // Atmospheric pressure
unsigned char HumiditySource = 0; // Inside humidity unsigned char HumiditySource = 0; // Inside humidity
@@ -785,8 +791,12 @@ void sensorTask(void *param){
vTaskDelete(NULL); vTaskDelete(NULL);
} }
void createSensorTask(SharedData *shared) {
void createSensorTask(SharedData *shared){ TaskHandle_t xHandle = NULL;
xTaskCreate(sensorTask,"readSensors",10000,shared,3,NULL); GwLog *logger = shared->api->getLogger();
esp_err_t err = xTaskCreate(sensorTask, "readSensors", configMINIMAL_STACK_SIZE + 8192, shared, 3, &xHandle);
if ( err != pdPASS) {
logger->logDebug(GwLog::ERROR, "Failed to create sensor task! (err=%d)", err);
};
} }
#endif #endif

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include "GwSynchronized.h" #include "GwSynchronized.h"
#include "GwApi.h" #include "GwApi.h"

178
lib/obp60task/PageAIS.cpp Normal file
View File

@@ -0,0 +1,178 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
/*
AIS Overview
- circle with certain range, e.g. 5nm
- AIS-Targets in range with speed and heading
- perhaps collision alarm
Data: LAT LON SOG HDT
Feature possibilities
- switch between North up / Heading up
- filter
- zoom
- special vessel symbols
*/
class PageAIS : public Page
{
private:
int scale = 5; // Radius of display circle in nautical miles
bool alarm = false;
bool alarm_enabled = false;
int alarm_range = 3;
char mode = 'N'; // (N)ormal, (C)onfig
void displayModeNormal(PageData &pageData) {
// TBD Boatvalues: ...
logger->logDebug(GwLog::DEBUG,"Drawing at PageAIS");
Point c = {200, 150}; // center = current boat position
uint16_t r = 125;
const std::vector<Point> pts_boat = { // polygon lines
{c.x - 5, c.y},
{c.x - 5, c.y - 10},
{c.x, c.y - 16},
{c.x + 5, c.y - 10},
{c.x + 5, c.y}
};
drawPoly(pts_boat, commonData->fgcolor);
// Title and corner value headings
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("AIS");
// zoom scale
epd->drawLine(c.x + 10, c.y, c.x + r - 4, c.y, commonData->fgcolor);
// arrow left
epd->drawLine(c.x + 10, c.y, c.x + 16, c.y - 4, commonData->fgcolor);
epd->drawLine(c.x + 10, c.y, c.x + 16, c.y + 4, commonData->fgcolor);
// arrow right
epd->drawLine(c.x + r - 4, c.y, c.x + r - 10, c.y - 4, commonData->fgcolor);
epd->drawLine(c.x + r - 4, c.y, c.x + r - 10, c.y + 4, commonData->fgcolor);
epd->setFont(&Ubuntu_Bold8pt8b);
drawTextCenter(c.x + r / 2, c.y + 8, String(scale) + "nm");
}
void displayModeConfig() {
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("AIS configuration");
epd->setFont(&Ubuntu_Bold8pt8b);
// TODO menu
}
public:
PageAIS(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageAIS");
alarm_range = 3;
}
void setupKeys(){
Page::setupKeys();
commonData->keydata[0].label = "MODE";
commonData->keydata[1].label = "ALARM";
}
#ifdef BOARD_OBP60S3
int handleKey(int key){
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
} else {
mode = 'N';
}
return 0;
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
#ifdef BOARD_OBP40S3
int handleKey(int key) {
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
} else {
mode = 'N';
}
return 0;
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){
// Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageAIS; Mode=%c", mode);
// Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height());
if (mode == 'N') {
displayModeNormal(pageData);
} else if (mode == 'C') {
displayModeConfig();
}
return PAGE_UPDATE;
};
};
static Page *createPage(CommonData &common){
return new PageAIS(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect
* this will be number of BoatValue pointers in pageData.values
*/
PageDescription registerPageAIS(
"AIS", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{"LAT", "LON", "SOG", "HDT"}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h)
true // Show display header on/off
);
#endif

View File

@@ -0,0 +1,432 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
#include "ConfigMenu.h"
/*
Anchor overview with additional associated data
This page is in experimental stage so be warned!
North is up.
Boatdata used
DBS - Water depth
HDT - Boat heading
AWS - Wind strength; Boat not moving so we assume AWS=TWS and AWD=TWD
AWD - Wind direction
LAT/LON - Boat position, current
HDOP - Position error
This is the fist page to contain a configuration page with
data entry option.
Also it will make use of the new alarm function.
Data
Anchor position lat/lon
Depth at anchor position
Chain length used
Boat position current
Depth at boat position
Boat heading
Wind direction
Wind strength
Alarm j/n
Alarm radius
GPS position error
Timestamp while dropping anchor
Drop / raise function in device OBP40 has to be done inside
config mode because of limited number of buttons.
Save position in FRAM
Alarm: gps fix lost
switch unit feet/meter
*/
#define anchor_width 16
#define anchor_height 16
static unsigned char anchor_bits[] = {
0x80, 0x01, 0x40, 0x02, 0x40, 0x02, 0x80, 0x01, 0xf0, 0x0f, 0x80, 0x01,
0x80, 0x01, 0x88, 0x11, 0x8c, 0x31, 0x8e, 0x71, 0x84, 0x21, 0x86, 0x61,
0x86, 0x61, 0xfc, 0x3f, 0xf8, 0x1f, 0x80, 0x01 };
class PageAnchor : public Page
{
private:
String lengthformat;
int scale = 50; // Radius of display circle in meter
bool alarm = false;
bool alarm_enabled = false;
uint8_t alarm_range;
uint8_t chain_length;
uint8_t chain;
bool anchor_set = false;
double anchor_lat;
double anchor_lon;
double anchor_depth;
int anchor_ts; // time stamp anchor dropped
char mode = 'N'; // (N)ormal, (C)onfig
int8_t editmode = -1; // marker for menu/edit/set function
ConfigMenu *menu;
void displayModeNormal(PageData &pageData) {
// Boatvalues: DBS, HDT, AWS, AWD, LAT, LON, HDOP
GwApi::BoatValue *bv_dbs = pageData.values[0]; // DBS
String sval_dbs = commonData->fmt->formatValue(bv_dbs, *commonData).svalue;
String sunit_dbs = commonData->fmt->formatValue(bv_dbs, *commonData).unit;
GwApi::BoatValue *bv_hdt = pageData.values[1]; // HDT
String sval_hdt = commonData->fmt->formatValue(bv_hdt, *commonData).svalue;
GwApi::BoatValue *bv_aws = pageData.values[2]; // AWS
String sval_aws = commonData->fmt->formatValue(bv_aws, *commonData).svalue;
String sunit_aws = commonData->fmt->formatValue(bv_aws, *commonData).unit;
GwApi::BoatValue *bv_awd = pageData.values[3]; // AWD
String sval_awd = commonData->fmt->formatValue(bv_awd, *commonData).svalue;
GwApi::BoatValue *bv_lat = pageData.values[4]; // LAT
String sval_lat = commonData->fmt->formatValue(bv_lat, *commonData).svalue;
GwApi::BoatValue *bv_lon = pageData.values[5]; // LON
String sval_lon = commonData->fmt->formatValue(bv_lon, *commonData).svalue;
GwApi::BoatValue *bv_hdop = pageData.values[6]; // HDOP
String sval_hdop = commonData->fmt->formatValue(bv_hdop, *commonData).svalue;
String sunit_hdop = commonData->fmt->formatValue(bv_hdop, *commonData).unit;
logger->logDebug(GwLog::DEBUG, "Drawing at PageAnchor; DBS=%f, HDT=%f, AWS=%f", bv_dbs->value, bv_hdt->value, bv_aws->value);
Point c = {200, 150}; // center = anchor position
uint16_t r = 125;
Point b = {200, 180}; // boat position while dropping anchor
const std::vector<Point> pts_boat = { // polygon lines
{b.x - 5, b.y},
{b.x - 5, b.y - 10},
{b.x, b.y - 16},
{b.x + 5, b.y - 10},
{b.x + 5, b.y}
};
//rotatePoints und dann Linien zeichnen
// TODO rotate boat according to current heading
//drawPoly(rotatePoints(c, pts, RadToDeg(value2)), commonData->fgcolor);
drawPoly(pts_boat, commonData->fgcolor);
// Draw wind arrow
const std::vector<Point> pts_wind = {
{c.x, c.y - r + 25},
{c.x - 12, c.y - r - 4},
{c.x, c.y - r + 6},
{c.x + 12, c.y - r - 4}
};
if (bv_awd->valid) {
fillPoly4(rotatePoints(c, pts_wind, bv_awd->value), commonData->fgcolor);
}
// Title and corner value headings
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("Anchor");
epd->setFont(&Ubuntu_Bold10pt8b);
epd->setCursor(8, 200);
epd->print("Depth");
drawTextRalign(392, 38, "Chain");
drawTextRalign(392, 200, "Wind");
// Units
epd->setCursor(8, 272);
epd->print(sunit_dbs);
drawTextRalign(392, 272, sunit_aws);
drawTextRalign(392, 100, lengthformat); // chain unit not implemented
// Corner values
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(8, 70);
epd->print("Alarm: ");
epd->print(alarm_enabled ? "On" : "Off");
epd->setCursor(8, 90);
epd->print("HDOP");
epd->setCursor(8, 106);
if (bv_hdop->valid) {
epd->print(round(bv_hdop->value), 0);
epd->print(sunit_hdop);
} else {
epd->print("n/a");
}
// Values
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
// Current chain used
epd->setCursor(328, 85);
epd->print("27");
// Depth
epd->setCursor(8, 250);
epd->print(sval_dbs);
// Wind
epd->setCursor(328, 250);
epd->print(sval_aws);
epd->drawCircle(c.x, c.y, r, commonData->fgcolor);
epd->drawCircle(c.x, c.y, r + 1, commonData->fgcolor);
// zoom scale
epd->drawLine(c.x + 10, c.y, c.x + r - 4, c.y, commonData->fgcolor);
// arrow left
epd->drawLine(c.x + 10, c.y, c.x + 16, c.y - 4, commonData->fgcolor);
epd->drawLine(c.x + 10, c.y, c.x + 16, c.y + 4, commonData->fgcolor);
// arrow right
epd->drawLine(c.x + r - 4, c.y, c.x + r - 10, c.y - 4, commonData->fgcolor);
epd->drawLine(c.x + r - 4, c.y, c.x + r - 10, c.y + 4, commonData->fgcolor);
epd->setFont(&Ubuntu_Bold8pt8b);
drawTextCenter(c.x + r / 2, c.y + 8, String(scale) + "m");
// alarm range circle
if (alarm_enabled) {
// alarm range in meter has to be smaller than the scale in meter
// r and r_range are pixel values
uint16_t r_range = int(alarm_range * r / scale);
logger->logDebug(GwLog::LOG, "Drawing at PageAnchor; Alarm range = %d", r_range);
epd->drawCircle(c.x, c.y, r_range, commonData->fgcolor);
}
// draw anchor symbol (as bitmap)
epd->drawXBitmap(c.x - anchor_width / 2, c.y - anchor_height / 2,
anchor_bits, anchor_width, anchor_height, commonData->fgcolor);
}
void displayModeConfig() {
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("Anchor configuration");
// TODO
// show lat/lon for anchor pos
// show lat/lon for boat pos
// show distance anchor <-> boat
epd->setFont(&Ubuntu_Bold8pt8b);
for (int i = 0 ; i < menu->getItemCount(); i++) {
ConfigMenuItem *itm = menu->getItemByIndex(i);
if (!itm) {
logger->logDebug(GwLog::ERROR, "Menu item not found: %d", i);
} else {
Rect r = menu->getItemRect(i);
bool inverted = (i == menu->getActiveIndex());
drawTextBoxed(r, itm->getLabel(), commonData->fgcolor, commonData->bgcolor, inverted, false);
if (inverted and editmode > 0) {
// triangle as edit marker
epd->fillTriangle(r.x + r.w + 20, r.y, r.x + r.w + 30, r.y + r.h / 2, r.x + r.w + 20, r.y + r.h, commonData->fgcolor);
}
epd->setCursor(r.x + r.w + 40, r.y + r.h - 4);
if (itm->getType() == "int") {
epd->print(itm->getValue());
epd->print(itm->getUnit());
} else {
epd->print(itm->getValue() == 0 ? "No" : "Yes");
}
}
}
}
public:
PageAnchor(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageAnchor");
// preload configuration data
lengthformat = config->getString(config->lengthFormat);
chain_length = config->getInt(config->chainLength);
chain = 0;
anchor_set = false;
alarm_range = 30;
// Initialize config menu
menu = new ConfigMenu("Options", 40, 80);
menu->setItemDimension(150, 20);
ConfigMenuItem *newitem;
newitem = menu->addItem("chain", "Chain out", "int", 0, "m");
if (! newitem) {
// Demo: in case of failure exit here, should never be happen
logger->logDebug(GwLog::ERROR, "Menu item creation failed");
return;
}
newitem->setRange(0, 200, {1, 5, 10});
newitem = menu->addItem("chainmax", "Chain max", "int", chain_length, "m");
newitem->setRange(0, 200, {1, 5, 10});
newitem = menu->addItem("zoom", "Zoom", "int", 50, "m");
newitem->setRange(0, 200, {1, });
newitem = menu->addItem("range", "Alarm range", "int", 40, "m");
newitem->setRange(0, 200, {1, 5, 10});
newitem = menu->addItem("alat", "Adjust anchor lat.", "int", 0, "m");
newitem->setRange(0, 200, {1, 5, 10});
newitem = menu->addItem("alon", "Adjust anchor lon.", "int", 0, "m");
newitem->setRange(0, 200, {1, 5, 10});
#ifdef BOARD_OBP40S3
// Intodruced here because of missing keys for OBP40
newitem = menu->addItem("anchor", "Anchor down", "bool", 0, "");
#endif
menu->setItemActive("zoom");
}
void setupKeys(){
Page::setupKeys();
commonData->keydata[0].label = "MODE";
commonData->keydata[1].label = "ALARM";
}
#ifdef BOARD_OBP60S3
int handleKey(int key){
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
} else {
mode = 'N';
}
return 0;
}
if (mode == 'N') {
if (key == 2) { // Toggle alarm
alarm_enabled = !alarm_enabled;
return 0;
}
} else { // Config mode
if (key == 3) {
// menu down
menu->goNext();
return 0;
}
if (key == 4) {
// menu up
menu->goPrev();
return 0;
}
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
#ifdef BOARD_OBP40S3
int handleKey(int key){
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
commonData->keydata[1].label = "EDIT";
} else {
mode = 'N';
commonData->keydata[1].label = "ALARM";
}
return 0;
}
if (mode == 'N') {
if (key == 2) { // Toggle alarm
alarm_enabled = !alarm_enabled;
return 0;
}
} else { // Config mode
// TODO different code for OBP40 / OBP60
if (key == 9) {
// menu down
if (editmode > 0) {
// decrease item value
menu->getActiveItem()->decValue();
} else {
menu->goNext();
}
return 0;
}
if (key == 10) {
// menu up or value up
if (editmode > 0) {
// increase item value
menu->getActiveItem()->incValue();
} else {
menu->goPrev();
}
return 0;
}
if (key == 2) {
// enter / leave edit mode for current menu item
if (editmode > 0) {
commonData->keydata[1].label = "EDIT";
editmode = 0;
} else {
commonData->keydata[1].label = "SET";
editmode = 1;
}
return 0;
}
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
void displayNew(PageData &pageData){
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){
// Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageAnchor; Mode=%c", mode);
// Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
if (mode == 'N') {
displayModeNormal(pageData);
} else if (mode == 'C') {
displayModeConfig();
}
return PAGE_UPDATE;
};
};
static Page *createPage(CommonData &common){
return new PageAnchor(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect
* this will be number of BoatValue pointers in pageData.values
*/
PageDescription registerPageAnchor(
"Anchor", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{"DBS", "HDT", "AWS", "AWD", "LAT", "LON", "HDOP"}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h)
true // Show display header on/off
);
#endif

View File

@@ -0,0 +1,138 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
/*
Autopilot
*/
class PageAutopilot : public Page
{
private:
char mode = 'N'; // (N)ormal, (C)onfig
void displayModeNormal(PageData &pageData) {
// TBD Boatvalues: ...
logger->logDebug(GwLog::DEBUG, "Drawing at PageAutopilot");
// Title and corner value headings
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("Autopilot");
}
void displayModeConfig() {
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("Autopilot configuration");
epd->setFont(&Ubuntu_Bold8pt8b);
// TODO menu
}
public:
PageAutopilot(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageAutopilot");
}
void setupKeys() {
Page::setupKeys();
commonData->keydata[0].label = "MODE";
}
#ifdef BOARD_OBP60S3
int handleKey(int key) {
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
} else {
mode = 'N';
}
return 0;
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
#ifdef BOARD_OBP40S3
int handleKey(int key){
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
commonData->keydata[1].label = "EDIT";
} else {
mode = 'N';
commonData->keydata[1].label = "ALARM";
}
return 0;
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
void displayNew(PageData &pageData){
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){
// Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageAutopilot; Mode=%c", mode);
// Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height());
if (mode == 'N') {
displayModeNormal(pageData);
} else if (mode == 'C') {
displayModeConfig();
}
return PAGE_UPDATE;
};
};
static Page *createPage(CommonData &common){
return new PageAutopilot(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect
* this will be number of BoatValue pointers in pageData.values
*/
PageDescription registerPageAutopilot(
"Autopilot", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h)
false // Show display header on/off
);
#endif

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -5,13 +6,21 @@
class PageBME280 : public Page class PageBME280 : public Page
{ {
public: private:
PageBME280(CommonData &common){ String tempformat;
commonData = &common; String useenvsensor;
common.logger->logDebug(GwLog::LOG,"Instantiate PageBME280");
public:
PageBME280(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageBME280");
// Get config data
tempformat = config->getString(config->tempFormat);
useenvsensor = config->getString(config->useEnvSensor);
} }
virtual int handleKey(int key){ int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -21,8 +30,6 @@ class PageBME280 : public Page
} }
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
double value1 = 0; double value1 = 0;
double value2 = 0; double value2 = 0;
@@ -30,13 +37,6 @@ class PageBME280 : public Page
String svalue1 = ""; String svalue1 = "";
String svalue2 = ""; String svalue2 = "";
String svalue3 = ""; String svalue3 = "";
// Get config data
String tempformat = config->getString(config->tempFormat);
bool simulation = config->getBool(config->useSimuData);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
String useenvsensor = config->getString(config->useEnvSensor);
// Get sensor values #1 // Get sensor values #1
String name1 = "Temp"; // Value name String name1 = "Temp"; // Value name
@@ -99,82 +99,82 @@ class PageBME280 : public Page
} }
// Logging boat values // Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageBME280, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3); logger->logDebug(GwLog::LOG, "Drawing at PageBME280, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 55); epd->setCursor(20, 55);
getdisplay().print(name1); // Page name epd->print(name1); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 90); epd->setCursor(20, 90);
getdisplay().print(unit1); // Unit epd->print(unit1); // Unit
// Switch font if format for any values // Switch font if format for any values
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b); epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 90); epd->setCursor(180, 90);
// Show bus data // Show bus data
getdisplay().print(svalue1); // Real value as formated string epd->print(svalue1); // Real value as formated string
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor); epd->fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 145); epd->setCursor(20, 145);
getdisplay().print(name2); // Page name epd->print(name2); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 180); epd->setCursor(20, 180);
getdisplay().print(unit2); // Unit epd->print(unit2); // Unit
// Switch font if format for any values // Switch font if format for any values
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b); epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 180); epd->setCursor(180, 180);
// Show bus data // Show bus data
getdisplay().print(svalue2); // Real value as formated string epd->print(svalue2); // Real value as formated string
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor); epd->fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 235); epd->setCursor(20, 235);
getdisplay().print(name3); // Page name epd->print(name3); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 270); epd->setCursor(20, 270);
getdisplay().print(unit3); // Unit epd->print(unit3); // Unit
// Switch font if format for any values // Switch font if format for any values
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b); epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(140, 270); epd->setCursor(140, 270);
// Show bus data // Show bus data
getdisplay().print(svalue3); // Real value as formated string epd->print(svalue3); // Real value as formated string
return PAGE_UPDATE; return PAGE_UPDATE;
}; };

View File

@@ -0,0 +1,238 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
/*
* Barograph WIP
* - Zoom feature
* - Saves data in FRAM if available
*/
#include "Pagedata.h"
#include "OBP60Extensions.h"
class PageBarograph : public Page
{
private:
bool keylock = false;
bool has_fram = false;
String flashLED;
String useenvsensor;
char source = 'I'; // (I)nternal, e(X)ternal
const int series[5] = {75, 150, 300, 600, 900};
const int zoom[5] = {1, 2, 3, 6, 12};
int zoomindex = 4;
uint16_t data[336] = {0}; // current data to display
// y-axis
uint16_t vmin;
uint16_t vmax;
uint16_t scalemin = 1000;
uint16_t scalemax = 1020;
uint16_t scalestep = 5;
int hist1 = 0; // one hour trend
int hist3 = 0; // three hours trend
long refresh = 0; // millis
void loadData() {
// Transfer data from history to page buffer,
// set y-axis according to data
int i = zoom[zoomindex];
// get min and max values of measured data
vmin = data[0];
vmax = data[0];
for (int x = 0; x < 336; x++) {
if (data[x] != 0) {
if (data[x] < vmin) {
vmin = data[x];
} else if (data[x] > vmax) {
vmax = data[x];
}
}
}
// calculate y-axis scale
uint16_t diff = vmax - vmin;
if (diff < 20) {
scalestep = 5;
} else if (diff < 40) {
scalestep = 10;
} else {
scalestep = 15;
}
scalemin = vmin - (vmin % scalestep);
scalemax = vmax + scalestep - (vmax % scalestep);
// TODO implement history buffer
};
public:
PageBarograph(CommonData &common): Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageBarograph");
// Get config data
useenvsensor = config->getString(common.config->useEnvSensor);
// possible values for internal sensor
static std::vector<String> sensorList = {
"BME280", "BMP280", "BMP180", "BMP085", "HTU21", "SHT21"
};
if (std::find(sensorList.begin(), sensorList.end(), useenvsensor) != sensorList.end()) {
source = 'I';
} else {
// "off" means user external data if available
source = 'X';
}
//common.logger->logDebug(GwLog::LOG,"Source=%s (%s)", source, useenvsensor);
loadData(); // initial load
}
int handleKey(int key) {
if (key == 1) {
// zoom in
if (zoomindex > 0) {
zoomindex -= 1;
}
return 0;
}
if (key == 2) {
// zoom out
if (zoomindex < sizeof(zoom)) {
zoomindex += 1;
}
return 0;
}
if (key == 11) {
keylock = !keylock;
return 0;
}
return key;
}
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
// Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageBarograph");
// Draw page
//***********************************************************
// Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
// Frames
epd->fillRect(0, 75, 400, 2, commonData->fgcolor); // fillRect: x, y, w, h
epd->fillRect(130, 20, 2, 55, commonData->fgcolor);
epd->fillRect(270, 20, 2, 55, commonData->fgcolor);
epd->fillRect(325, 20, 2, 55, commonData->fgcolor);
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold8pt8b);
if (source == 'I') {
drawTextCenter(360, 40, useenvsensor);
} else {
drawTextCenter(360, 40, "ext.");
}
// Trend
drawTextCenter(295, 62, "0.0");
// Alarm placeholder
drawTextCenter(70, 62, "Alarm Off");
// Zoom
int datastep = series[zoomindex];
String fmt;
if (datastep > 120) {
if (datastep % 60 == 0) {
fmt = String(datastep / 60.0, 0) + " min";
} else {
fmt = String(datastep / 60.0, 1) + " min";
}
} else {
fmt = String(datastep) + " s";
}
drawTextCenter(360, 62, fmt);
// Current measurement
epd->setFont(&Ubuntu_Bold16pt8b);
drawTextCenter(200, 40, String(commonData->data.airPressure / 100, 1));
epd->setFont(&Ubuntu_Bold8pt8b);
drawTextCenter(200, 62, "hPa"); // Unit
// Diagram
const int xstep = 48; // x-axis-grid
const int x0 = 350; // origin
const int y0 = 270;
const int w = 7 * 48;
const int h = 180;
// epd->drawRect(x0 - w, y0 - h, w, h, commonData->fgcolor);
// x-axis are hours
for (int i = 1; i <= 6; i++) {
String label = String(-1 * zoom[zoomindex] * i);
epd->drawLine(x0 - i * xstep, y0, x0 - i * xstep, y0 - h, commonData->fgcolor);
drawTextCenter(x0 - i * xstep, y0 - 10, label);
}
// y-axis
epd->drawLine(x0 + 5, y0, x0 + 5, y0 - h, commonData->fgcolor); // drawLine: x1, y1, x2, y2
epd->drawLine(x0 - w, y0, x0 - w, y0 - h, commonData->fgcolor);
epd->drawLine(x0 - w - 5, y0, x0 - w - 5, y0 - h, commonData->fgcolor);
epd->drawLine(x0, y0, x0, y0 - h, commonData->fgcolor);
int16_t dy = 9; // px for one hPa
int16_t y = y0;
int16_t ys = scalemin;
while (y >= y0 - h) {
if (y % scalestep == 0) {
// big step, show label and long line
epd->setCursor(x0 + 10, y + 5);
epd->print(String(ys));
epd->drawLine(x0 + 5, y, x0 - w - 5, y, commonData->fgcolor);
} else {
// small step, only short lines left and right
epd->drawLine(x0 + 5, y, x0, y, commonData->fgcolor);
epd->drawLine(x0 - w - 5, y, x0 - w, y, commonData->fgcolor);
}
y -= dy;
ys += 1;
}
return PAGE_UPDATE;
};
};
static Page* createPage(CommonData &common){
return new PageBarograph(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect
* this will be number of BoatValue pointers in pageData.values
*/
PageDescription registerPageBarograph(
"Barograph", // Page name
createPage, // Action
0, // No bus values needed
true // Show display header on/off
);
#endif

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -5,20 +6,25 @@
class PageBattery : public Page class PageBattery : public Page
{ {
private:
String powsensor1;
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
public: public:
PageBattery(CommonData &common){ PageBattery(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageBattery"); logger->logDebug(GwLog::LOG, "Instantiate PageBattery");
// Get config data
String powsensor1 = config->getString(config->usePowSensor1);
} }
virtual void setupKeys(){ void setupKeys(){
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "AVG"; commonData->keydata[0].label = "AVG";
} }
virtual int handleKey(int key){ int handleKey(int key){
// Change average // Change average
if(key == 1){ if(key == 1){
average ++; average ++;
@@ -34,10 +40,18 @@ class PageBattery : public Page
return key; return key;
} }
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
double value1 = 0; double value1 = 0;
static String svalue1old = ""; static String svalue1old = "";
@@ -48,15 +62,7 @@ class PageBattery : public Page
double value3 = 0; double value3 = 0;
static String svalue3old = ""; static String svalue3old = "";
static String unit3old = ""; static String unit3old = "";
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
String powsensor1 = config->getString(config->usePowSensor1);
bool simulation = config->getBool(config->useSimuData);
// Get voltage value // Get voltage value
String name1 = "VBat"; // Value name String name1 = "VBat"; // Value name
if(String(powsensor1) == "INA219" || String(powsensor1) == "INA226"){ if(String(powsensor1) == "INA219" || String(powsensor1) == "INA226"){
@@ -145,147 +151,141 @@ class PageBattery : public Page
String svalue3 = String(value3); // Formatted value as string including unit conversion and switching decimal places String svalue3 = String(value3); // Formatted value as string including unit conversion and switching decimal places
String unit3 = "W"; // Unit of value String unit3 = "W"; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values // Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageBattery, %s: %f, %s: %f, %s: %f, Avg: %d", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, average); logger->logDebug(GwLog::LOG, "Drawing at PageBattery, %s: %f, %s: %f, %s: %f, Avg: %d", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, average);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
// Show average settings // Show average settings
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
switch (average) { switch (average) {
case 0: case 0:
getdisplay().setCursor(60, 90); epd->setCursor(60, 90);
getdisplay().print("Avg: 1s"); epd->print("Avg: 1s");
getdisplay().setCursor(60, 180); epd->setCursor(60, 180);
getdisplay().print("Avg: 1s"); epd->print("Avg: 1s");
getdisplay().setCursor(60, 270); epd->setCursor(60, 270);
getdisplay().print("Avg: 1s"); epd->print("Avg: 1s");
break; break;
case 1: case 1:
getdisplay().setCursor(60, 90); epd->setCursor(60, 90);
getdisplay().print("Avg: 10s"); epd->print("Avg: 10s");
getdisplay().setCursor(60, 180); epd->setCursor(60, 180);
getdisplay().print("Avg: 10s"); epd->print("Avg: 10s");
getdisplay().setCursor(60, 270); epd->setCursor(60, 270);
getdisplay().print("Avg: 10s"); epd->print("Avg: 10s");
break; break;
case 2: case 2:
getdisplay().setCursor(60, 90); epd->setCursor(60, 90);
getdisplay().print("Avg: 60s"); epd->print("Avg: 60s");
getdisplay().setCursor(60, 180); epd->setCursor(60, 180);
getdisplay().print("Avg: 60s"); epd->print("Avg: 60s");
getdisplay().setCursor(60, 270); epd->setCursor(60, 270);
getdisplay().print("Avg: 60s"); epd->print("Avg: 60s");
break; break;
case 3: case 3:
getdisplay().setCursor(60, 90); epd->setCursor(60, 90);
getdisplay().print("Avg: 300s"); epd->print("Avg: 300s");
getdisplay().setCursor(60, 180); epd->setCursor(60, 180);
getdisplay().print("Avg: 300s"); epd->print("Avg: 300s");
getdisplay().setCursor(60, 270); epd->setCursor(60, 270);
getdisplay().print("Avg: 300s"); epd->print("Avg: 300s");
break; break;
default: default:
getdisplay().setCursor(60, 90); epd->setCursor(60, 90);
getdisplay().print("Avg: 1s"); epd->print("Avg: 1s");
getdisplay().setCursor(60, 180); epd->setCursor(60, 180);
getdisplay().print("Avg: 1s"); epd->print("Avg: 1s");
getdisplay().setCursor(60, 270); epd->setCursor(60, 270);
getdisplay().print("Avg: 1s"); epd->print("Avg: 1s");
break; break;
} }
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 55); epd->setCursor(20, 55);
getdisplay().print(name1); // Value name epd->print(name1); // Value name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 90); epd->setCursor(20, 90);
getdisplay().print(unit1); // Unit epd->print(unit1); // Unit
// Show value // Show value
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b); epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 90); epd->setCursor(180, 90);
// Show bus data // Show bus data
if(String(powsensor1) != "off"){ if(String(powsensor1) != "off"){
getdisplay().print(value1,2); // Real value as formated string epd->print(value1,2); // Real value as formated string
} }
else{ else{
getdisplay().print("---"); // No sensor data (sensor is off) epd->print(commonData->fmt->placeholder); // No sensor data (sensor is off)
} }
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor); epd->fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 145); epd->setCursor(20, 145);
getdisplay().print(name2); // Value name epd->print(name2); // Value name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 180); epd->setCursor(20, 180);
getdisplay().print(unit2); // Unit epd->print(unit2); // Unit
// Show value // Show value
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b); epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 180); epd->setCursor(180, 180);
// Show bus data // Show bus data
if(String(powsensor1) != "off"){ if(String(powsensor1) != "off"){
getdisplay().print(value2,1); // Real value as formated string epd->print(value2,1); // Real value as formated string
} }
else{ else{
getdisplay().print("---"); // No sensor data (sensor is off) epd->print(commonData->fmt->placeholder); // No sensor data (sensor is off)
} }
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor); epd->fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 235); epd->setCursor(20, 235);
getdisplay().print(name3); // Value name epd->print(name3); // Value name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 270); epd->setCursor(20, 270);
getdisplay().print(unit3); // Unit epd->print(unit3); // Unit
// Show value // Show value
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b); epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 270); epd->setCursor(180, 270);
// Show bus data // Show bus data
if(String(powsensor1) != "off"){ if(String(powsensor1) != "off"){
getdisplay().print(value3,1); // Real value as formated string epd->print(value3,1); // Real value as formated string
} }
else{ else{
getdisplay().print("---"); // No sensor data (sensor is off) epd->print(commonData->fmt->placeholder); // No sensor data (sensor is off)
} }
return PAGE_UPDATE; return PAGE_UPDATE;

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -6,23 +7,34 @@
class PageBattery2 : public Page class PageBattery2 : public Page
{ {
bool init = false; // Marker for init done private:
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s String batVoltage;
bool trend = true; // Trend indicator [0|1], 0=off, 1=on int batCapacity;
double raw = 0; String batType;
String powerSensor;
bool init = false; // Marker for init done
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
bool trend = true; // Trend indicator [0|1], 0=off, 1=on
double raw = 0;
public: public:
PageBattery2(CommonData &common){ PageBattery2(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageBattery2"); logger->logDebug(GwLog::LOG, "Instantiate PageBattery2");
// Get config data
batVoltage = config->getString(config->batteryVoltage);
batCapacity = config->getInt(config->batteryCapacity);
batType = config->getString(config->batteryType);
powerSensor = config->getString(config->usePowSensor1);
} }
virtual void setupKeys(){ void setupKeys(){
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "AVG"; commonData->keydata[0].label = "AVG";
} }
virtual int handleKey(int key){ int handleKey(int key) {
// Change average // Change average
if(key == 1){ if(key == 1){
average ++; average ++;
@@ -44,11 +56,7 @@ public:
return key; return key;
} }
int displayPage(PageData &pageData) int displayPage(PageData &pageData) {
{
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Polynominal coefficients second order for battery energy level calculation // Polynominal coefficients second order for battery energy level calculation
// index 0 = Pb, 1 = Gel, 2 = AGM, 3 = LiFePo4 // index 0 = Pb, 1 = Gel, 2 = AGM, 3 = LiFePo4
float x0[4] = {+3082.5178, +1656.1571, +1316.8766, +14986.9336}; // Offset float x0[4] = {+3082.5178, +1656.1571, +1316.8766, +14986.9336}; // Offset
@@ -57,16 +65,6 @@ public:
int batPercentage = 0; // Battery level int batPercentage = 0; // Battery level
float batRange = 0; // Range in hours float batRange = 0; // Range in hours
// Get config data
bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String batVoltage = config->getString(config->batteryVoltage);
int batCapacity = config->getInt(config->batteryCapacity);
String batType = config->getString(config->batteryType);
String backlightMode = config->getString(config->backlight);
String powerSensor = config->getString(config->usePowSensor1);
double value1 = 0; // Battery voltage double value1 = 0; // Battery voltage
double value2 = 0; // Battery current double value2 = 0; // Battery current
double value3 = 0; // Battery power consumption double value3 = 0; // Battery power consumption
@@ -142,149 +140,133 @@ public:
if(batRange > 99) batRange = 99; if(batRange > 99) batRange = 99;
// Optical warning by limit violation // Optical warning by limit violation
if(String(flashLED) == "Limit Violation"){ if (flashLED == "Limit Violation") {
// Limits for Pb battery bool violation = false;
if(String(batType) == "Pb" && (raw < 11.8 || raw > 14.8)){ if (batType == "Pb") {
violation = (raw < 11.8 || raw > 14.8);
} else if (batType == "Gel") {
violation = (raw < 11.8 || raw > 14.4);
} else if (batType == "AGM") {
violation = (raw < 11.8 || raw > 14.7);
} else if (batType == "LiFePo4") {
violation = (raw < 12.0 || raw > 14.6);
}
if (violation) {
setBlinkingLED(true); setBlinkingLED(true);
} } else {
if(String(batType) == "Pb" && (raw >= 11.8 && raw <= 14.8)){
setBlinkingLED(false);
setFlashLED(false);
}
// Limits for Gel battery
if(String(batType) == "Gel" && (raw < 11.8 || raw > 14.4)){
setBlinkingLED(true);
}
if(String(batType) == "Gel" && (raw >= 11.8 && raw <= 14.4)){
setBlinkingLED(false);
setFlashLED(false);
}
// Limits for AGM battery
if(String(batType) == "AGM" && (raw < 11.8 || raw > 14.7)){
setBlinkingLED(true);
}
if(String(batType) == "AGM" && (raw >= 11.8 && raw <= 14.7)){
setBlinkingLED(false);
setFlashLED(false);
}
// Limits for LiFePo4 battery
if(String(batType) == "LiFePo4" && (raw < 12.0 || raw > 14.6)){
setBlinkingLED(true);
}
if(String(batType) == "LiFePo4" && (raw >= 12.0 && raw <= 14.6)){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
} }
// Logging voltage value // Logging voltage value
LOG_DEBUG(GwLog::LOG,"Drawing at PageBattery2, Type:%s %s:=%f", batType.c_str(), name1.c_str(), raw); logger->logDebug(GwLog::LOG, "Drawing at PageBattery2, Type:%s %s:=%f", batType.c_str(), name1.c_str(), raw);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(10, 65); epd->setCursor(10, 65);
getdisplay().print("Bat."); epd->print("Bat.");
// Show battery type // Show battery type
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(90, 65); epd->setCursor(90, 65);
getdisplay().print(batType); epd->print(batType);
// Show voltage type // Show voltage type
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 140); epd->setCursor(10, 140);
int bvoltage = 0; int bvoltage = 0;
if(String(batVoltage) == "12V") bvoltage = 12; if(String(batVoltage) == "12V") bvoltage = 12;
else bvoltage = 24; else bvoltage = 24;
getdisplay().print(bvoltage); epd->print(bvoltage);
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V"); epd->print("V");
// Show battery capacity // Show battery capacity
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 200); epd->setCursor(10, 200);
if(batCapacity <= 999) getdisplay().print(batCapacity, 0); if(batCapacity <= 999) epd->print(batCapacity, 0);
if(batCapacity > 999) getdisplay().print(float(batCapacity/1000.0), 1); if(batCapacity > 999) epd->print(float(batCapacity/1000.0), 1);
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
if(batCapacity <= 999) getdisplay().print("Ah"); if(batCapacity <= 999) epd->print("Ah");
if(batCapacity > 999) getdisplay().print("kAh"); if(batCapacity > 999) epd->print("kAh");
// Show info // Show info
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 235); epd->setCursor(10, 235);
getdisplay().print("Installed"); epd->print("Installed");
getdisplay().setCursor(10, 255); epd->setCursor(10, 255);
getdisplay().print("Battery Type"); epd->print("Battery Type");
// Show battery with fill level // Show battery with fill level
batteryGraphic(150, 45, batPercentage, commonData->fgcolor, commonData->bgcolor); batteryGraphic(150, 45, batPercentage, commonData->fgcolor, commonData->bgcolor);
// Show average settings // Show average settings
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(150, 145); epd->setCursor(150, 145);
switch (average) { switch (average) {
case 0: case 0:
getdisplay().print("Avg: 1s"); epd->print("Avg: 1s");
break; break;
case 1: case 1:
getdisplay().print("Avg: 10s"); epd->print("Avg: 10s");
break; break;
case 2: case 2:
getdisplay().print("Avg: 60s"); epd->print("Avg: 60s");
break; break;
case 3: case 3:
getdisplay().print("Avg: 300s"); epd->print("Avg: 300s");
break; break;
default: default:
getdisplay().print("Avg: 1s"); epd->print("Avg: 1s");
break; break;
} }
// Show fill level in percent // Show fill level in percent
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 200); epd->setCursor(150, 200);
getdisplay().print(batPercentage); epd->print(batPercentage);
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("%"); epd->print("%");
// Show time to full discharge // Show time to full discharge
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 260); epd->setCursor(150, 260);
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){ if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
if(batRange < 9.9) getdisplay().print(batRange, 1); if(batRange < 9.9) epd->print(batRange, 1);
else getdisplay().print(batRange, 0); else epd->print(batRange, 0);
} }
else getdisplay().print("--"); else epd->print("--");
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("h"); epd->print("h");
// Show sensor type info // Show sensor type info
String i2cAddr = ""; String i2cAddr = "";
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(270, 60); epd->setCursor(270, 60);
if(powerSensor == "off") getdisplay().print("Internal"); if(powerSensor == "off") epd->print("Internal");
if(powerSensor == "INA219"){ if(powerSensor == "INA219"){
getdisplay().print("INA219"); epd->print("INA219");
} }
if(powerSensor == "INA226"){ if(powerSensor == "INA226"){
getdisplay().print("INA226"); epd->print("INA226");
i2cAddr = " (0x" + String(INA226_I2C_ADDR1, HEX) + ")"; i2cAddr = " (0x" + String(INA226_I2C_ADDR1, HEX) + ")";
} }
getdisplay().print(i2cAddr); epd->print(i2cAddr);
getdisplay().setCursor(270, 80); epd->setCursor(270, 80);
getdisplay().print("Sensor Modul"); epd->print("Sensor Modul");
// Reading bus data or using simulation data // Reading bus data or using simulation data
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 140); epd->setCursor(260, 140);
if(simulation == true){ if(simulation == true){
if(batVoltage == "12V"){ if(batVoltage == "12V"){
value1 = 12.0; value1 = 12.0;
@@ -293,46 +275,50 @@ public:
value1 = 24.0; value1 = 24.0;
} }
value1 += float(random(0, 5)) / 10; // Simulation data value1 += float(random(0, 5)) / 10; // Simulation data
getdisplay().print(value1,1); epd->print(value1,1);
} }
else{ else{
// Check for valid real data, display also if hold values activated // Check for valid real data, display also if hold values activated
if(valid1 == true || holdvalues == true){ if(valid1 == true || holdvalues == true){
// Resolution switching // Resolution switching
if(value1 <= 9.9) getdisplay().print(value1, 2); if(value1 <= 9.9) epd->print(value1, 2);
if(value1 > 9.9 && value1 <= 99.9)getdisplay().print(value1, 1); if(value1 > 9.9 && value1 <= 99.9)epd->print(value1, 1);
if(value1 > 99.9) getdisplay().print(value1, 0); if(value1 > 99.9) epd->print(value1, 0);
} }
else{ else{
getdisplay().print("---"); // Missing bus data epd->print(commonData->fmt->placeholder); // Missing bus data
} }
} }
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V"); epd->print("V");
// Show actual current in A // Show actual current in A
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 200); epd->setCursor(260, 200);
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){ if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
if(value2 <= 9.9) getdisplay().print(value2, 2); if(value2 <= 9.9) epd->print(value2, 2);
if(value2 > 9.9 && value2 <= 99.9)getdisplay().print(value2, 1); if(value2 > 9.9 && value2 <= 99.9)epd->print(value2, 1);
if(value2 > 99.9) getdisplay().print(value2, 0); if(value2 > 99.9) epd->print(value2, 0);
} }
else getdisplay().print("---"); else {
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->print(commonData->fmt->placeholder);
getdisplay().print("A"); }
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("A");
// Show actual consumption in W // Show actual consumption in W
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 260); epd->setCursor(260, 260);
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){ if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
if(value3 <= 9.9) getdisplay().print(value3, 2); if(value3 <= 9.9) epd->print(value3, 2);
if(value3 > 9.9 && value3 <= 99.9)getdisplay().print(value3, 1); if(value3 > 9.9 && value3 <= 99.9)epd->print(value3, 1);
if(value3 > 99.9) getdisplay().print(value3, 0); if(value3 > 99.9) epd->print(value3, 0);
} }
else getdisplay().print("---"); else {
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->print(commonData->fmt->placeholder);
getdisplay().print("W"); }
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("W");
return PAGE_UPDATE; return PAGE_UPDATE;
}; };

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -16,30 +17,41 @@
class PageClock : public Page class PageClock : public Page
{ {
bool simulation = false; private:
int simtime; fmtDate dateformat;
bool keylock = false; int simtime;
char source = 'R'; // time source (R)TC | (G)PS | (N)TP bool keylock = false;
char mode = 'A'; // display mode (A)nalog | (D)igital | race (T)imer char source = 'R'; // time source (R)TC | (G)PS | (N)TP
char tz = 'L'; // time zone (L)ocal | (U)TC char mode = 'A'; // display mode (A)nalog | (D)igital | race (T)imer
double timezone = 0; // there are timezones with non int offsets, e.g. 5.5 or 5.75 char tz = 'L'; // time zone (L)ocal | (U)TC
double homelat; double timezone = 0; // there are timezones with non int offsets, e.g. 5.5 or 5.75
double homelon; double homelat;
bool homevalid = false; // homelat and homelon are valid double homelon;
bool homevalid = false; // homelat and homelon are valid
public: public:
PageClock(CommonData &common){ PageClock(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageClock"); logger->logDebug(GwLog::LOG, "Instantiate PageClock");
simulation = common.config->getBool(common.config->useSimuData);
timezone = common.config->getString(common.config->timeZone).toDouble(); // Get config data
homelat = common.config->getString(common.config->homeLAT).toDouble(); dateformat = common.fmt->getDateFormat(config->getString(config->dateFormat));
homelon = common.config->getString(common.config->homeLON).toDouble(); timezone = config->getString(config->timeZone).toDouble();
homelat = config->getString(config->homeLAT).toDouble();
homelon = config->getString(config->homeLON).toDouble();
homevalid = homelat >= -180.0 and homelat <= 180 and homelon >= -90.0 and homelon <= 90.0; homevalid = homelat >= -180.0 and homelat <= 180 and homelon >= -90.0 and homelon <= 90.0;
simtime = 38160; // time value 11:36 simtime = 38160; // time value 11:36
#ifdef BOARD_OBP60S3
// WIP time source
String use_rtc = config->getString(config->useRTC);
if (use_rtc == "off") {
source = 'G';
}
#endif
} }
virtual void setupKeys(){ void setupKeys(){
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "SRC"; commonData->keydata[0].label = "SRC";
commonData->keydata[1].label = "MODE"; commonData->keydata[1].label = "MODE";
@@ -47,7 +59,7 @@ bool homevalid = false; // homelat and homelon are valid
} }
// Key functions // Key functions
virtual int handleKey(int key){ int handleKey(int key){
// Time source // Time source
if (key == 1) { if (key == 1) {
if (source == 'G') { if (source == 'G') {
@@ -85,10 +97,17 @@ bool homevalid = false; // homelat and homelon are valid
return key; return key;
} }
int displayPage(PageData &pageData) void displayNew(PageData &pageData) {
{ #ifdef BOARD_OBP60S3
GwConfigHandler *config = commonData->config; // Clear optical warning
GwLog *logger = commonData->logger; if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
static String svalue1old = ""; static String svalue1old = "";
static String unit1old = ""; static String unit1old = "";
@@ -100,16 +119,10 @@ bool homevalid = false; // homelat and homelon are valid
static String svalue5old = ""; static String svalue5old = "";
static String svalue6old = ""; static String svalue6old = "";
double value1 = 0; double value1 = 0; // GPS time
double value2 = 0; double value2 = 0; // GPS date FIXME date defined as uint32_t!
double value3 = 0; double value3 = 0; // HDOP
bool gpsvalid = false;
// Get config data
String lengthformat = config->getString(config->lengthFormat);
String dateformat = config->getString(config->dateFormat);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Get boat values for GPS time // Get boat values for GPS time
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
@@ -122,8 +135,8 @@ bool homevalid = false; // homelat and homelon are valid
value1 = simtime++; // Simulation data for time value 11:36 in seconds value1 = simtime++; // Simulation data for time value 11:36 in seconds
} // Other simulation data see OBP60Formatter.cpp } // Other simulation data see OBP60Formatter.cpp
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = commonData->fmt->formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save old value svalue1old = svalue1; // Save old value
unit1old = unit1; // Save old unit unit1old = unit1; // Save old unit
@@ -135,8 +148,8 @@ bool homevalid = false; // homelat and homelon are valid
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
value2 = bvalue2->value; // Value as double in SI unit value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = commonData->fmt->formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value String unit2 = commonData->fmt->formatValue(bvalue2, *commonData).unit; // Unit of value
if(valid2 == true){ if(valid2 == true){
svalue2old = svalue2; // Save old value svalue2old = svalue2; // Save old value
unit2old = unit2; // Save old unit unit2old = unit2; // Save old unit
@@ -148,13 +161,16 @@ bool homevalid = false; // homelat and homelon are valid
name3 = name3.substring(0, 6); // String length limit for value name name3 = name3.substring(0, 6); // String length limit for value name
value3 = bvalue3->value; // Value as double in SI unit value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = commonData->fmt->formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value String unit3 = commonData->fmt->formatValue(bvalue3, *commonData).unit; // Unit of value
if(valid3 == true){ if(valid3 == true){
svalue3old = svalue3; // Save old value svalue3old = svalue3; // Save old value
unit3old = unit3; // Save old unit unit3old = unit3; // Save old unit
} }
// GPS date and time are valid and can be used
gpsvalid = (valid1 && valid2 && valid3);
// Optical warning by limit violation (unused) // Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){ if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false); setBlinkingLED(false);
@@ -163,108 +179,108 @@ bool homevalid = false; // homelat and homelon are valid
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageClock, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2); logger->logDebug(GwLog::LOG, "Drawing at PageClock, %s:%f, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
time_t tv = mktime(&commonData->data.rtcTime) + timezone * 3600; time_t tv = mktime(&commonData->data.rtcTime) + timezone * 3600;
struct tm *local_tm = localtime(&tv); struct tm *local_tm = localtime(&tv);
// Show values GPS date // Show values GPS date
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 65); epd->setCursor(10, 65);
if (holdvalues == false) { if (holdvalues == false) {
if (source == 'G') { if (source == 'G') {
// GPS value // GPS value
getdisplay().print(svalue2); epd->print(svalue2);
} else if (commonData->data.rtcValid) { } else if (commonData->data.rtcValid) {
// RTC value // RTC value
if (tz == 'L') { if (tz == 'L') {
getdisplay().print(formatDate(dateformat, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday)); epd->print(formatDate(dateformat, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday));
} }
else { else {
getdisplay().print(formatDate(dateformat, commonData->data.rtcTime.tm_year + 1900, commonData->data.rtcTime.tm_mon + 1, commonData->data.rtcTime.tm_mday)); epd->print(formatDate(dateformat, commonData->data.rtcTime.tm_year + 1900, commonData->data.rtcTime.tm_mon + 1, commonData->data.rtcTime.tm_mday));
} }
} else { } else {
getdisplay().print("---"); epd->print(commonData->fmt->placeholder);
} }
} else { } else {
getdisplay().print(svalue2old); epd->print(svalue2old);
} }
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 95); epd->setCursor(10, 95);
getdisplay().print("Date"); // Name epd->print("Date"); // Name
// Horizintal separator left // Horizintal separator left
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor); epd->fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show values GPS time // Show values GPS time
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 250); epd->setCursor(10, 250);
if (holdvalues == false) { if (holdvalues == false) {
if (source == 'G') { if (source == 'G') {
getdisplay().print(svalue1); // Value epd->print(svalue1); // Value
} }
else if (commonData->data.rtcValid) { else if (commonData->data.rtcValid) {
if (tz == 'L') { if (tz == 'L') {
getdisplay().print(formatTime('s', local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec)); epd->print(formatTime(fmtTime::MMHHSS, local_tm->tm_hour, local_tm->tm_min, local_tm->tm_sec));
} }
else { else {
getdisplay().print(formatTime('s', commonData->data.rtcTime.tm_hour, commonData->data.rtcTime.tm_min, commonData->data.rtcTime.tm_sec)); epd->print(formatTime(fmtTime::MMHHSS, commonData->data.rtcTime.tm_hour, commonData->data.rtcTime.tm_min, commonData->data.rtcTime.tm_sec));
} }
} else { } else {
getdisplay().print("---"); epd->print(commonData->fmt->placeholder);
} }
} }
else { else {
getdisplay().print(svalue1old); epd->print(svalue1old);
} }
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 220); epd->setCursor(10, 220);
getdisplay().print("Time"); // Name epd->print("Time"); // Name
// Show values sunrise // Show values sunrise
String sunrise = "---"; String sunrise = commonData->fmt->placeholder;
if ((valid1 and valid2 and valid3 == true) or (homevalid and commonData->data.rtcValid)) { if (((source == 'G') and gpsvalid) or (homevalid and commonData->data.rtcValid)) {
sunrise = String(commonData->sundata.sunriseHour) + ":" + String(commonData->sundata.sunriseMinute + 100).substring(1); sunrise = String(commonData->sundata.sunriseHour) + ":" + String(commonData->sundata.sunriseMinute + 100).substring(1);
svalue5old = sunrise; svalue5old = sunrise;
} else if (simulation) { } else if (simulation) {
sunrise = String("06:42"); sunrise = String("06:42");
} }
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 65); epd->setCursor(335, 65);
if(holdvalues == false) getdisplay().print(sunrise); // Value if(holdvalues == false) epd->print(sunrise); // Value
else getdisplay().print(svalue5old); else epd->print(svalue5old);
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 95); epd->setCursor(335, 95);
getdisplay().print("SunR"); // Name epd->print("SunR"); // Name
// Horizintal separator right // Horizintal separator right
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor); epd->fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show values sunset // Show values sunset
String sunset = "---"; String sunset = commonData->fmt->placeholder;
if ((valid1 and valid2 and valid3 == true) or (homevalid and commonData->data.rtcValid)) { if (((source == 'G') and gpsvalid) or (homevalid and commonData->data.rtcValid)) {
sunset = String(commonData->sundata.sunsetHour) + ":" + String(commonData->sundata.sunsetMinute + 100).substring(1); sunset = String(commonData->sundata.sunsetHour) + ":" + String(commonData->sundata.sunsetMinute + 100).substring(1);
svalue6old = sunset; svalue6old = sunset;
} else if (simulation) { } else if (simulation) {
sunset = String("21:03"); sunset = String("21:03");
} }
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 250); epd->setCursor(335, 250);
if(holdvalues == false) getdisplay().print(sunset); // Value if(holdvalues == false) epd->print(sunset); // Value
else getdisplay().print(svalue6old); else epd->print(svalue6old);
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 220); epd->setCursor(335, 220);
getdisplay().print("SunS"); // Name epd->print("SunS"); // Name
//******************************************************************************************* //*******************************************************************************************
@@ -272,8 +288,8 @@ bool homevalid = false; // homelat and homelon are valid
int rInstrument = 110; // Radius of clock int rInstrument = 110; // Radius of clock
float pi = 3.141592; float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle epd->fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
for(int i=0; i<360; i=i+1) for(int i=0; i<360; i=i+1)
{ {
@@ -301,11 +317,11 @@ bool homevalid = false; // homelat and homelon are valid
// Print text centered on position x, y // Print text centered on position x, y
int16_t x1, y1; // Return values of getTextBounds int16_t x1, y1; // Return values of getTextBounds
uint16_t w, h; // Return values of getTextBounds uint16_t w, h; // Return values of getTextBounds
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string epd->getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
getdisplay().setCursor(x-w/2, y+h/2); epd->setCursor(x-w/2, y+h/2);
if(i % 30 == 0){ if(i % 30 == 0){
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().print(ii); epd->print(ii);
} }
// Draw sub scale with dots // Draw sub scale with dots
@@ -314,7 +330,7 @@ bool homevalid = false; // homelat and homelon are valid
if(i % 6 == 0){ if(i % 6 == 0){
float x1c = 200 + rInstrument*sin(i/180.0*pi); float x1c = 200 + rInstrument*sin(i/180.0*pi);
float y1c = 150 - rInstrument*cos(i/180.0*pi); float y1c = 150 - rInstrument*cos(i/180.0*pi);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor); epd->fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
sinx=sin(i/180.0*pi); sinx=sin(i/180.0*pi);
cosx=cos(i/180.0*pi); cosx=cos(i/180.0*pi);
} }
@@ -326,31 +342,31 @@ bool homevalid = false; // homelat and homelon are valid
float xx2 = +dx; float xx2 = +dx;
float yy1 = -(rInstrument-10); float yy1 = -(rInstrument-10);
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2), 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
} }
} }
// Print Unit in clock // Print Unit in clock
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(175, 110); epd->setCursor(175, 110);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(tz == 'L' ? "LOT" : "UTC"); epd->print(tz == 'L' ? "LOT" : "UTC");
} }
else{ else{
getdisplay().print(unit2old); // date unit epd->print(unit2old); // date unit
} }
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(185, 190); epd->setCursor(185, 190);
if (source == 'G') { if (source == 'G') {
getdisplay().print("GPS"); epd->print("GPS");
} else { } else {
getdisplay().print("RTC"); epd->print("RTC");
} }
// Clock values // Clock values
@@ -380,7 +396,7 @@ bool homevalid = false; // homelat and homelon are valid
if (hour > 12) { if (hour > 12) {
hour -= 12.0; hour -= 12.0;
} }
LOG_DEBUG(GwLog::DEBUG,"... PageClock, value1: %f hour: %f minute:%f", value1, hour, minute); logger->logDebug(GwLog::DEBUG, "... PageClock, value1: %f hour: %f minute:%f", value1, hour, minute);
// Draw hour pointer // Draw hour pointer
float startwidth = 8; // Start width of pointer float startwidth = 8; // Start width of pointer
@@ -393,7 +409,7 @@ bool homevalid = false; // homelat and homelon are valid
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument * 0.5); float yy2 = -(rInstrument * 0.5);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
@@ -403,7 +419,7 @@ bool homevalid = false; // homelat and homelon are valid
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument * 0.5); float iy1 = -(rInstrument * 0.5);
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), epd->fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
} }
@@ -419,7 +435,7 @@ bool homevalid = false; // homelat and homelon are valid
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument - 15); float yy2 = -(rInstrument - 15);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
@@ -429,14 +445,14 @@ bool homevalid = false; // homelat and homelon are valid
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument - 15); float iy1 = -(rInstrument - 15);
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), epd->fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
} }
// Center circle // Center circle
getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor); epd->fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor); epd->fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
return PAGE_UPDATE; return PAGE_UPDATE;
}; };

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -24,58 +25,58 @@ const float Compass_LineDelta = 8.0;// compass band: 1deg = 5 Pixels, 10deg = 50
class PageCompass : public Page class PageCompass : public Page
{ {
private:
int WhichDataCompass = ShowHDM; int WhichDataCompass = ShowHDM;
int WhichDataDisplay = ShowHDM; int WhichDataDisplay = ShowHDM;
public: public:
PageCompass(CommonData &common){ PageCompass(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageCompass"); logger->logDebug(GwLog::LOG, "Instantiate PageCompass");
} }
virtual void setupKeys(){ void setupKeys(){
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "CMP"; commonData->keydata[0].label = "CMP";
commonData->keydata[1].label = "SRC"; commonData->keydata[1].label = "SRC";
} }
virtual int handleKey(int key){ int handleKey(int key){
// Code for keylock // Code for keylock
if (key == 1) {
if ( key == 1 ) {
WhichDataCompass += 1; WhichDataCompass += 1;
if ( WhichDataCompass > ShowCOG) if ( WhichDataCompass > ShowCOG)
WhichDataCompass = ShowHDM; WhichDataCompass = ShowHDM;
return 0; return 0;
} }
if ( key == 2 ) { if (key == 2) {
WhichDataDisplay += 1; WhichDataDisplay += 1;
if ( WhichDataDisplay > ShowDBS) if (WhichDataDisplay > ShowDBS)
WhichDataDisplay = ShowHDM; WhichDataDisplay = ShowHDM;
} }
if (key == 11) {
if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
return 0; // Commit the key return 0; // Commit the key
} }
return key; return key;
} }
int displayPage(PageData &pageData){ void displayNew(PageData &pageData) {
GwConfigHandler *config = commonData->config; #ifdef BOARD_OBP60S3
GwLog *logger = commonData->logger; // Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
// Old values for hold function // Old values for hold function
static String OldDataText[HowManyValues] = {"", "", "","", "", ""}; static String OldDataText[HowManyValues] = {"", "", "","", "", ""};
static String OldDataUnits[HowManyValues] = {"", "", "","", "", ""}; static String OldDataUnits[HowManyValues] = {"", "", "","", "", ""};
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
GwApi::BoatValue *bvalue; GwApi::BoatValue *bvalue;
String DataName[HowManyValues]; String DataName[HowManyValues];
double DataValue[HowManyValues]; double DataValue[HowManyValues];
@@ -87,21 +88,15 @@ class PageCompass : public Page
for (int i = 0; i < HowManyValues; i++){ for (int i = 0; i < HowManyValues; i++){
bvalue = pageData.values[i]; bvalue = pageData.values[i];
TheFormattedData = formatValue(bvalue, *commonData); TheFormattedData = commonData->fmt->formatValue(bvalue, *commonData);
DataName[i] = xdrDelete(bvalue->getName()); DataName[i] = xdrDelete(bvalue->getName());
DataName[i] = DataName[i].substring(0, 6); // String length limit for value name DataName[i] = DataName[i].substring(0, 6); // String length limit for value name
DataUnits[i] = formatValue(bvalue, *commonData).unit; DataUnits[i] = commonData->fmt->formatValue(bvalue, *commonData).unit;
DataText[i] = TheFormattedData.svalue; // Formatted value as string including unit conversion and switching decimal places DataText[i] = TheFormattedData.svalue; // Formatted value as string including unit conversion and switching decimal places
DataValue[i] = TheFormattedData.value; // Value as double in SI unit DataValue[i] = TheFormattedData.value; // Value as double in SI unit
DataValid[i] = bvalue->valid; DataValid[i] = bvalue->valid;
DataFormat[i] = bvalue->getFormat(); // Unit of value DataFormat[i] = bvalue->getFormat(); // Unit of value
LOG_DEBUG(GwLog::LOG,"Drawing at PageCompass: %d %s %f %s %s", i, DataName[i], DataValue[i], DataFormat[i], DataText[i] ); logger->logDebug(GwLog::LOG, "Drawing at PageCompass: %d %s %f %s %s", i, DataName[i], DataValue[i], DataFormat[i], DataText[i] );
}
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
} }
if (bvalue == NULL) return PAGE_OK; // WTF why this statement? if (bvalue == NULL) return PAGE_OK; // WTF why this statement?
@@ -109,27 +104,27 @@ class PageCompass : public Page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
// Horizontal line 2 pix top & bottom // Horizontal line 2 pix top & bottom
// Print data on top half // Print data on top half
getdisplay().fillRect(0, 130, 400, 2, commonData->fgcolor); epd->fillRect(0, 130, 400, 2, commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(10, 70); epd->setCursor(10, 70);
getdisplay().print(DataName[WhichDataDisplay]); // Page name epd->print(DataName[WhichDataDisplay]); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 120); epd->setCursor(10, 120);
getdisplay().print(DataUnits[WhichDataDisplay]); epd->print(DataUnits[WhichDataDisplay]);
getdisplay().setCursor(190, 120); epd->setCursor(190, 120);
getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b); epd->setFont(&DSEG7Classic_BoldItalic42pt7b);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(DataText[WhichDataDisplay]); // Real value as formated string epd->print(DataText[WhichDataDisplay]); // Real value as formated string
} }
else{ else{
getdisplay().print(OldDataText[WhichDataDisplay]); // Old value as formated string epd->print(OldDataText[WhichDataDisplay]); // Old value as formated string
} }
if(DataValid[WhichDataDisplay] == true){ if(DataValid[WhichDataDisplay] == true){
OldDataText[WhichDataDisplay] = DataText[WhichDataDisplay]; // Save the old value OldDataText[WhichDataDisplay] = DataText[WhichDataDisplay]; // Save the old value
@@ -148,14 +143,14 @@ class PageCompass : public Page
char buffer[bsize+1]; char buffer[bsize+1];
buffer[0]=0; buffer[0]=0;
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(10, Compass_Y0-60); epd->setCursor(10, Compass_Y0-60);
getdisplay().print(DataName[WhichDataCompass]); // Page name epd->print(DataName[WhichDataCompass]); // Page name
// Draw compass base line and pointer // Draw compass base line and pointer
getdisplay().fillRect(0, Compass_Y0, 400, 3, commonData->fgcolor); epd->fillRect(0, Compass_Y0, 400, 3, commonData->fgcolor);
getdisplay().fillTriangle(Compass_X0,Compass_Y0-40,Compass_X0-10,Compass_Y0-80,Compass_X0+10,Compass_Y0-80,commonData->fgcolor); epd->fillTriangle(Compass_X0,Compass_Y0-40,Compass_X0-10,Compass_Y0-80,Compass_X0+10,Compass_Y0-80,commonData->fgcolor);
// Draw trendlines // Draw trendlines
for ( int i = 1; i < abs(TheTrend) / 2; i++){ for ( int i = 1; i < abs(TheTrend) / 2; i++){
int x1; int x1;
@@ -164,7 +159,7 @@ class PageCompass : public Page
else else
x1 = Compass_X0 - 20 * ( i + 1 ); x1 = Compass_X0 - 20 * ( i + 1 );
getdisplay().fillRect(x1, Compass_Y0 -55, 10, 6, commonData->fgcolor); epd->fillRect(x1, Compass_Y0 -55, 10, 6, commonData->fgcolor);
} }
// Central line + satellite lines // Central line + satellite lines
double NextSector = round(TheAngle / ( M_PI / 9 )) * ( M_PI / 9 ); // Get the next 20degree value double NextSector = round(TheAngle / ( M_PI / 9 )) * ( M_PI / 9 ); // Get the next 20degree value
@@ -174,28 +169,28 @@ class PageCompass : public Page
for ( int i = 0; i <=4; i++ ){ for ( int i = 0; i <=4; i++ ){
int x0; int x0;
x0 = Compass_X0 + Delta_X + 2 * i * 5 * Compass_LineDelta; x0 = Compass_X0 + Delta_X + 2 * i * 5 * Compass_LineDelta;
getdisplay().fillRect(x0-2, Compass_Y0 - 2 * Compass_LineLength, 5, 2 * Compass_LineLength, commonData->fgcolor); epd->fillRect(x0-2, Compass_Y0 - 2 * Compass_LineLength, 5, 2 * Compass_LineLength, commonData->fgcolor);
x0 = Compass_X0 + Delta_X + ( 2 * i + 1 ) * 5 * Compass_LineDelta; x0 = Compass_X0 + Delta_X + ( 2 * i + 1 ) * 5 * Compass_LineDelta;
getdisplay().fillRect(x0-1, Compass_Y0 - Compass_LineLength, 3, Compass_LineLength, commonData->fgcolor); epd->fillRect(x0-1, Compass_Y0 - Compass_LineLength, 3, Compass_LineLength, commonData->fgcolor);
x0 = Compass_X0 + Delta_X - 2 * i * 5 * Compass_LineDelta; x0 = Compass_X0 + Delta_X - 2 * i * 5 * Compass_LineDelta;
getdisplay().fillRect(x0-2, Compass_Y0 - 2 * Compass_LineLength, 5, 2 * Compass_LineLength, commonData->fgcolor); epd->fillRect(x0-2, Compass_Y0 - 2 * Compass_LineLength, 5, 2 * Compass_LineLength, commonData->fgcolor);
x0 = Compass_X0 + Delta_X - ( 2 * i + 1 ) * 5 * Compass_LineDelta; x0 = Compass_X0 + Delta_X - ( 2 * i + 1 ) * 5 * Compass_LineDelta;
getdisplay().fillRect(x0-1, Compass_Y0 - Compass_LineLength, 3, Compass_LineLength, commonData->fgcolor); epd->fillRect(x0-1, Compass_Y0 - Compass_LineLength, 3, Compass_LineLength, commonData->fgcolor);
} }
getdisplay().fillRect(0, Compass_Y0, 400, 3, commonData->fgcolor); epd->fillRect(0, Compass_Y0, 400, 3, commonData->fgcolor);
// Add the numbers to the compass band // Add the numbers to the compass band
int x0; int x0;
float AngleToDisplay = NextSector * 180.0 / M_PI; float AngleToDisplay = NextSector * 180.0 / M_PI;
x0 = Compass_X0 + Delta_X; x0 = Compass_X0 + Delta_X;
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b); epd->setFont(&DSEG7Classic_BoldItalic16pt7b);
do { do {
getdisplay().setCursor(x0 - 40, Compass_Y0 + 40); epd->setCursor(x0 - 40, Compass_Y0 + 40);
snprintf(buffer,bsize,"%03.0f", AngleToDisplay); snprintf(buffer,bsize,"%03.0f", AngleToDisplay);
getdisplay().print(buffer); epd->print(buffer);
AngleToDisplay += 20; AngleToDisplay += 20;
if ( AngleToDisplay >= 360.0 ) if ( AngleToDisplay >= 360.0 )
AngleToDisplay -= 360.0; AngleToDisplay -= 360.0;
@@ -208,7 +203,7 @@ class PageCompass : public Page
x0 = Compass_X0 + Delta_X + 4 * 5 * Compass_LineDelta; x0 = Compass_X0 + Delta_X + 4 * 5 * Compass_LineDelta;
do { do {
getdisplay().setCursor(x0 - 40, Compass_Y0 + 40); epd->setCursor(x0 - 40, Compass_Y0 + 40);
snprintf(buffer,bsize,"%03.0f", AngleToDisplay); snprintf(buffer,bsize,"%03.0f", AngleToDisplay);
// Quick and dirty way to prevent wrapping text in next line // Quick and dirty way to prevent wrapping text in next line
if ( ( x0 - 40 ) > 380 ) if ( ( x0 - 40 ) > 380 )
@@ -218,7 +213,7 @@ class PageCompass : public Page
else if ( ( x0 - 40 ) > 325 ) else if ( ( x0 - 40 ) > 325 )
buffer[2] = 0; buffer[2] = 0;
getdisplay().print(buffer); epd->print(buffer);
AngleToDisplay -= 20; AngleToDisplay -= 20;
if ( AngleToDisplay < 0 ) if ( AngleToDisplay < 0 )
@@ -230,8 +225,8 @@ class PageCompass : public Page
// x_test += 2; // x_test += 2;
// snprintf(buffer,bsize,"%03d", x_test); // snprintf(buffer,bsize,"%03d", x_test);
// getdisplay().setCursor(x_test, Compass_Y0 - 60); // epd->setCursor(x_test, Compass_Y0 - 60);
// getdisplay().print(buffer); // epd->print(buffer);
// if ( x_test > 390) // if ( x_test > 390)
// x_test = 320; // x_test = 320;

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -5,13 +6,19 @@
class PageDST810 : public Page class PageDST810 : public Page
{ {
private:
String lengthformat;
public: public:
PageDST810(CommonData &common){ PageDST810(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageDST810"); logger->logDebug(GwLog::LOG, "Instantiate PageDST810");
// Get config data
lengthformat = config->getString(config->lengthFormat);
} }
virtual int handleKey(int key){ int handleKey(int key) {
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -20,9 +27,7 @@ public:
return key; return key;
} }
int displayPage(PageData &pageData){ int displayPage(PageData &pageData) {
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -34,21 +39,14 @@ public:
static String svalue4old = ""; static String svalue4old = "";
static String unit4old = ""; static String unit4old = "";
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Get boat values #1 // Get boat values #1
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = commonData->fmt->formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 // Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue)
@@ -56,8 +54,8 @@ public:
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = commonData->fmt->formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value String unit2 = commonData->fmt->formatValue(bvalue2, *commonData).unit; // Unit of value
// Get boat values #3 // Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue)
@@ -65,8 +63,8 @@ public:
name3 = name3.substring(0, 6); // String length limit for value name name3 = name3.substring(0, 6); // String length limit for value name
double value3 = bvalue3->value; // Value as double in SI unit double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = commonData->fmt->formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value String unit3 = commonData->fmt->formatValue(bvalue3, *commonData).unit; // Unit of value
// Get boat values #4 // Get boat values #4
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue)
@@ -74,8 +72,8 @@ public:
name4 = name4.substring(0, 6); // String length limit for value name name4 = name4.substring(0, 6); // String length limit for value name
double value4 = bvalue4->value; // Value as double in SI unit double value4 = bvalue4->value; // Value as double in SI unit
bool valid4 = bvalue4->valid; // Valid information bool valid4 = bvalue4->valid; // Valid information
String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue4 = commonData->fmt->formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value String unit4 = commonData->fmt->formatValue(bvalue4, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused) // Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){ if(String(flashLED) == "Limit Violation"){
@@ -85,43 +83,43 @@ public:
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageDST810, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4); logger->logDebug(GwLog::LOG, "Drawing at PageDST810, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 55); epd->setCursor(20, 55);
getdisplay().print("Depth"); // Page name epd->print("Depth"); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 90); epd->setCursor(20, 90);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit1); // Unit epd->print(unit1); // Unit
} }
else{ else{
getdisplay().print(unit1old); epd->print(unit1old);
} }
// Set font // Set font
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b); epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 90); epd->setCursor(180, 90);
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue1); // Real value as formated string epd->print(svalue1); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue1old); // Old value as formated string epd->print(svalue1old); // Old value as formated string
} }
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save the old value svalue1old = svalue1; // Save the old value
@@ -131,35 +129,35 @@ public:
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor); epd->fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 145); epd->setCursor(20, 145);
getdisplay().print("Speed"); // Page name epd->print("Speed"); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 180); epd->setCursor(20, 180);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit2); // Unit epd->print(unit2); // Unit
} }
else{ else{
getdisplay().print(unit2old); epd->print(unit2old);
} }
// Setfont // Setfont
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b); epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 180); epd->setCursor(180, 180);
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue2); // Real value as formated string epd->print(svalue2); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue2old); // Old value as formated string epd->print(svalue2old); // Old value as formated string
} }
if(valid2 == true){ if(valid2 == true){
svalue2old = svalue2; // Save the old value svalue2old = svalue2; // Save the old value
@@ -169,35 +167,35 @@ public:
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor); epd->fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 220); epd->setCursor(20, 220);
getdisplay().print("Log"); // Page name epd->print("Log"); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 240); epd->setCursor(20, 240);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit3); // Unit epd->print(unit3); // Unit
} }
else{ else{
getdisplay().print(unit3old); epd->print(unit3old);
} }
// Set font // Set font
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(80, 270); epd->setCursor(80, 270);
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue3); // Real value as formated string epd->print(svalue3); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue3old); // Old value as formated string epd->print(svalue3old); // Old value as formated string
} }
if(valid3 == true){ if(valid3 == true){
svalue3old = svalue3; // Save the old value svalue3old = svalue3; // Save the old value
@@ -207,35 +205,35 @@ public:
// ############### Vertical Line ################ // ############### Vertical Line ################
// Vertical line 3 pix // Vertical line 3 pix
getdisplay().fillRect(200, 195, 3, 75, commonData->fgcolor); epd->fillRect(200, 195, 3, 75, commonData->fgcolor);
// ############### Value 4 ################ // ############### Value 4 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(220, 220); epd->setCursor(220, 220);
getdisplay().print("Temp"); // Page name epd->print("Temp"); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(220, 240); epd->setCursor(220, 240);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit4); // Unit epd->print(unit4); // Unit
} }
else{ else{
getdisplay().print(unit4old); epd->print(unit4old);
} }
// Set font // Set font
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(280, 270); epd->setCursor(280, 270);
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue4); // Real value as formated string epd->print(svalue4); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue4old); // Old value as formated string epd->print(svalue4old); // Old value as formated string
} }
if(valid4 == true){ if(valid4 == true){
svalue4old = svalue4; // Save the old value svalue4old = svalue4; // Save the old value

View File

@@ -0,0 +1,145 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
/*
Electric propulsion
- Current, voltage, power
- 12, 24, 48 etc. Voltage
- rpm
- throttle position
- controller state
- error codes
- temperature engine, controller, batteries
*/
class PageEPropulsion : public Page
{
private:
char mode = 'N'; // (N)ormal, (C)onfig
void displayModeNormal(PageData &pageData) {
// TBD Boatvalues: ...
logger->logDebug(GwLog::DEBUG, "Drawing at PageEPropulsion");
// Title and corner value headings
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("Electric propulsion");
}
void displayModeConfig() {
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("EPropulsion configuration");
epd->setFont(&Ubuntu_Bold8pt8b);
// TODO menu
}
public:
PageEPropulsion(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG,"Instantiate PageEPropulsion");
}
void setupKeys(){
Page::setupKeys();
}
#ifdef BOARD_OBP60S3
int handleKey(int key){
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
} else {
mode = 'N';
}
return 0;
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
#ifdef BOARD_OBP40S3
int handleKey(int key){
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
commonData->keydata[1].label = "EDIT";
} else {
mode = 'N';
commonData->keydata[1].label = "ALARM";
}
return 0;
}
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
#endif
void displayNew(PageData &pageData){
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){
// Logging boat values
logger->logDebug(GwLog::LOG,"Drawing at PageEPropulsion; Mode=%c", mode);
// Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height());
if (mode == 'N') {
displayModeNormal(pageData);
} else if (mode == 'C') {
displayModeConfig();
}
return PAGE_UPDATE;
};
};
static Page *createPage(CommonData &common){
return new PageEPropulsion(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect
* this will be number of BoatValue pointers in pageData.values
*/
PageDescription registerPageEPropulsion(
"EPropulsion", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h)
true // Show display header on/off
);
#endif

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -66,25 +67,22 @@ static unsigned char fish_bits[] = {
class PageFluid : public Page class PageFluid : public Page
{ {
bool simulation = false; private:
double simgoto; double simgoto;
double simval; double simval;
double simstep; double simstep;
bool holdvalues = false;
int fluidtype; int fluidtype;
public: public:
PageFluid(CommonData &common){ PageFluid(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageFluid"); logger->logDebug(GwLog::LOG, "Instantiate PageFluid");
simulation = common.config->getBool(common.config->useSimuData);
holdvalues = common.config->getBool(common.config->holdvalues);
simval = double(random(0, 100)); simval = double(random(0, 100));
simgoto = double(random(0, 100)); simgoto = double(random(0, 100));
simstep = (simgoto - simval) / 20.0; simstep = (simgoto - simval) / 20.0;
} }
virtual int handleKey(int key){ int handleKey(int key) {
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -93,27 +91,22 @@ class PageFluid : public Page
return key; return key;
} }
virtual void displayNew(PageData &pageData){ void displayNew(PageData &pageData) {
fluidtype = commonData->config->getInt("page" + String(pageData.pageNumber) + "fluid", 0); fluidtype = config->getInt("page" + String(pageData.pageNumber) + "fluid", 0);
commonData->logger->logDebug(GwLog::LOG,"New PageFluid: fluidtype=%d", fluidtype); logger->logDebug(GwLog::LOG, "New PageFluid: fluidtype=%d", fluidtype);
} #ifdef BOARD_OBP60S3
// Clear optical warning
int displayPage(PageData &pageData){ if (flashLED == "Limit Violation") {
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function
static double value1old;
// Get config data
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
#endif
}
int displayPage(PageData &pageData) {
// Old values for hold function
static double value1old;
GwApi::BoatValue *bvalue1 = pageData.values[0]; GwApi::BoatValue *bvalue1 = pageData.values[0];
String name1 = bvalue1->getName(); String name1 = bvalue1->getName();
@@ -132,23 +125,23 @@ class PageFluid : public Page
} }
// Logging boat values // Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageFluid: value=%f", bvalue1->value); logger->logDebug(GwLog::LOG, "Drawing at PageFluid: value=%f", bvalue1->value);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); epd->setPartialWindow(0, 0, epd->width(), epd->height());
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
// descriptions // descriptions
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 60); epd->setCursor(20, 60);
getdisplay().print("Fluid"); epd->print("Fluid");
getdisplay().setCursor(300, 60); epd->setCursor(300, 60);
getdisplay().print(xdrDelete(name1).substring(0, 6)); epd->print(xdrDelete(name1).substring(0, 6));
// analog instrument // analog instrument
// scale from -120 to 120 // scale from -120 to 120
@@ -158,11 +151,11 @@ class PageFluid : public Page
uint8_t r = 110; uint8_t r = 110;
// circular frame // circular frame
getdisplay().drawCircle(c.x, c.y, r+5, commonData->fgcolor); epd->drawCircle(c.x, c.y, r+5, commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, r+2, commonData->fgcolor); epd->fillCircle(c.x, c.y, r+2, commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, r-1, commonData->bgcolor); epd->fillCircle(c.x, c.y, r-1, commonData->bgcolor);
// center of pointer as dot // center of pointer as dot
getdisplay().fillCircle(c.x, c.y, 8, commonData->fgcolor); epd->fillCircle(c.x, c.y, 8, commonData->fgcolor);
// value down centered // value down centered
char buffer[6]; char buffer[6];
@@ -176,32 +169,32 @@ class PageFluid : public Page
// draw symbol (as bitmap) // draw symbol (as bitmap)
switch (fluidtype) { switch (fluidtype) {
case 0: case 0:
getdisplay().drawXBitmap(c.x-8, c.y-50, fuel_bits, fuel_width, fuel_height, commonData->fgcolor); epd->drawXBitmap(c.x-8, c.y-50, fuel_bits, fuel_width, fuel_height, commonData->fgcolor);
break; break;
case 1: case 1:
getdisplay().drawXBitmap(c.x-8, c.y-50, water_bits, water_width, water_height, commonData->fgcolor); epd->drawXBitmap(c.x-8, c.y-50, water_bits, water_width, water_height, commonData->fgcolor);
break; break;
case 2: // gray water no symbol yet case 2: // gray water no symbol yet
// getdisplay().drawXBitmap(c.x-8, c.y-50, gray_bits, gray_width, gray_height, commonData->fgcolor); // epd->drawXBitmap(c.x-8, c.y-50, gray_bits, gray_width, gray_height, commonData->fgcolor);
break; break;
case 3: case 3:
getdisplay().drawXBitmap(c.x-8, c.y-50, fish_bits, fish_width, fish_height, commonData->fgcolor); epd->drawXBitmap(c.x-8, c.y-50, fish_bits, fish_width, fish_height, commonData->fgcolor);
break; break;
case 4: case 4:
getdisplay().drawXBitmap(c.x-8, c.y-50, oil_bits, oil_width, oil_height, commonData->fgcolor); epd->drawXBitmap(c.x-8, c.y-50, oil_bits, oil_width, oil_height, commonData->fgcolor);
break; break;
case 5: case 5:
getdisplay().drawXBitmap(c.x-8, c.y-50, waste_bits, waste_width, waste_height, commonData->fgcolor); epd->drawXBitmap(c.x-8, c.y-50, waste_bits, waste_width, waste_height, commonData->fgcolor);
break; break;
case 6: case 6:
getdisplay().drawXBitmap(c.x-8, c.y-50, gasoline_bits, gasoline_width, gasoline_height, commonData->fgcolor); epd->drawXBitmap(c.x-8, c.y-50, gasoline_bits, gasoline_width, gasoline_height, commonData->fgcolor);
break; break;
} }
Point p, pr; Point p, pr;
// scale texts // scale texts
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
p = {c.x, c.y - r + 30}; p = {c.x, c.y - r + 30};
drawTextCenter(p.x, p.y, "1/2"); drawTextCenter(p.x, p.y, "1/2");
pr = rotatePoint(c, p, -60); pr = rotatePoint(c, p, -60);
@@ -210,7 +203,7 @@ class PageFluid : public Page
drawTextCenter(pr.x, pr.y, "3/4"); drawTextCenter(pr.x, pr.y, "3/4");
// empty and full // empty and full
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
p = rotatePoint(c, {c.x, c.y - r + 30}, -130); p = rotatePoint(c, {c.x, c.y - r + 30}, -130);
drawTextCenter(p.x, p.y, "E"); drawTextCenter(p.x, p.y, "E");
p = rotatePoint(c, {c.x, c.y - r + 30}, 130); p = rotatePoint(c, {c.x, c.y - r + 30}, 130);
@@ -236,7 +229,7 @@ class PageFluid : public Page
continue; continue;
} }
p = rotatePoint(c, {c.x, c.y - r + 10}, angle); p = rotatePoint(c, {c.x, c.y - r + 10}, angle);
getdisplay().fillCircle(p.x, p.y, 3, commonData->fgcolor); epd->fillCircle(p.x, p.y, 3, commonData->fgcolor);
} }
// pointer // pointer
@@ -249,7 +242,7 @@ class PageFluid : public Page
}; };
fillPoly4(rotatePoints(c, pts, -120 + fluidlevel * 2.4), commonData->fgcolor); fillPoly4(rotatePoints(c, pts, -120 + fluidlevel * 2.4), commonData->fgcolor);
// Pointer axis is white // Pointer axis is white
getdisplay().fillCircle(c.x, c.y, 6, commonData->bgcolor); epd->fillCircle(c.x, c.y, 6, commonData->bgcolor);
} }
return PAGE_UPDATE; return PAGE_UPDATE;

View File

@@ -1,18 +1,28 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h" #include "BoatDataCalibration.h"
#endif
class PageFourValues : public Page class PageFourValues : public Page
{ {
public: private:
PageFourValues(CommonData &common){ String lengthformat;
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageFourValues"); public:
PageFourValues(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageFourValues");
// Get config data
lengthformat = config->getString(config->lengthFormat);
} }
virtual int handleKey(int key){ int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -21,9 +31,17 @@ class PageFourValues : public Page
return key; return key;
} }
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){ int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -34,109 +52,104 @@ class PageFourValues : public Page
static String unit3old = ""; static String unit3old = "";
static String svalue4old = ""; static String svalue4old = "";
static String unit4old = ""; static String unit4old = "";
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Get boat values #1 // Get boat values #1
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = commonData->fmt->formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 // Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
#endif
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = commonData->fmt->formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value String unit2 = commonData->fmt->formatValue(bvalue2, *commonData).unit; // Unit of value
// Get boat values #3 // Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list
String name3 = xdrDelete(bvalue3->getName()); // Value name String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for value name name3 = name3.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
#endif
double value3 = bvalue3->value; // Value as double in SI unit double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = commonData->fmt->formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value String unit3 = commonData->fmt->formatValue(bvalue3, *commonData).unit; // Unit of value
// Get boat values #4 // Get boat values #4
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Fourth element in list GwApi::BoatValue *bvalue4 = pageData.values[3]; // Fourth element in list
String name4 = xdrDelete(bvalue4->getName()); // Value name String name4 = xdrDelete(bvalue4->getName()); // Value name
name4 = name4.substring(0, 6); // String length limit for value name name4 = name4.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
#endif
double value4 = bvalue4->value; // Value as double in SI unit double value4 = bvalue4->value; // Value as double in SI unit
bool valid4 = bvalue4->valid; // Valid information bool valid4 = bvalue4->valid; // Valid information
String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue4 = commonData->fmt->formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value String unit4 = commonData->fmt->formatValue(bvalue4, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageFourValues, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4); logger->logDebug(GwLog::LOG, "Drawing at PageFourValues, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(20, 45); epd->setCursor(20, 45);
getdisplay().print(name1); // Page name epd->print(name1); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 65); epd->setCursor(20, 65);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit1); // Unit epd->print(unit1); // Unit
} }
else{ else{
getdisplay().print(unit1old); epd->print(unit1old);
} }
// Switch font if format for any values // Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){ if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(120, 55); epd->setCursor(120, 55);
} }
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){ else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(150, 58); epd->setCursor(150, 58);
} }
else{ else{
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(180, 65); epd->setCursor(180, 65);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue1); // Real value as formated string epd->print(svalue1); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue1old); // Old value as formated string epd->print(svalue1old); // Old value as formated string
} }
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save the old value svalue1old = svalue1; // Save the old value
@@ -146,45 +159,45 @@ class PageFourValues : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 80, 400, 3, commonData->fgcolor); epd->fillRect(0, 80, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(20, 113); epd->setCursor(20, 113);
getdisplay().print(name2); // Page name epd->print(name2); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 133); epd->setCursor(20, 133);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit2); // Unit epd->print(unit2); // Unit
} }
else{ else{
getdisplay().print(unit2old); epd->print(unit2old);
} }
// Switch font if format for any values // Switch font if format for any values
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){ if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(120, 123); epd->setCursor(120, 123);
} }
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){ else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(150, 123); epd->setCursor(150, 123);
} }
else{ else{
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(180, 133); epd->setCursor(180, 133);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue2); // Real value as formated string epd->print(svalue2); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue2old); // Old value as formated string epd->print(svalue2old); // Old value as formated string
} }
if(valid2 == true){ if(valid2 == true){
svalue2old = svalue2; // Save the old value svalue2old = svalue2; // Save the old value
@@ -194,45 +207,45 @@ class PageFourValues : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 146, 400, 3, commonData->fgcolor); epd->fillRect(0, 146, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(20, 181); epd->setCursor(20, 181);
getdisplay().print(name3); // Page name epd->print(name3); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 201); epd->setCursor(20, 201);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit3); // Unit epd->print(unit3); // Unit
} }
else{ else{
getdisplay().print(unit3old); epd->print(unit3old);
} }
// Switch font if format for any values // Switch font if format for any values
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){ if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(120, 191); epd->setCursor(120, 191);
} }
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){ else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(150, 191); epd->setCursor(150, 191);
} }
else{ else{
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(180, 201); epd->setCursor(180, 201);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue3); // Real value as formated string epd->print(svalue3); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue3old); // Old value as formated string epd->print(svalue3old); // Old value as formated string
} }
if(valid3 == true){ if(valid3 == true){
svalue3old = svalue3; // Save the old value svalue3old = svalue3; // Save the old value
@@ -242,45 +255,45 @@ class PageFourValues : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 214, 400, 3, commonData->fgcolor); epd->fillRect(0, 214, 400, 3, commonData->fgcolor);
// ############### Value 4 ################ // ############### Value 4 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(20, 249); epd->setCursor(20, 249);
getdisplay().print(name4); // Page name epd->print(name4); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 269); epd->setCursor(20, 269);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit4); // Unit epd->print(unit4); // Unit
} }
else{ else{
getdisplay().print(unit4old); epd->print(unit4old);
} }
// Switch font if format for any values // Switch font if format for any values
if(bvalue4->getFormat() == "formatLatitude" || bvalue4->getFormat() == "formatLongitude"){ if(bvalue4->getFormat() == "formatLatitude" || bvalue4->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(120, 259); epd->setCursor(120, 259);
} }
else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){ else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(150, 259); epd->setCursor(150, 259);
} }
else{ else{
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(180, 269); epd->setCursor(180, 269);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue4); // Real value as formated string epd->print(svalue4); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue4old); // Old value as formated string epd->print(svalue4old); // Old value as formated string
} }
if(valid4 == true){ if(valid4 == true){
svalue4old = svalue4; // Save the old value svalue4old = svalue4; // Save the old value
@@ -289,6 +302,11 @@ class PageFourValues : public Page
return PAGE_UPDATE; return PAGE_UPDATE;
}; };
void leavePage(PageData &pageData) {
logger->logDebug(GwLog::LOG, "Leaving PageFourvalues");
}
}; };
static Page *createPage(CommonData &common){ static Page *createPage(CommonData &common){

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -6,13 +7,19 @@
class PageFourValues2 : public Page class PageFourValues2 : public Page
{ {
public: private:
PageFourValues2(CommonData &common){ String lengthformat;
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageFourValues2"); public:
PageFourValues2(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageFourValues2");
// Get config data
lengthformat = config->getString(config->lengthFormat);
} }
virtual int handleKey(int key){ int handleKey(int key) {
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; // Toggle keylock commonData->keylock = !commonData->keylock; // Toggle keylock
@@ -21,9 +28,17 @@ class PageFourValues2 : public Page
return key; return key;
} }
int displayPage(PageData &pageData){ void displayNew(PageData &pageData) {
GwConfigHandler *config = commonData->config; #ifdef BOARD_OBP60S3
GwLog *logger = commonData->logger; // Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -34,109 +49,104 @@ class PageFourValues2 : public Page
static String unit3old = ""; static String unit3old = "";
static String svalue4old = ""; static String svalue4old = "";
static String unit4old = ""; static String unit4old = "";
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Get boat values #1 // Get boat values #1
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = commonData->fmt->formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 // Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue)
String name2 = xdrDelete(bvalue2->getName()); // Value name String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
#endif
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = commonData->fmt->formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value String unit2 = commonData->fmt->formatValue(bvalue2, *commonData).unit; // Unit of value
// Get boat values #3 // Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue)
String name3 = xdrDelete(bvalue3->getName()); // Value name String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for value name name3 = name3.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
#endif
double value3 = bvalue3->value; // Value as double in SI unit double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = commonData->fmt->formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value String unit3 = commonData->fmt->formatValue(bvalue3, *commonData).unit; // Unit of value
// Get boat values #4 // Get boat values #4
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue)
String name4 = xdrDelete(bvalue4->getName()); // Value name String name4 = xdrDelete(bvalue4->getName()); // Value name
name4 = name4.substring(0, 6); // String length limit for value name name4 = name4.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
#endif
double value4 = bvalue4->value; // Value as double in SI unit double value4 = bvalue4->value; // Value as double in SI unit
bool valid4 = bvalue4->valid; // Valid information bool valid4 = bvalue4->valid; // Valid information
String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue4 = commonData->fmt->formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value String unit4 = commonData->fmt->formatValue(bvalue4, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageFourValues2, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4); logger->logDebug(GwLog::LOG, "Drawing at PageFourValues2, %s: %f, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 55); epd->setCursor(20, 55);
getdisplay().print(name1); // Page name epd->print(name1); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 90); epd->setCursor(20, 90);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit1); // Unit epd->print(unit1); // Unit
} }
else{ else{
getdisplay().print(unit1old); epd->print(unit1old);
} }
// Switch font if format for any values // Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){ if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(100, 90); epd->setCursor(100, 90);
} }
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){ else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(180, 77); epd->setCursor(180, 77);
} }
else{ else{
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b); epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 90); epd->setCursor(180, 90);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue1); // Real value as formated string epd->print(svalue1); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue1old); // Old value as formated string epd->print(svalue1old); // Old value as formated string
} }
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save the old value svalue1old = svalue1; // Save the old value
@@ -146,45 +156,45 @@ class PageFourValues2 : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor); epd->fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 145); epd->setCursor(20, 145);
getdisplay().print(name2); // Page name epd->print(name2); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 180); epd->setCursor(20, 180);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit2); // Unit epd->print(unit2); // Unit
} }
else{ else{
getdisplay().print(unit2old); epd->print(unit2old);
} }
// Switch font if format for any values // Switch font if format for any values
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){ if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(100, 180); epd->setCursor(100, 180);
} }
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){ else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(180, 158); epd->setCursor(180, 158);
} }
else{ else{
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b); epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 180); epd->setCursor(180, 180);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue2); // Real value as formated string epd->print(svalue2); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue2old); // Old value as formated string epd->print(svalue2old); // Old value as formated string
} }
if(valid2 == true){ if(valid2 == true){
svalue2old = svalue2; // Save the old value svalue2old = svalue2; // Save the old value
@@ -194,45 +204,45 @@ class PageFourValues2 : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor); epd->fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 220); epd->setCursor(20, 220);
getdisplay().print(name3); // Page name epd->print(name3); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 240); epd->setCursor(20, 240);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit3); // Unit epd->print(unit3); // Unit
} }
else{ else{
getdisplay().print(unit3old); epd->print(unit3old);
} }
// Switch font if format for any values // Switch font if format for any values
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){ if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(50, 240); epd->setCursor(50, 240);
} }
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){ else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(100, 240); epd->setCursor(100, 240);
} }
else{ else{
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(80, 270); epd->setCursor(80, 270);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue3); // Real value as formated string epd->print(svalue3); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue3old); // Old value as formated string epd->print(svalue3old); // Old value as formated string
} }
if(valid3 == true){ if(valid3 == true){
svalue3old = svalue3; // Save the old value svalue3old = svalue3; // Save the old value
@@ -242,45 +252,45 @@ class PageFourValues2 : public Page
// ############### Vertical Line ################ // ############### Vertical Line ################
// Vertical line 3 pix // Vertical line 3 pix
getdisplay().fillRect(200, 195, 3, 75, commonData->fgcolor); epd->fillRect(200, 195, 3, 75, commonData->fgcolor);
// ############### Value 4 ################ // ############### Value 4 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(220, 220); epd->setCursor(220, 220);
getdisplay().print(name4); // Page name epd->print(name4); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(220, 240); epd->setCursor(220, 240);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit4); // Unit epd->print(unit4); // Unit
} }
else{ else{
getdisplay().print(unit4old); epd->print(unit4old);
} }
// Switch font if format for any values // Switch font if format for any values
if(bvalue4->getFormat() == "formatLatitude" || bvalue4->getFormat() == "formatLongitude"){ if(bvalue4->getFormat() == "formatLatitude" || bvalue4->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(250, 240); epd->setCursor(250, 240);
} }
else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){ else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(300, 240); epd->setCursor(300, 240);
} }
else{ else{
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(280, 270); epd->setCursor(280, 270);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue4); // Real value as formated string epd->print(svalue4); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue4old); // Old value as formated string epd->print(svalue4old); // Old value as formated string
} }
if(valid4 == true){ if(valid4 == true){
svalue4old = svalue4; // Save the old value svalue4old = svalue4; // Save the old value

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -6,12 +7,23 @@
class PageGenerator : public Page class PageGenerator : public Page
{ {
private:
String batVoltage;
int genPower;
String powerSensor;
public: public:
PageGenerator(CommonData &common){ PageGenerator(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageGenerator"); logger->logDebug(GwLog::LOG, "Instantiate PageGenerator");
// Get config data
batVoltage = config->getString(config->batteryVoltage);
genPower = config->getInt(config->genPower);
powerSensor = config->getString(config->usePowSensor3);
} }
virtual int handleKey(int key){
int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -20,20 +32,8 @@ public:
return key; return key;
} }
int displayPage(PageData &pageData) int displayPage(PageData &pageData) {
{
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Get config data
bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String batVoltage = config->getString(config->batteryVoltage);
int genPower = config->getInt(config->genPower);
String backlightMode = config->getString(config->backlight);
String powerSensor = config->getString(config->usePowSensor3);
double value1 = 0; // Solar voltage double value1 = 0; // Solar voltage
double value2 = 0; // Solar current double value2 = 0; // Solar current
double value3 = 0; // Solar output power double value3 = 0; // Solar output power
@@ -60,100 +60,95 @@ public:
bool valid1 = true; bool valid1 = true;
// Optical warning by limit violation // Optical warning by limit violation
if(String(flashLED) == "Limit Violation"){ if (flashLED == "Limit Violation") {
// Over voltage // Over voltage?
if(value1 > 14.8 && batVoltage == "12V"){ if (batVoltage == "12V") {
setBlinkingLED(true); setBlinkingLED(value1 > 14.8);
} } else if (batVoltage == "24V") {
if(value1 <= 14.8 && batVoltage == "12V"){ setBlinkingLED(value1 > 29.6);
} else {
setBlinkingLED(false); setBlinkingLED(false);
} }
if(value1 > 29.6 && batVoltage == "24V"){
setBlinkingLED(true);
}
if(value1 <= 29.6 && batVoltage == "24V"){
setBlinkingLED(false);
}
} }
// Logging voltage value // Logging voltage value
LOG_DEBUG(GwLog::LOG,"Drawing at PageGenerator, Type:%iW %s:=%f", genPower, name1.c_str(), value1); logger->logDebug(GwLog::LOG, "Drawing at PageGenerator, Type:%iW %s:=%f", genPower, name1.c_str(), value1);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(10, 65); epd->setCursor(10, 65);
getdisplay().print("Power"); epd->print("Power");
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(12, 82); epd->setCursor(12, 82);
getdisplay().print("Generator"); epd->print("Generator");
// Show voltage type // Show voltage type
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 140); epd->setCursor(10, 140);
int bvoltage = 0; int bvoltage = 0;
if(String(batVoltage) == "12V") bvoltage = 12; if(String(batVoltage) == "12V") bvoltage = 12;
else bvoltage = 24; else bvoltage = 24;
getdisplay().print(bvoltage); epd->print(bvoltage);
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V"); epd->print("V");
// Show solar power // Show solar power
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 200); epd->setCursor(10, 200);
if(genPower <= 999) getdisplay().print(genPower, 0); if(genPower <= 999) epd->print(genPower, 0);
if(genPower > 999) getdisplay().print(float(genPower/1000.0), 1); if(genPower > 999) epd->print(float(genPower/1000.0), 1);
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
if(genPower <= 999) getdisplay().print("W"); if(genPower <= 999) epd->print("W");
if(genPower > 999) getdisplay().print("kW"); if(genPower > 999) epd->print("kW");
// Show info // Show info
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 235); epd->setCursor(10, 235);
getdisplay().print("Installed"); epd->print("Installed");
getdisplay().setCursor(10, 255); epd->setCursor(10, 255);
getdisplay().print("Power Modul"); epd->print("Power Modul");
// Show generator // Show generator
generatorGraphic(200, 95, commonData->fgcolor, commonData->bgcolor); generatorGraphic(200, 95, commonData->fgcolor, commonData->bgcolor);
// Show load level in percent // Show load level in percent
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 200); epd->setCursor(150, 200);
getdisplay().print(genPercentage); epd->print(genPercentage);
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("%"); epd->print("%");
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(150, 235); epd->setCursor(150, 235);
getdisplay().print("Load"); epd->print("Load");
// Show sensor type info // Show sensor type info
String i2cAddr = ""; String i2cAddr = "";
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(270, 60); epd->setCursor(270, 60);
if(powerSensor == "off") getdisplay().print("Internal"); if(powerSensor == "off") epd->print("Internal");
if(powerSensor == "INA219"){ if(powerSensor == "INA219"){
getdisplay().print("INA219"); epd->print("INA219");
i2cAddr = " (0x" + String(INA219_I2C_ADDR3, HEX) + ")"; i2cAddr = " (0x" + String(INA219_I2C_ADDR3, HEX) + ")";
} }
if(powerSensor == "INA226"){ if(powerSensor == "INA226"){
getdisplay().print("INA226"); epd->print("INA226");
i2cAddr = " (0x" + String(INA226_I2C_ADDR3, HEX) + ")"; i2cAddr = " (0x" + String(INA226_I2C_ADDR3, HEX) + ")";
} }
getdisplay().print(i2cAddr); epd->print(i2cAddr);
getdisplay().setCursor(270, 80); epd->setCursor(270, 80);
getdisplay().print("Sensor Modul"); epd->print("Sensor Modul");
// Reading bus data or using simulation data // Reading bus data or using simulation data
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 140); epd->setCursor(260, 140);
if(simulation == true){ if(simulation == true){
if(batVoltage == "12V"){ if(batVoltage == "12V"){
value1 = 12.0; value1 = 12.0;
@@ -162,46 +157,59 @@ public:
value1 = 24.0; value1 = 24.0;
} }
value1 += float(random(0, 5)) / 10; // Simulation data value1 += float(random(0, 5)) / 10; // Simulation data
getdisplay().print(value1,1); epd->print(value1,1);
} }
else{ else{
// Check for valid real data, display also if hold values activated // Check for valid real data, display also if hold values activated
if(valid1 == true || holdvalues == true){ if(valid1 == true || holdvalues == true){
// Resolution switching // Resolution switching
if(value1 <= 9.9) getdisplay().print(value1, 2); if(value1 <= 9.9) epd->print(value1, 2);
if(value1 > 9.9 && value1 <= 99.9)getdisplay().print(value1, 1); if(value1 > 9.9 && value1 <= 99.9)epd->print(value1, 1);
if(value1 > 99.9) getdisplay().print(value1, 0); if(value1 > 99.9) epd->print(value1, 0);
} }
else{ else{
getdisplay().print("---"); // Missing bus data epd->print(commonData->fmt->placeholder); // Missing bus data
} }
} }
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V"); epd->print("V");
// Show actual current in A // Show actual current in A
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 200); epd->setCursor(260, 200);
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){ if ((powerSensor == "INA219" || powerSensor == "INA226") && (simulation == false)) {
if(value2 <= 9.9) getdisplay().print(value2, 2); // TODO use formatter for this?
if(value2 > 9.9 && value2 <= 99.9)getdisplay().print(value2, 1); if (value2 <= 9.9) {
if(value2 > 99.9) getdisplay().print(value2, 0); epd->print(value2, 2);
} else if (value2 <= 99.9) {
epd->print(value2, 1);
} else {
epd->print(value2, 0);
}
} }
else getdisplay().print("---"); else {
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->print(commonData->fmt->placeholder);
getdisplay().print("A"); }
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("A");
// Show actual consumption in W // Show actual consumption in W
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 260); epd->setCursor(260, 260);
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){ if ((powerSensor == "INA219" || powerSensor == "INA226") && (simulation == false)) {
if(value3 <= 9.9) getdisplay().print(value3, 2); if(value3 <= 9.9) {
if(value3 > 9.9 && value3 <= 99.9)getdisplay().print(value3, 1); epd->print(value3, 2);
if(value3 > 99.9) getdisplay().print(value3, 0); } else if (value3 <= 99.9) {
epd->print(value3, 1);
} else {
epd->print(value3, 0);
}
} }
else getdisplay().print("---"); else {
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->print(commonData->fmt->placeholder);
getdisplay().print("W"); }
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("W");
return PAGE_UPDATE; return PAGE_UPDATE;
}; };

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -5,14 +6,24 @@
class PageKeelPosition : public Page class PageKeelPosition : public Page
{ {
private:
String lengthformat;
String rotsensor;
String rotfunction;
public: public:
PageKeelPosition(CommonData &common){ PageKeelPosition(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageKeelPosition"); logger->logDebug(GwLog::LOG, "Instantiate PageKeelPosition");
// Get config data
lengthformat = config->getString(config->lengthFormat);
rotsensor = config->getString(config->useRotSensor);
rotfunction = config->getString(config->rotFunction);
} }
// Key functions // Key functions
virtual int handleKey(int key){ int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -21,23 +32,21 @@ public:
return key; return key;
} }
int displayPage(PageData &pageData) void displayNew(PageData &pageData) {
{ #ifdef BOARD_OBP60S3
GwConfigHandler *config = commonData->config; // Clear optical warning
GwLog *logger = commonData->logger; if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
double value1 = 0; double value1 = 0;
double value1old = 0; double value1old = 0;
// Get config data
String lengthformat = config->getString(config->lengthFormat);
bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
String rotsensor = config->getString(config->useRotSensor);
String rotfunction = config->getString(config->rotFunction);
// Get boat values for Keel position // Get boat values for Keel position
bool valid1 = commonData->data.validRotAngle; // Valid information bool valid1 = commonData->data.validRotAngle; // Valid information
if(simulation == false && rotsensor == "AS5600" && rotfunction == "Keel"){ if(simulation == false && rotsensor == "AS5600" && rotfunction == "Keel"){
@@ -55,20 +64,14 @@ public:
value1old = value1; // Save old value value1old = value1; // Save old value
} }
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values // Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageKeelPosition, Keel:%f", value1); logger->logDebug(GwLog::LOG, "Drawing at PageKeelPosition, Keel:%f", value1);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
//******************************************************************************************* //*******************************************************************************************
@@ -76,9 +79,9 @@ public:
int rInstrument = 110; // Radius of KeelPosition int rInstrument = 110; // Radius of KeelPosition
float pi = 3.141592; float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle epd->fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
getdisplay().fillRect(0, 30, 400, 122, commonData->bgcolor); // Delete half top circle epd->fillRect(0, 30, 400, 122, commonData->bgcolor); // Delete half top circle
for(int i=90; i<=270; i=i+10) for(int i=90; i<=270; i=i+10)
{ {
@@ -105,17 +108,17 @@ public:
// Print text centered on position x, y // Print text centered on position x, y
int16_t x1, y1; // Return values of getTextBounds int16_t x1, y1; // Return values of getTextBounds
uint16_t w, h; // Return values of getTextBounds uint16_t w, h; // Return values of getTextBounds
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string epd->getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
getdisplay().setCursor(x-w/2, y+h/2); epd->setCursor(x-w/2, y+h/2);
if(i % 30 == 0){ if(i % 30 == 0){
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().print(ii); epd->print(ii);
} }
// Draw sub scale with dots // Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*pi); float x1c = 200 + rInstrument*sin(i/180.0*pi);
float y1c = 150 - rInstrument*cos(i/180.0*pi); float y1c = 150 - rInstrument*cos(i/180.0*pi);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor); epd->fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*pi); float sinx=sin(i/180.0*pi);
float cosx=cos(i/180.0*pi); float cosx=cos(i/180.0*pi);
@@ -126,10 +129,10 @@ public:
float xx2 = +dx; float xx2 = +dx;
float yy1 = -(rInstrument-10); float yy1 = -(rInstrument-10);
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2), 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
} }
@@ -163,7 +166,7 @@ public:
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument * 0.6); float yy2 = -(rInstrument * 0.6);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
@@ -173,36 +176,36 @@ public:
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument * 0.6); float iy1 = -(rInstrument * 0.6);
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), epd->fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
// Draw counterweight // Draw counterweight
getdisplay().fillCircle(200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2), 5, commonData->fgcolor); epd->fillCircle(200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2), 5, commonData->fgcolor);
} }
// Center circle // Center circle
getdisplay().fillCircle(200, 140, startwidth + 22, commonData->bgcolor); epd->fillCircle(200, 140, startwidth + 22, commonData->bgcolor);
getdisplay().fillCircle(200, 140, startwidth + 20, commonData->fgcolor); // Boat circle epd->fillCircle(200, 140, startwidth + 20, commonData->fgcolor); // Boat circle
getdisplay().fillRect(200 - 30, 140 - 30, 2 * 30, 30, commonData->bgcolor); // Delete half top of boat circle epd->fillRect(200 - 30, 140 - 30, 2 * 30, 30, commonData->bgcolor); // Delete half top of boat circle
getdisplay().fillRect(150, 150, 100, 4, commonData->fgcolor); // Water line epd->fillRect(150, 150, 100, 4, commonData->fgcolor); // Water line
// Print label // Print label
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(100, 70); epd->setCursor(100, 70);
getdisplay().print("Keel Position"); // Label epd->print("Keel Position"); // Label
if((rotsensor == "AS5600" && rotfunction == "Keel" && (valid1 == true || holdvalues == true)) || simulation == true){ if((rotsensor == "AS5600" && rotfunction == "Keel" && (valid1 == true || holdvalues == true)) || simulation == true){
// Print Unit of keel position // Print Unit of keel position
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(175, 110); epd->setCursor(175, 110);
getdisplay().print(unit1); // Unit epd->print(unit1); // Unit
} }
else{ else{
// Print Unit of keel position // Print Unit of keel position
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(145, 110); epd->setCursor(145, 110);
getdisplay().print("No sensor data"); // Info missing sensor epd->print("No sensor data"); // Info missing sensor
} }
return PAGE_UPDATE; return PAGE_UPDATE;

View File

@@ -1,18 +1,28 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h" #include "BoatDataCalibration.h"
#endif
class PageOneValue : public Page class PageOneValue : public Page
{ {
public: private:
PageOneValue(CommonData &common){ String lengthformat;
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageOneValue"); public:
PageOneValue(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageOneValue");
// Get config data
lengthformat = config->getString(config->lengthFormat);
} }
virtual int handleKey(int key){ int handleKey(int key) {
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -21,83 +31,81 @@ class PageOneValue : public Page
return key; return key;
} }
int displayPage(PageData &pageData){ void displayNew(PageData &pageData) {
GwConfigHandler *config = commonData->config; #ifdef BOARD_OBP60S3
GwLog *logger = commonData->logger; // Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
static String unit1old = ""; static String unit1old = "";
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Get boat values // Get boat values
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = commonData->fmt->formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageOneValue, %s: %f", name1.c_str(), value1); logger->logDebug(GwLog::LOG, "Drawing at PageOneValue, %s: %f", name1.c_str(), value1);
// Draw page // Draw page
//*********************************************************** //***********************************************************
/// Set display in partial refresh mode /// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
// Show name // Show name
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold32pt8b); epd->setFont(&Ubuntu_Bold32pt8b);
getdisplay().setCursor(20, 100); epd->setCursor(20, 100);
getdisplay().print(name1); // Page name epd->print(name1); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(270, 100); epd->setCursor(270, 100);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit1); // Unit epd->print(unit1); // Unit
} }
else{ else{
getdisplay().print(unit1old); epd->print(unit1old);
} }
// Switch font if format for any values // Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){ if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 180); epd->setCursor(20, 180);
} }
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){ else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold32pt8b); epd->setFont(&Ubuntu_Bold32pt8b);
getdisplay().setCursor(20, 200); epd->setCursor(20, 200);
} }
else{ else{
getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b); epd->setFont(&DSEG7Classic_BoldItalic60pt7b);
getdisplay().setCursor(20, 240); epd->setCursor(20, 240);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue1); // Real value as formated string epd->print(svalue1); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue1old); // Old value as formated string epd->print(svalue1old); // Old value as formated string
} }
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save the old value svalue1old = svalue1; // Save the old value

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -5,14 +6,30 @@
class PageRollPitch : public Page class PageRollPitch : public Page
{ {
private:
String lengthformat;
int rolllimit;
String roffset;
double rolloffset;
String poffset;
double pitchoffset;
public: public:
PageRollPitch(CommonData &common){ PageRollPitch(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageRollPitch"); logger->logDebug(GwLog::LOG, "Instantiate PageRollPitch");
// Get config data
String lengthformat = config->getString(config->lengthFormat);
rolllimit = config->getInt(config->rollLimit);
roffset = config->getString(config->rollOffset);
rolloffset = roffset.toFloat() / 360 * (2 * M_PI);
poffset = config->getString(config->pitchOffset);
pitchoffset = poffset.toFloat() / 360 * (2 * M_PI);
} }
// Key functions // Key functions
virtual int handleKey(int key){ int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -21,9 +38,7 @@ public:
return key; return key;
} }
int displayPage(PageData &pageData){ int displayPage(PageData &pageData) {
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
double value1 = 0; double value1 = 0;
double value2 = 0; double value2 = 0;
@@ -32,19 +47,6 @@ public:
String svalue2 = ""; String svalue2 = "";
String svalue2old = ""; String svalue2old = "";
// Get config data
String lengthformat = config->getString(config->lengthFormat);
bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
int rolllimit = config->getInt(config->rollLimit);
String roffset = config->getString(config->rollOffset);
double rolloffset = roffset.toFloat()/360*(2*M_PI);
String poffset = config->getString(config->pitchOffset);
double pitchoffset = poffset.toFloat()/360*(2*M_PI);
// Get boat values for roll // Get boat values for roll
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (xdrRoll) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (xdrRoll)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
@@ -97,71 +99,70 @@ public:
} }
// Optical warning by limit violation // Optical warning by limit violation
if(String(flashLED) == "Limit Violation"){ if (flashLED == "Limit Violation") {
// Limits for roll // Limits for roll
if(value1*360/(2*M_PI) >= -1*rolllimit && value1*360/(2*M_PI) <= rolllimit){ if (value1*360/(2*M_PI) >= -1*rolllimit && value1*360/(2*M_PI) <= rolllimit) {
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} } else {
else{
setBlinkingLED(true); setBlinkingLED(true);
} }
} }
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageRollPitch, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2); logger->logDebug(GwLog::LOG, "Drawing at PageRollPitch, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
// Show roll limit // Show roll limit
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 65); epd->setCursor(10, 65);
getdisplay().print(rolllimit); // Value epd->print(rolllimit); // Value
//getdisplay().print(svalue1); // Value //epd->print(svalue1); // Value
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 95); epd->setCursor(10, 95);
getdisplay().print("Limit"); // Name epd->print("Limit"); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 115); epd->setCursor(10, 115);
getdisplay().print("DEG"); epd->print("DEG");
// Horizintal separator left // Horizintal separator left
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor); epd->fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show roll value // Show roll value
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 270); epd->setCursor(10, 270);
if(holdvalues == false) getdisplay().print(svalue1); // Value if(holdvalues == false) epd->print(svalue1); // Value
else getdisplay().print(svalue1old); else epd->print(svalue1old);
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 220); epd->setCursor(10, 220);
getdisplay().print(name1); // Name epd->print(name1); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 190); epd->setCursor(10, 190);
getdisplay().print("Deg"); epd->print("Deg");
// Horizintal separator right // Horizintal separator right
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor); epd->fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show pitch value // Show pitch value
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 270); epd->setCursor(295, 270);
if(holdvalues == false) getdisplay().print(svalue2); // Value if(holdvalues == false) epd->print(svalue2); // Value
else getdisplay().print(svalue2old); else epd->print(svalue2old);
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 220); epd->setCursor(335, 220);
getdisplay().print(name2); // Name epd->print(name2); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 190); epd->setCursor(335, 190);
getdisplay().print("Deg"); epd->print("Deg");
//******************************************************************************************* //*******************************************************************************************
@@ -169,8 +170,8 @@ public:
int rInstrument = 100; // Radius of instrument int rInstrument = 100; // Radius of instrument
float pi = 3.141592; float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle epd->fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
for(int i=0; i<360; i=i+10) for(int i=0; i<360; i=i+10)
{ {
@@ -194,17 +195,17 @@ public:
// Print text centered on position x, y // Print text centered on position x, y
int16_t x1, y1; // Return values of getTextBounds int16_t x1, y1; // Return values of getTextBounds
uint16_t w, h; // Return values of getTextBounds uint16_t w, h; // Return values of getTextBounds
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string epd->getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
getdisplay().setCursor(x-w/2, y+h/2); epd->setCursor(x-w/2, y+h/2);
if(i % 20 == 0){ if(i % 20 == 0){
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().print(ii); epd->print(ii);
} }
// Draw sub scale with dots // Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*M_PI); float x1c = 200 + rInstrument*sin(i/180.0*M_PI);
float y1c = 150 - rInstrument*cos(i/180.0*M_PI); float y1c = 150 - rInstrument*cos(i/180.0*M_PI);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor); epd->fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*M_PI); float sinx=sin(i/180.0*M_PI);
float cosx=cos(i/180.0*M_PI); float cosx=cos(i/180.0*M_PI);
@@ -215,10 +216,10 @@ public:
float xx2 = +dx; float xx2 = +dx;
float yy1 = -(rInstrument-10); float yy1 = -(rInstrument-10);
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2), 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
} }
@@ -239,7 +240,7 @@ public:
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument * 0.7); float yy2 = -(rInstrument * 0.7);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
@@ -249,28 +250,28 @@ public:
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument * 0.7); float iy1 = -(rInstrument * 0.7);
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), epd->fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
// Draw counterweight // Draw counterweight
getdisplay().fillCircle(200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2), 5, commonData->fgcolor); epd->fillCircle(200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2), 5, commonData->fgcolor);
} }
// Center circle // Center circle
getdisplay().fillCircle(200, 150, startwidth + 22, commonData->bgcolor); epd->fillCircle(200, 150, startwidth + 22, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 20, commonData->fgcolor); // Boat circle epd->fillCircle(200, 150, startwidth + 20, commonData->fgcolor); // Boat circle
int x0 = 200; int x0 = 200;
int y0 = 150; int y0 = 150;
int x1 = x0 + 50*cos(value1); int x1 = x0 + 50*cos(value1);
int y1 = y0 + 50*sin(value1); int y1 = y0 + 50*sin(value1);
int x2 = x0 + 50*cos(value1 - pi/2); int x2 = x0 + 50*cos(value1 - pi/2);
int y2 = y0 + 50*sin(value1 - pi/2); int y2 = y0 + 50*sin(value1 - pi/2);
getdisplay().fillTriangle(x0, y0, x1, y1, x2, y2, commonData->bgcolor); // Clear half top side of boat circle (right triangle) epd->fillTriangle(x0, y0, x1, y1, x2, y2, commonData->bgcolor); // Clear half top side of boat circle (right triangle)
x1 = x0 + 50*cos(value1 + pi); x1 = x0 + 50*cos(value1 + pi);
y1 = y0 + 50*sin(value1 + pi); y1 = y0 + 50*sin(value1 + pi);
getdisplay().fillTriangle(x0, y0, x1, y1, x2, y2, commonData->bgcolor); // Clear half top side of boat circle (left triangle) epd->fillTriangle(x0, y0, x1, y1, x2, y2, commonData->bgcolor); // Clear half top side of boat circle (left triangle)
getdisplay().fillRect(150, 160, 100, 4, commonData->fgcolor); // Water line epd->fillRect(150, 160, 100, 4, commonData->fgcolor); // Water line
// Draw roll pointer // Draw roll pointer
startwidth = 4; // Start width of pointer startwidth = 4; // Start width of pointer
@@ -283,7 +284,7 @@ public:
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument - 15); float yy2 = -(rInstrument - 15);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
@@ -293,15 +294,15 @@ public:
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument - 15); float iy1 = -(rInstrument - 15);
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), epd->fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
} }
else{ else{
// Print sensor info // Print sensor info
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(145, 200); epd->setCursor(145, 200);
getdisplay().print("No sensor data"); // Info missing sensor epd->print("No sensor data"); // Info missing sensor
} }
return PAGE_UPDATE; return PAGE_UPDATE;

View File

@@ -1,19 +1,29 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h" #include "BoatDataCalibration.h"
#endif
class PageRudderPosition : public Page class PageRudderPosition : public Page
{ {
private:
String lengthformat;
public: public:
PageRudderPosition(CommonData &common){ PageRudderPosition(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Show PageRudderPosition"); logger->logDebug(GwLog::LOG, "Instantiate PageRudderPosition");
// Get config data
String lengthformat = config->getString(config->lengthFormat);
} }
// Key functions // Key functions
virtual int handleKey(int key){ int handleKey(int key) {
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -22,69 +32,64 @@ public:
return key; return key;
} }
int displayPage(PageData &pageData){ void displayNew(PageData &pageData) {
GwConfigHandler *config = commonData->config; #ifdef BOARD_OBP60S3
GwLog *logger = commonData->logger; // Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
static String unit1old = ""; static String unit1old = "";
double value1 = 0.1; double value1 = 0.1;
double value1old = 0.1; double value1old = 0.1;
// Get config data
String lengthformat = config->getString(config->lengthFormat);
bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Get boat values for rudder position // Get boat values for rudder position
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list
String name1 = bvalue1->getName().c_str(); // Value name String name1 = bvalue1->getName().c_str(); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
value1 = bvalue1->value; // Raw value without unit convertion value1 = bvalue1->value; // Raw value without unit convertion
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = commonData->fmt->formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value
if(valid1 == true){ if (valid1 == true) {
value1old = value1; // Save old value value1old = value1; // Save old value
unit1old = unit1; // Save old unit unit1old = unit1; // Save old unit
} else { } else {
if(simulation == true){ if (simulation == true) {
value1 = (3 + float(random(0, 50)) / 10.0)/360*2*PI; value1 = (3 + float(random(0, 50)) / 10.0) / 360 * 2 * M_PI;
unit1 = "Deg"; unit1 = "Deg";
} } else {
else{
value1 = 0; value1 = 0;
} }
} }
// Optical warning by limit violation (unused) // Log boat values
if(String(flashLED) == "Limit Violation"){ logger->logDebug(GwLog::LOG, "Drawing at PageRudderPosition, %s:%f", name1.c_str(), value1);
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageRudderPosition, %s:%f", name1.c_str(), value1);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
//******************************************************************************************* //*******************************************************************************************
// Draw RudderPosition // Draw RudderPosition
int rInstrument = 110; // Radius of RudderPosition int rInstrument = 110; // Radius of RudderPosition
float pi = 3.141592; const float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle epd->fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
getdisplay().fillRect(0, 30, 400, 122, commonData->bgcolor); // Delete half top circle epd->fillRect(0, 30, 400, 122, commonData->bgcolor); // Delete half top circle
for(int i=90; i<=270; i=i+10) for(int i=90; i<=270; i=i+10)
{ {
@@ -92,51 +97,50 @@ public:
float x = 200 + (rInstrument-30)*sin(i/180.0*pi); // x-coordinate dots float x = 200 + (rInstrument-30)*sin(i/180.0*pi); // x-coordinate dots
float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate cots float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate cots
const char *ii = " "; const char *ii = " ";
switch (i) switch (i) {
{ case 0: ii=" "; break; // Use a blank for a empty scale value
case 0: ii=" "; break; // Use a blank for a empty scale value case 30 : ii=" "; break;
case 30 : ii=" "; break; case 60 : ii=" "; break;
case 60 : ii=" "; break; case 90 : ii="45"; break;
case 90 : ii="45"; break; case 120 : ii="30"; break;
case 120 : ii="30"; break; case 150 : ii="15"; break;
case 150 : ii="15"; break; case 180 : ii="0"; break;
case 180 : ii="0"; break; case 210 : ii="15"; break;
case 210 : ii="15"; break; case 240 : ii="30"; break;
case 240 : ii="30"; break; case 270 : ii="45"; break;
case 270 : ii="45"; break; case 300 : ii=" "; break;
case 300 : ii=" "; break; case 330 : ii=" "; break;
case 330 : ii=" "; break; default: break;
default: break;
} }
// Print text centered on position x, y // Print text centered on position x, y
int16_t x1, y1; // Return values of getTextBounds int16_t x1, y1; // Return values of getTextBounds
uint16_t w, h; // Return values of getTextBounds uint16_t w, h; // Return values of getTextBounds
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string epd->getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
getdisplay().setCursor(x-w/2, y+h/2); epd->setCursor(x-w/2, y+h/2);
if(i % 30 == 0){ if(i % 30 == 0){
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().print(ii); epd->print(ii);
} }
// Draw sub scale with dots // Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*pi); float x1c = 200 + rInstrument*sin(i/180.0*pi);
float y1c = 150 - rInstrument*cos(i/180.0*pi); float y1c = 150 - rInstrument*cos(i/180.0*pi);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor); epd->fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*pi); float sinx=sin(i/180.0*pi);
float cosx=cos(i/180.0*pi); float cosx=cos(i/180.0*pi);
// Draw sub scale with lines (two triangles) // Draw sub scale with lines (two triangles)
if(i % 30 == 0){ if(i % 30 == 0){
float dx=2; // Line thickness = 2*dx+1 float dx = 2; // Line thickness = 2*dx+1
float xx1 = -dx; float xx1 = -dx;
float xx2 = +dx; float xx2 = +dx;
float yy1 = -(rInstrument-10); float yy1 = -(rInstrument-10);
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2), 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
} }
@@ -144,28 +148,28 @@ public:
} }
// Print label // Print label
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(80, 70); epd->setCursor(80, 70);
getdisplay().print("Rudder Position"); // Label epd->print("Rudder Position"); // Label
// Print Unit in RudderPosition // Print Unit in RudderPosition
if(valid1 == true || simulation == true){ if(valid1 == true || simulation == true){
if(holdvalues == false){ if(holdvalues == false){
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(175, 110); epd->setCursor(175, 110);
getdisplay().print(unit1); // Unit epd->print(unit1); // Unit
} }
else{ else{
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(175, 110); epd->setCursor(175, 110);
getdisplay().print(unit1old); // Unit epd->print(unit1old); // Unit
} }
} }
else{ else{
// Print Unit of keel position // Print Unit of keel position
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(145, 110); epd->setCursor(145, 110);
getdisplay().print("No sensor data"); // Info missing sensor epd->print("No sensor data"); // Info missing sensor
} }
// Calculate rudder position // Calculate rudder position
@@ -188,7 +192,7 @@ public:
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument * 0.5); float yy2 = -(rInstrument * 0.5);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
@@ -198,14 +202,14 @@ public:
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument * 0.5); float iy1 = -(rInstrument * 0.5);
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), epd->fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
} }
// Center circle // Center circle
getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor); epd->fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor); epd->fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
return PAGE_UPDATE; return PAGE_UPDATE;
}; };

View File

@@ -1,8 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h" #include "BoatDataCalibration.h"
#endif
const int SixValues_x1 = 5; const int SixValues_x1 = 5;
const int SixValues_DeltaX = 200; const int SixValues_DeltaX = 200;
@@ -14,13 +18,13 @@ const int HowManyValues = 6;
class PageSixValues : public Page class PageSixValues : public Page
{ {
public: public:
PageSixValues(CommonData &common){ PageSixValues(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageSixValues"); logger->logDebug(GwLog::LOG, "Instantiate PageSixValues");
} }
virtual int handleKey(int key){ int handleKey(int key) {
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -29,130 +33,133 @@ class PageSixValues : public Page
return key; return key;
} }
int displayPage(PageData &pageData){ void displayNew(PageData &pageData) {
GwConfigHandler *config = commonData->config; #ifdef BOARD_OBP60S3
GwLog *logger = commonData->logger; // Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
// Old values for hold function setFlashLED(false);
static String OldDataText[HowManyValues] = {"", "", "", "", "", ""}; }
static String OldDataUnits[HowManyValues] = {"", "", "", "", "", ""}; #endif
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
GwApi::BoatValue *bvalue;
String DataName[HowManyValues];
double DataValue[HowManyValues];
bool DataValid[HowManyValues];
String DataText[HowManyValues];
String DataUnits[HowManyValues];
String DataFormat[HowManyValues];
for (int i = 0; i < HowManyValues; i++){
bvalue = pageData.values[i];
DataName[i] = xdrDelete(bvalue->getName());
DataName[i] = DataName[i].substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue, logger); // Check if boat data value is to be calibrated
DataValue[i] = bvalue->value; // Value as double in SI unit
DataValid[i] = bvalue->valid;
DataText[i] = formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
DataUnits[i] = formatValue(bvalue, *commonData).unit;
DataFormat[i] = bvalue->getFormat(); // Unit of value
}
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
if (bvalue == NULL) return PAGE_OK; // WTF why this statement?
// Draw page
//***********************************************************
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor);
for (int i = 0; i < ( HowManyValues / 2 ); i++){
if (i < (HowManyValues / 2) - 1) { // Don't draw horizontal line after last line of values -> standard design
// Horizontal line 3 pix
getdisplay().fillRect(0, SixValues_y1+(i+1)*SixValues_DeltaY, 400, 3, commonData->fgcolor);
}
for (int j = 0; j < 2; j++){
int ValueIndex = i * 2 + j;
int x0 = SixValues_x1 + j * SixValues_DeltaX;
int y0 = SixValues_y1 + i * SixValues_DeltaY;
LOG_DEBUG(GwLog::LOG,"Drawing at PageSixValue: %d %s %f %s", ValueIndex, DataName[ValueIndex], DataValue[ValueIndex], DataFormat[ValueIndex] );
// Show name
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(x0, y0+25);
getdisplay().print(DataName[ValueIndex]); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(x0, y0+72);
if(holdvalues == false){
getdisplay().print(DataUnits[ValueIndex]); // Unit
}
else{
getdisplay().print(OldDataUnits[ValueIndex]);
}
// Switch font if format for any values
if(DataFormat[ValueIndex] == "formatLatitude" || DataFormat[ValueIndex] == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(x0+10, y0+60);
}
else if(DataFormat[ValueIndex] == "formatTime" || DataFormat[ValueIndex] == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(x0+20,y0+55);
}
// pressure in hPa
else if(DataFormat[ValueIndex] == "formatXdr:P:P"){
getdisplay().setFont(&DSEG7Classic_BoldItalic26pt7b);
getdisplay().setCursor(x0+5, y0+70);
}
// RPM
else if(DataFormat[ValueIndex] == "formatXdr:T:R"){
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setCursor(x0+25, y0+70);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic26pt7b);
if ( DataText[ValueIndex][0] == '-' )
getdisplay().setCursor(x0+25, y0+70);
else
getdisplay().setCursor(x0+65, y0+70);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(DataText[ValueIndex]); // Real value as formated string
}
else{
getdisplay().print(OldDataText[ValueIndex]); // Old value as formated string
}
if(DataValid[ValueIndex] == true){
OldDataText[ValueIndex] = DataText[ValueIndex]; // Save the old value
OldDataUnits[ValueIndex] = DataUnits[ValueIndex]; // Save the old unit
}
}
// Vertical line 3 pix
getdisplay().fillRect(SixValues_x1+SixValues_DeltaX-8, SixValues_y1+i*SixValues_DeltaY, 3, SixValues_DeltaY, commonData->fgcolor);
}
return PAGE_UPDATE;
};
}; };
int displayPage(PageData &pageData) {
// Old values for hold function
static String OldDataText[HowManyValues] = {"", "", "", "", "", ""};
static String OldDataUnits[HowManyValues] = {"", "", "", "", "", ""};
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
GwApi::BoatValue *bvalue;
String DataName[HowManyValues];
double DataValue[HowManyValues];
bool DataValid[HowManyValues];
String DataText[HowManyValues];
String DataUnits[HowManyValues];
String DataFormat[HowManyValues];
for (int i = 0; i < HowManyValues; i++){
bvalue = pageData.values[i];
DataName[i] = xdrDelete(bvalue->getName());
DataName[i] = DataName[i].substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue, logger); // Check if boat data value is to be calibrated
#endif
DataValue[i] = bvalue->value; // Value as double in SI unit
DataValid[i] = bvalue->valid;
DataText[i] = commonData->fmt->formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
DataUnits[i] = commonData->fmt->formatValue(bvalue, *commonData).unit;
DataFormat[i] = bvalue->getFormat(); // Unit of value
}
if (bvalue == NULL) return PAGE_OK; // WTF why this statement?
// Draw page
//***********************************************************
// Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
epd->setTextColor(commonData->fgcolor);
for (int i = 0; i < ( HowManyValues / 2 ); i++) {
if (i < (HowManyValues / 2) - 1) { // Don't draw horizontal line after last line of values -> standard design
// Horizontal line 3 pix
epd->fillRect(0, SixValues_y1+(i+1)*SixValues_DeltaY, 400, 3, commonData->fgcolor);
}
for (int j = 0; j < 2; j++) {
int ValueIndex = i * 2 + j;
int x0 = SixValues_x1 + j * SixValues_DeltaX;
int y0 = SixValues_y1 + i * SixValues_DeltaY;
LOG_DEBUG(GwLog::LOG, "Drawing at PageSixValue: %d %s %f %s", ValueIndex, DataName[ValueIndex], DataValue[ValueIndex], DataFormat[ValueIndex]);
// Show name
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(x0, y0+25);
epd->print(DataName[ValueIndex]); // Page name
// Show unit
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(x0, y0+72);
if (holdvalues == false) {
epd->print(DataUnits[ValueIndex]); // Unit
} else {
epd->print(OldDataUnits[ValueIndex]);
}
// Switch font if format for any values
if (DataFormat[ValueIndex] == "formatLatitude" || DataFormat[ValueIndex] == "formatLongitude") {
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(x0+10, y0+60);
}
else if (DataFormat[ValueIndex] == "formatTime" || DataFormat[ValueIndex] == "formatDate") {
epd->setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(x0+20,y0+55);
}
// pressure in hPa
else if (DataFormat[ValueIndex] == "formatXdr:P:P") {
epd->setFont(&DSEG7Classic_BoldItalic26pt7b);
epd->setCursor(x0+5, y0+70);
}
// RPM
else if (DataFormat[ValueIndex] == "formatXdr:T:R") {
epd->setFont(&DSEG7Classic_BoldItalic16pt7b);
epd->setCursor(x0+25, y0+70);
}
else {
epd->setFont(&DSEG7Classic_BoldItalic26pt7b);
if (DataText[ValueIndex][0] == '-' ) {
epd->setCursor(x0+25, y0+70);
} else {
epd->setCursor(x0+65, y0+70);
}
}
// Show bus data
if (holdvalues == false) {
epd->print(DataText[ValueIndex]); // Real value as formated string
} else{
epd->print(OldDataText[ValueIndex]); // Old value as formated string
}
if (DataValid[ValueIndex] == true) {
OldDataText[ValueIndex] = DataText[ValueIndex]; // Save the old value
OldDataUnits[ValueIndex] = DataUnits[ValueIndex]; // Save the old unit
}
} // for j
// Vertical line 3 pix
epd->fillRect(SixValues_x1+SixValues_DeltaX-8, SixValues_y1+i*SixValues_DeltaY, 3, SixValues_DeltaY, commonData->fgcolor);
} // for i
return PAGE_UPDATE;
};
};
static Page *createPage(CommonData &common){ static Page *createPage(CommonData &common){
return new PageSixValues(common); return new PageSixValues(common);
}/** }/**

View File

@@ -0,0 +1,196 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
#include <vector>
#include <algorithm> // for vector sorting
/*
* SkyView / Satellites
*/
class PageSkyView : public Page
{
private:
GwBoatData *bd;
public:
PageSkyView(CommonData &common) : Page(common)
{
// task name access is for example purpose only
TaskHandle_t currentTaskHandle = xTaskGetCurrentTaskHandle();
const char* taskName = pcTaskGetName(currentTaskHandle);
logger->logDebug(GwLog::LOG, "Instantiate PageSkyView in task '%s'", taskName);
}
int handleKey(int key) {
// return 0 to mark the key handled completely
// return the key to allow further action
if (key == 11) {
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
bd = pageData.api->getBoatData();
};
// Comparator function to sort by SNR
static bool compareBySNR(const GwSatInfo& a, const GwSatInfo& b) {
return a.SNR > b.SNR; // Sort in descending order
}
int displayPage(PageData &pageData) {
std::vector<GwSatInfo> sats;
int nSat = bd->SatInfo->getNumSats();
logger->logDebug(GwLog::LOG, "Drawing at PageSkyView, %d satellites", nSat);
for (int i = 0; i < nSat; i++) {
sats.push_back(*bd->SatInfo->getAt(i));
}
std::sort(sats.begin(), sats.end(), compareBySNR);
// Draw page
//***********************************************************
// Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
// current position
epd->setFont(&Ubuntu_Bold8pt8b);
GwApi::BoatValue *bv_lat = pageData.values[0];
String sv_lat = commonData->fmt->formatValue(bv_lat, *commonData).svalue;
//epd->setCursor(300, 40);
//epd->print(sv_lat);
GwApi::BoatValue *bv_lon = pageData.values[1];
String sv_lon = commonData->fmt->formatValue(bv_lon, *commonData).svalue;
//epd->setCursor(300, 60);
//epd->print(sv_lon);
GwApi::BoatValue *bv_hdop = pageData.values[2];
String sv_hdop = commonData->fmt->formatValue(bv_hdop, *commonData).svalue;
//epd->setCursor(300, 80);
//epd->print(sv_hdop);
// sky view
Point c = {130, 148};
uint16_t r = 125;
uint16_t r1 = r / 2;
epd->fillCircle(c.x, c.y, r, commonData->bgcolor);
epd->drawCircle(c.x, c.y, r + 1, commonData->fgcolor);
epd->drawCircle(c.x, c.y, r + 2, commonData->fgcolor);
epd->drawCircle(c.x, c.y, r1, commonData->fgcolor);
// separation lines
epd->drawLine(c.x - r, c.y, c.x + r, c.y, commonData->fgcolor);
epd->drawLine(c.x, c.y - r, c.x, c.y + r, commonData->fgcolor);
Point p = {c.x, c.y - r};
Point p1, p2;
p1 = rotatePoint(c, p, 45);
p2 = rotatePoint(c, p, 45 + 180);
epd->drawLine(p1.x, p1.y, p2.x, p2.y, commonData->fgcolor);
p1 = rotatePoint(c, p, -45);
p2 = rotatePoint(c, p, -45 + 180);
epd->drawLine(p1.x, p1.y, p2.x, p2.y, commonData->fgcolor);
// directions
int16_t x1, y1;
uint16_t w, h;
epd->setFont(&Ubuntu_Bold12pt8b);
epd->getTextBounds("N", 0, 150, &x1, &y1, &w, &h);
epd->setCursor(c.x - w / 2, c.y - r + h + 2);
epd->print("N");
epd->getTextBounds("S", 0, 150, &x1, &y1, &w, &h);
epd->setCursor(c.x - w / 2, c.y + r - 2);
epd->print("S");
epd->getTextBounds("E", 0, 150, &x1, &y1, &w, &h);
epd->setCursor(c.x + r - w - 2, c.y + h / 2);
epd->print("E");
epd->getTextBounds("W", 0, 150, &x1, &y1, &w, &h);
epd->setCursor(c.x - r + 2 , c.y + h / 2);
epd->print("W");
// show satellites in "map"
epd->setFont(&Atari6px);
for (int i = 0; i < nSat; i++) {
float arad = sats[i].Azimut * M_PI / 180.0;
float erad = sats[i].Elevation * M_PI / 180.0;
uint16_t x = c.x + sin(arad) * erad * r;
uint16_t y = c.y + cos(arad) * erad * r;
epd->drawRect(x-4, y-4, 8, 8, commonData->fgcolor);
// Add Sat number
epd->setCursor(x+5, y);
char buffer[3];
snprintf(buffer, 3, "%02d", static_cast<int>(sats[i].PRN));
epd->print(String(buffer));
}
// Signal / Noise bars
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(325, 34);
epd->print("SNR");
epd->drawRect(270, 20, 125, 257, commonData->fgcolor);
int maxsat = std::min(nSat, 12);
for (int i = 0; i < maxsat; i++) {
uint16_t y = 29 + (i + 1) * 20;
epd->setCursor(276, y);
char buffer[3];
snprintf(buffer, 3, "%02d", static_cast<int>(sats[i].PRN));
epd->print(String(buffer));
epd->drawRect(305, y-12, 85, 14, commonData->fgcolor);
epd->setCursor(315, y);
// TODO SNR as number or as bar via mode key?
if (sats[i].SNR <= 100) {
// epd->print(sats[i].SNR);
epd->fillRect(307, y-10, int(81 * sats[i].SNR / 100.0), 10, commonData->fgcolor);
} else {
epd->print("n/a");
}
}
return PAGE_UPDATE;
};
};
static Page* createPage(CommonData &common){
return new PageSkyView(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect
* this will be number of BoatValue pointers in pageData.values
*/
PageDescription registerPageSkyView(
"SkyView", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{"LAT", "LON", "HDOP"}, // Bus values we need in the page
true // Show display header on/off
);
#endif

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -6,12 +7,23 @@
class PageSolar : public Page class PageSolar : public Page
{ {
private:
String batVoltage;
int solPower;
String powerSensor;
public: public:
PageSolar(CommonData &common){ PageSolar(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageSolar"); logger->logDebug(GwLog::LOG, "Instantiate PageSolar");
// Get config data
String batVoltage = config->getString(config->batteryVoltage);
int solPower = config->getInt(config->solarPower);
String powerSensor = config->getString(config->usePowSensor2);
} }
virtual int handleKey(int key){
int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -20,19 +32,8 @@ public:
return key; return key;
} }
int displayPage(PageData &pageData){ int displayPage(PageData &pageData) {
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Get config data
bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String batVoltage = config->getString(config->batteryVoltage);
int solPower = config->getInt(config->solarPower);
String backlightMode = config->getString(config->backlight);
String powerSensor = config->getString(config->usePowSensor2);
double value1 = 0; // Solar voltage double value1 = 0; // Solar voltage
double value2 = 0; // Solar current double value2 = 0; // Solar current
double value3 = 0; // Solar output power double value3 = 0; // Solar output power
@@ -58,98 +59,93 @@ public:
bool valid1 = true; bool valid1 = true;
// Optical warning by limit violation // Optical warning by limit violation
if(String(flashLED) == "Limit Violation"){ if (flashLED == "Limit Violation") {
// Over voltage // Over voltage?
if(value1 > 14.8 && batVoltage == "12V"){ if (batVoltage == "12V") {
setBlinkingLED(true); setBlinkingLED(value1 > 14.8);
} } else if (batVoltage == "24V") {
if(value1 <= 14.8 && batVoltage == "12V"){ setBlinkingLED(value1 > 29.6);
} else {
setBlinkingLED(false); setBlinkingLED(false);
} }
if(value1 > 29.6 && batVoltage == "24V"){
setBlinkingLED(true);
}
if(value1 <= 29.6 && batVoltage == "24V"){
setBlinkingLED(false);
}
} }
// Logging voltage value // Logging voltage value
LOG_DEBUG(GwLog::LOG,"Drawing at PageSolar, Type:%iW %s:=%f", solPower, name1.c_str(), value1); logger->logDebug(GwLog::LOG, "Drawing at PageSolar, Type:%iW %s:=%f", solPower, name1.c_str(), value1);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(10, 65); epd->setCursor(10, 65);
getdisplay().print("Solar"); epd->print("Solar");
// Show voltage type // Show voltage type
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 140); epd->setCursor(10, 140);
int bvoltage = 0; int bvoltage = 0;
if(String(batVoltage) == "12V") bvoltage = 12; if(String(batVoltage) == "12V") bvoltage = 12;
else bvoltage = 24; else bvoltage = 24;
getdisplay().print(bvoltage); epd->print(bvoltage);
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V"); epd->print("V");
// Show solar power // Show solar power
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 200); epd->setCursor(10, 200);
if(solPower <= 999) getdisplay().print(solPower, 0); if(solPower <= 999) epd->print(solPower, 0);
if(solPower > 999) getdisplay().print(float(solPower/1000.0), 1); if(solPower > 999) epd->print(float(solPower/1000.0), 1);
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
if(solPower <= 999) getdisplay().print("W"); if(solPower <= 999) epd->print("W");
if(solPower > 999) getdisplay().print("kW"); if(solPower > 999) epd->print("kW");
// Show info // Show info
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 235); epd->setCursor(10, 235);
getdisplay().print("Installed"); epd->print("Installed");
getdisplay().setCursor(10, 255); epd->setCursor(10, 255);
getdisplay().print("Solar Modul"); epd->print("Solar Modul");
// Show solar panel // Show solar panel
solarGraphic(150, 45, commonData->fgcolor, commonData->bgcolor); solarGraphic(150, 45, commonData->fgcolor, commonData->bgcolor);
// Show load level in percent // Show load level in percent
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 200); epd->setCursor(150, 200);
getdisplay().print(solPercentage); epd->print(solPercentage);
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("%"); epd->print("%");
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(150, 235); epd->setCursor(150, 235);
getdisplay().print("Load"); epd->print("Load");
// Show sensor type info // Show sensor type info
String i2cAddr = ""; String i2cAddr = "";
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(270, 60); epd->setCursor(270, 60);
if(powerSensor == "off") getdisplay().print("Internal"); if(powerSensor == "off") epd->print("Internal");
if(powerSensor == "INA219"){ if(powerSensor == "INA219"){
getdisplay().print("INA219"); epd->print("INA219");
i2cAddr = " (0x" + String(INA219_I2C_ADDR2, HEX) + ")"; i2cAddr = " (0x" + String(INA219_I2C_ADDR2, HEX) + ")";
} }
if(powerSensor == "INA226"){ if(powerSensor == "INA226"){
getdisplay().print("INA226"); epd->print("INA226");
i2cAddr = " (0x" + String(INA226_I2C_ADDR2, HEX) + ")"; i2cAddr = " (0x" + String(INA226_I2C_ADDR2, HEX) + ")";
} }
getdisplay().print(i2cAddr); epd->print(i2cAddr);
getdisplay().setCursor(270, 80); epd->setCursor(270, 80);
getdisplay().print("Sensor Modul"); epd->print("Sensor Modul");
// Reading bus data or using simulation data // Reading bus data or using simulation data
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 140); epd->setCursor(260, 140);
if(simulation == true){ if(simulation == true){
if(batVoltage == "12V"){ if(batVoltage == "12V"){
value1 = 12.0; value1 = 12.0;
@@ -158,46 +154,62 @@ public:
value1 = 24.0; value1 = 24.0;
} }
value1 += float(random(0, 5)) / 10; // Simulation data value1 += float(random(0, 5)) / 10; // Simulation data
getdisplay().print(value1,1); epd->print(value1,1);
} }
else{ else{
// Check for valid real data, display also if hold values activated // Check for valid real data, display also if hold values activated
if(valid1 == true || holdvalues == true){ if(valid1 == true || holdvalues == true){
// Resolution switching // Resolution switching
if(value1 <= 9.9) getdisplay().print(value1, 2); if (value1 <= 9.9) {
if(value1 > 9.9 && value1 <= 99.9)getdisplay().print(value1, 1); epd->print(value1, 2);
if(value1 > 99.9) getdisplay().print(value1, 0); } else if (value1 <= 99.9) {
epd->print(value1, 1);
} else {
epd->print(value1, 0);
}
} }
else{ else {
getdisplay().print("---"); // Missing bus data epd->print(commonData->fmt->placeholder); // Missing bus data
} }
} }
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V"); epd->print("V");
// Show actual current in A // Show actual current in A
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 200); epd->setCursor(260, 200);
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){ if ((powerSensor == "INA219" || powerSensor == "INA226") && (simulation == false)) {
if(value2 <= 9.9) getdisplay().print(value2, 2); if (value2 <= 9.9) {
if(value2 > 9.9 && value2 <= 99.9)getdisplay().print(value2, 1); epd->print(value2, 2);
if(value2 > 99.9) getdisplay().print(value2, 0); } else if (value2 <= 99.9) {
epd->print(value2, 1);
} else {
epd->print(value2, 0);
}
} }
else getdisplay().print("---"); else {
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->print(commonData->fmt->placeholder);
getdisplay().print("A"); }
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("A");
// Show actual consumption in W // Show actual consumption in W
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 260); epd->setCursor(260, 260);
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){ if ((powerSensor == "INA219" || powerSensor == "INA226") && (simulation == false)) {
if(value3 <= 9.9) getdisplay().print(value3, 2); if (value3 <= 9.9) {
if(value3 > 9.9 && value3 <= 99.9)getdisplay().print(value3, 1); epd->print(value3, 2);
if(value3 > 99.9) getdisplay().print(value3, 0); } else if (value3 <= 99.9) {
epd->print(value3, 1);
} else {
epd->print(value3, 0);
}
} }
else getdisplay().print("---"); else {
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->print(commonData->fmt->placeholder);
getdisplay().print("W"); }
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("W");
return PAGE_UPDATE; return PAGE_UPDATE;
}; };

View File

@@ -1,14 +1,38 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
/*
* Special system page, called directly with fast key sequence 5,4
* Out of normal page order.
* Consists of some sub-pages with following content:
* 1. Hard and software information
* 2. System settings
* 3. System configuration: running and NVRAM
* 4. NMEA2000 device list if NMEA2000 enabled
* 5. SD Card information if available
*
* TODO
* - setCpuFrequencyMhz(80|160|240);
* - Accesspoint / ! Änderung im Gatewaycode erforderlich?
* if (! isApActive()) {
* wifiSSID = config->getString(config->wifiSSID);
* wifiPass = config->getString(config->wifiPass);
* wifiSoftAP(wifiSSID, wifiPass);
* }
* - Power mode
* powerInit(powermode);
*/
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "ConfigMenu.h"
#include "images/logo64.xbm" #include "images/logo64.xbm"
#include <esp32/clk.h> #include <esp32/clk.h>
#include "qrcode.h" #include "qrcode.h"
#include "Nmea2kTwai.h"
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
#include <SD.h> #include "dirent.h"
#include <FS.h>
#endif #endif
#define STRINGIZE_IMPL(x) #x #define STRINGIZE_IMPL(x) #x
@@ -19,64 +43,552 @@
#define DISPLAYINFO STRINGIZE(EPDTYPE) #define DISPLAYINFO STRINGIZE(EPDTYPE)
#define GXEPD2INFO STRINGIZE(GXEPD2VERS) #define GXEPD2INFO STRINGIZE(GXEPD2VERS)
/* #define N2K_INACTIVE_AGE 30000
* Special system page, called directly with fast key sequence 5,4
* Out of normal page order.
* Consists of some sub-pages with following content:
* 1. Hard and software information
* 2. System settings
* 3. NMEA2000 device list
*/
class PageSystem : public Page class PageSystem : public Page
{ {
uint64_t chipid; private:
bool simulation; // NVRAM config options
bool sdcard; String flashLED;
String buzzer_mode;
uint8_t buzzer_power;
String cpuspeed;
String rtc_module;
String gps_module;
String env_module;
String batt_sensor; // Generic data access
String solar_sensor;
String gen_sensor;
String rot_sensor;
double homelat;
double homelon;
char mode = 'N'; // (N)ormal, (S)ettings, (D)evice list, (C)ard uint64_t chipid;
bool use_sdcard;
String buzzer_mode;
uint8_t buzzer_power;
String cpuspeed;
String rtc_module;
String gps_module;
String env_module;
public: String batt_sensor;
PageSystem(CommonData &common){ String solar_sensor;
commonData = &common; String gen_sensor;
common.logger->logDebug(GwLog::LOG,"Instantiate PageSystem"); String rot_sensor;
if (hasFRAM) { double homelat;
mode = fram.read(FRAM_SYSTEM_MODE); double homelon;
Nmea2kTwai *NMEA2000;
unsigned long n2kRxOld = 0; // to detect bus activity
unsigned long n2kTxOld = 0;
long n2k_ts = 0; // timestamp of last activity
bool n2k_active = false;
char mode = 'N'; // (N)ormal, (S)ettings, (C)onfiguration, (D)evice list, c(A)rd
int8_t editmode = -1; // marker for menu/edit/set function
ConfigMenu *menu;
void incMode() {
if (mode == 'N') { // Normal
mode = 'S';
} else if (mode == 'S') { // Settings
mode = 'C';
} else if (mode == 'C') { // Config
mode = 'D';
} else if (mode == 'D') { // Device list
if (hasSDCard) {
mode = 'A'; // SD-Card
} else {
mode = 'N';
}
} else {
mode = 'N';
} }
chipid = ESP.getEfuseMac(); if (hasFRAM) fram.write(FRAM_SYSTEM_MODE, mode);
simulation = common.config->getBool(common.config->useSimuData);
#ifdef BOARD_OBP40S3
sdcard = common.config->getBool(common.config->useSDCard);
#endif
buzzer_mode = common.config->getString(common.config->buzzerMode);
buzzer_mode.toLowerCase();
buzzer_power = common.config->getInt(common.config->buzzerPower);
cpuspeed = common.config->getString(common.config->cpuSpeed);
env_module = common.config->getString(common.config->useEnvSensor);
rtc_module = common.config->getString(common.config->useRTC);
gps_module = common.config->getString(common.config->useGPS);
batt_sensor = common.config->getString(common.config->usePowSensor1);
solar_sensor = common.config->getString(common.config->usePowSensor2);
gen_sensor = common.config->getString(common.config->usePowSensor3);
rot_sensor = common.config->getString(common.config->useRotSensor);
homelat = common.config->getString(common.config->homeLAT).toDouble();
homelon = common.config->getString(common.config->homeLON).toDouble();
} }
virtual void setupKeys(){ void decMode() {
if (mode == 'N') {
if (hasSDCard) {
mode = 'A';
} else {
mode = 'D';
}
} else if (mode == 'S') { // Settings
mode = 'N';
} else if (mode == 'C') { // Config
mode = 'S';
} else if (mode == 'D') { // Device list
mode = 'C';
} else {
mode = 'D';
}
if (hasFRAM) fram.write(FRAM_SYSTEM_MODE, mode);
}
void displayModeNormal() {
// Default system page view
uint16_t y0 = 155;
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("System information");
epd->drawXBitmap(320, 25, logo64_bits, logo64_width, logo64_height, commonData->fgcolor);
epd->setFont(&Ubuntu_Bold8pt8b);
char ssid[13];
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
displayBarcode(String(ssid), 320, 200, 2);
epd->setCursor(8, 70);
epd->print(String("MCUDEVICE-") + String(ssid));
epd->setCursor(8, 95);
epd->print("Firmware version: ");
epd->setCursor(150, 95);
epd->print(VERSINFO);
epd->setCursor(8, 113);
epd->print("Board version: ");
epd->setCursor(150, 113);
epd->print(BOARDINFO);
epd->print(String(" HW ") + String(PCBINFO));
epd->setCursor(8, 131);
epd->print("Display version: ");
epd->setCursor(150, 131);
epd->print(DISPLAYINFO);
epd->print("; GxEPD2 v");
epd->print(GXEPD2INFO);
epd->setCursor(8, 265);
#ifdef BOARD_OBP60S3
epd->print("Press STBY to enter deep sleep mode");
#endif
#ifdef BOARD_OBP40S3
epd->print("Press wheel to enter deep sleep mode");
#endif
// Flash memory size
uint32_t flash_size = ESP.getFlashChipSize();
epd->setCursor(8, y0);
epd->print("FLASH:");
epd->setCursor(90, y0);
epd->print(String(flash_size / 1024) + String(" kB"));
// PSRAM memory size
uint32_t psram_size = ESP.getPsramSize();
epd->setCursor(8, y0 + 16);
epd->print("PSRAM:");
epd->setCursor(90, y0 + 16);
epd->print(String(psram_size / 1024) + String(" kB"));
// FRAM available / status
epd->setCursor(8, y0 + 32);
epd->print("FRAM:");
epd->setCursor(90, y0 + 32);
epd->print(hasFRAM ? "available" : "not found");
#ifdef BOARD_OBP40S3
// SD-Card
epd->setCursor(8, y0 + 48);
epd->print("SD-Card:");
epd->setCursor(90, y0 + 48);
if (hasSDCard) {
uint64_t cardsize = ((uint64_t) sdcard->csd.capacity) * sdcard->csd.sector_size / (1024 * 1024);
epd->printf("%llu MB", cardsize);
if (!use_sdcard) {
epd->print(" (disabled)");
}
} else {
epd->print("off");
}
#endif
// Uptime
int64_t uptime = esp_timer_get_time() / 1000000;
String uptime_unit;
if (uptime < 120) {
uptime_unit = " seconds";
} else {
if (uptime < 2 * 3600) {
uptime /= 60;
uptime_unit = " minutes";
} else if (uptime < 2 * 3600 * 24) {
uptime /= 3600;
uptime_unit = " hours";
} else {
uptime /= 86400;
uptime_unit = " days";
}
}
epd->setCursor(8, y0 + 80);
epd->print("Uptime:");
epd->setCursor(90, y0 + 80);
epd->print(uptime);
epd->print(uptime_unit);
// CPU speed config / active
epd->setCursor(202, y0);
epd->print("CPU speed:");
epd->setCursor(300, y0);
epd->print(cpuspeed);
epd->print(" / ");
int cpu_freq = esp_clk_cpu_freq() / 1000000;
epd->print(String(cpu_freq));
// total RAM free
int Heap_free = esp_get_free_heap_size();
epd->setCursor(202, y0 + 16);
epd->print("Total free:");
epd->setCursor(300, y0 + 16);
epd->print(String(Heap_free));
// RAM free for task
UBaseType_t RAM_free = uxTaskGetStackHighWaterMark(NULL);
epd->setCursor(202, y0 + 32);
epd->print("Task free:");
epd->setCursor(300, y0 + 32);
epd->print(String(RAM_free));
}
void displayModeConfig() {
// Configuration interface
uint16_t x0 = 16;
uint16_t y0 = 80;
uint16_t dy = 20;
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("System configuration");
epd->setFont(&Ubuntu_Bold8pt8b);
/*epd->setCursor(x0, y0);
epd->print("CPU speed: 80 | 160 | 240");
epd->setCursor(x0, y0 + 1 * dy);
epd->print("Power mode: Max | 5V | Min");
epd->setCursor(x0, y0 + 2 * dy);
epd->print("Accesspoint: On | Off");
// TODO Change NVRAM-preferences settings here
epd->setCursor(x0, y0 + 4 * dy);
epd->print("Simulation: On | Off"); */
epd->setFont(&Ubuntu_Bold8pt8b);
for (int i = 0 ; i < menu->getItemCount(); i++) {
ConfigMenuItem *itm = menu->getItemByIndex(i);
if (!itm) {
logger->logDebug(GwLog::ERROR, "Menu item not found: %d", i);
} else {
Rect r = menu->getItemRect(i);
bool inverted = (i == menu->getActiveIndex());
drawTextBoxed(r, itm->getLabel(), commonData->fgcolor, commonData->bgcolor, inverted, false);
if (inverted and editmode > 0) {
// triangle as edit marker
epd->fillTriangle(r.x + r.w + 20, r.y, r.x + r.w + 30, r.y + r.h / 2, r.x + r.w + 20, r.y + r.h, commonData->fgcolor);
}
epd->setCursor(r.x + r.w + 40, r.y + r.h - 4);
if (itm->getType() == "int") {
epd->print(itm->getValue());
epd->print(itm->getUnit());
} else {
epd->print(itm->getValue() == 0 ? "No" : "Yes");
}
}
}
}
void displayModeSettings() {
// View some of the current settings
const uint16_t x0 = 8;
const uint16_t y0 = 72;
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(x0, 48);
epd->print("System settings");
epd->setFont(&Ubuntu_Bold8pt8b);
// left column
epd->setCursor(x0, y0);
epd->print("Simulation:");
epd->setCursor(120, y0);
epd->print(simulation ? "on" : "off");
epd->setCursor(x0, y0 + 16);
epd->print("Environment:");
epd->setCursor(120, y0 + 16);
epd->print(env_module);
epd->setCursor(x0, y0 + 32);
epd->print("Buzzer:");
epd->setCursor(120, y0 + 32);
epd->print(buzzer_mode);
epd->setCursor(x0, y0 + 64);
epd->print("GPS:");
epd->setCursor(120, y0 + 64);
epd->print(gps_module);
epd->setCursor(x0, y0 + 80);
epd->print("RTC:");
epd->setCursor(120, y0 + 80);
epd->print(rtc_module);
epd->setCursor(x0, y0 + 96);
epd->print("Wifi:");
epd->setCursor(120, y0 + 96);
epd->print(commonData->status.wifiApOn ? "on" : "off");
// Home location
epd->setCursor(x0, y0 + 128);
epd->print("Home Lat.:");
drawTextRalign(230, y0 + 128, formatLatitude(homelat));
epd->setCursor(x0, y0 + 144);
epd->print("Home Lon.:");
drawTextRalign(230, y0 + 144, formatLongitude(homelon));
// right column
epd->setCursor(202, y0);
epd->print("Batt. sensor:");
epd->setCursor(320, y0);
epd->print(batt_sensor);
// Solar sensor
epd->setCursor(202, y0 + 16);
epd->print("Solar sensor:");
epd->setCursor(320, y0 + 16);
epd->print(solar_sensor);
// Generator sensor
epd->setCursor(202, y0 + 32);
epd->print("Gen. sensor:");
epd->setCursor(320, y0 + 32);
epd->print(gen_sensor);
#ifdef BOARD_OBP60S3
// Backlight infos
epd->setCursor(202, y0 + 64);
epd->print("Backlight:");
epd->setCursor(320, y0 + 64);
epd->printf("%d%%", commonData->backlight.brightness);
// TODO test function with OBP60 device
epd->setCursor(202, y0 + 80);
epd->print("Bl color:");
epd->setCursor(320, y0 + 80);
epd->print(commonData->backlight.color.toName());
epd->setCursor(202, y0 + 96);
epd->print("Bl mode:");
epd->setCursor(320, y0 + 96);
epd->print(commonData->backlight.mode);
// TODO Buzzer mode and power
#endif
// Gyro sensor
// WIP / FEATURE
}
void displayModeSDCard() {
// SD card info
uint16_t x0 = 20;
uint16_t y0 = 72;
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("SD card info");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(x0, y0);
#ifdef BOARD_OBP60S3
// This mode should not be callable by devices without card hardware
// In case of accidential reaching this, display a friendly message
epd->print("This mode is not indended to be reached!\n");
epd->print("There's nothing to see here. Move on.");
#endif
#ifdef BOARD_OBP40S3
/* TODO identify card as OBP-Card:
magic.dat
version.dat
readme.txt
BAK/
CHARTS/
DATA/
IMAGES/
LOGS/
*/
// Simple test for magic file in root
epd->setCursor(x0, y0 + 16);
const char* file_magic = MOUNT_POINT "/magic.dat";
logger->logDebug(GwLog::LOG, "Test magicfile: %s", file_magic);
struct stat st;
if (stat(file_magic, &st) == 0) {
epd->printf("Magicfile %s exists", file_magic);
} else {
epd->printf("Magicfile %s not found", file_magic);
}
// Root directory check
DIR* dir = opendir(MOUNT_POINT);
int dy = 0;
if (dir != NULL) {
logger->logDebug(GwLog::LOG, "Root directory: %s", MOUNT_POINT);
struct dirent* entry;
while (((entry = readdir(dir)) != NULL) and (dy < 140)) {
epd->setCursor(x0, y0 + 64 + dy);
epd->print(entry->d_name);
// type 1 is file, type 2 is dir
if (entry->d_type == 2) {
epd->print("/");
}
dy += 20;
logger->logDebug(GwLog::LOG, " %s type %d", entry->d_name, entry->d_type);
}
closedir(dir);
} else {
logger->logDebug(GwLog::LOG, "Failed to open root directory");
}
// try to load example pbm file
// TODO create PBM load function in imglib
String filepath = MOUNT_POINT "/IMAGES/icon-settings2.pbm";
const char* file_img = filepath.c_str();
FILE* fh = fopen(file_img, "r");
if (fh != NULL) {
logger->logDebug(GwLog::LOG, "Open file for reading: %s", file_img);
char magic[3];
char buffer[80];
// 2 Byte Magic: P1=ASCII, P4=Binary
fgets(magic, sizeof(magic), fh);
if (strcmp(magic, "P1") != 0) {
logger->logDebug(GwLog::LOG, "Not a valid PBM file of type P1 (%s)", magic);
} else {
uint16_t width = 0;
uint16_t height = 0;
while (fgets(buffer, sizeof(buffer), fh)) {
// logger->logDebug(GwLog::LOG, "%s", buffer);
if (buffer[0] == '#') {
continue; // skip comment
}
if (sscanf(buffer, "%d %d", &width, &height) == 2) {
break;
}
}
logger->logDebug(GwLog::LOG, "Image: %dx%d", width, height);
}
/*size_t bytesRead = fread(buffer, sizeof(char), sizeof(buffer) - 1, fh);
buffer[bytesRead] = '\0'; // Null-terminate the string
if (bytesRead > 0) {
logger->logDebug(GwLog::LOG, "Read %d bytes", bytesRead);
logger->logDebug(GwLog::LOG, ">%s<", buffer);
} */
/* WIP
// Read pixel data and pack into the buffer
for (int i = 0; i < width * height; i++) {
int pixel;
fscanf(file, "%d", &pixel);
// Calculate the byte index and bit position
int byteIndex = i / 8;
int bitPosition = 7 - (i % 8); // Store MSB first
// Set the corresponding bit in the byte
if (pixel == 1) {
buffer[byteIndex] |= (1 << bitPosition);
}
}
*/
fclose(fh);
// epd->drawXBitmap(20, 200, buffer, width, height, commonData.fgcolor);
}
#endif
}
void displayModeDevicelist(bool bus_active) {
// NMEA2000 device list
// TODO Check if NMEA2000 enabled globally
// if disabled this page should not be shown
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("NMEA2000 device list");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(20, 80);
epd->print("RxD: ");
epd->print(String(commonData->status.n2kRx));
epd->setCursor(20, 100);
epd->print("TxD: ");
epd->print(String(commonData->status.n2kTx));
// show bus activity
epd->setCursor(20, 120);
epd->print("Bus: ");
epd->print(bus_active ? "active" : "inactive");
epd->setCursor(20, 140);
epd->printf("N2k source address: %d", NMEA2000->GetN2kSource());
}
void storeConfig() {
menu->storeValues();
}
public:
PageSystem(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageSystem");
if (hasFRAM) {
mode = fram.read(FRAM_SYSTEM_MODE);
logger->logDebug(GwLog::LOG, "Loaded mode '%c' from FRAM", mode);
}
flashLED = config->getString(config->flashLED);
chipid = ESP.getEfuseMac();
#ifdef BOARD_OBP40S3
use_sdcard = config->getBool(config->useSDCard);
#endif
buzzer_mode = config->getString(config->buzzerMode);
buzzer_mode.toLowerCase();
buzzer_power = config->getInt(config->buzzerPower);
cpuspeed = config->getString(config->cpuSpeed);
env_module = config->getString(config->useEnvSensor);
rtc_module = config->getString(config->useRTC);
gps_module = config->getString(config->useGPS);
batt_sensor = config->getString(config->usePowSensor1);
solar_sensor = config->getString(config->usePowSensor2);
gen_sensor = config->getString(config->usePowSensor3);
rot_sensor = config->getString(config->useRotSensor);
homelat = config->getString(config->homeLAT).toDouble();
homelon = config->getString(config->homeLON).toDouble();
// CPU speed: 80 | 160 | 240
// Power mode: Max | 5V | Min
// Accesspoint: On | Off
// TODO Change NVRAM-preferences settings here
// epd->setCursor(x0, y0 + 4 * dy);
// epd->print("Simulation: On | Off");
// Initialize config menu
menu = new ConfigMenu("Options", 40, 80);
menu->setItemDimension(150, 20);
ConfigMenuItem *newitem;
newitem = menu->addItem("accesspoint", "Accesspoint", "bool", 0, "");
newitem = menu->addItem("simulation", "Simulation", "on/off", 0, "");
menu->setItemActive("accesspoint");
// Create info-file if not exists
// TODO
}
void setupKeys() {
commonData->keydata[0].label = "EXIT"; commonData->keydata[0].label = "EXIT";
commonData->keydata[1].label = "MODE"; commonData->keydata[1].label = "MODE";
commonData->keydata[2].label = ""; commonData->keydata[2].label = "";
@@ -85,25 +597,12 @@ public:
commonData->keydata[5].label = "ILUM"; commonData->keydata[5].label = "ILUM";
} }
virtual int handleKey(int key){ int handleKey(int key) {
// do *NOT* handle key #1 this handled by obp60task as exit // do *NOT* handle key #1 this handled by obp60task as exit
// Switch display mode // Switch display mode
commonData->logger->logDebug(GwLog::LOG, "System keyboard handler"); logger->logDebug(GwLog::LOG, "System keyboard handler");
if (key == 2) { if (key == 2) {
if (mode == 'N') { incMode();
mode = 'S';
} else if (mode == 'S') {
mode = 'D';
} else if (mode == 'D') {
if (sdcard) {
mode = 'C';
} else {
mode = 'N';
}
} else {
mode = 'N';
}
if (hasFRAM) fram.write(FRAM_SYSTEM_MODE, mode);
return 0; return 0;
} }
#ifdef BOARD_OBP60S3 #ifdef BOARD_OBP60S3
@@ -117,7 +616,8 @@ public:
} }
// standby / deep sleep // standby / deep sleep
if (key == 5) { if (key == 5) {
deepSleep(*commonData); logger->logDebug(GwLog::LOG, "System going into deep sleep mode...");
deepSleep(*commonData);
} }
// Code for keylock // Code for keylock
if (key == 11) { if (key == 11) {
@@ -126,12 +626,18 @@ public:
} }
#endif #endif
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
// grab cursor keys to disable page navigation // use cursor keys for local mode navigation
if (key == 9 or key == 10) { if (key == 9) {
incMode();
return 0;
}
if (key == 10) {
decMode();
return 0; return 0;
} }
// standby / deep sleep // standby / deep sleep
if (key == 12) { if (key == 12) {
logger->logDebug(GwLog::LOG, "System going into deep sleep mode...");
deepSleep(*commonData); deepSleep(*commonData);
} }
#endif #endif
@@ -155,7 +661,7 @@ public:
for (uint8_t j = 0; j < qrcode.size; j++) { for (uint8_t j = 0; j < qrcode.size; j++) {
for (uint8_t i = 0; i < qrcode.size; i++) { for (uint8_t i = 0; i < qrcode.size; i++) {
if (qrcode_getModule(&qrcode, i, j)) { if (qrcode_getModule(&qrcode, i, j)) {
getdisplay().fillRect(x, y, s, s, commonData->fgcolor); epd->fillRect(x, y, s, s, commonData->fgcolor);
} }
x += s; x += s;
} }
@@ -164,233 +670,56 @@ public:
} }
} }
int displayPage(PageData &pageData){ void displayNew(PageData &pageData) {
GwConfigHandler *config = commonData->config; #ifdef BOARD_OBP60S3
GwLog *logger = commonData->logger; // Clear optical warning
if (flashLED == "Limit Violation") {
// Get config data
String flashLED = config->getString(config->flashLED);
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
#endif
// Get references from API
logger->logDebug(GwLog::LOG, "New page display: PageSystem");
NMEA2000 = pageData.api->getNMEA2000();
};
// Logging boat values int displayPage(PageData &pageData) {
LOG_DEBUG(GwLog::LOG,"Drawing at PageSystem");
// Draw page // Logging page information
//*********************************************************** logger->logDebug(GwLog::LOG, "Drawing at PageSystem, Mode=%c", mode);
uint16_t x0 = 8; // left column
uint16_t y0 = 48; // data table starts here
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height());
if (mode == 'N') { // call current system page
switch (mode) {
getdisplay().setFont(&Ubuntu_Bold12pt8b); case 'N':
getdisplay().setCursor(8, 48); displayModeNormal();
getdisplay().print("System Information"); break;
case 'S':
getdisplay().drawXBitmap(320, 25, logo64_bits, logo64_width, logo64_height, commonData->fgcolor); displayModeSettings();
break;
getdisplay().setFont(&Ubuntu_Bold8pt8b); case 'C':
y0 = 155; displayModeConfig();
break;
char ssid[13]; case 'A':
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid); displayModeSDCard();
displayBarcode(String(ssid), 320, 200, 2); break;
getdisplay().setCursor(8, 70); case 'D':
getdisplay().print(String("MCUDEVICE-") + String(ssid)); // check bus status
if (commonData->status.n2kRx != n2kRxOld || commonData->status.n2kTx != n2kTxOld) {
getdisplay().setCursor(8, 95); n2kRxOld = commonData->status.n2kRx;
getdisplay().print("Firmware version: "); n2kTxOld = commonData->status.n2kTx;
getdisplay().setCursor(150, 95); n2k_ts = millis();
getdisplay().print(VERSINFO); } else {
n2k_active = (millis() - n2k_ts <= N2K_INACTIVE_AGE);
getdisplay().setCursor(8, 113); }
getdisplay().print("Board version: "); displayModeDevicelist(n2k_active);
getdisplay().setCursor(150, 113); break;
getdisplay().print(BOARDINFO);
getdisplay().print(String(" HW ") + String(PCBINFO));
getdisplay().setCursor(8, 131);
getdisplay().print("Display version: ");
getdisplay().setCursor(150, 131);
getdisplay().print(DISPLAYINFO);
getdisplay().print("; GxEPD2 v");
getdisplay().print(GXEPD2INFO);
getdisplay().setCursor(8, 265);
#ifdef BOARD_OBP60S3
getdisplay().print("Press STBY to enter deep sleep mode");
#endif
#ifdef BOARD_OBP40S3
getdisplay().print("Press wheel to enter deep sleep mode");
#endif
// Flash memory size
uint32_t flash_size = ESP.getFlashChipSize();
getdisplay().setCursor(8, y0);
getdisplay().print("FLASH:");
getdisplay().setCursor(90, y0);
getdisplay().print(String(flash_size / 1024) + String(" kB"));
// PSRAM memory size
uint32_t psram_size = ESP.getPsramSize();
getdisplay().setCursor(8, y0 + 16);
getdisplay().print("PSRAM:");
getdisplay().setCursor(90, y0 + 16);
getdisplay().print(String(psram_size / 1024) + String(" kB"));
// FRAM available / status
getdisplay().setCursor(8, y0 + 32);
getdisplay().print("FRAM:");
getdisplay().setCursor(90, y0 + 32);
getdisplay().print(hasFRAM ? "available" : "not found");
#ifdef BOARD_OBP40S3
// SD-Card
getdisplay().setCursor(8, y0 + 48);
getdisplay().print("SD-Card:");
getdisplay().setCursor(90, y0 + 48);
if (sdcard) {
uint64_t cardsize = SD.cardSize() / (1024 * 1024);
getdisplay().print(String(cardsize) + String(" MB"));
} else {
getdisplay().print("off");
}
#endif
// CPU speed config / active
getdisplay().setCursor(202, y0);
getdisplay().print("CPU speed:");
getdisplay().setCursor(300, y0);
getdisplay().print(cpuspeed);
getdisplay().print(" / ");
int cpu_freq = esp_clk_cpu_freq() / 1000000;
getdisplay().print(String(cpu_freq));
// total RAM free
int Heap_free = esp_get_free_heap_size();
getdisplay().setCursor(202, y0 + 16);
getdisplay().print("Total free:");
getdisplay().setCursor(300, y0 + 16);
getdisplay().print(String(Heap_free));
// RAM free for task
int RAM_free = uxTaskGetStackHighWaterMark(NULL);
getdisplay().setCursor(202, y0 + 32);
getdisplay().print("Task free:");
getdisplay().setCursor(300, y0 + 32);
getdisplay().print(String(RAM_free));
} else if (mode == 'S') {
// Settings
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(x0, 48);
getdisplay().print("System settings");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
x0 = 8;
y0 = 72;
// left column
getdisplay().setCursor(x0, y0);
getdisplay().print("Simulation:");
getdisplay().setCursor(120, y0);
getdisplay().print(simulation ? "on" : "off");
getdisplay().setCursor(x0, y0 + 16);
getdisplay().print("Environment:");
getdisplay().setCursor(120, y0 + 16);
getdisplay().print(env_module);
getdisplay().setCursor(x0, y0 + 32);
getdisplay().print("Buzzer:");
getdisplay().setCursor(120, y0 + 32);
getdisplay().print(buzzer_mode);
getdisplay().setCursor(x0, y0 + 64);
getdisplay().print("GPS:");
getdisplay().setCursor(120, y0 + 64);
getdisplay().print(gps_module);
getdisplay().setCursor(x0, y0 + 80);
getdisplay().print("RTC:");
getdisplay().setCursor(120, y0 + 80);
getdisplay().print(rtc_module);
getdisplay().setCursor(x0, y0 + 96);
getdisplay().print("Wifi:");
getdisplay().setCursor(120, y0 + 96);
getdisplay().print(commonData->status.wifiApOn ? "on" : "off");
// Home location
getdisplay().setCursor(x0, y0 + 128);
getdisplay().print("Home Lat.:");
getdisplay().setCursor(120, y0 + 128);
getdisplay().print(formatLatitude(homelat));
getdisplay().setCursor(x0, y0 + 144);
getdisplay().print("Home Lon.:");
getdisplay().setCursor(120, y0 + 144);
getdisplay().print(formatLongitude(homelon));
// right column
getdisplay().setCursor(202, y0);
getdisplay().print("Batt. sensor:");
getdisplay().setCursor(320, y0);
getdisplay().print(batt_sensor);
// Solar sensor
getdisplay().setCursor(202, y0 + 16);
getdisplay().print("Solar sensor:");
getdisplay().setCursor(320, y0 + 16);
getdisplay().print(solar_sensor);
// Generator sensor
getdisplay().setCursor(202, y0 + 32);
getdisplay().print("Gen. sensor:");
getdisplay().setCursor(320, y0 + 32);
getdisplay().print(gen_sensor);
// Gyro sensor
} else if (mode == 'C') {
// Card info
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("SD Card info");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
x0 = 20;
y0 = 72;
getdisplay().setCursor(x0, y0);
getdisplay().print("Work in progress...");
} else {
// NMEA2000 device list
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("NMEA2000 device list");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 80);
getdisplay().print("RxD: ");
getdisplay().print(String(commonData->status.n2kRx));
getdisplay().setCursor(20, 100);
getdisplay().print("TxD: ");
getdisplay().print(String(commonData->status.n2kTx));
} }
// Update display // Update display
getdisplay().nextPage(); // Partial update (fast) epd->nextPage(); // Partial update (fast)
return PAGE_OK; return PAGE_OK;
}; };
}; };

View File

@@ -1,18 +1,28 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h" #include "BoatDataCalibration.h"
#endif
class PageThreeValues : public Page class PageThreeValues : public Page
{ {
public: private:
PageThreeValues(CommonData &common){ String lengthformat;
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageThreeValue"); public:
PageThreeValues(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageThreeValue");
// Get config data
lengthformat = config->getString(config->lengthFormat);
} }
virtual int handleKey(int key){ int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -21,9 +31,17 @@ class PageThreeValues : public Page
return key; return key;
} }
int displayPage(PageData &pageData){ void displayNew(PageData &pageData) {
GwConfigHandler *config = commonData->config; #ifdef BOARD_OBP60S3
GwLog *logger = commonData->logger; // Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -33,97 +51,92 @@ class PageThreeValues : public Page
static String svalue3old = ""; static String svalue3old = "";
static String unit3old = ""; static String unit3old = "";
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Get boat values #1 // Get boat values #1
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = commonData->fmt->formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 // Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
#endif
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = commonData->fmt->formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value String unit2 = commonData->fmt->formatValue(bvalue2, *commonData).unit; // Unit of value
// Get boat values #3 // Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list
String name3 = xdrDelete(bvalue3->getName()); // Value name String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for value name name3 = name3.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
#endif
double value3 = bvalue3->value; // Value as double in SI unit double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = commonData->fmt->formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value String unit3 = commonData->fmt->formatValue(bvalue3, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused) // Log boat values
if(String(flashLED) == "Limit Violation"){ logger->logDebug(GwLog::LOG, "Drawing at PageThreeValues, %s: %f, %s: %f, %s: %f",
setBlinkingLED(false); name1.c_str(), value1,
setFlashLED(false); name2.c_str(), value2,
} name3.c_str(), value3);
// Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageThreeValues, %s: %f, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3);
// Draw page // Draw page
//*********************************************************** //***********************************************************
/// Set display in partial refresh mode /// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 55); epd->setCursor(20, 55);
getdisplay().print(name1); // Page name epd->print(name1); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 90); epd->setCursor(20, 90);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit1); // Unit epd->print(unit1); // Unit
} }
else{ else{
getdisplay().print(unit1old); epd->print(unit1old);
} }
// Switch font if format for any values // Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){ if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 90); epd->setCursor(50, 90);
} }
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){ else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 68); epd->setCursor(170, 68);
} }
else{ else{
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b); epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 90); epd->setCursor(180, 90);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue1); // Real value as formated string epd->print(svalue1); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue1old); // Old value as formated string epd->print(svalue1old); // Old value as formated string
} }
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save the old value svalue1old = svalue1; // Save the old value
@@ -133,45 +146,45 @@ class PageThreeValues : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor); epd->fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 145); epd->setCursor(20, 145);
getdisplay().print(name2); // Page name epd->print(name2); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 180); epd->setCursor(20, 180);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit2); // Unit epd->print(unit2); // Unit
} }
else{ else{
getdisplay().print(unit2old); epd->print(unit2old);
} }
// Switch font if format for any values // Switch font if format for any values
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){ if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 180); epd->setCursor(50, 180);
} }
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){ else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 158); epd->setCursor(170, 158);
} }
else{ else{
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b); epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 180); epd->setCursor(180, 180);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue2); // Real value as formated string epd->print(svalue2); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue2old); // Old value as formated string epd->print(svalue2old); // Old value as formated string
} }
if(valid2 == true){ if(valid2 == true){
svalue2old = svalue2; // Save the old value svalue2old = svalue2; // Save the old value
@@ -181,45 +194,45 @@ class PageThreeValues : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor); epd->fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################ // ############### Value 3 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 235); epd->setCursor(20, 235);
getdisplay().print(name3); // Page name epd->print(name3); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 270); epd->setCursor(20, 270);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit3); // Unit epd->print(unit3); // Unit
} }
else{ else{
getdisplay().print(unit3old); epd->print(unit3old);
} }
// Switch font if format for any values // Switch font if format for any values
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){ if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 270); epd->setCursor(50, 270);
} }
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){ else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 248); epd->setCursor(170, 248);
} }
else{ else{
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b); epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 270); epd->setCursor(180, 270);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue3); // Real value as formated string epd->print(svalue3); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue3old); // Old value as formated string epd->print(svalue3old); // Old value as formated string
} }
if(valid3 == true){ if(valid3 == true){
svalue3old = svalue3; // Save the old value svalue3old = svalue3; // Save the old value

View File

@@ -0,0 +1,121 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
/*
Tracker
- standalone with SD card backend
- standalone with server backend
- Regatta Hero integration
*/
class PageTracker : public Page
{
private:
char mode = 'N'; // (N)ormal, (C)onfig
void displayModeNormal(PageData &pageData) {
// TBD Boatvalues: ...
logger->logDebug(GwLog::DEBUG,"Drawing at PageTracker");
// Title
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("Tracker");
}
void displayModeConfig() {
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 48);
epd->print("Tracker configuration");
epd->setFont(&Ubuntu_Bold8pt8b);
// TODO
}
public:
PageTracker(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageTracker");
}
void setupKeys(){
Page::setupKeys();
commonData->keydata[0].label = "START";
commonData->keydata[1].label = "STOP";
}
int handleKey(int key){
if (key == 1) { // Switch between normal and config mode
if (mode == 'N') {
mode = 'C';
} else {
mode = 'N';
}
return 0;
}
if (key == 11) {
commonData->keylock = !commonData->keylock;
return 0;
}
return key;
}
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData){
// Logging boat values
logger->logDebug(GwLog::LOG, "Drawing at PageTracker; Mode=%c", mode);
// Set display in partial refresh mode
epd->setPartialWindow(0, 0, epd->width(), epd->height());
if (mode == 'N') {
displayModeNormal(pageData);
} else if (mode == 'C') {
displayModeConfig();
}
return PAGE_UPDATE;
};
};
static Page *createPage(CommonData &common){
return new PageTracker(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config
* we define which function is to be called
* and we provide the number of user parameters we expect
* this will be number of BoatValue pointers in pageData.values
*/
PageDescription registerPageTracker(
"Tracker", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{"LAT", "LON"}, // Names of bus values undepends on selection in Web configuration (refer GwBoatData.h)
true // Show display header on/off
);
#endif

View File

@@ -1,18 +1,28 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h" #include "BoatDataCalibration.h"
#endif
class PageTwoValues : public Page class PageTwoValues : public Page
{ {
public: private:
PageTwoValues(CommonData &common){ String lengthformat;
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageTwoValue"); public:
PageTwoValues(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageTwoValue");
// Get config data
lengthformat = config->getString(config->lengthFormat);
} }
virtual int handleKey(int key){ int handleKey(int key){
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -21,9 +31,17 @@ class PageTwoValues : public Page
return key; return key;
} }
int displayPage(PageData &pageData){ void displayNew(PageData &pageData) {
GwConfigHandler *config = commonData->config; #ifdef BOARD_OBP60S3
GwLog *logger = commonData->logger; // Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
// Old values for hold function // Old values for hold function
static String svalue1old = ""; static String svalue1old = "";
@@ -31,87 +49,79 @@ class PageTwoValues : public Page
static String svalue2old = ""; static String svalue2old = "";
static String unit2old = ""; static String unit2old = "";
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Get boat values #1 // Get boat values #1
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = commonData->fmt->formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values #2 // Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
#endif
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = commonData->fmt->formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value String unit2 = commonData->fmt->formatValue(bvalue2, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageTwoValues, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2); logger->logDebug(GwLog::LOG, "Drawing at PageTwoValues, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
// ############### Value 1 ################ // ############### Value 1 ################
// Show name // Show name
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 80); epd->setCursor(20, 80);
getdisplay().print(name1); // Page name epd->print(name1); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 130); epd->setCursor(20, 130);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit1); // Unit epd->print(unit1); // Unit
} }
else{ else{
getdisplay().print(unit1old); epd->print(unit1old);
} }
// Switch font if format for any values // Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){ if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 130); epd->setCursor(50, 130);
} }
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){ else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 105); epd->setCursor(170, 105);
} }
else{ else{
getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b); epd->setFont(&DSEG7Classic_BoldItalic42pt7b);
getdisplay().setCursor(180, 130); epd->setCursor(180, 130);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue1); // Real value as formated string epd->print(svalue1); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue1old); // Old value as formated string epd->print(svalue1old); // Old value as formated string
} }
if(valid1 == true){ if(valid1 == true){
svalue1old = svalue1; // Save the old value svalue1old = svalue1; // Save the old value
@@ -121,45 +131,45 @@ class PageTwoValues : public Page
// ############### Horizontal Line ################ // ############### Horizontal Line ################
// Horizontal line 3 pix // Horizontal line 3 pix
getdisplay().fillRect(0, 145, 400, 3, commonData->fgcolor); epd->fillRect(0, 145, 400, 3, commonData->fgcolor);
// ############### Value 2 ################ // ############### Value 2 ################
// Show name // Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 190); epd->setCursor(20, 190);
getdisplay().print(name2); // Page name epd->print(name2); // Page name
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 240); epd->setCursor(20, 240);
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(unit2); // Unit epd->print(unit2); // Unit
} }
else{ else{
getdisplay().print(unit2old); epd->print(unit2old);
} }
// Switch font if format for any values // Switch font if format for any values
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){ if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 240); epd->setCursor(50, 240);
} }
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){ else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 215); epd->setCursor(170, 215);
} }
else{ else{
getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b); epd->setFont(&DSEG7Classic_BoldItalic42pt7b);
getdisplay().setCursor(180, 240); epd->setCursor(180, 240);
} }
// Show bus data // Show bus data
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue2); // Real value as formated string epd->print(svalue2); // Real value as formated string
} }
else{ else{
getdisplay().print(svalue2old); // Old value as formated string epd->print(svalue2old); // Old value as formated string
} }
if(valid2 == true){ if(valid2 == true){
svalue2old = svalue2; // Save the old value svalue2old = svalue2; // Save the old value

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -6,16 +7,24 @@
class PageVoltage : public Page class PageVoltage : public Page
{ {
bool init = false; // Marker for init done private:
uint8_t average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s String batVoltage;
bool trend = true; // Trend indicator [0|1], 0=off, 1=on String batType;
double raw = 0; bool init = false; // Marker for init done
char mode = 'D'; // display mode (A)nalog | (D)igital uint8_t average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
bool trend = true; // Trend indicator [0|1], 0=off, 1=on
double raw = 0;
char mode = 'D'; // display mode (A)nalog | (D)igital
public: public:
PageVoltage(CommonData &common){ PageVoltage(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageVoltage"); logger->logDebug(GwLog::LOG, "Instantiate PageVoltage");
// Get config data
batVoltage = config->getString(config->batteryVoltage);
batType = config->getString(config->batteryType);
if (hasFRAM) { if (hasFRAM) {
average = fram.read(FRAM_VOLTAGE_AVG); average = fram.read(FRAM_VOLTAGE_AVG);
trend = fram.read(FRAM_VOLTAGE_TREND); trend = fram.read(FRAM_VOLTAGE_TREND);
@@ -23,14 +32,18 @@ public:
} }
} }
virtual void setupKeys(){ ~PageVoltage(){
logger->logDebug(GwLog::LOG, "Destroy PageVoltage");
}
void setupKeys() {
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "AVG"; commonData->keydata[0].label = "AVG";
commonData->keydata[1].label = "MODE"; commonData->keydata[1].label = "MODE";
commonData->keydata[4].label = "TRD"; commonData->keydata[4].label = "TRD";
} }
virtual int handleKey(int key){ int handleKey(int key) {
// Change average // Change average
if(key == 1){ if(key == 1){
average ++; average ++;
@@ -66,51 +79,41 @@ public:
} }
void printAvg(int avg, uint16_t x, uint16_t y, bool prefix) { void printAvg(int avg, uint16_t x, uint16_t y, bool prefix) {
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(x, y); epd->setCursor(x, y);
if (prefix) { if (prefix) {
getdisplay().print("Avg: "); epd->print("Avg: ");
} }
switch (average) { switch (average) {
case 0: case 0:
getdisplay().print("1s"); epd->print("1s");
break; break;
case 1: case 1:
getdisplay().print("10s"); epd->print("10s");
break; break;
case 2: case 2:
getdisplay().print("60s"); epd->print("60s");
break; break;
case 3: case 3:
getdisplay().print("300s"); epd->print("300s");
break; break;
default: default:
getdisplay().print("1s"); epd->print("1s");
break; break;
} }
} }
void printVoltageSymbol(uint16_t x, uint16_t y, uint16_t color) { void printVoltageSymbol(uint16_t x, uint16_t y, uint16_t color) {
getdisplay().setFont(&Ubuntu_Bold16pt8b); epd->setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(x, y); epd->setCursor(x, y);
getdisplay().print("V"); epd->print("V");
getdisplay().fillRect(x, y + 6, 22, 3, color); epd->fillRect(x, y + 6, 22, 3, color);
getdisplay().fillRect(x, y + 11, 6, 3, color); epd->fillRect(x, y + 11, 6, 3, color);
getdisplay().fillRect(x + 8, y + 11, 6, 3, color); epd->fillRect(x + 8, y + 11, 6, 3, color);
getdisplay().fillRect(x + 16, y + 11, 6, 3, color); epd->fillRect(x + 16, y + 11, 6, 3, color);
} }
int displayPage(PageData &pageData){ int displayPage(PageData &pageData) {
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Get config data
bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String batVoltage = config->getString(config->batteryVoltage);
String batType = config->getString(config->batteryType);
String backlightMode = config->getString(config->backlight);
double value1 = 0; double value1 = 0;
double valueTrend = 0; // Average over 10 values double valueTrend = 0; // Average over 10 values
@@ -151,92 +154,76 @@ public:
bool valid1 = true; bool valid1 = true;
// Optical warning by limit violation // Optical warning by limit violation
if(String(flashLED) == "Limit Violation"){ if (flashLED == "Limit Violation") {
// Limits for Pb battery bool violation = false;
if(String(batType) == "Pb" && (raw < 11.8 || raw > 14.8)){ if (batType == "Pb") {
violation = (raw < 11.8 || raw > 14.8);
} else if (batType == "Gel") {
violation = (raw < 11.8 || raw > 14.4);
} else if (batType == "AGM") {
violation = (raw < 11.8 || raw > 14.7);
} else if (batType == "LiFePo4") {
violation = (raw < 12.0 || raw > 14.6);
}
if (violation) {
setBlinkingLED(true); setBlinkingLED(true);
} } else {
if(String(batType) == "Pb" && (raw >= 11.8 && raw <= 14.8)){
setBlinkingLED(false);
setFlashLED(false);
}
// Limits for Gel battery
if(String(batType) == "Gel" && (raw < 11.8 || raw > 14.4)){
setBlinkingLED(true);
}
if(String(batType) == "Gel" && (raw >= 11.8 && raw <= 14.4)){
setBlinkingLED(false);
setFlashLED(false);
}
// Limits for AGM battery
if(String(batType) == "AGM" && (raw < 11.8 || raw > 14.7)){
setBlinkingLED(true);
}
if(String(batType) == "AGM" && (raw >= 11.8 && raw <= 14.7)){
setBlinkingLED(false);
setFlashLED(false);
}
// Limits for LiFePo4 battery
if(String(batType) == "LiFePo4" && (raw < 12.0 || raw > 14.6)){
setBlinkingLED(true);
}
if(String(batType) == "LiFePo4" && (raw >= 12.0 && raw <= 14.6)){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
} }
// Logging voltage value // Logging voltage value
LOG_DEBUG(GwLog::LOG,"Drawing at PageVoltage, Type:%s %s:=%f", batType, name1.c_str(), raw); logger->logDebug(GwLog::LOG, "Drawing at PageVoltage, Type:%s %s:=%f", batType, name1.c_str(), raw);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
if (mode == 'D') { if (mode == 'D') {
// Display mode digital // Display mode digital
// Show name // Show name
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold32pt8b); epd->setFont(&Ubuntu_Bold32pt8b);
getdisplay().setCursor(20, 100); epd->setCursor(20, 100);
getdisplay().print(name1); // Value name epd->print(name1); // Value name
#if defined BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR #if defined BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR
// Show charge status // Show charge status
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(185, 100); epd->setCursor(185, 100);
if(commonData->data.BatteryChargeStatus == true){ if(commonData->data.BatteryChargeStatus == true){
getdisplay().print("Charge"); epd->print("Charge");
} }
else{ else{
getdisplay().print("Discharge"); epd->print("Discharge");
} }
#endif #endif
// Show unit // Show unit
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(270, 100); epd->setCursor(270, 100);
getdisplay().print("V"); epd->print("V");
// Show battery type // Show battery type
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(295, 100); epd->setCursor(295, 100);
#ifdef BOARD_OBP60S3 #ifdef BOARD_OBP60S3
getdisplay().print(batType); epd->print(batType);
#endif #endif
#if defined BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR #if defined BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR
getdisplay().print("LiPo"); epd->print("LiPo");
#endif #endif
// Show average settings // Show average settings
printAvg(average, 320, 84, true); printAvg(average, 320, 84, true);
// Reading bus data or using simulation data // Reading bus data or using simulation data
getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b); epd->setFont(&DSEG7Classic_BoldItalic60pt7b);
getdisplay().setCursor(20, 240); epd->setCursor(20, 240);
if(simulation == true){ if(simulation == true){
if(batVoltage == "12V"){ if(batVoltage == "12V"){
value1 = 12.0; value1 = 12.0;
@@ -245,30 +232,30 @@ public:
value1 = 24.0; value1 = 24.0;
} }
value1 += float(random(0, 5)) / 10; // Simulation data value1 += float(random(0, 5)) / 10; // Simulation data
getdisplay().print(value1,1); epd->print(value1,1);
} }
else{ else{
// Check for valid real data, display also if hold values activated // Check for valid real data, display also if hold values activated
if(valid1 == true || holdvalues == true){ if(valid1 == true || holdvalues == true){
// Resolution switching // Resolution switching
if(value1 < 10){ if(value1 < 10){
getdisplay().print(value1,2); epd->print(value1,2);
} }
if(value1 >= 10 && value1 < 100){ if(value1 >= 10 && value1 < 100){
getdisplay().print(value1,1); epd->print(value1,1);
} }
if(value1 >= 100){ if(value1 >= 100){
getdisplay().print(value1,0); epd->print(value1,0);
} }
} }
else{ else{
getdisplay().print("---"); // Missing bus data epd->print(commonData->fmt->placeholder); // Missing bus data
} }
} }
// Show trend indicator // Show trend indicator
if(trend == true){ if(trend == true){
getdisplay().fillRect(315, 183, 35, 4, commonData->fgcolor); // Draw separator epd->fillRect(315, 183, 35, 4, commonData->fgcolor); // Draw separator
if(int(raw * 10) > int(valueTrend * 10)){ if(int(raw * 10) > int(valueTrend * 10)){
displayTrendHigh(320, 174, 11, commonData->fgcolor); // Show high indicator displayTrendHigh(320, 174, 11, commonData->fgcolor); // Show high indicator
} }
@@ -289,9 +276,9 @@ public:
std::vector<Point> pts; std::vector<Point> pts;
// Instrument // Instrument
getdisplay().drawCircleHelper(c.x, c.y, r + 2, 0x01, commonData->fgcolor); epd->drawCircleHelper(c.x, c.y, r + 2, 0x01, commonData->fgcolor);
getdisplay().drawCircleHelper(c.x, c.y, r + 1, 0x01, commonData->fgcolor); epd->drawCircleHelper(c.x, c.y, r + 1, 0x01, commonData->fgcolor);
getdisplay().drawCircleHelper(c.x, c.y, r , 0x01, commonData->fgcolor); epd->drawCircleHelper(c.x, c.y, r , 0x01, commonData->fgcolor);
// Scale // Scale
// angle to voltage scale mapping // angle to voltage scale mapping
@@ -304,7 +291,7 @@ public:
{c.x - r + 12, c.y + 1}, {c.x - r + 12, c.y + 1},
{c.x - r, c.y + 1} {c.x - r, c.y + 1}
}; };
getdisplay().setFont(&Ubuntu_Bold10pt8b); epd->setFont(&Ubuntu_Bold10pt8b);
for (int angle = 3; angle < 90; angle += 3) { for (int angle = 3; angle < 90; angle += 3) {
if (angle % 15 == 0) { if (angle % 15 == 0) {
fillPoly4(rotatePoints(c, pts, angle), commonData->fgcolor); fillPoly4(rotatePoints(c, pts, angle), commonData->fgcolor);
@@ -314,7 +301,7 @@ public:
else { else {
p1 = rotatePoint(c, {c.x - r, c.y}, angle); p1 = rotatePoint(c, {c.x - r, c.y}, angle);
p2 = rotatePoint(c, {c.x - r + 6, c.y}, angle); p2 = rotatePoint(c, {c.x - r + 6, c.y}, angle);
getdisplay().drawLine(p1.x, p1.y, p2.x, p2.y, commonData->fgcolor); epd->drawLine(p1.x, p1.y, p2.x, p2.y, commonData->fgcolor);
} }
} }
@@ -354,31 +341,31 @@ public:
fillPoly4(rotatePoints(c, pts, angle), commonData->fgcolor); fillPoly4(rotatePoints(c, pts, angle), commonData->fgcolor);
// base // base
getdisplay().fillCircle(c.x, c.y, 7, commonData->fgcolor); epd->fillCircle(c.x, c.y, 7, commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, 4, commonData->bgcolor); epd->fillCircle(c.x, c.y, 4, commonData->bgcolor);
// Symbol // Symbol
printVoltageSymbol(40, 60, commonData->fgcolor); printVoltageSymbol(40, 60, commonData->fgcolor);
// Additional information at right side // Additional information at right side
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(300, 60); epd->setCursor(300, 60);
getdisplay().print("Source:"); epd->print("Source:");
getdisplay().setCursor(300, 80); epd->setCursor(300, 80);
getdisplay().print(name1); epd->print(name1);
getdisplay().setCursor(300, 110); epd->setCursor(300, 110);
getdisplay().print("Type:"); epd->print("Type:");
getdisplay().setCursor(300, 130); epd->setCursor(300, 130);
getdisplay().print(batType); epd->print(batType);
getdisplay().setCursor(300, 160); epd->setCursor(300, 160);
getdisplay().print("Avg:"); epd->print("Avg:");
printAvg(average, 300, 180, false); printAvg(average, 300, 180, false);
// FRAM indicator // FRAM indicator
if (hasFRAM) { if (hasFRAM) {
getdisplay().drawXBitmap(300, 240, fram_bits, icon_width, icon_height, commonData->fgcolor); epd->drawXBitmap(300, 240, fram_bits, icon_width, icon_height, commonData->fgcolor);
} }
} }

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -13,16 +14,17 @@
class PageWhite : public Page class PageWhite : public Page
{ {
char mode = 'W'; // display mode (W)hite | (L)ogo | (M)FD logo private:
char mode = 'W'; // display mode (W)hite | (L)ogo | (M)FD logo
public: public:
PageWhite(CommonData &common){ PageWhite(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageWhite"); logger->logDebug(GwLog::LOG, "Instantiate PageWhite");
refreshtime = 15000; refreshtime = 15000;
} }
virtual int handleKey(int key) { int handleKey(int key) {
// Change display mode // Change display mode
if (key == 1) { if (key == 1) {
if (mode == 'W') { if (mode == 'W') {
@@ -37,21 +39,20 @@ public:
return key; return key;
} }
int displayPage(PageData &pageData){ void displayNew(PageData &pageData) {
GwConfigHandler *config = commonData->config; #ifdef BOARD_OBP60S3
GwLog *logger = commonData->logger; // Clear optical warning
if (flashLED == "Limit Violation") {
// Get config data
String flashLED = config->getString(config->flashLED);
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
#endif
};
int displayPage(PageData &pageData) {
// Logging boat values // Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageWhite"); logger->logDebug(GwLog::LOG, "Drawing at PageWhite");
// Draw page // Draw page
//*********************************************************** //***********************************************************
@@ -61,19 +62,19 @@ public:
// Set display in partial refresh mode // Set display in partial refresh mode
if (mode == 'W') { if (mode == 'W') {
getdisplay().setFullWindow(); epd->setFullWindow();
} else { } else {
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
} }
if (mode == 'L') { if (mode == 'L') {
getdisplay().drawXBitmap(0, 0, OBP_400x300_bits, OBP_400x300_width, OBP_400x300_height, commonData->fgcolor); epd->drawXBitmap(0, 0, OBP_400x300_bits, OBP_400x300_width, OBP_400x300_height, commonData->fgcolor);
} else if (mode == 'M') { } else if (mode == 'M') {
#ifdef BOARD_OBP60S3 #ifdef BOARD_OBP60S3
getdisplay().drawXBitmap(0, 0, OBP60_400x300_bits, OBP60_400x300_width, OBP60_400x300_height, commonData->fgcolor); epd->drawXBitmap(0, 0, OBP60_400x300_bits, OBP60_400x300_width, OBP60_400x300_height, commonData->fgcolor);
#endif #endif
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
getdisplay().drawXBitmap(0, 0, OBP40_400x300_bits, OBP40_400x300_width, OBP40_400x300_height, commonData->fgcolor); epd->drawXBitmap(0, 0, OBP40_400x300_bits, OBP40_400x300_width, OBP40_400x300_height, commonData->fgcolor);
#endif #endif
} }

View File

@@ -1,9 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "N2kMessages.h" #include "N2kMessages.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h" #include "BoatDataCalibration.h"
#endif
#define front_width 120 #define front_width 120
#define front_height 162 #define front_height 162
@@ -214,15 +218,21 @@ static unsigned char front_bits[] PROGMEM = {
class PageWind : public Page class PageWind : public Page
{ {
bool keylock = false; // Keylock private:
int8_t lp = 80; // Pointer length String lengthformat;
char mode = 'N'; // page mode (N)ormal | (L)ens | e(X)ample bool keylock = false; // Keylock
char source = 'A'; // data source (A)pparent | (T)rue int8_t lp = 80; // Pointer length
char mode = 'N'; // page mode (N)ormal | (L)ens | e(X)ample
char source = 'A'; // data source (A)pparent | (T)rue
public: public:
PageWind(CommonData &common){ PageWind(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageWind"); logger->logDebug(GwLog::LOG, "Instantiate PageWind");
// Get config data
lengthformat = config->getString(config->lengthFormat);
if (hasFRAM) { if (hasFRAM) {
lp = fram.read(FRAM_WIND_SIZE); lp = fram.read(FRAM_WIND_SIZE);
source = fram.read(FRAM_WIND_SRC); source = fram.read(FRAM_WIND_SRC);
@@ -230,7 +240,7 @@ public:
} }
} }
virtual void setupKeys(){ void setupKeys() {
Page::setupKeys(); Page::setupKeys();
commonData->keydata[0].label = "MODE"; commonData->keydata[0].label = "MODE";
if (mode == 'X') { if (mode == 'X') {
@@ -242,7 +252,7 @@ public:
} }
// Key functions // Key functions
virtual int handleKey(int key){ int handleKey(int key) {
if(key == 1){ // Mode switch if(key == 1){ // Mode switch
if(mode == 'N'){ if(mode == 'N'){
@@ -296,23 +306,23 @@ public:
return key; return key;
} }
int displayPage(PageData &pageData) void displayNew(PageData &pageData) {
{ #ifdef BOARD_OBP60S3
GwConfigHandler *config = commonData->config; // Clear optical warning
GwLog *logger = commonData->logger; if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
static String svalue1old = ""; static String svalue1old = "";
static String unit1old = ""; static String unit1old = "";
static String svalue2old = ""; static String svalue2old = "";
static String unit2old = ""; static String unit2old = "";
// Get config data
String lengthformat = config->getString(config->lengthFormat);
bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
GwApi::BoatValue *bvalue1; // Value 1 for speed on top GwApi::BoatValue *bvalue1; // Value 1 for speed on top
GwApi::BoatValue *bvalue2; // Value 2 for angle on bottom GwApi::BoatValue *bvalue2; // Value 2 for angle on bottom
@@ -324,11 +334,13 @@ public:
} }
String name1 = bvalue1->getName().c_str(); // Value name String name1 = bvalue1->getName().c_str(); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
// bool valid1 = bvalue1->valid; // Valid information // bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue1 = commonData->fmt->formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value String unit1 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value
// Get boat values for angle (AWD/TWD) // Get boat values for angle (AWD/TWD)
if (source == 'A') { if (source == 'A') {
@@ -338,74 +350,70 @@ public:
} }
String name2 = bvalue2->getName().c_str(); // Value name String name2 = bvalue2->getName().c_str(); // Value name
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
#endif
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
// bool valid2 = bvalue2->valid; // Valid information // bool valid2 = bvalue2->valid; // Valid information
if (simulation) { if (simulation) {
value2 = 0.62731; // some random value value2 = 0.62731; // some random value
} }
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue2 = commonData->fmt->formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value String unit2 = commonData->fmt->formatValue(bvalue2, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageWind, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2); logger->logDebug(GwLog::LOG, "Drawing at PageWind, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
if (mode == 'X') { if (mode == 'X') {
// Original example code with scaling circle // Original example code with scaling circle
// Show values AWS/TWS // Show values AWS/TWS
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 50); epd->setCursor(20, 50);
getdisplay().print(name1); // Value name epd->print(name1); // Value name
getdisplay().print(": "); epd->print(": ");
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue1); // Value epd->print(svalue1); // Value
getdisplay().print(" "); epd->print(" ");
getdisplay().print(unit1); // Unit epd->print(unit1); // Unit
} }
else{ else{
getdisplay().print(svalue1old); // Value old epd->print(svalue1old); // Value old
getdisplay().print(" "); epd->print(" ");
getdisplay().print(unit1old); // Unit old epd->print(unit1old); // Unit old
} }
// Show values AWD/TWD // Show values AWD/TWD
getdisplay().setFont(&Ubuntu_Bold20pt8b); epd->setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 260); epd->setCursor(20, 260);
getdisplay().print(name2); // Value name epd->print(name2); // Value name
getdisplay().print(": "); epd->print(": ");
if(holdvalues == false){ if(holdvalues == false){
getdisplay().print(svalue2); // Value epd->print(svalue2); // Value
getdisplay().print(" "); epd->print(" ");
getdisplay().print(unit2); // Unit epd->print(unit2); // Unit
} }
else{ else{
getdisplay().print(svalue2old); // Value old epd->print(svalue2old); // Value old
getdisplay().print(" "); epd->print(" ");
getdisplay().print(unit2old); // Unit old epd->print(unit2old); // Unit old
} }
Point c = {200, 145}; Point c = {200, 145};
// Draw instrument // Draw instrument
getdisplay().fillCircle(c.x, c.y, lp + 5, commonData->fgcolor); epd->fillCircle(c.x, c.y, lp + 5, commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, lp + 1, commonData->bgcolor); epd->fillCircle(c.x, c.y, lp + 1, commonData->bgcolor);
// Wind pointer // Wind pointer
if (bvalue2->valid or simulation) { if (bvalue2->valid or simulation) {
@@ -420,7 +428,7 @@ public:
}; };
fillPoly4(rotatePoints(c, pts, RadToDeg(value2)), commonData->fgcolor); fillPoly4(rotatePoints(c, pts, RadToDeg(value2)), commonData->fgcolor);
} else { } else {
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
drawTextCenter(c.x, c.y, "no data"); drawTextCenter(c.x, c.y, "no data");
} }
@@ -438,7 +446,7 @@ public:
}; };
int angle; int angle;
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
// starbord // starbord
// text with line // text with line
@@ -453,7 +461,7 @@ public:
for (int i = 30; i < 138; i += 6) { for (int i = 30; i < 138; i += 6) {
if (i % 15 != 0) { if (i % 15 != 0) {
p = rotatePoint(c, {c.x, c.y - r + 5}, i); p = rotatePoint(c, {c.x, c.y - r + 5}, i);
getdisplay().fillCircle(p.x, p.y, 2, commonData->fgcolor); epd->fillCircle(p.x, p.y, 2, commonData->fgcolor);
} }
} }
@@ -470,17 +478,17 @@ public:
for (int i = 228; i < 330; i += 6) { for (int i = 228; i < 330; i += 6) {
if (i % 15 != 0) { if (i % 15 != 0) {
p = rotatePoint(c, {c.x, c.y - r + 5}, i); p = rotatePoint(c, {c.x, c.y - r + 5}, i);
getdisplay().fillCircle(p.x, p.y, 2, commonData->fgcolor); epd->fillCircle(p.x, p.y, 2, commonData->fgcolor);
} }
} }
// data source // data source
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 50); epd->setCursor(8, 50);
if (source == 'A') { if (source == 'A') {
getdisplay().print("APP"); epd->print("APP");
} else { } else {
getdisplay().print("TRUE"); epd->print("TRUE");
} }
// Wind pointer (angle) // Wind pointer (angle)
@@ -500,7 +508,7 @@ public:
alpha *= -1; alpha *= -1;
} }
getdisplay().fillCircle(c.x, c.y, 8, commonData->fgcolor); epd->fillCircle(c.x, c.y, 8, commonData->fgcolor);
pts = { pts = {
{c.x - 1, c.y - (r - 20)}, {c.x - 1, c.y - (r - 20)},
{c.x + 1, c.y - (r - 20)}, {c.x + 1, c.y - (r - 20)},
@@ -508,39 +516,39 @@ public:
{c.x - 6, c.y + 15} {c.x - 6, c.y + 15}
}; };
fillPoly4(rotatePoints(c, pts, alpha), commonData->fgcolor); fillPoly4(rotatePoints(c, pts, alpha), commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, 6, commonData->bgcolor); epd->fillCircle(c.x, c.y, 6, commonData->bgcolor);
} else { } else {
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
drawTextCenter(c.x, c.y, "no data"); drawTextCenter(c.x, c.y, "no data");
} }
// Wind speed as decimal number // Wind speed as decimal number
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 250); epd->setCursor(150, 250);
if (holdvalues == false) { if (holdvalues == false) {
getdisplay().print(svalue1); epd->print(svalue1);
} else { } else {
getdisplay().print(svalue1old); epd->print(svalue1old);
} }
// unit // unit
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(220, 265); epd->setCursor(220, 265);
getdisplay().print("kts"); epd->print("kts");
} }
else { else {
// Normal mode // Normal mode
// data source // data source
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 50); epd->setCursor(8, 50);
if (source == 'A') { if (source == 'A') {
getdisplay().print("APP"); epd->print("APP");
} else { } else {
getdisplay().print("TRUE"); epd->print("TRUE");
} }
// draw ship front symbol (as bitmap) // draw ship front symbol (as bitmap)
getdisplay().drawXBitmap(140, 30, front_bits, front_width, front_height, commonData->fgcolor); epd->drawXBitmap(140, 30, front_bits, front_width, front_height, commonData->fgcolor);
Point c = {200, 155}; Point c = {200, 155};
uint16_t r = 150; uint16_t r = 150;
@@ -565,7 +573,7 @@ public:
for (int i = 30; i < 150; i += 10) { for (int i = 30; i < 150; i += 10) {
if (i % 30 != 0) { if (i % 30 != 0) {
p = rotatePoint(c, {c.x, c.y - r + 5}, i); p = rotatePoint(c, {c.x, c.y - r + 5}, i);
getdisplay().fillCircle(p.x, p.y, 3, commonData->fgcolor); epd->fillCircle(p.x, p.y, 3, commonData->fgcolor);
} }
} }
@@ -582,27 +590,27 @@ public:
for (int i = 210; i < 340; i += 10) { for (int i = 210; i < 340; i += 10) {
if (i % 30 != 0) { if (i % 30 != 0) {
p = rotatePoint(c, {c.x, c.y - r + 5}, i); p = rotatePoint(c, {c.x, c.y - r + 5}, i);
getdisplay().fillCircle(p.x, p.y, 3, commonData->fgcolor); epd->fillCircle(p.x, p.y, 3, commonData->fgcolor);
} }
} }
// Wind speed as decimal number // Wind speed as decimal number
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 250); epd->setCursor(150, 250);
if (holdvalues == false) { if (holdvalues == false) {
getdisplay().print(svalue1); epd->print(svalue1);
} else { } else {
getdisplay().print(svalue1old); epd->print(svalue1old);
} }
// unit // unit
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(220, 265); epd->setCursor(220, 265);
getdisplay().print("kts"); epd->print("kts");
// Wind pointer (angle) // Wind pointer (angle)
if (bvalue2->valid or simulation) { if (bvalue2->valid or simulation) {
float alpha = RadToDeg(value2); float alpha = RadToDeg(value2);
getdisplay().fillCircle(c.x, c.y, 8, commonData->fgcolor); epd->fillCircle(c.x, c.y, 8, commonData->fgcolor);
pts = { pts = {
{c.x - 1, c.y - (r - 20)}, {c.x - 1, c.y - (r - 20)},
{c.x + 1, c.y - (r - 20)}, {c.x + 1, c.y - (r - 20)},
@@ -610,9 +618,9 @@ public:
{c.x - 6, c.y + 15} {c.x - 6, c.y + 15}
}; };
fillPoly4(rotatePoints(c, pts, alpha), commonData->fgcolor); fillPoly4(rotatePoints(c, pts, alpha), commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, 6, commonData->bgcolor); epd->fillCircle(c.x, c.y, 6, commonData->bgcolor);
} else { } else {
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
drawTextCenter(c.x, c.y, "no data"); drawTextCenter(c.x, c.y, "no data");
} }

View File

@@ -1,23 +1,53 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "BoatDataCalibration.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "OBPRingBuffer.h" #include "OBPRingBuffer.h"
#include "Pagedata.h" #include "OBPDataOperations.h"
#include "BoatDataCalibration.h"
#include <vector> #include <vector>
static const double radToDeg = 180.0 / M_PI; // Conversion factor from radians to degrees static const double radToDeg = 180.0 / M_PI; // Conversion factor from radians to degrees
// Get maximum difference of last <amount> of TWD ringbuffer values to center chart; returns "0" if data is not valid
int getCntr(const RingBuffer<int16_t>& windDirHstry, size_t amount)
{
const int MAX_VAL = windDirHstry.getMaxVal();
size_t count = windDirHstry.getCurrentSize();
if (windDirHstry.isEmpty() || amount <= 0) {
return 0;
}
if (amount > count)
amount = count;
uint16_t midWndDir, minWndDir, maxWndDir = 0;
int wndCenter = 0;
midWndDir = windDirHstry.getMid(amount);
if (midWndDir != MAX_VAL) {
midWndDir = midWndDir / 1000.0 * radToDeg;
wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value
minWndDir = windDirHstry.getMin(amount) / 1000.0 * radToDeg;
maxWndDir = windDirHstry.getMax(amount) / 1000.0 * radToDeg;
if ((maxWndDir - minWndDir) > 180 && !(minWndDir > maxWndDir)) { // if wind range is > 180 and no 0° crossover, adjust wndCenter to smaller wind range end
wndCenter = WindUtils::to360(wndCenter + 180);
}
}
return wndCenter;
}
// Get maximum difference of last <amount> of TWD ringbuffer values to center chart // Get maximum difference of last <amount> of TWD ringbuffer values to center chart
int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount) int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
{ {
int minVal = windDirHstry.getMinVal(); int minVal = windDirHstry.getMinVal();
const int MAX_VAL = windDirHstry.getMaxVal();
size_t count = windDirHstry.getCurrentSize(); size_t count = windDirHstry.getCurrentSize();
// size_t capacity = windDirHstry.getCapacity();
// size_t last = windDirHstry.getLastIdx();
if (windDirHstry.isEmpty() || amount <= 0) { if (windDirHstry.isEmpty() || amount <= 0) {
return minVal; return MAX_VAL;
} }
if (amount > count) if (amount > count)
amount = count; amount = count;
@@ -27,11 +57,10 @@ int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
int maxRng = minVal; int maxRng = minVal;
// Start from the newest value (last) and go backwards x times // Start from the newest value (last) and go backwards x times
for (size_t i = 0; i < amount; i++) { for (size_t i = 0; i < amount; i++) {
// value = windDirHstry.get(((last - i) % capacity + capacity) % capacity);
value = windDirHstry.get(count - 1 - i); value = windDirHstry.get(count - 1 - i);
if (value == minVal) { if (value == MAX_VAL) {
continue; continue; // ignore invalid values
} }
value = value / 1000.0 * radToDeg; value = value / 1000.0 * radToDeg;
@@ -43,36 +72,39 @@ int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
maxRng = 180; maxRng = 180;
} }
return maxRng; return (maxRng != minVal ? maxRng : MAX_VAL);
} }
// **************************************************************** // ****************************************************************
class PageWindPlot : public Page { class PageWindPlot : public Page {
private:
bool keylock = false; // Keylock bool keylock = false; // Keylock
char chrtMode = 'D'; // Chart mode: 'D' for TWD, 'S' for TWS, 'B' for both char chrtMode = 'D'; // Chart mode: 'D' for TWD, 'S' for TWS, 'B' for both
bool showTruW = true; // Show true wind or apparant wind in chart area
bool oldShowTruW = false; // remember recent user selection of wind data type
int dataIntv = 1; // Update interval for wind history chart: int dataIntv = 1; // Update interval for wind history chart:
// (1)|(2)|(3)|(4) seconds for approx. 4, 8, 12, 16 min. history chart // (1)|(2)|(3)|(4) seconds for approx. 4, 8, 12, 16 min. history chart
bool showTWS = true; // Show TWS value in chart area
public: public:
PageWindPlot(CommonData& common) PageWindPlot(CommonData& common) : Page(common)
{ {
commonData = &common; logger->logDebug(GwLog::LOG, "Instantiate PageWindPlot");
common.logger->logDebug(GwLog::LOG, "Instantiate PageWindPlot");
} }
virtual void setupKeys() void setupKeys() {
{
Page::setupKeys(); Page::setupKeys();
// commonData->keydata[0].label = "MODE"; // commonData->keydata[0].label = "MODE";
#if defined BOARD_OBP60S3
commonData->keydata[1].label = "SRC";
commonData->keydata[4].label = "INTV";
#elif defined BOARD_OBP40S3
commonData->keydata[1].label = "INTV"; commonData->keydata[1].label = "INTV";
commonData->keydata[4].label = "TWS"; #endif
} }
// Key functions // Key functions
virtual int handleKey(int key) int handleKey(int key) {
{
// Set chart mode TWD | TWS -> to be implemented // Set chart mode TWD | TWS -> to be implemented
if (key == 1) { if (key == 1) {
if (chrtMode == 'D') { if (chrtMode == 'D') {
@@ -85,8 +117,18 @@ public:
return 0; // Commit the key return 0; // Commit the key
} }
// Set interval for wind history chart update time #if defined BOARD_OBP60S3
// Set data source TRUE | APP
if (key == 2) { if (key == 2) {
showTruW = !showTruW;
return 0; // Commit the key
}
// Set interval for wind history chart update time (interval)
if (key == 5) {
#elif defined BOARD_OBP40S3
if (key == 2) {
#endif
if (dataIntv == 1) { if (dataIntv == 1) {
dataIntv = 2; dataIntv = 2;
} else if (dataIntv == 2) { } else if (dataIntv == 2) {
@@ -99,12 +141,6 @@ public:
return 0; // Commit the key return 0; // Commit the key
} }
// Switch TWS on/off
if (key == 5) {
showTWS = !showTWS;
return 0; // Commit the key
}
// Keylock function // Keylock function
if (key == 11) { // Code for keylock if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -113,32 +149,47 @@ public:
return key; return key;
} }
int displayPage(PageData& pageData) void displayNew(PageData &pageData) {
{ #ifdef BOARD_OBP60S3
GwConfigHandler* config = commonData->config; // Clear optical warning
GwLog* logger = commonData->logger; if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
#ifdef BOARD_OBP40S3
String wndSrc; // Wind source true/apparant wind - preselection for OBP40
float twsValue; // TWS value in chart area wndSrc = commonData->config->getString("page" + String(pageData.pageNumber) + "wndsrc");
static String twdName, twdUnit; // TWD name and unit if (wndSrc =="True wind") {
static int updFreq; // Update frequency for TWD showTruW = true;
static int16_t twdLowest, twdHighest; // TWD range } else {
// static int16_t twdBufMinVal; // lowest possible twd buffer value; used for non-set data showTruW = false; // Wind source is apparant wind
}
commonData->logger->logDebug(GwLog::LOG,"New PageWindPlot: wind source=%s", wndSrc);
#endif
oldShowTruW = !showTruW; // makes wind source being initialized at initial page call
}
// current boat data values; TWD only for validation test, TWS for display of current value int displayPage(PageData& pageData) {
static RingBuffer<int16_t>* wdHstry; // Wind direction data buffer
static RingBuffer<uint16_t>* wsHstry; // Wind speed data buffer
static String wdName, wdFormat; // Wind direction name and format
static String wsName, wsFormat; // Wind speed name and format
static int16_t wdMAX_VAL; // Max. value of wd history buffer, indicating invalid values
float wsValue; // Wind speed value in chart area
String wsUnit; // Wind speed unit in chart area
static GwApi::BoatValue* wsBVal = new GwApi::BoatValue("TWS"); // temp BoatValue for wind speed unit identification; required by OBP60Formater
// current boat data values; TWD/AWD only for validation test
const int numBoatData = 2; const int numBoatData = 2;
GwApi::BoatValue* bvalue; GwApi::BoatValue* bvalue;
String BDataName[numBoatData];
double BDataValue[numBoatData];
bool BDataValid[numBoatData]; bool BDataValid[numBoatData];
String BDataText[numBoatData];
String BDataUnit[numBoatData];
String BDataFormat[numBoatData];
static bool isInitialized = false; // Flag to indicate that page is initialized static bool isInitialized = false; // Flag to indicate that page is initialized
static bool wndDataValid = false; // Flag to indicate if wind data is valid static bool wndDataValid = false; // Flag to indicate if wind data is valid
static int numNoData; // Counter for multiple invalid data values in a row static int numNoData; // Counter for multiple invalid data values in a row
static bool simulation = false;
static bool holdValues = false;
static int width; // Screen width static int width; // Screen width
static int height; // Screen height static int height; // Screen height
@@ -161,10 +212,8 @@ public:
static int wndRight; // chart wind right value position static int wndRight; // chart wind right value position
static int chrtRng; // Range of wind values from mid wind value to min/max wind value in degrees static int chrtRng; // Range of wind values from mid wind value to min/max wind value in degrees
int diffRng; // Difference between mid and current wind value int diffRng; // Difference between mid and current wind value
static const int dfltRng = 40; // Default range for chart static const int dfltRng = 60; // Default range for chart
int midWndDir; // New value for wndCenter after chart start / shift int midWndDir; // New value for wndCenter after chart start / shift
static int simTwd; // Simulation value for TWD
static float simTws; // Simulation value for TWS
int x, y; // x and y coordinates for drawing int x, y; // x and y coordinates for drawing
static int prevX, prevY; // Last x and y coordinates for drawing static int prevX, prevY; // Last x and y coordinates for drawing
@@ -172,30 +221,20 @@ public:
int chrtVal; // Current wind value int chrtVal; // Current wind value
static int chrtPrevVal; // Last wind value in chart area for check if value crosses 180 degree line static int chrtPrevVal; // Last wind value in chart area for check if value crosses 180 degree line
LOG_DEBUG(GwLog::LOG, "Display page WindPlot"); logger->logDebug(GwLog::LOG, "Display PageWindPlot");
ulong timer = millis();
// Get config data
simulation = config->getBool(config->useSimuData);
holdValues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
if (!isInitialized) { if (!isInitialized) {
width = getdisplay().width(); width = epd->width();
height = getdisplay().height(); height = epd->height();
xCenter = width / 2; xCenter = width / 2;
cHeight = height - yOffset - 22; cHeight = height - yOffset - 22;
bufSize = pageData.boatHstry.twdHstry->getCapacity();
numNoData = 0; numNoData = 0;
simTwd = pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg;
simTws = 0;
twsValue = 0;
bufStart = 0; bufStart = 0;
oldDataIntv = 0; oldDataIntv = 0;
wsValue = 0;
numAddedBufVals, currIdx, lastIdx = 0; numAddedBufVals, currIdx, lastIdx = 0;
lastAddedIdx = pageData.boatHstry.twdHstry->getLastIdx(); wndCenter = INT_MAX;
pageData.boatHstry.twdHstry->getMetaData(twdName, twdUnit, updFreq, twdLowest, twdHighest);
wndCenter = INT_MIN;
midWndDir = 0; midWndDir = 0;
diffRng = dfltRng; diffRng = dfltRng;
chrtRng = dfltRng; chrtRng = dfltRng;
@@ -206,14 +245,7 @@ public:
// read boat data values; TWD only for validation test, TWS for display of current value // read boat data values; TWD only for validation test, TWS for display of current value
for (int i = 0; i < numBoatData; i++) { for (int i = 0; i < numBoatData; i++) {
bvalue = pageData.values[i]; bvalue = pageData.values[i];
BDataName[i] = xdrDelete(bvalue->getName());
BDataName[i] = BDataName[i].substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue, logger); // Check if boat data value is to be calibrated
BDataValue[i] = bvalue->value; // Value as double in SI unit
BDataValid[i] = bvalue->valid; BDataValid[i] = bvalue->valid;
BDataText[i] = formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
BDataUnit[i] = formatValue(bvalue, *commonData).unit;
BDataFormat[i] = bvalue->getFormat(); // Unit of value
} }
// Optical warning by limit violation (unused) // Optical warning by limit violation (unused)
@@ -222,9 +254,27 @@ public:
setFlashLED(false); setFlashLED(false);
} }
if (showTruW != oldShowTruW) {
if (showTruW) {
wdHstry = pageData.boatHstry->hstryBufList.twdHstry;
wsHstry = pageData.boatHstry->hstryBufList.twsHstry;
} else {
wdHstry = pageData.boatHstry->hstryBufList.awdHstry;
wsHstry = pageData.boatHstry->hstryBufList.awsHstry;
}
wdHstry->getMetaData(wdName, wdFormat);
wsHstry->getMetaData(wsName, wsFormat);
wdMAX_VAL = wdHstry->getMaxVal();
bufSize = wdHstry->getCapacity();
wsBVal->setFormat(wsHstry->getFormat());
lastAddedIdx = wdHstry->getLastIdx();
oldShowTruW = showTruW;
}
// Identify buffer size and buffer start position for chart // Identify buffer size and buffer start position for chart
count = pageData.boatHstry.twdHstry->getCurrentSize(); count = wdHstry->getCurrentSize();
currIdx = pageData.boatHstry.twdHstry->getLastIdx(); currIdx = wdHstry->getLastIdx();
numAddedBufVals = (currIdx - lastAddedIdx + bufSize) % bufSize; // Number of values added to buffer since last display numAddedBufVals = (currIdx - lastAddedIdx + bufSize) % bufSize; // Number of values added to buffer since last display
if (dataIntv != oldDataIntv || count == 1) { if (dataIntv != oldDataIntv || count == 1) {
// new data interval selected by user // new data interval selected by user
@@ -240,29 +290,25 @@ public:
bufStart = max(0, bufStart - numAddedBufVals); bufStart = max(0, bufStart - numAddedBufVals);
} }
} }
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Dataset: count: %d, TWD: %.0f, TWS: %.1f, TWD_valid? %d, intvBufSize: %d, numWndVals: %d, bufStart: %d, numAddedBufVals: %d, lastIdx: %d, old: %d, act: %d", logger->logDebug(GwLog::DEBUG, "PageWindPlot Dataset: count: %d, xWD: %.1f, xWS: %.2f, xWD_valid? %d, intvBufSize: %d, numWndVals: %d, bufStart: %d, numAddedBufVals: %d, lastIdx: %d, wind source: %s",
count, pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg, pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384, BDataValid[0], count, wdHstry->getLast() / 1000.0 * radToDeg, wsHstry->getLast() / 1000.0 * 1.94384, BDataValid[0], intvBufSize, numWndVals, bufStart, numAddedBufVals, wdHstry->getLastIdx(),
intvBufSize, numWndVals, bufStart, numAddedBufVals, pageData.boatHstry.twdHstry->getLastIdx(), oldDataIntv, dataIntv); showTruW ? "True" : "App");
// Set wndCenter from 1st real buffer value // Set wndCenter from 1st real buffer value
if (wndCenter == INT_MIN || (wndCenter == 0 && count == 1)) { if (wndCenter == INT_MAX || (wndCenter == 0 && count == 1)) {
midWndDir = pageData.boatHstry.twdHstry->getMid(numWndVals); wndCenter = getCntr(*wdHstry, numWndVals);
if (midWndDir != INT16_MIN) { logger->logDebug(GwLog::DEBUG, "PageWindPlot Range Init: count: %d, xWD: %.1f, wndCenter: %d, diffRng: %d, chrtRng: %d, Min: %.0f, Max: %.0f", count, wdHstry->getLast() / 1000.0 * radToDeg,
midWndDir = midWndDir / 1000.0 * radToDeg; wndCenter, diffRng, chrtRng, wdHstry->getMin(numWndVals) / 1000.0 * radToDeg, wdHstry->getMax(numWndVals) / 1000.0 * radToDeg);
wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value
} else {
wndCenter = 0;
}
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Range Init: count: %d, TWD: %.0f, wndCenter: %d, diffRng: %d, chrtRng: %d", count, pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg,
wndCenter, diffRng, chrtRng);
} else { } else {
// check and adjust range between left, center, and right chart limit // check and adjust range between left, center, and right chart limit
diffRng = getRng(*pageData.boatHstry.twdHstry, wndCenter, numWndVals); diffRng = getRng(*wdHstry, wndCenter, numWndVals);
diffRng = (diffRng == INT16_MIN ? 0 : diffRng); diffRng = (diffRng == wdMAX_VAL ? 0 : diffRng);
if (diffRng > chrtRng) { if (diffRng > chrtRng) {
chrtRng = int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10; // Round up to next 10 degree value chrtRng = int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10; // Round up to next 10 degree value
} else if (diffRng + 10 < chrtRng) { // Reduce chart range for higher resolution if possible } else if (diffRng + 10 < chrtRng) { // Reduce chart range for higher resolution if possible
chrtRng = max(dfltRng, int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10); chrtRng = max(dfltRng, int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10);
logger->logDebug(GwLog::DEBUG, "PageWindPlot Range adjust: wndCenter: %d, diffRng: %d, chrtRng: %d, Min: %.0f, Max: %.0f", wndCenter, diffRng, chrtRng,
wdHstry->getMin(numWndVals) / 1000.0 * radToDeg, wdHstry->getMax(numWndVals) / 1000.0 * radToDeg);
} }
} }
chrtScl = float(width) / float(chrtRng) / 2.0; // Chart scale: pixels per degree chrtScl = float(width) / float(chrtRng) / 2.0; // Chart scale: pixels per degree
@@ -277,38 +323,38 @@ public:
//*********************************************************************** //***********************************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, width, height); // Set partial update epd->setPartialWindow(0, 0, width, height); // Set partial update
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
// chart lines // chart lines
getdisplay().fillRect(0, yOffset, width, 2, commonData->fgcolor); epd->fillRect(0, yOffset, width, 2, commonData->fgcolor);
getdisplay().fillRect(xCenter, yOffset, 1, cHeight, commonData->fgcolor); epd->fillRect(xCenter, yOffset, 1, cHeight, commonData->fgcolor);
// chart labels // chart labels
char sWndLbl[4]; // char buffer for Wind angle label char sWndLbl[4]; // char buffer for Wind angle label
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(xCenter - 88, yOffset - 3); epd->setCursor(xCenter - 88, yOffset - 3);
getdisplay().print("TWD"); // Wind data name epd->print(wdName); // Wind data name
snprintf(sWndLbl, 4, "%03d", (wndCenter < 0) ? (wndCenter + 360) : wndCenter); snprintf(sWndLbl, 4, "%03d", (wndCenter < 0) ? (wndCenter + 360) : wndCenter);
drawTextCenter(xCenter, yOffset - 11, sWndLbl); drawTextCenter(xCenter, yOffset - 11, sWndLbl);
getdisplay().drawCircle(xCenter + 25, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol epd->drawCircle(xCenter + 25, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
getdisplay().drawCircle(xCenter + 25, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol epd->drawCircle(xCenter + 25, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
getdisplay().setCursor(1, yOffset - 3); epd->setCursor(1, yOffset - 3);
snprintf(sWndLbl, 4, "%03d", (wndLeft < 0) ? (wndLeft + 360) : wndLeft); snprintf(sWndLbl, 4, "%03d", (wndLeft < 0) ? (wndLeft + 360) : wndLeft);
getdisplay().print(sWndLbl); // Wind left value epd->print(sWndLbl); // Wind left value
getdisplay().drawCircle(46, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol epd->drawCircle(46, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
getdisplay().drawCircle(46, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol epd->drawCircle(46, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
getdisplay().setCursor(width - 50, yOffset - 3); epd->setCursor(width - 50, yOffset - 3);
snprintf(sWndLbl, 4, "%03d", (wndRight < 0) ? (wndRight + 360) : wndRight); snprintf(sWndLbl, 4, "%03d", (wndRight < 0) ? (wndRight + 360) : wndRight);
getdisplay().print(sWndLbl); // Wind right value epd->print(sWndLbl); // Wind right value
getdisplay().drawCircle(width - 5, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol epd->drawCircle(width - 5, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
getdisplay().drawCircle(width - 5, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol epd->drawCircle(width - 5, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
if (pageData.boatHstry.twdHstry->getMax() == pageData.boatHstry.twdHstry->getMinVal()) { if (wdHstry->getMax() == wdMAX_VAL) {
// only <INT16_MIN> values in buffer -> no valid wind data available // only <MAX_VAL> values in buffer -> no valid wind data available
wndDataValid = false; wndDataValid = false;
} else if (!BDataValid[0]) { } else if (!BDataValid[0] && !simulation) {
// currently no valid TWD data available // currently no valid xWD data available and no simulation mode
numNoData++; numNoData++;
wndDataValid = true; wndDataValid = true;
if (numNoData > 3) { if (numNoData > 3) {
@@ -323,19 +369,18 @@ public:
//*********************************************************************** //***********************************************************************
if (wndDataValid) { if (wndDataValid) {
for (int i = 0; i < (numWndVals / dataIntv); i++) { for (int i = 0; i < (numWndVals / dataIntv); i++) {
chrtVal = static_cast<int>(pageData.boatHstry.twdHstry->get(bufStart + (i * dataIntv))); // show the latest wind values in buffer; keep 1st value constant in a rolling buffer chrtVal = static_cast<int>(wdHstry->get(bufStart + (i * dataIntv))); // show the latest wind values in buffer; keep 1st value constant in a rolling buffer
if (chrtVal == INT16_MIN) { if (chrtVal == wdMAX_VAL) {
chrtPrevVal = INT16_MIN; chrtPrevVal = wdMAX_VAL;
} else { } else {
chrtVal = static_cast<int>((chrtVal / 1000.0 * radToDeg) + 0.5); // Convert to degrees and round chrtVal = static_cast<int>((chrtVal / 1000.0 * radToDeg) + 0.5); // Convert to degrees and round
x = ((chrtVal - wndLeft + 360) % 360) * chrtScl; x = ((chrtVal - wndLeft + 360) % 360) * chrtScl;
y = yOffset + cHeight - i; // Position in chart area y = yOffset + cHeight - i; // Position in chart area
// if (i >= (numWndVals / dataIntv) - 10) if (i >= (numWndVals / dataIntv) - 1) // log chart data of 1 line (adjust for test purposes)
if (i >= (numWndVals / dataIntv) - 1) logger->logDebug(GwLog::DEBUG, "PageWindPlot Chart: i: %d, chrtVal: %d, bufStart: %d, count: %d, linesToShow: %d", i, chrtVal, bufStart, count, (numWndVals / dataIntv));
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Chart: i: %d, chrtVal: %d, bufStart: %d, count: %d, linesToShow: %d", i, chrtVal, bufStart, count, (numWndVals / dataIntv));
if ((i == 0) || (chrtPrevVal == INT16_MIN)) { if ((i == 0) || (chrtPrevVal == wdMAX_VAL)) {
// just a dot for 1st chart point or after some invalid values // just a dot for 1st chart point or after some invalid values
prevX = x; prevX = x;
prevY = y; prevY = y;
@@ -347,15 +392,15 @@ public:
if (((chrtPrevVal180 >= -180) && (chrtPrevVal180 < -90) && (chrtVal180 > 90)) || ((chrtPrevVal180 <= 179) && (chrtPrevVal180 > 90) && chrtVal180 <= -90)) { if (((chrtPrevVal180 >= -180) && (chrtPrevVal180 < -90) && (chrtVal180 > 90)) || ((chrtPrevVal180 <= 179) && (chrtPrevVal180 > 90) && chrtVal180 <= -90)) {
// If current value crosses chart borders compared to previous value, split line // If current value crosses chart borders compared to previous value, split line
int xSplit = (((chrtPrevVal180 > 0 ? wndRight : wndLeft) - wndLeft + 360) % 360) * chrtScl; int xSplit = (((chrtPrevVal180 > 0 ? wndRight : wndLeft) - wndLeft + 360) % 360) * chrtScl;
getdisplay().drawLine(prevX, prevY, xSplit, y, commonData->fgcolor); epd->drawLine(prevX, prevY, xSplit, y, commonData->fgcolor);
getdisplay().drawLine(prevX, prevY - 1, ((xSplit != prevX) ? xSplit : xSplit - 1), ((xSplit != prevX) ? y - 1 : y), commonData->fgcolor); epd->drawLine(prevX, prevY - 1, ((xSplit != prevX) ? xSplit : xSplit - 1), ((xSplit != prevX) ? y - 1 : y), commonData->fgcolor);
prevX = (((chrtVal180 > 0 ? wndRight : wndLeft) - wndLeft + 360) % 360) * chrtScl; prevX = (((chrtVal180 > 0 ? wndRight : wndLeft) - wndLeft + 360) % 360) * chrtScl;
} }
} }
// Draw line with 2 pixels width + make sure vertical line are drawn correctly // Draw line with 2 pixels width + make sure vertical line are drawn correctly
getdisplay().drawLine(prevX, prevY, x, y, commonData->fgcolor); epd->drawLine(prevX, prevY, x, y, commonData->fgcolor);
getdisplay().drawLine(prevX, prevY - 1, ((x != prevX) ? x : x - 1), ((x != prevX) ? y - 1 : y), commonData->fgcolor); epd->drawLine(prevX, prevY - 1, ((x != prevX) ? x : x - 1), ((x != prevX) ? y - 1 : y), commonData->fgcolor);
chrtPrevVal = chrtVal; chrtPrevVal = chrtVal;
prevX = x; prevX = x;
prevY = y; prevY = y;
@@ -364,41 +409,27 @@ public:
if (i >= (cHeight - 1)) { if (i >= (cHeight - 1)) {
oldDataIntv = 0; // force reset of buffer start and number of values to show in next display loop oldDataIntv = 0; // force reset of buffer start and number of values to show in next display loop
int minWndDir = pageData.boatHstry.twdHstry->getMin(numWndVals) / 1000.0 * radToDeg; int minWndDir = wdHstry->getMin(numWndVals) / 1000.0 * radToDeg;
int maxWndDir = pageData.boatHstry.twdHstry->getMax(numWndVals) / 1000.0 * radToDeg; int maxWndDir = wdHstry->getMax(numWndVals) / 1000.0 * radToDeg;
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot FreeTop: Minimum: %d, Maximum: %d, OldwndCenter: %d", minWndDir, maxWndDir, wndCenter); logger->logDebug(GwLog::DEBUG, "PageWindPlot FreeTop: Minimum: %d, Maximum: %d, OldwndCenter: %d", minWndDir, maxWndDir, wndCenter);
// if ((minWndDir + 540 >= wndCenter + 540) || (maxWndDir + 540 <= wndCenter + 540)) { // if (((minWndDir - wndCenter >= 0) && (minWndDir - wndCenter < 180)) || ((maxWndDir - wndCenter <= 0) && (maxWndDir - wndCenter >=180))) {
if (((minWndDir - wndCenter >= 0) && (minWndDir - wndCenter < 180)) || ((maxWndDir - wndCenter <= 0) && (maxWndDir - wndCenter >=180))) { if ((wndRight > wndCenter && (minWndDir >= wndCenter && minWndDir <= wndRight)) || (wndRight <= wndCenter && (minWndDir >= wndCenter || minWndDir <= wndRight)) || (wndLeft < wndCenter && (maxWndDir <= wndCenter && maxWndDir >= wndLeft)) || (wndLeft >= wndCenter && (maxWndDir <= wndCenter || maxWndDir >= wndLeft))) {
// Check if all wind value are left or right of center value -> optimize chart range // Check if all wind value are left or right of center value -> optimize chart center
midWndDir = pageData.boatHstry.twdHstry->getMid(numWndVals) / 1000.0 * radToDeg; wndCenter = getCntr(*wdHstry, numWndVals);
if (midWndDir != INT16_MIN) {
wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value
}
} }
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot FreeTop: cHeight: %d, bufStart: %d, numWndVals: %d, wndCenter: %d", cHeight, bufStart, numWndVals, wndCenter); logger->logDebug(GwLog::DEBUG, "PageWindPlot FreeTop: cHeight: %d, bufStart: %d, numWndVals: %d, wndCenter: %d", cHeight, bufStart, numWndVals, wndCenter);
break; break;
} }
} }
} else { // Print wind speed value
// No valid data available
LOG_DEBUG(GwLog::LOG, "PageWindPlot: No valid data available");
getdisplay().setFont(&Ubuntu_Bold10pt8b);
getdisplay().fillRect(xCenter - 33, height / 2 - 20, 66, 24, commonData->bgcolor); // Clear area for message
drawTextCenter(xCenter, height / 2 - 10, "No data");
}
// Print TWS value
if (showTWS) {
int currentZone; int currentZone;
static int lastZone = 0; static int lastZone = 0;
static bool flipTws = false; static bool flipTws = false;
int xPosTws; int xPosTws;
static const int yPosTws = yOffset + 40; static const int yPosTws = yOffset + 40;
twsValue = pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384; // TWS value in knots xPosTws = flipTws ? 20 : width - 145;
xPosTws = flipTws ? 20 : width - 138;
currentZone = (y >= yPosTws - 38) && (y <= yPosTws + 6) && (x >= xPosTws - 4) && (x <= xPosTws + 146) ? 1 : 0; // Define current zone for TWS value currentZone = (y >= yPosTws - 38) && (y <= yPosTws + 6) && (x >= xPosTws - 4) && (x <= xPosTws + 146) ? 1 : 0; // Define current zone for TWS value
if (currentZone != lastZone) { if (currentZone != lastZone) {
// Only flip when x moves to a different zone // Only flip when x moves to a different zone
@@ -409,39 +440,49 @@ public:
} }
lastZone = currentZone; lastZone = currentZone;
getdisplay().fillRect(xPosTws - 4, yPosTws - 38, 142, 44, commonData->bgcolor); // Clear area for TWS value wsValue = wsHstry->getLast();
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b); wsBVal->value = wsValue / 1000.0; // temp variable to retreive data unit from OBP60Formater
getdisplay().setCursor(xPosTws, yPosTws); wsBVal->valid = (static_cast<uint16_t>(wsValue) != wsHstry->getMinVal());
if (!BDataValid[1]) { String swsValue = commonData->fmt->formatValue(wsBVal, *commonData).svalue; // value (string)
getdisplay().print("--.-"); wsUnit = commonData->fmt->formatValue(wsBVal, *commonData).unit; // Unit of value
epd->fillRect(xPosTws - 4, yPosTws - 38, 142, 44, commonData->bgcolor); // Clear area for TWS value
epd->setFont(&DSEG7Classic_BoldItalic16pt7b);
epd->setCursor(xPosTws, yPosTws);
epd->print(swsValue); // Value
/* if (!wsBVal->valid) {
epd->print("--.-");
} else { } else {
double dbl = BDataValue[1] * 3.6 / 1.852; wsValue = wsValue / 10.0 * 1.94384; // Wind speed value in knots
if (dbl < 10.0) { if (wsValue < 10.0) {
getdisplay().printf("!%3.1f", dbl); // Value, round to 1 decimal epd->printf("!%3.1f", wsValue); // Value, round to 1 decimal
} else { } else {
getdisplay().printf("%4.1f", dbl); // Value, round to 1 decimal epd->printf("%4.1f", wsValue); // Value, round to 1 decimal
} }
} } */
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(xPosTws + 82, yPosTws - 14); epd->setCursor(xPosTws + 82, yPosTws - 14);
// getdisplay().print("TWS"); // Name epd->print(wsName); // Name
getdisplay().print(BDataName[1]); // Name epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setCursor(xPosTws + 82, yPosTws + 1);
// getdisplay().setCursor(xPosTws + 78, yPosTws + 1); epd->print(wsUnit); // Unit
getdisplay().setCursor(xPosTws + 82, yPosTws + 1);
// getdisplay().printf(" kn"); // Unit } else {
getdisplay().print(BDataUnit[1]); // Unit // No valid data available
LOG_DEBUG(GwLog::LOG, "PageWindPlot: No valid data available");
epd->setFont(&Ubuntu_Bold10pt8b);
epd->fillRect(xCenter - 33, height / 2 - 20, 66, 24, commonData->bgcolor); // Clear area for message
drawTextCenter(xCenter, height / 2 - 10, "No data");
} }
// chart Y axis labels; print at last to overwrite potential chart lines in label area // chart Y axis labels; print at last to overwrite potential chart lines in label area
int yPos; int yPos;
int chrtLbl; int chrtLbl;
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
for (int i = 1; i <= 3; i++) { for (int i = 1; i <= 3; i++) {
yPos = yOffset + (i * 60); yPos = yOffset + (i * 60);
getdisplay().fillRect(0, yPos, width, 1, commonData->fgcolor); epd->fillRect(0, yPos, width, 1, commonData->fgcolor);
getdisplay().fillRect(0, yPos - 8, 24, 16, commonData->bgcolor); // Clear small area to remove potential chart lines epd->fillRect(0, yPos - 8, 24, 16, commonData->bgcolor); // Clear small area to remove potential chart lines
getdisplay().setCursor(1, yPos + 4); epd->setCursor(1, yPos + 4);
if (count >= intvBufSize) { if (count >= intvBufSize) {
// Calculate minute value for label // Calculate minute value for label
chrtLbl = ((i - 1 + (prevY < yOffset + 30)) * dataIntv) * -1; // change label if last data point is more than 30 lines (= seconds) from chart line chrtLbl = ((i - 1 + (prevY < yOffset + 30)) * dataIntv) * -1; // change label if last data point is more than 30 lines (= seconds) from chart line
@@ -449,9 +490,10 @@ public:
int j = 3 - i; int j = 3 - i;
chrtLbl = (int((((numWndVals / dataIntv) - 50) * dataIntv / 60) + 1) - (j * dataIntv)) * -1; // 50 lines left below last chart line chrtLbl = (int((((numWndVals / dataIntv) - 50) * dataIntv / 60) + 1) - (j * dataIntv)) * -1; // 50 lines left below last chart line
} }
getdisplay().printf("%3d", chrtLbl); // Wind value label epd->printf("%3d", chrtLbl); // Wind value label
} }
logger->logDebug(GwLog::DEBUG, "PageWindPlot time: %ld", millis() - timer);
return PAGE_UPDATE; return PAGE_UPDATE;
}; };
}; };
@@ -460,20 +502,18 @@ static Page* createPage(CommonData& common)
{ {
return new PageWindPlot(common); return new PageWindPlot(common);
} }
/**
* with the code below we make this page known to the PageTask /* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config * we give it a type (name) that can be selected in the config
* we define which function is to be called * we define which function is to be called
* and we provide the number of user parameters we expect (0 here) * and we provide the number of user parameters we expect (0 here)
* and will will provide the names of the fixed values we need * and will will provide the names of the fixed values we need */
*/
PageDescription registerPageWindPlot( PageDescription registerPageWindPlot(
"WindPlot", // Page name "WindPlot", // Page name
createPage, // Action createPage, // Action
0, // Number of bus values depends on selection in Web configuration 0, // Number of bus values depends on selection in Web configuration
{ "TWD", "TWS" }, // Bus values we need in the page { "TWD", "AWD"}, // Bus values we need in the page
// {}, // Bus values we need in the page
true // Show display header on/off true // Show display header on/off
); );
#endif #endif

View File

@@ -1,21 +1,30 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h" #include "BoatDataCalibration.h"
#endif
class PageWindRose : public Page class PageWindRose : public Page
{ {
int16_t lp = 80; // Pointer length private:
String lengthformat;
int16_t lp = 80; // Pointer length
public: public:
PageWindRose(CommonData &common){ PageWindRose(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageWindRose"); logger->logDebug(GwLog::LOG, "Instantiate PageWindRose");
// Get config data
String lengthformat = config->getString(config->lengthFormat);
} }
// Key functions // Key functions
virtual int handleKey(int key){ int handleKey(int key) {
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -24,222 +33,179 @@ public:
return key; return key;
} }
int displayPage(PageData &pageData){ void displayNew(PageData &pageData) {
GwConfigHandler *config = commonData->config; #ifdef BOARD_OBP60S3
GwLog *logger = commonData->logger; // Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
static String svalue1old = ""; int displayPage(PageData &pageData) {
static String unit1old = "";
static String svalue2old = "";
static String unit2old = "";
static String svalue3old = "";
static String unit3old = "";
static String svalue4old = "";
static String unit4old = "";
static String svalue5old = "";
static String unit5old = "";
static String svalue6old = "";
static String unit6old = "";
// Get config data // storage for hold valued
String lengthformat = config->getString(config->lengthFormat); static FormattedData bvf_awa_old;
bool simulation = config->getBool(config->useSimuData); static FormattedData bvf_aws_old;
bool holdvalues = config->getBool(config->holdvalues); static FormattedData bvf_twd_old;
String flashLED = config->getString(config->flashLED); static FormattedData bvf_tws_old;
String backlightMode = config->getString(config->backlight); static FormattedData bvf_dbt_old;
static FormattedData bvf_stw_old;
// Get boat value for AWA // Get boat value for AWA
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) GwApi::BoatValue *bv_awa = pageData.values[0]; // First element in list
String name1 = xdrDelete(bvalue1->getName()); // Value name String name_awa = xdrDelete(bv_awa->getName(), 6); // get name without prefix and limit length
name1 = name1.substring(0, 6); // String length limit for value name #ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bv_awa, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit #endif
bool valid1 = bvalue1->valid; // Valid information FormattedData bvf_awa = commonData->fmt->formatValue(bv_awa, *commonData);
value1 = formatValue(bvalue1, *commonData).value;// Format only nesaccery for simulation data for pointer if (bv_awa->valid) { // Save formatted data for hold feature
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places bvf_awa_old = bvf_awa;
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
if(valid1 == true){
svalue1old = svalue1; // Save old value
unit1old = unit1; // Save old unit
} }
// Get boat value for AWS // Get boat value for AWS
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list GwApi::BoatValue *bv_aws = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name String name_aws = xdrDelete(bv_aws->getName(), 6); // get name without prefix and limit length
name2 = name2.substring(0, 6); // String length limit for value name #ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bv_aws, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit #endif
bool valid2 = bvalue2->valid; // Valid information FormattedData bvf_aws = commonData->fmt->formatValue(bv_aws, *commonData);
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places if (bv_aws->valid) { // Save formatted data for hold feature
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value bvf_aws_old = bvf_aws;
if(valid2 == true){
svalue2old = svalue2; // Save old value
unit2old = unit2; // Save old unit
} }
// Get boat value for TWD // Get boat value for TWD
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list GwApi::BoatValue *bv_twd = pageData.values[2]; // Third element in list
String name3 = xdrDelete(bvalue3->getName()); // Value name String name_twd = xdrDelete(bv_twd->getName(), 6); // get name without prefix and limit length
name3 = name3.substring(0, 6); // String length limit for value name #ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bv_twd, logger); // Check if boat data value is to be calibrated
double value3 = bvalue3->value; // Value as double in SI unit #endif
bool valid3 = bvalue3->valid; // Valid information FormattedData bvf_twd = commonData->fmt->formatValue(bv_twd, *commonData);
String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places if (bv_twd->valid) { // Save formatted data for hold feature
String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value bvf_twd_old = bvf_twd;
if(valid3 == true){
svalue3old = svalue3; // Save old value
unit3old = unit3; // Save old unit
} }
// Get boat value for TWS // Get boat value for TWS
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Fourth element in list GwApi::BoatValue *bv_tws = pageData.values[3]; // Fourth element in list
String name4 = xdrDelete(bvalue4->getName()); // Value name String name_tws = xdrDelete(bv_tws->getName(), 6); // get name without prefix and limit length
name4 = name4.substring(0, 6); // String length limit for value name #ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bv_tws, logger); // Check if boat data value is to be calibrated
double value4 = bvalue4->value; // Value as double in SI unit #endif
bool valid4 = bvalue4->valid; // Valid information FormattedData bvf_tws = commonData->fmt->formatValue(bv_tws, *commonData);
String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places if (bv_tws->valid) { // Save formatted data for hold feature
String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value bvf_tws_old = bvf_tws;
if(valid4 == true){
svalue4old = svalue4; // Save old value
unit4old = unit4; // Save old unit
} }
// Get boat value for DBT // Get boat value for DBT
GwApi::BoatValue *bvalue5 = pageData.values[4]; // Fifth element in list GwApi::BoatValue *bv_dbt = pageData.values[4]; // Fifth element in list
String name5 = xdrDelete(bvalue5->getName()); // Value name String name_dbt = xdrDelete(bv_dbt->getName(), 6); // get name without prefix and limit length
name5 = name5.substring(0, 6); // String length limit for value name #ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue5, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bv_dbt, logger); // Check if boat data value is to be calibrated
double value5 = bvalue5->value; // Value as double in SI unit #endif
bool valid5 = bvalue5->valid; // Valid information FormattedData bvf_dbt = commonData->fmt->formatValue(bv_dbt, *commonData);
String svalue5 = formatValue(bvalue5, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places if (bv_dbt->valid) { // Save formatted data for hold feature
String unit5 = formatValue(bvalue5, *commonData).unit; // Unit of value bvf_dbt_old = bvf_dbt;
if(valid5 == true){
svalue5old = svalue5; // Save old value
unit5old = unit5; // Save old unit
} }
// Get boat value for STW // Get boat value for STW
GwApi::BoatValue *bvalue6 = pageData.values[5]; // Sixth element in list GwApi::BoatValue *bv_stw = pageData.values[5]; // Sixth element in list
String name6 = xdrDelete(bvalue6->getName()); // Value name String name_stw = xdrDelete(bv_stw->getName(), 6); // get name without prefix and limit length
name6 = name6.substring(0, 6); // String length limit for value name #ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue6, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bv_stw, logger); // Check if boat data value is to be calibrated
double value6 = bvalue6->value; // Value as double in SI unit #endif
bool valid6 = bvalue6->valid; // Valid information FormattedData bvf_stw = commonData->fmt->formatValue(bv_stw, *commonData);
String svalue6 = formatValue(bvalue6, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places if (bv_stw->valid) { // Save formatted data for hold feature
String unit6 = formatValue(bvalue6, *commonData).unit; // Unit of value bvf_stw_old = bvf_stw;
if(valid6 == true){
svalue6old = svalue6; // Save old value
unit6old = unit6; // Save old unit
} }
// Optical warning by limit violation (unused) // Log boat values
if(String(flashLED) == "Limit Violation"){ logger->logDebug(GwLog::LOG, "Drawing at PageWindRose, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f",
setBlinkingLED(false); name_awa.c_str(), bv_awa->value,
setFlashLED(false); name_aws.c_str(), bv_aws->value,
} name_twd.c_str(), bv_twd->value,
name_tws.c_str(), bv_tws->value,
// Logging boat values name_dbt.c_str(), bv_dbt->value,
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? name_stw.c_str(), bv_stw->value);
LOG_DEBUG(GwLog::LOG,"Drawing at PageWindRose, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4, name5.c_str(), value5, name6.c_str(), value6);
// Draw page // Draw page
//*********************************************************** // *********************************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height());
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
// Show values AWA // Show values AWA
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 65); epd->setCursor(10, 65);
getdisplay().print(svalue1); // Value epd->print(holdvalues ? bvf_awa_old.value : bvf_awa.value);
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 95); epd->setCursor(10, 95);
getdisplay().print(name1); // Name epd->print(name_awa);
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 115); epd->setCursor(10, 115);
getdisplay().print(" "); epd->print(" ");
if(holdvalues == false){ epd->print(holdvalues ? bvf_awa_old.unit : bvf_awa.unit);
getdisplay().print(unit1); // Unit
}
else{
getdisplay().print(unit1old); // Unit
}
// Horizintal separator left // Horizintal separator left
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor); epd->fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show values AWS // Show values AWS
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 270); epd->setCursor(10, 270);
getdisplay().print(svalue2); // Value epd->print(holdvalues ? bvf_aws_old.value : bvf_aws.value);
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 220); epd->setCursor(10, 220);
getdisplay().print(name2); // Name epd->print(name_aws);
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 190); epd->setCursor(10, 190);
getdisplay().print(" "); epd->print(" ");
if(holdvalues == false){ epd->print(holdvalues ? bvf_aws_old.unit : bvf_aws.unit);
getdisplay().print(unit2); // Unit
}
else{
getdisplay().print(unit2old); // Unit
}
// Show values TWD // Show value TWD
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 65); epd->setCursor(295, 65);
if(valid3 == true){ // TODO WTF? Der Formatter sollte das korrekt machen
getdisplay().print(abs(value3 * 180 / PI), 0); // Value if (bv_twd->valid) {
epd->print(abs(bv_twd->value * 180 / PI), 0); // Value
} }
else{ else {
getdisplay().print("---"); // Value epd->print(commonData->fmt->placeholder);
}
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 95);
getdisplay().print(name3); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 115);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit3); // Unit
}
else{
getdisplay().print(unit3old); // Unit
} }
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(335, 95);
epd->print(name_twd); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 115);
epd->print(" ");
epd->print(holdvalues ? bvf_twd_old.unit : bvf_twd.unit);
// Horizintal separator right // Horizintal separator right
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor); epd->fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show values TWS // Show values TWS
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 270); epd->setCursor(295, 270);
getdisplay().print(svalue4); // Value epd->print(name_tws);
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 220); epd->setCursor(335, 220);
getdisplay().print(name4); // Name epd->print(holdvalues ? bvf_tws_old.value : bvf_tws.value);
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 190); epd->setCursor(335, 190);
getdisplay().print(" "); epd->print(" ");
if(holdvalues == false){ epd->print(holdvalues ? bvf_tws_old.unit : bvf_tws.unit);
getdisplay().print(unit4); // Unit
}
else{
getdisplay().print(unit4old); // Unit
}
//******************************************************************************************* // *********************************************************************
// Draw wind rose // Draw wind rose
int rInstrument = 110; // Radius of grafic instrument int rInstrument = 110; // Radius of grafic instrument
float pi = 3.141592; float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle epd->fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument - 10, commonData->fgcolor); // Inner circle epd->fillCircle(200, 150, rInstrument - 10, commonData->fgcolor); // Inner circle
getdisplay().fillCircle(200, 150, rInstrument - 13, commonData->bgcolor); // Inner circle epd->fillCircle(200, 150, rInstrument - 13, commonData->bgcolor); // Inner circle
for(int i=0; i<360; i=i+10) for(int i=0; i<360; i=i+10)
{ {
@@ -247,37 +213,36 @@ public:
float x = 200 + (rInstrument-30)*sin(i/180.0*pi); // x-coordinate dots float x = 200 + (rInstrument-30)*sin(i/180.0*pi); // x-coordinate dots
float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate cots float y = 150 - (rInstrument-30)*cos(i/180.0*pi); // y-coordinate cots
const char *ii = ""; const char *ii = "";
switch (i) switch (i) {
{ case 0: ii="0"; break;
case 0: ii="0"; break; case 30 : ii="30"; break;
case 30 : ii="30"; break; case 60 : ii="60"; break;
case 60 : ii="60"; break; case 90 : ii="90"; break;
case 90 : ii="90"; break; case 120 : ii="120"; break;
case 120 : ii="120"; break; case 150 : ii="150"; break;
case 150 : ii="150"; break; case 180 : ii="180"; break;
case 180 : ii="180"; break; case 210 : ii="210"; break;
case 210 : ii="210"; break; case 240 : ii="240"; break;
case 240 : ii="240"; break; case 270 : ii="270"; break;
case 270 : ii="270"; break; case 300 : ii="300"; break;
case 300 : ii="300"; break; case 330 : ii="330"; break;
case 330 : ii="330"; break; default: break;
default: break;
} }
// Print text centered on position x, y // Print text centered on position x, y
int16_t x1, y1; // Return values of getTextBounds int16_t x1, y1; // Return values of getTextBounds
uint16_t w, h; // Return values of getTextBounds uint16_t w, h; // Return values of getTextBounds
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string epd->getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
getdisplay().setCursor(x-w/2, y+h/2); epd->setCursor(x-w/2, y+h/2);
if(i % 30 == 0){ if (i % 30 == 0) {
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b); // TODO move out of loop
getdisplay().print(ii); epd->print(ii);
} }
// Draw sub scale with dots // Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*pi); float x1c = 200 + rInstrument*sin(i/180.0*pi);
float y1c = 150 - rInstrument*cos(i/180.0*pi); float y1c = 150 - rInstrument*cos(i/180.0*pi);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor); epd->fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*pi); float sinx=sin(i/180.0*pi);
float cosx=cos(i/180.0*pi); float cosx=cos(i/180.0*pi);
@@ -288,10 +253,10 @@ public:
float xx2 = +dx; float xx2 = +dx;
float yy1 = -(rInstrument-10); float yy1 = -(rInstrument-10);
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2), 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
} }
@@ -299,16 +264,16 @@ public:
// Draw wind pointer // Draw wind pointer
float startwidth = 8; // Start width of pointer float startwidth = 8; // Start width of pointer
if(valid2 == true || holdvalues == true || simulation == true){ if (bv_aws->valid || holdvalues || simulation) {
float sinx=sin(value1); // Wind direction float sinx = sin(bv_awa->value); // Wind direction
float cosx=cos(value1); float cosx = cos(bv_awa->value);
// Normal pointer // Normal pointer
// Pointer as triangle with center base 2*width // Pointer as triangle with center base 2*width
float xx1 = -startwidth; float xx1 = -startwidth;
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument-15); float yy2 = -(rInstrument-15);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
@@ -318,47 +283,42 @@ public:
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument-15); float iy1 = -(rInstrument-15);
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), epd->fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
} }
// Center circle // Center circle
getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor); epd->fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor); epd->fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
//******************************************************************************************* // *********************************************************************
// Show values DBT // Show value DBT
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b); epd->setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setCursor(160, 200); epd->setCursor(160, 200);
getdisplay().print(svalue5); // Value epd->print(holdvalues ? bvf_dbt_old.value : bvf_dbt.value);
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(190, 215); epd->setCursor(190, 215);
getdisplay().print(" "); epd->print(" ");
if(holdvalues == false){ epd->print(holdvalues ? bvf_dbt_old.unit : bvf_dbt.unit);
getdisplay().print(unit5); // Unit
}
else{
getdisplay().print(unit5old); // Unit
}
// Show values STW // Show value STW
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b); epd->setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setCursor(160, 130); epd->setCursor(160, 130);
getdisplay().print(svalue6); // Value epd->print(holdvalues ? bvf_stw_old.value : bvf_stw.value);
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(190, 90); epd->setCursor(190, 90);
getdisplay().print(" "); epd->print(" ");
if(holdvalues == false){ epd->print(holdvalues ? bvf_stw_old.unit : bvf_stw.unit);
getdisplay().print(unit6); // Unit
}
else{
getdisplay().print(unit6old); // Unit
}
return PAGE_UPDATE; return PAGE_UPDATE;
}; };
void leavePage(PageData &pageData) {
logger->logDebug(GwLog::LOG, "Leaving PageWindRose");
}
}; };
static Page *createPage(CommonData &common){ static Page *createPage(CommonData &common){

View File

@@ -1,21 +1,37 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#ifdef ENABLE_CALIBRATION
#include "BoatDataCalibration.h" #include "BoatDataCalibration.h"
#endif
class PageWindRoseFlex : public Page class PageWindRoseFlex : public Page
{ {
int16_t lp = 80; // Pointer length private:
String lengthformat;
int16_t lp = 80; // Pointer length
char source = 'A'; // data source (A)pparent | (T)rue
String ssource="App."; // String for Data Source
public: public:
PageWindRoseFlex(CommonData &common){ PageWindRoseFlex(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageWindRoseFlex"); logger->logDebug(GwLog::LOG, "Instantiate PageWindRoseFlex");
// Get config data
lengthformat = config->getString(config->lengthFormat);
}
void setupKeys() {
Page::setupKeys();
commonData->keydata[1].label = "SRC";
} }
// Key functions // Key functions
virtual int handleKey(int key){ int handleKey(int key) {
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -24,9 +40,17 @@ public:
return key; return key;
} }
int displayPage(PageData &pageData){ void displayNew(PageData &pageData) {
GwConfigHandler *config = commonData->config; #ifdef BOARD_OBP60S3
GwLog *logger = commonData->logger; // Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
static String svalue1old = ""; static String svalue1old = "";
static String unit1old = ""; static String unit1old = "";
@@ -41,206 +65,198 @@ public:
static String svalue6old = ""; static String svalue6old = "";
static String unit6old = ""; static String unit6old = "";
// Get config data GwApi::BoatValue *bvalue1; // Value 1 for angle
String lengthformat = config->getString(config->lengthFormat); GwApi::BoatValue *bvalue2; // Value 2 for speed
bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
// Get boat values #1 // Get boat value for wind angle (AWA/TWA), shown by pointer
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue) if (source == 'A') {
String name1 = xdrDelete(bvalue1->getName()); // Value name bvalue1 = pageData.values[4];
} else {
bvalue1 = pageData.values[6];
}
String name1 = bvalue1->getName().c_str(); // Value name
name1 = name1.substring(0, 6); // String length limit for value name name1 = name1.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
#endif
double value1 = bvalue1->value; // Value as double in SI unit double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information bool valid1 = bvalue1->valid; // Valid information
value1 = formatValue(bvalue1, *commonData).value;// Format only nesaccery for simulation data for pointer String svalue1 = commonData->fmt->formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String unit1 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value if (valid1 == true) {
if(valid1 == true){
svalue1old = svalue1; // Save old value svalue1old = svalue1; // Save old value
unit1old = unit1; // Save old unit unit1old = unit1; // Save old unit
} }
// Get boat values #2 // Get boat value for wind speed (AWS/TWS), shown in top left corner
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list if (source == 'A') {
String name2 = xdrDelete(bvalue2->getName()); // Value name bvalue2 =pageData.values[5];
} else {
bvalue2 = pageData.values[7];
}
String name2 = bvalue2->getName().c_str(); // Value name
name2 = name2.substring(0, 6); // String length limit for value name name2 = name2.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
#endif
double value2 = bvalue2->value; // Value as double in SI unit double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places if (simulation) {
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value value2 = 0.62731; // some random value
if(valid2 == true){ }
String svalue2 = commonData->fmt->formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = commonData->fmt->formatValue(bvalue2, *commonData).unit; // Unit of value
if (valid2 == true) {
svalue2old = svalue2; // Save old value svalue2old = svalue2; // Save old value
unit2old = unit2; // Save old unit unit2old = unit2; // Save old unit
} }
// Get boat values #3 // Get boat value for bottom left corner
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list GwApi::BoatValue *bvalue3 = pageData.values[0];
String name3 = xdrDelete(bvalue3->getName()); // Value name String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for value name name3 = name3.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
#endif
double value3 = bvalue3->value; // Value as double in SI unit double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue3 = commonData->fmt->formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value String unit3 = commonData->fmt->formatValue(bvalue3, *commonData).unit; // Unit of value
if(valid3 == true){ if(valid3 == true){
svalue3old = svalue3; // Save old value svalue3old = svalue3; // Save old value
unit3old = unit3; // Save old unit unit3old = unit3; // Save old unit
} }
// Get boat values #4 // Get boat value for top right corner
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Fourth element in list GwApi::BoatValue *bvalue4 = pageData.values[1];
String name4 = xdrDelete(bvalue4->getName()); // Value name String name4 = xdrDelete(bvalue4->getName()); // Value name
name4 = name4.substring(0, 6); // String length limit for value name name4 = name4.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
#endif
double value4 = bvalue4->value; // Value as double in SI unit double value4 = bvalue4->value; // Value as double in SI unit
bool valid4 = bvalue4->valid; // Valid information bool valid4 = bvalue4->valid; // Valid information
String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue4 = commonData->fmt->formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value String unit4 = commonData->fmt->formatValue(bvalue4, *commonData).unit; // Unit of value
if(valid4 == true){ if(valid4 == true){
svalue4old = svalue4; // Save old value svalue4old = svalue4; // Save old value
unit4old = unit4; // Save old unit unit4old = unit4; // Save old unit
} }
// Get boat values #5 // Get boat value for bottom right corner
GwApi::BoatValue *bvalue5 = pageData.values[4]; // Fifth element in list GwApi::BoatValue *bvalue5 = pageData.values[2];
String name5 = xdrDelete(bvalue5->getName()); // Value name String name5 = xdrDelete(bvalue5->getName()); // Value name
name5 = name5.substring(0, 6); // String length limit for value name name5 = name5.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue5, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue5, logger); // Check if boat data value is to be calibrated
#endif
double value5 = bvalue5->value; // Value as double in SI unit double value5 = bvalue5->value; // Value as double in SI unit
bool valid5 = bvalue5->valid; // Valid information bool valid5 = bvalue5->valid; // Valid information
String svalue5 = formatValue(bvalue5, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue5 = commonData->fmt->formatValue(bvalue5, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit5 = formatValue(bvalue5, *commonData).unit; // Unit of value String unit5 = commonData->fmt->formatValue(bvalue5, *commonData).unit; // Unit of value
if(valid5 == true){ if(valid5 == true){
svalue5old = svalue5; // Save old value svalue5old = svalue5; // Save old value
unit5old = unit5; // Save old unit unit5old = unit5; // Save old unit
} }
// Get boat values #5 // Get boat value for center
GwApi::BoatValue *bvalue6 = pageData.values[5]; // Sixth element in list GwApi::BoatValue *bvalue6 = pageData.values[3];
String name6 = xdrDelete(bvalue6->getName()); // Value name String name6 = xdrDelete(bvalue6->getName()); // Value name
name6 = name6.substring(0, 6); // String length limit for value name name6 = name6.substring(0, 6); // String length limit for value name
#ifdef ENABLE_CALIBRATION
calibrationData.calibrateInstance(bvalue6, logger); // Check if boat data value is to be calibrated calibrationData.calibrateInstance(bvalue6, logger); // Check if boat data value is to be calibrated
#endif
double value6 = bvalue6->value; // Value as double in SI unit double value6 = bvalue6->value; // Value as double in SI unit
bool valid6 = bvalue6->valid; // Valid information bool valid6 = bvalue6->valid; // Valid information
String svalue6 = formatValue(bvalue6, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places String svalue6 = commonData->fmt->formatValue(bvalue6, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit6 = formatValue(bvalue6, *commonData).unit; // Unit of value String unit6 = commonData->fmt->formatValue(bvalue6, *commonData).unit; // Unit of value
if(valid6 == true){ if(valid6 == true){
svalue6old = svalue6; // Save old value svalue6old = svalue6; // Save old value
unit6old = unit6; // Save old unit unit6old = unit6; // Save old unit
} }
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values // Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement? if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageWindRoseFlex, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4, name5.c_str(), value5, name6.c_str(), value6); logger->logDebug(GwLog::LOG, "Drawing at PageWindRoseFlex, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f, %s:%f", name1.c_str(), value1, name2.c_str(), value2, name3.c_str(), value3, name4.c_str(), value4, name5.c_str(), value5, name6.c_str(), value6);
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
// Show value 2 at position of value 1 (top left) // Show AWS or TWS top left
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 65); epd->setCursor(10, 65);
getdisplay().print(svalue2); // Value epd->print(svalue2); // Value
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 95); epd->setCursor(10, 95);
getdisplay().print(name2); // Name epd->print(name2); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 115); epd->setCursor(10, 115);
getdisplay().print(" "); epd->print(" ");
if(holdvalues == false){ epd->print(holdvalues ? unit2old : unit2);
getdisplay().print(unit2); // Unit
}
else{
getdisplay().print(unit2old); // Unit
}
// Horizintal separator left // Horizintal separator left
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor); epd->fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show value 3 at bottom left // Show value 3 (=first user-configured parameter) at bottom left
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 270); epd->setCursor(10, 270);
getdisplay().print(svalue3); // Value epd->print(svalue3); // Value
getdisplay().setFont(&Ubuntu_Bold12pt8b); epd->setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 220); epd->setCursor(10, 220);
getdisplay().print(name3); // Name epd->print(name3); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 190); epd->setCursor(10, 190);
getdisplay().print(" "); epd->print(" ");
if(holdvalues == false){ epd->print(holdvalues ? unit3old : unit3);
getdisplay().print(unit3); // Unit
}
else{
getdisplay().print(unit3old); // Unit
}
// Show value 4 at top right
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); // Show value 4 (=second user-configured parameter) at top right
getdisplay().setCursor(295, 65); epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(295, 65);
if(valid3 == true){ if(valid3 == true){
// getdisplay().print(abs(value3 * 180 / M_PI), 0); // Value epd->print(svalue4); // Value
getdisplay().print(svalue4); // Value
} }
else{ else{
getdisplay().print("---"); // Value epd->print(commonData->fmt->placeholder);
}
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 95);
getdisplay().print(name4); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 115);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit4); // Unit
}
else{
getdisplay().print(unit4old); // Unit
} }
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(335, 95);
epd->print(name4); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 115);
epd->print(" ");
epd->print(holdvalues ? unit4old : unit4);
// Horizintal separator right // Horizintal separator right
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor); epd->fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show value 5 at bottom right
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 270);
getdisplay().print(svalue5); // Value
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 220);
getdisplay().print(name5); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 190);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit5); // Unit
}
else{
getdisplay().print(unit5old); // Unit
}
// Show value 5 (=third user-configured parameter) at bottom right
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(295, 270);
epd->print(svalue5); // Value
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(335, 220);
epd->print(name5); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 190);
epd->print(" ");
epd->print(holdvalues ? unit5old : unit5);
//******************************************************************************************* //*******************************************************************************************
// Draw wind rose // Draw wind rose
int rInstrument = 110; // Radius of grafic instrument int rInstrument = 110; // Radius of grafic instrument
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle epd->fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument - 10, commonData->fgcolor); // Inner circle epd->fillCircle(200, 150, rInstrument - 10, commonData->fgcolor); // Inner circle
getdisplay().fillCircle(200, 150, rInstrument - 13, commonData->bgcolor); // Inner circle epd->fillCircle(200, 150, rInstrument - 13, commonData->bgcolor); // Inner circle
for(int i=0; i<360; i=i+10) for(int i=0; i<360; i=i+10)
{ {
@@ -267,17 +283,17 @@ public:
// Print text centered on position x, y // Print text centered on position x, y
int16_t x1, y1; // Return values of getTextBounds int16_t x1, y1; // Return values of getTextBounds
uint16_t w, h; // Return values of getTextBounds uint16_t w, h; // Return values of getTextBounds
getdisplay().getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string epd->getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
getdisplay().setCursor(x-w/2, y+h/2); epd->setCursor(x-w/2, y+h/2);
if(i % 30 == 0){ if(i % 30 == 0){
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().print(ii); epd->print(ii);
} }
// Draw sub scale with dots // Draw sub scale with dots
float x1c = 200 + rInstrument*sin(i/180.0*M_PI); float x1c = 200 + rInstrument*sin(i/180.0*M_PI);
float y1c = 150 - rInstrument*cos(i/180.0*M_PI); float y1c = 150 - rInstrument*cos(i/180.0*M_PI);
getdisplay().fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor); epd->fillCircle((int)x1c, (int)y1c, 2, commonData->fgcolor);
float sinx=sin(i/180.0*M_PI); float sinx=sin(i/180.0*M_PI);
float cosx=cos(i/180.0*M_PI); float cosx=cos(i/180.0*M_PI);
@@ -288,10 +304,10 @@ public:
float xx2 = +dx; float xx2 = +dx;
float yy1 = -(rInstrument-10); float yy1 = -(rInstrument-10);
float yy2 = -(rInstrument+10); float yy2 = -(rInstrument+10);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),commonData->fgcolor);
getdisplay().fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2), 200+(int)(cosx*xx1-sinx*yy2),150+(int)(sinx*xx1+cosx*yy2),
200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
} }
@@ -308,7 +324,7 @@ public:
float xx2 = startwidth; float xx2 = startwidth;
float yy1 = -startwidth; float yy1 = -startwidth;
float yy2 = -(rInstrument-15); float yy2 = -(rInstrument-15);
getdisplay().fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1), epd->fillTriangle(200+(int)(cosx*xx1-sinx*yy1),150+(int)(sinx*xx1+cosx*yy1),
200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1), 200+(int)(cosx*xx2-sinx*yy1),150+(int)(sinx*xx2+cosx*yy1),
200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer // Inverted pointer
@@ -318,37 +334,50 @@ public:
float ix2 = -endwidth; float ix2 = -endwidth;
float iy1 = -(rInstrument-15); float iy1 = -(rInstrument-15);
float iy2 = -endwidth; float iy2 = -endwidth;
getdisplay().fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1), epd->fillTriangle(200+(int)(cosx*ix1-sinx*iy1),150+(int)(sinx*ix1+cosx*iy1),
200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1), 200+(int)(cosx*ix2-sinx*iy1),150+(int)(sinx*ix2+cosx*iy1),
200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor); 200+(int)(cosx*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
} }
// Center circle // Center circle
getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor); epd->fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor); epd->fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
//******************************************************************************************* //*******************************************************************************************
// Show value6, so that it does not collide with the wind pointer // Show value6 (=fourth user-configured parameter) and ssource, so that they do not collide with the wind pointer
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b); if (cos(value1) > 0) {
if (cos(value1) > 0){ // pointer points upwards
getdisplay().setCursor(160, 200); epd->setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().print(svalue6); // Value epd->setCursor(160, 200);
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->print(svalue6); // Value
getdisplay().setCursor(190, 215); epd->setFont(&Ubuntu_Bold8pt8b);
} else{ epd->setCursor(190, 215);
getdisplay().setCursor(160, 130); epd->print(" ");
getdisplay().print(svalue6); // Value epd->print(holdvalues ? unit6old : unit6);
getdisplay().setFont(&Ubuntu_Bold8pt8b); if (sin(value1) > 0) {
getdisplay().setCursor(190, 90); epd->setCursor(160, 130);
} else {
epd->setCursor(220, 130);
} }
getdisplay().print(" "); epd->print(ssource); // true or app.
if(holdvalues == false){ }
getdisplay().print(unit6); // Unit else {
} // pointer points downwards
else{ epd->setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().print(unit6old); // Unit epd->setCursor(160, 130);
epd->print(svalue6);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(190, 90);
epd->print(" ");
epd->print(holdvalues ? unit6old : unit6);
if (sin(value1) > 0) {
epd->setCursor(160, 200);
} else {
epd->setCursor(220, 200);
} }
epd->print(ssource); //true or app.
}
return PAGE_UPDATE; return PAGE_UPDATE;
}; };
@@ -361,13 +390,14 @@ static Page *createPage(CommonData &common){
* with the code below we make this page known to the PageTask * with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config * we give it a type (name) that can be selected in the config
* we define which function is to be called * we define which function is to be called
* and we provide the number of user parameters we expect (0 here) * and we provide the number of user parameters we expect (4 here)
* and will will provide the names of the fixed values we need * and will will provide the names of the fixed values we need
*/ */
PageDescription registerPageWindRoseFlex( PageDescription registerPageWindRoseFlex(
"WindRoseFlex", // Page name "WindRoseFlex", // Page name
createPage, // Action createPage, // Action
6, // Number of bus values depends on selection in Web configuration; was zero 4, // Number of bus values depends on selection in Web configuration
{"AWA", "AWS", "TWA", "TWS"}, // fixed values we need in the page. They are inserted AFTER the web-configured values.
true // Show display header on/off true // Show display header on/off
); );

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 #if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h" #include "Pagedata.h"
@@ -28,15 +29,18 @@ static unsigned char ship_bits[] PROGMEM = {
class PageXTETrack : public Page class PageXTETrack : public Page
{ {
bool simulation = false; private:
bool holdvalues = false; String trackStep;
double seg_step;
public: public:
PageXTETrack(CommonData &common){ PageXTETrack(CommonData &common) : Page(common)
commonData = &common; {
common.logger->logDebug(GwLog::LOG,"Instantiate PageXTETrack"); logger->logDebug(GwLog::LOG, "Instantiate PageXTETrack");
simulation = common.config->getBool(common.config->useSimuData);
holdvalues = common.config->getBool(common.config->holdvalues); // Get config data
String trackStep = config->getString(config->trackStep);
seg_step = trackStep.toDouble() * M_PI / 180;
} }
void drawSegment(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, void drawSegment(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,
@@ -45,18 +49,18 @@ class PageXTETrack : public Page
if (fill == true) { if (fill == true) {
// no primitive for quadrangular object // no primitive for quadrangular object
// we create it from 2 triangles // we create it from 2 triangles
getdisplay().fillTriangle(x0, y0, x1, y1, x3, y3, color); epd->fillTriangle(x0, y0, x1, y1, x3, y3, color);
getdisplay().fillTriangle(x1, y1, x2, y2, x3, y3, color); epd->fillTriangle(x1, y1, x2, y2, x3, y3, color);
} else { } else {
// draw outline // draw outline
getdisplay().drawLine(x0, y0, x1, y1, color); epd->drawLine(x0, y0, x1, y1, color);
getdisplay().drawLine(x1, y1, x2, y2, color); epd->drawLine(x1, y1, x2, y2, color);
getdisplay().drawLine(x2, y2, x3, y3, color); epd->drawLine(x2, y2, x3, y3, color);
getdisplay().drawLine(x3, y3, x0, y0, color); epd->drawLine(x3, y3, x0, y0, color);
} }
} }
virtual int handleKey(int key){ int handleKey(int key) {
// Code for keylock // Code for keylock
if(key == 11){ if(key == 11){
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@@ -65,96 +69,93 @@ class PageXTETrack : public Page
return key; return key;
} }
int displayPage(PageData &pageData){ void displayNew(PageData &pageData) {
GwConfigHandler *config = commonData->config; #ifdef BOARD_OBP60S3
GwLog *logger = commonData->logger; // Clear optical warning
if (flashLED == "Limit Violation") {
// Get config data
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
String trackStep = config->getString(config->trackStep);
double seg_step = trackStep.toFloat() * PI / 180;
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false); setBlinkingLED(false);
setFlashLED(false); setFlashLED(false);
} }
#endif
};
int displayPage(PageData &pageData) {
// Logging boat values // Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageXTETrack"); logger->logDebug(GwLog::LOG, "Drawing at PageXTETrack");
// Draw page // Draw page
//*********************************************************** //***********************************************************
// Set display in partial refresh mode // Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
getdisplay().setTextColor(commonData->fgcolor); epd->setTextColor(commonData->fgcolor);
// descriptions // descriptions
getdisplay().setFont(&Ubuntu_Bold8pt8b); epd->setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(50, 188); epd->setCursor(50, 188);
getdisplay().print("Cross-track error"); epd->print("Cross-track error");
getdisplay().setCursor(270, 188); epd->setCursor(270, 188);
getdisplay().print("Track"); epd->print("Track");
getdisplay().setCursor(45, 275); epd->setCursor(45, 275);
getdisplay().print("Distance to waypoint"); epd->print("Distance to waypoint");
getdisplay().setCursor(260, 275); epd->setCursor(260, 275);
getdisplay().print("Bearing"); epd->print("Bearing");
// values // values
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b); epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
int16_t x, y; int16_t x, y;
uint16_t w, h; uint16_t w, h;
GwApi::BoatValue *bv_xte = pageData.values[0]; // XTE GwApi::BoatValue *bv_xte = pageData.values[0]; // XTE
String sval_xte = formatValue(bv_xte, *commonData).svalue; String sval_xte = commonData->fmt->formatValue(bv_xte, *commonData).svalue;
getdisplay().getTextBounds(sval_xte, 0, 0, &x, &y, &w, &h); epd->getTextBounds(sval_xte, 0, 0, &x, &y, &w, &h);
getdisplay().setCursor(160-w, 170); epd->setCursor(160-w, 170);
getdisplay().print(sval_xte); epd->print(sval_xte);
GwApi::BoatValue *bv_cog = pageData.values[1]; // COG GwApi::BoatValue *bv_cog = pageData.values[1]; // COG
String sval_cog = formatValue(bv_cog, *commonData).svalue; String sval_cog = commonData->fmt->formatValue(bv_cog, *commonData).svalue;
getdisplay().getTextBounds(sval_cog, 0, 0, &x, &y, &w, &h); epd->getTextBounds(sval_cog, 0, 0, &x, &y, &w, &h);
getdisplay().setCursor(360-w, 170); epd->setCursor(360-w, 170);
getdisplay().print(sval_cog); epd->print(sval_cog);
GwApi::BoatValue *bv_dtw = pageData.values[2]; // DTW GwApi::BoatValue *bv_dtw = pageData.values[2]; // DTW
String sval_dtw = formatValue(bv_dtw, *commonData).svalue; String sval_dtw = commonData->fmt->formatValue(bv_dtw, *commonData).svalue;
getdisplay().getTextBounds(sval_dtw, 0, 0, &x, &y, &w, &h); epd->getTextBounds(sval_dtw, 0, 0, &x, &y, &w, &h);
getdisplay().setCursor(160-w, 257); epd->setCursor(160-w, 257);
getdisplay().print(sval_dtw); epd->print(sval_dtw);
GwApi::BoatValue *bv_btw = pageData.values[3]; // BTW GwApi::BoatValue *bv_btw = pageData.values[3]; // BTW
String sval_btw = formatValue(bv_btw, *commonData).svalue; String sval_btw = commonData->fmt->formatValue(bv_btw, *commonData).svalue;
getdisplay().getTextBounds(sval_btw, 0, 0, &x, &y, &w, &h); epd->getTextBounds(sval_btw, 0, 0, &x, &y, &w, &h);
getdisplay().setCursor(360-w, 257); epd->setCursor(360-w, 257);
getdisplay().print(sval_btw); epd->print(sval_btw);
GwApi::BoatValue *bv_wpname = pageData.values[4]; // WPName
bool valid = bv_cog->valid && bv_btw->valid; bool valid = bv_cog->valid && bv_btw->valid;
// XTETrack view // XTETrack view
// draw ship symbol (as bitmap) // draw ship symbol (as bitmap)
getdisplay().drawXBitmap(184, 68, ship_bits, ship_width, ship_height, commonData->fgcolor); epd->drawXBitmap(184, 68, ship_bits, ship_width, ship_height, commonData->fgcolor);
// draw next waypoint name // draw next waypoint name
String sval_wpname = "no data"; String sval_wpname = "no data";
if (valid) { if (valid) {
sval_wpname = "Tonne 122"; sval_wpname = bv_wpname->svalue;
} }
getdisplay().setFont(&Ubuntu_Bold10pt8b); epd->setFont(&Ubuntu_Bold10pt8b);
getdisplay().getTextBounds(sval_wpname, 0, 150, &x, &y, &w, &h); epd->getTextBounds(sval_wpname, 0, 150, &x, &y, &w, &h);
// TODO if text don't fix use smaller font size. // TODO if text don't fix use smaller font size.
// if smallest size does not fit use 2 lines // if smallest size does not fit use 2 lines
// last resort: clip with ellipsis // last resort: clip with ellipsis
getdisplay().setCursor(200 - w / 2, 60); epd->setCursor(200 - w / 2, 60);
getdisplay().print(sval_wpname); epd->print(sval_wpname);
// draw course segments // draw course segments
@@ -226,7 +227,7 @@ PageDescription registerPageXTETrack(
"XTETrack", // Page name "XTETrack", // Page name
createPage, // Action createPage, // Action
0, // Number of bus values depends on selection in Web configuration 0, // Number of bus values depends on selection in Web configuration
{"XTE", "COG", "DTW", "BTW"}, // Bus values we need in the page {"XTE", "COG", "DTW", "BTW", "WPName"}, // Bus values we need in the page
true // Show display header on/off true // Show display header on/off
); );

View File

@@ -1,10 +1,11 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <Arduino.h> #include <Arduino.h>
#include "GwApi.h" #include "GwApi.h"
#include <functional> #include <functional>
#include <vector> #include <vector>
#include "LedSpiTask.h" #include "LedSpiTask.h"
#include "OBPRingBuffer.h"
#include "OBPDataOperations.h" #include "OBPDataOperations.h"
#define MAX_PAGE_NUMBER 10 // Max number of pages for show data #define MAX_PAGE_NUMBER 10 // Max number of pages for show data
@@ -12,11 +13,12 @@
typedef std::vector<GwApi::BoatValue *> ValueList; typedef std::vector<GwApi::BoatValue *> ValueList;
typedef struct{ typedef struct{
GwApi *api;
String pageName; String pageName;
uint8_t pageNumber; // page number in sequence of visible pages uint8_t pageNumber; // page number in sequence of visible pages
//the values will always contain the user defined values first //the values will always contain the user defined values first
ValueList values; ValueList values;
tBoatHstryData boatHstry; HstryBuf* boatHstry;
} PageData; } PageData;
// Sensor data structure (only for extended sensors, not for NMEA bus sensors) // Sensor data structure (only for extended sensors, not for NMEA bus sensors)
@@ -99,30 +101,55 @@ typedef struct{
} AlarmData; } AlarmData;
typedef struct{ typedef struct{
GwApi::Status status; int voltage = 0;
GwLog *logger=NULL; } AvgData;
GwConfigHandler *config=NULL;
SensorData data; class Formatter; // forward declaration
SunData sundata; typedef struct{
TouchKeyData keydata[6]; GwApi::Status status;
BacklightData backlight; GwLog *logger = nullptr;
AlarmData alarm; GwConfigHandler *config = nullptr;
GwApi::BoatValue *time=NULL; Formatter *fmt = nullptr;
GwApi::BoatValue *date=NULL; SensorData data;
uint16_t fgcolor; SunData sundata;
uint16_t bgcolor; TouchKeyData keydata[6];
bool keylock = false; BacklightData backlight;
String powermode; AlarmData alarm;
AvgData avgdata;
GwApi::BoatValue *time = nullptr;
GwApi::BoatValue *date = nullptr;
uint16_t fgcolor;
uint16_t bgcolor;
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
class Page{ class Page{
protected: protected:
// TODO Future: GwApi *api;
CommonData *commonData; CommonData *commonData;
public: GwConfigHandler *config;
GwLog *logger;
bool simulation = false;
bool holdvalues = false;
String flashLED;
String backlightMode;
public:
Page(CommonData &common) {
commonData = &common;
config = commonData->config;
logger = commonData->logger;
// preload generic configuration data
simulation = config->getBool(config->useSimuData);
holdvalues = config->getBool(config->holdvalues);
flashLED = config->getString(config->flashLED);
backlightMode = config->getString(config->backlight);
}
int refreshtime = 1000; int refreshtime = 1000;
virtual int displayPage(PageData &pageData)=0; virtual int displayPage(PageData &pageData)=0;
virtual void displayNew(PageData &pageData){} virtual void displayNew(PageData &pageData){}
virtual void leavePage(PageData &pageData){}
virtual void setupKeys() { virtual void setupKeys() {
#ifdef HARDWARE_V21 #ifdef HARDWARE_V21
commonData->keydata[0].label = ""; commonData->keydata[0].label = "";
@@ -181,23 +208,7 @@ class PageDescription{
class PageStruct{ class PageStruct{
public: public:
Page *page=NULL; Page *page = nullptr;
PageData parameters; PageData parameters;
PageDescription *description=NULL; PageDescription *description = nullptr;
}; };
// Standard format functions without overhead
String formatDate(String fmttype, uint16_t year, uint8_t month, uint8_t day);
String formatTime(char fmttype, uint8_t hour, uint8_t minute, uint8_t second);
String formatLatitude(double lat);
String formatLongitude(double lon);
// Structure for formatted boat values
typedef struct{
double value;
String svalue;
String unit;
} FormattedData;
// Formatter for boat values
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata);

97
lib/obp60task/README Normal file
View File

@@ -0,0 +1,97 @@
Development information
=======================
This file contains some hints concerning building the firmware as well as
developing and debugging it.
Coding style
------------
WIP
Please format your new code the same as already existing code.
Some rules:
- Preprocessor directives go to column zero
- Identation is 4 spaces
Git commands
------------
Some useful commands are
git status
git fetch upstream
git diff --name-status upstream/master
git checkout upstream/master platformio.ini
# how to reset my Repo to match norbert's status
git remote add upstream https://github.com/norbert-walter/esp32-nmea2000-obp60
git fetch upstream
git checkout master
git reset --hard upstream/master
git push origin master --force
New pages
---------
To create a new page for OBP60 the following steps are necessary:
1. Create a page under /lib/obp60task/PageXXXX.cpp. You can use a simple
page e.g. PageOneValue.cpp as template
2. Set page name in PageXXXX.cpp on file name
3. Register new page in /lib/obp60task/obp60task.cpp in function
'registerAllPages'
4. Add new page in /lib/obp60task/config.json for each page type
or use gen_set.py to auto-generate the relevant section of
config.json. For further information on that read the comments
in gen_set.py.
5. Copy the changes in config.json to config_obp40.json and rename
strings accordingly. E.g. obp60 to obp40.
Using Gitpod
------------
Warning: You have to register with gitpod!
Open web page:
https://gitpod.io/#https://github.com/norbert-walter/esp32-nmea2000-obp60/tree/master/lib/obp60task
Input in terminal:
cd /workspace/esp32-nmea2000-obp60
bash /workspace/esp32-nmea2000-obp60/lib/obp60task/run_installing_tools
bash /workspace/esp32-nmea2000-obp60/lib/obp60task/run_obp60_s3
bash /workspace/esp32-nmea2000-obp60/lib/obp60task/run_obp40_s3
Compile result for OBP60:
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/bootloader.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/firmware.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/partitions.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-dev<yyyymmdd>-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-dev<yyyymmdd>-update.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-all.bin, ready to flash to offset 0x0000
Compile result for OBP40 (CrowPanel 4.2):
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/bootloader.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/firmware.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/partitions.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-dev<yyyymmdd>-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-dev<yyyymmdd>-update.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-all.bin, ready to flash to offset 0x0000
Compilation issues
------------------
? Error while linking: "undefined reference to `registerPageXXX'"
: Check if the required page is enabled for current board/environment: #if defined ...
Debugging tool
--------------
log.txt = text file with error messages from terminal console
tools/decoder.py -p ESP32S3 -t ~/.platformio/packages/toolchain-xtensa-esp32s3/ -e .pio/build/obp60_s3/firmware.elf log.txt

19
lib/obp60task/TODO Normal file
View File

@@ -0,0 +1,19 @@
- fix unstable accesspoint availability
- page refresh after page change and not connected to key codes
- config: getFloat, getDouble
- dseg7 font to new version
- new pages: ais, autopilot, epropulsion
- automate config.json generation with extra_task.py
- extend boatdata: ais, waypoints, alarms
- page clock: sunrise / sunset in local time or UTC
- implement alerts
- implement formatter as class

View File

@@ -1,38 +0,0 @@
Using Gitpod
############
Open web page:
https://gitpod.io/#https://github.com/norbert-walter/esp32-nmea2000-obp60/tree/master/lib/obp60task
Input in terminal:
cd /workspace/esp32-nmea2000-obp60
bash /workspace/esp32-nmea2000-obp60/lib/obp60task/run_installing_tools
bash /workspace/esp32-nmea2000-obp60/lib/obp60task/run_obp60_s3
bash /workspace/esp32-nmea2000-obp60/lib/obp60task/run_obp40_s3
Compile result for OBP60
########################
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/bootloader.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/firmware.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/partitions.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-dev20231220-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-dev20231220-update.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp60_s3/obp60_s3-all.bin, ready to flash to offset 0x0000
Compile result for OBP40 (CrowPanel 4.2)
########################################
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/bootloader.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/firmware.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/partitions.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-dev20231220-all.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-dev20231220-update.bin
/workspace/esp32-nmea2000-obp60/.pio/build/obp40_s3/obp40_s3-all.bin, ready to flash to offset 0x0000

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +0,0 @@
Debugging tool
##############
log.txt = text file with error messages from terminal console
tools/decoder.py -p ESP32S3 -t ~/.platformio/packages/toolchain-xtensa-esp32s3/ -e .pio/build/obp60_s3/firmware.elf log.txt

View File

@@ -28,3 +28,4 @@ except:
env["CPPDEFINES"].extend([("BOARD", env["BOARD"]), ("EPDTYPE", epdtype), ("PCBVERS", pcbvers), ("GXEPD2VERS", gxepd2vers)]) env["CPPDEFINES"].extend([("BOARD", env["BOARD"]), ("EPDTYPE", epdtype), ("PCBVERS", pcbvers), ("GXEPD2VERS", gxepd2vers)])
print("added hardware info to CPPDEFINES") print("added hardware info to CPPDEFINES")
print("friendly board name is '{}'".format(env.GetProjectOption("board_name")))

View File

@@ -0,0 +1,273 @@
const uint8_t Atari6pxBitmaps[] PROGMEM = {
0x00, 0xF0, 0x30, 0xCF, 0x38, 0x80, 0x53, 0xF5, 0x14, 0xFD, 0x40, 0x7E,
0x47, 0x85, 0x13, 0xE1, 0x00, 0xC7, 0x21, 0x00, 0x4E, 0x30, 0x63, 0x26,
0x39, 0xC3, 0x26, 0x40, 0x6F, 0x00, 0x7B, 0x24, 0xC0, 0xCD, 0xA5, 0x80,
0x8B, 0x3E, 0x00, 0x44, 0x10, 0x4F, 0xC4, 0x10, 0x40, 0x6F, 0x00, 0xF8,
0xF0, 0x0C, 0x66, 0x18, 0x82, 0x00, 0x7A, 0x39, 0x58, 0x61, 0xE0, 0x75,
0x50, 0xF8, 0x17, 0xA0, 0x83, 0xF0, 0xF8, 0x17, 0x80, 0x07, 0xE0, 0x39,
0x28, 0xA2, 0xFC, 0x20, 0xFE, 0x0F, 0x80, 0x07, 0xE0, 0x7A, 0x0F, 0xC0,
0x01, 0xE0, 0xF8, 0x44, 0x02, 0x10, 0x7A, 0x17, 0x80, 0x85, 0xE0, 0x7A,
0x17, 0xC0, 0x01, 0xE0, 0xF0, 0xF0, 0xF3, 0x58, 0x1B, 0x30, 0x42, 0x0C,
0xF8, 0x3E, 0xC3, 0x06, 0xC4, 0x60, 0x7A, 0x31, 0x86, 0x00, 0x01, 0x80,
0x7A, 0x19, 0xE6, 0x82, 0x07, 0xC0, 0x7A, 0x1F, 0xE1, 0x86, 0x10, 0xFA,
0x1F, 0xA0, 0x87, 0xE0, 0x7E, 0x08, 0x00, 0x01, 0xF0, 0xFA, 0x18, 0x60,
0x83, 0xE0, 0xFE, 0x0F, 0xA0, 0x83, 0xF0, 0xFE, 0x0F, 0xA0, 0x82, 0x00,
0x7E, 0x08, 0xC1, 0x05, 0xF0, 0x86, 0x1F, 0xE1, 0x86, 0x10, 0xF4, 0x44,
0x4F, 0x04, 0x10, 0x41, 0x85, 0xE0, 0x8C, 0xB9, 0x09, 0x44, 0x84, 0x21,
0x08, 0x7C, 0x83, 0xDE, 0x4C, 0x18, 0x30, 0x40, 0x83, 0xC6, 0x4C, 0x18,
0xF0, 0x40, 0x7A, 0x18, 0x40, 0x01, 0xE0, 0xFA, 0x1F, 0xA0, 0x82, 0x00,
0x7A, 0x18, 0x40, 0x01, 0xE0, 0xC0, 0xFA, 0x1F, 0xA4, 0x92, 0x30, 0x7E,
0x07, 0x80, 0x07, 0xE0, 0xFC, 0x41, 0x04, 0x10, 0x40, 0x86, 0x18, 0x61,
0x85, 0xE0, 0x86, 0x10, 0x00, 0x48, 0x40, 0x83, 0x06, 0x4C, 0x1E, 0xF0,
0x40, 0x85, 0x21, 0x00, 0x4A, 0x10, 0x86, 0x14, 0x80, 0x10, 0x40, 0xFC,
0x21, 0x10, 0x43, 0xF0, 0xFC, 0xCC, 0xCF, 0xC1, 0x81, 0x86, 0x04, 0x10,
0xF3, 0x33, 0x3F, 0x11, 0xEC, 0xC0, 0xFC, 0xD9, 0x80, 0x78, 0x10, 0x7F,
0x7C, 0x83, 0xE8, 0x61, 0x83, 0xE0, 0x7E, 0x08, 0x00, 0x7C, 0x05, 0xF8,
0x61, 0x05, 0xF0, 0x7B, 0xF8, 0x00, 0x78, 0x1A, 0x3E, 0x84, 0x20, 0x7E,
0x17, 0xC1, 0x07, 0xE0, 0x81, 0x33, 0x8C, 0x18, 0x30, 0x40, 0xC4, 0x44,
0xF0, 0x08, 0x42, 0x10, 0x87, 0xC0, 0x82, 0x2F, 0x20, 0x8A, 0x10, 0xC4,
0x44, 0x4F, 0x4B, 0xF9, 0x61, 0x84, 0xFA, 0x18, 0x61, 0x84, 0x7A, 0x18,
0x40, 0x78, 0xFA, 0x18, 0x60, 0xFA, 0x00, 0x7E, 0x18, 0x41, 0x7C, 0x10,
0xF4, 0x61, 0x08, 0x00, 0x7E, 0x00, 0x3F, 0x00, 0x4F, 0x44, 0x41, 0x86,
0x10, 0x41, 0x7C, 0x86, 0x10, 0x12, 0x10, 0x83, 0x26, 0x4C, 0x1E, 0xE0,
0x8B, 0x18, 0x08, 0x80, 0x86, 0x17, 0xC1, 0x07, 0xE0, 0xF8, 0x94, 0x8F,
0x80, 0x76, 0xC6, 0x67, 0xFF, 0xFC, 0xE6, 0x36, 0x6E, 0x41, 0x0B, 0x6F,
0x08, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD,
0xFC, 0x11, 0xEC, 0x71, 0xFC, 0x11, 0xEC, 0xC4, 0x10, 0x41, 0x00, 0x10,
0x40, 0x33, 0x49, 0xE1, 0x00, 0x08, 0x1B, 0xD8, 0x40, 0x81, 0x00, 0x10,
0xE3, 0x39, 0x01, 0x02, 0x00, 0x7A, 0x5E, 0xC4, 0x11, 0xE0, 0xFF, 0xEF,
0x3C, 0xC6, 0x30, 0xEE, 0x57, 0xA1, 0x87, 0xB0, 0x06, 0x1A, 0x64, 0x86,
0x08, 0x00, 0x7D, 0x06, 0x4C, 0xD8, 0x30, 0x5F, 0x00, 0x18, 0xF9, 0xF7,
0xF0, 0x00, 0x06, 0x00, 0x08, 0x30, 0x9E, 0x7B, 0xE6, 0x00, 0xF1, 0x03,
0xBC, 0x48, 0x81, 0x82, 0x00, 0xF1, 0x02, 0x77, 0xA1, 0x43, 0x85, 0x80,
0x12, 0x24, 0x48, 0x97, 0x3E, 0x70, 0x80, 0x92, 0x4D, 0xD8, 0xFF, 0x1C,
0x73, 0xCF, 0x3F, 0xC0, 0xC4, 0x4F, 0xFF, 0xF0, 0x7C, 0x1F, 0xF0, 0xC3,
0x0F, 0xC0, 0xF8, 0x2F, 0xC3, 0x0C, 0x3F, 0xC0, 0xC3, 0x0C, 0xFF, 0x0C,
0x30, 0xC0, 0xFB, 0x0F, 0xC3, 0x0C, 0x3F, 0xC0, 0xC3, 0x0F, 0xE3, 0xCF,
0x3F, 0xC0, 0xFC, 0x13, 0xCC, 0x30, 0xC3, 0x00, 0x71, 0x47, 0x37, 0xDF,
0x7F, 0xC0, 0xFF, 0x3C, 0x7F, 0x0C, 0x30, 0xC0, 0x78, 0x00, 0x7F, 0x78,
0xF0, 0x80, 0xEF, 0x88, 0x88, 0xF8, 0x0F, 0x1E, 0xFF, 0x84, 0x08, 0x1E,
0x26, 0x00, 0xF3, 0x20, 0xC1, 0x05, 0xF1, 0x40, 0x42, 0x2C, 0x50, 0x40,
0x1E, 0xF0, 0x00, 0x07, 0x24, 0x80, 0x93, 0x80, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC, 0xFC, 0x7F, 0xDD, 0xFC,
0xFC, 0x7F, 0xDD, 0xFC };
const GFXglyph Atari6pxGlyphs[] PROGMEM = {
{ 0, 1, 1, 7, 0, 0 }, // 0x20 ' ' U+0020
{ 1, 2, 6, 6, 2, -5 }, // 0x21 '!' U+0021
{ 3, 6, 3, 7, 0, -5 }, // 0x22 '"' U+0022
{ 6, 6, 6, 7, 0, -5 }, // 0x23 '#' U+0023
{ 11, 6, 7, 7, 0, -5 }, // 0x24 '$' U+0024
{ 17, 6, 6, 7, 0, -5 }, // 0x25 '%' U+0025
{ 22, 6, 7, 7, 0, -6 }, // 0x26 '&' U+0026
{ 28, 3, 3, 6, 1, -5 }, // 0x27 ''' U+0027
{ 30, 3, 6, 6, 1, -5 }, // 0x28 '(' U+0028
{ 33, 3, 6, 6, 1, -5 }, // 0x29 ')' U+0029
{ 36, 5, 6, 7, 1, -5 }, // 0x2a '*' U+002A
{ 40, 6, 6, 7, 0, -5 }, // 0x2b '+' U+002B
{ 45, 3, 3, 6, 1, -1 }, // 0x2c ',' U+002C
{ 47, 5, 1, 7, 1, -3 }, // 0x2d '-' U+002D
{ 48, 2, 2, 6, 2, -1 }, // 0x2e '.' U+002E
{ 49, 6, 6, 7, 0, -5 }, // 0x2f '/' U+002F
{ 54, 6, 6, 7, 0, -5 }, // 0x30 '0' U+0030
{ 59, 2, 6, 7, 3, -5 }, // 0x31 '1' U+0031
{ 61, 6, 6, 7, 0, -5 }, // 0x32 '2' U+0032
{ 66, 6, 6, 7, 0, -5 }, // 0x33 '3' U+0033
{ 71, 6, 6, 7, 0, -5 }, // 0x34 '4' U+0034
{ 76, 6, 6, 7, 0, -5 }, // 0x35 '5' U+0035
{ 81, 6, 6, 7, 0, -5 }, // 0x36 '6' U+0036
{ 86, 5, 6, 7, 0, -5 }, // 0x37 '7' U+0037
{ 90, 6, 6, 7, 0, -5 }, // 0x38 '8' U+0038
{ 95, 6, 6, 7, 0, -5 }, // 0x39 '9' U+0039
{ 100, 2, 6, 6, 2, -5 }, // 0x3a ':' U+003A
{ 102, 2, 7, 6, 2, -5 }, // 0x3b ';' U+003B
{ 104, 5, 6, 7, 1, -5 }, // 0x3c '<' U+003C
{ 108, 5, 3, 7, 1, -4 }, // 0x3d '=' U+003D
{ 110, 5, 6, 7, 1, -5 }, // 0x3e '>' U+003E
{ 114, 6, 7, 7, 0, -5 }, // 0x3f '?' U+003F
{ 120, 6, 7, 7, 0, -5 }, // 0x40 '@' U+0040
{ 126, 6, 6, 7, 0, -5 }, // 0x41 'A' U+0041
{ 131, 6, 6, 7, 0, -5 }, // 0x42 'B' U+0042
{ 136, 6, 6, 7, 0, -5 }, // 0x43 'C' U+0043
{ 141, 6, 6, 7, 0, -5 }, // 0x44 'D' U+0044
{ 146, 6, 6, 7, 0, -5 }, // 0x45 'E' U+0045
{ 151, 6, 6, 7, 0, -5 }, // 0x46 'F' U+0046
{ 156, 6, 6, 7, 0, -5 }, // 0x47 'G' U+0047
{ 161, 6, 6, 7, 0, -5 }, // 0x48 'H' U+0048
{ 166, 4, 6, 7, 1, -5 }, // 0x49 'I' U+0049
{ 169, 6, 6, 7, 0, -5 }, // 0x4a 'J' U+004A
{ 174, 5, 6, 7, 1, -5 }, // 0x4b 'K' U+004B
{ 178, 5, 6, 7, 1, -5 }, // 0x4c 'L' U+004C
{ 182, 7, 6, 8, 0, -5 }, // 0x4d 'M' U+004D
{ 188, 7, 6, 8, 0, -5 }, // 0x4e 'N' U+004E
{ 194, 6, 6, 7, 0, -5 }, // 0x4f 'O' U+004F
{ 199, 6, 6, 7, 0, -5 }, // 0x50 'P' U+0050
{ 204, 6, 7, 7, 0, -5 }, // 0x51 'Q' U+0051
{ 210, 6, 6, 7, 0, -5 }, // 0x52 'R' U+0052
{ 215, 6, 6, 7, 0, -5 }, // 0x53 'S' U+0053
{ 220, 6, 6, 7, 0, -5 }, // 0x54 'T' U+0054
{ 225, 6, 6, 7, 0, -5 }, // 0x55 'U' U+0055
{ 230, 6, 6, 7, 0, -5 }, // 0x56 'V' U+0056
{ 235, 7, 6, 8, 0, -5 }, // 0x57 'W' U+0057
{ 241, 6, 6, 7, 0, -5 }, // 0x58 'X' U+0058
{ 246, 6, 6, 7, 0, -5 }, // 0x59 'Y' U+0059
{ 251, 6, 6, 7, 0, -5 }, // 0x5a 'Z' U+005A
{ 256, 4, 6, 7, 1, -5 }, // 0x5b '[' U+005B
{ 259, 6, 6, 7, 0, -5 }, // 0x5c '\' U+005C
{ 264, 4, 6, 7, 2, -5 }, // 0x5d ']' U+005D
{ 267, 6, 3, 7, 0, -5 }, // 0x5e '^' U+005E
{ 270, 6, 1, 7, 0, 0 }, // 0x5f '_' U+005F
{ 271, 3, 3, 6, 1, -5 }, // 0x60 '`' U+0060
{ 273, 6, 5, 7, 0, -4 }, // 0x61 'a' U+0061
{ 277, 6, 6, 7, 0, -5 }, // 0x62 'b' U+0062
{ 282, 6, 5, 7, 0, -4 }, // 0x63 'c' U+0063
{ 286, 6, 6, 7, 0, -5 }, // 0x64 'd' U+0064
{ 291, 6, 5, 7, 0, -4 }, // 0x65 'e' U+0065
{ 295, 5, 6, 7, 1, -5 }, // 0x66 'f' U+0066
{ 299, 6, 6, 7, 0, -4 }, // 0x67 'g' U+0067
{ 304, 7, 6, 8, 0, -5 }, // 0x68 'h' U+0068
{ 310, 4, 5, 7, 1, -4 }, // 0x69 'i' U+0069
{ 313, 5, 7, 7, 0, -5 }, // 0x6a 'j' U+006A
{ 318, 6, 6, 7, 0, -5 }, // 0x6b 'k' U+006B
{ 323, 4, 6, 7, 1, -5 }, // 0x6c 'l' U+006C
{ 326, 6, 5, 7, 0, -4 }, // 0x6d 'm' U+006D
{ 330, 6, 5, 7, 0, -4 }, // 0x6e 'n' U+006E
{ 334, 6, 5, 7, 0, -4 }, // 0x6f 'o' U+006F
{ 338, 6, 6, 7, 0, -4 }, // 0x70 'p' U+0070
{ 343, 6, 6, 7, 0, -4 }, // 0x71 'q' U+0071
{ 348, 5, 5, 7, 1, -4 }, // 0x72 'r' U+0072
{ 352, 5, 5, 7, 1, -4 }, // 0x73 's' U+0073
{ 356, 4, 6, 7, 1, -5 }, // 0x74 't' U+0074
{ 359, 6, 5, 7, 0, -4 }, // 0x75 'u' U+0075
{ 363, 6, 5, 7, 0, -4 }, // 0x76 'v' U+0076
{ 367, 7, 5, 8, 0, -4 }, // 0x77 'w' U+0077
{ 372, 5, 5, 7, 0, -4 }, // 0x78 'x' U+0078
{ 376, 6, 6, 7, 0, -4 }, // 0x79 'y' U+0079
{ 381, 5, 5, 7, 1, -4 }, // 0x7a 'z' U+007A
{ 385, 4, 6, 6, 1, -5 }, // 0x7b '{' U+007B
{ 388, 2, 7, 7, 4, -5 }, // 0x7c '|' U+007C
{ 390, 4, 6, 6, 1, -5 }, // 0x7d '}' U+007D
{ 393, 6, 5, 7, 0, -5 }, // 0x7e '~' U+007E
{ 397, 5, 6, 6, 0, -5 }, // 0x7f 'REPLACEMENT CHARACTER *' U+2370
{ 401, 5, 6, 6, 0, -5 }, // 0x80 'NO-BREAK SPACE' U+00A0
{ 405, 5, 6, 6, 0, -5 }, // 0x81 'INVERTED EXCLAMATION MARK' U+00A1
{ 409, 5, 6, 6, 0, -5 }, // 0x82 'CENT SIGN' U+00A2
{ 413, 5, 6, 6, 0, -5 }, // 0x83 'POUND SIGN' U+00A3
{ 417, 5, 6, 6, 0, -5 }, // 0x84 'CURRENCY SIGN' U+00A4
{ 421, 5, 6, 6, 0, -5 }, // 0x85 'YEN SIGN' U+00A5
{ 425, 5, 6, 6, 0, -5 }, // 0x86 'BROKEN BAR' U+00A6
{ 429, 5, 6, 6, 0, -5 }, // 0x87 'SECTION SIGN' U+00A7
{ 433, 5, 6, 6, 0, -5 }, // 0x88 'DIAERESIS' U+00A8
{ 437, 5, 6, 6, 0, -5 }, // 0x89 'COPYRIGHT SIGN' U+00A9
{ 441, 5, 6, 6, 0, -5 }, // 0x8a 'FEMININE ORDINAL INDICATOR' U+00AA
{ 445, 5, 6, 6, 0, -5 }, // 0x8b 'LEFT-POINTING DOUBLE ANGLE QUOTATION MARK' U+00AB
{ 449, 5, 6, 6, 0, -5 }, // 0x8c 'NOT SIGN' U+00AC
{ 453, 5, 6, 6, 0, -5 }, // 0x8d 'SOFT HYPHEN' U+00AD
{ 457, 5, 6, 6, 0, -5 }, // 0x8e 'REGISTERED SIGN' U+00AE
{ 461, 5, 6, 6, 0, -5 }, // 0x8f 'MACRON' U+00AF
{ 465, 5, 6, 6, 0, -5 }, // 0x90 'DEGREE SIGN' U+00B0
{ 469, 5, 6, 6, 0, -5 }, // 0x91 'PLUS-MINUS SIGN' U+00B1
{ 473, 5, 6, 6, 0, -5 }, // 0x92 'SUPERSCRIPT TWO' U+00B2
{ 477, 5, 6, 6, 0, -5 }, // 0x93 'SUPERSCRIPT THREE' U+00B3
{ 481, 5, 6, 6, 0, -5 }, // 0x94 'ACUTE ACCENT' U+00B4
{ 485, 5, 6, 6, 0, -5 }, // 0x95 'MICRO SIGN' U+00B5
{ 489, 5, 6, 6, 0, -5 }, // 0x96 'PILCROW SIGN' U+00B6
{ 493, 5, 6, 6, 0, -5 }, // 0x97 'MIDDLE DOT' U+00B7
{ 497, 5, 6, 6, 0, -5 }, // 0x98 'CEDILLA' U+00B8
{ 501, 5, 6, 6, 0, -5 }, // 0x99 'SUPERSCRIPT ONE' U+00B9
{ 505, 5, 6, 6, 0, -5 }, // 0x9a 'MASCULINE ORDINAL INDICATOR' U+00BA
{ 509, 5, 6, 6, 0, -5 }, // 0x9b 'RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK' U+00BB
{ 513, 5, 6, 6, 0, -5 }, // 0x9c 'VULGAR FRACTION ONE QUARTER' U+00BC
{ 517, 5, 6, 6, 0, -5 }, // 0x9d 'VULGAR FRACTION ONE HALF' U+00BD
{ 521, 5, 6, 6, 0, -5 }, // 0x9e 'VULGAR FRACTION THREE QUARTERS' U+00BE
{ 525, 5, 6, 6, 0, -5 }, // 0x9f 'INVERTED QUESTION MARK' U+00BF
{ 529, 6, 5, 7, 0, -4 }, // 0xa0 'LATIN CAPITAL LETTER A WITH GRAVE' U+00C0
{ 533, 6, 7, 7, 0, -5 }, // 0xa1 'LATIN CAPITAL LETTER A WITH ACUTE' U+00C1
{ 539, 6, 7, 7, 0, -5 }, // 0xa2 'LATIN CAPITAL LETTER A WITH CIRCUMFLEX' U+00C2
{ 545, 7, 6, 7, 0, -5 }, // 0xa3 'LATIN CAPITAL LETTER A WITH TILDE' U+00C3
{ 551, 7, 6, 7, 0, -5 }, // 0xa4 'LATIN CAPITAL LETTER A WITH DIAERESIS' U+00C4
{ 557, 6, 6, 7, 0, -5 }, // 0xa5 'LATIN CAPITAL LETTER A WITH RING ABOVE' U+00C5
{ 562, 6, 6, 7, 0, -5 }, // 0xa6 'LATIN CAPITAL LETTER AE' U+00C6
{ 567, 6, 6, 7, 0, -5 }, // 0xa7 'LATIN CAPITAL LETTER C WITH CEDILLA' U+00C7
{ 572, 7, 6, 7, 0, -5 }, // 0xa8 'LATIN CAPITAL LETTER E WITH GRAVE' U+00C8
{ 578, 7, 7, 7, 0, -5 }, // 0xa9 'LATIN CAPITAL LETTER E WITH ACUTE' U+00C9
{ 585, 7, 7, 7, 0, -5 }, // 0xaa 'LATIN CAPITAL LETTER E WITH CIRCUMFLEX' U+00CA
{ 592, 6, 7, 7, 0, -5 }, // 0xab 'LATIN CAPITAL LETTER E WITH DIAERESIS' U+00CB
{ 598, 7, 7, 7, 0, -5 }, // 0xac 'LATIN CAPITAL LETTER I WITH GRAVE' U+00CC
{ 605, 7, 7, 7, 0, -5 }, // 0xad 'LATIN CAPITAL LETTER I WITH ACUTE' U+00CD
{ 612, 7, 7, 8, 1, -5 }, // 0xae 'LATIN CAPITAL LETTER I WITH CIRCUMFLEX' U+00CE
{ 619, 3, 7, 6, 1, -5 }, // 0xaf 'LATIN CAPITAL LETTER I WITH DIAERESIS' U+00CF
{ 622, 6, 7, 7, 0, -5 }, // 0xb0 'LATIN CAPITAL LETTER ETH' U+00D0
{ 628, 4, 7, 7, 1, -5 }, // 0xb1 'LATIN CAPITAL LETTER N WITH TILDE' U+00D1
{ 632, 6, 7, 7, 0, -5 }, // 0xb2 'LATIN CAPITAL LETTER O WITH GRAVE' U+00D2
{ 638, 6, 7, 7, 0, -5 }, // 0xb3 'LATIN CAPITAL LETTER O WITH ACUTE' U+00D3
{ 644, 6, 7, 7, 0, -5 }, // 0xb4 'LATIN CAPITAL LETTER O WITH CIRCUMFLEX' U+00D4
{ 650, 6, 7, 7, 0, -5 }, // 0xb5 'LATIN CAPITAL LETTER O WITH TILDE' U+00D5
{ 656, 6, 7, 7, 0, -5 }, // 0xb6 'LATIN CAPITAL LETTER O WITH DIAERESIS' U+00D6
{ 662, 6, 7, 7, 0, -5 }, // 0xb7 'MULTIPLICATION SIGN' U+00D7
{ 668, 6, 7, 7, 0, -5 }, // 0xb8 'LATIN CAPITAL LETTER O WITH STROKE' U+00D8
{ 674, 6, 7, 7, 0, -5 }, // 0xb9 'LATIN CAPITAL LETTER U WITH GRAVE' U+00D9
{ 680, 6, 5, 7, 0, -4 }, // 0xba 'LATIN CAPITAL LETTER U WITH ACUTE' U+00DA
{ 684, 8, 7, 8, 0, -5 }, // 0xbb 'LATIN CAPITAL LETTER U WITH CIRCUMFLEX' U+00DB
{ 691, 7, 7, 8, 1, -5 }, // 0xbc 'LATIN CAPITAL LETTER U WITH DIAERESIS' U+00DC
{ 698, 6, 7, 7, 0, -5 }, // 0xbd 'LATIN CAPITAL LETTER Y WITH ACUTE' U+00DD
{ 704, 7, 7, 7, 0, -5 }, // 0xbe 'LATIN CAPITAL LETTER THORN' U+00DE
{ 711, 6, 6, 7, 0, -5 }, // 0xbf 'LATIN SMALL LETTER SHARP S' U+00DF
{ 716, 5, 6, 6, 0, -5 }, // 0xc0 'LATIN SMALL LETTER A WITH GRAVE' U+00E0
{ 720, 5, 6, 6, 0, -5 }, // 0xc1 'LATIN SMALL LETTER A WITH ACUTE' U+00E1
{ 724, 5, 6, 6, 0, -5 }, // 0xc2 'LATIN SMALL LETTER A WITH CIRCUMFLEX' U+00E2
{ 728, 5, 6, 6, 0, -5 }, // 0xc3 'LATIN SMALL LETTER A WITH TILDE' U+00E3
{ 732, 5, 6, 6, 0, -5 }, // 0xc4 'LATIN SMALL LETTER A WITH DIAERESIS' U+00E4
{ 736, 5, 6, 6, 0, -5 }, // 0xc5 'LATIN SMALL LETTER A WITH RING ABOVE' U+00E5
{ 740, 5, 6, 6, 0, -5 }, // 0xc6 'LATIN SMALL LETTER AE' U+00E6
{ 744, 5, 6, 6, 0, -5 }, // 0xc7 'LATIN SMALL LETTER C WITH CEDILLA' U+00E7
{ 748, 5, 6, 6, 0, -5 }, // 0xc8 'LATIN SMALL LETTER E WITH GRAVE' U+00E8
{ 752, 5, 6, 6, 0, -5 }, // 0xc9 'LATIN SMALL LETTER E WITH ACUTE' U+00E9
{ 756, 5, 6, 6, 0, -5 }, // 0xca 'LATIN SMALL LETTER E WITH CIRCUMFLEX' U+00EA
{ 760, 5, 6, 6, 0, -5 }, // 0xcb 'LATIN SMALL LETTER E WITH DIAERESIS' U+00EB
{ 764, 5, 6, 6, 0, -5 }, // 0xcc 'LATIN SMALL LETTER I WITH GRAVE' U+00EC
{ 768, 5, 6, 6, 0, -5 }, // 0xcd 'LATIN SMALL LETTER I WITH ACUTE' U+00ED
{ 772, 5, 6, 6, 0, -5 }, // 0xce 'LATIN SMALL LETTER I WITH CIRCUMFLEX' U+00EE
{ 776, 5, 6, 6, 0, -5 }, // 0xcf 'LATIN SMALL LETTER I WITH DIAERESIS' U+00EF
{ 780, 5, 6, 6, 0, -5 }, // 0xd0 'LATIN SMALL LETTER ETH' U+00F0
{ 784, 5, 6, 6, 0, -5 }, // 0xd1 'LATIN SMALL LETTER N WITH TILDE' U+00F1
{ 788, 5, 6, 6, 0, -5 }, // 0xd2 'LATIN SMALL LETTER O WITH GRAVE' U+00F2
{ 792, 5, 6, 6, 0, -5 }, // 0xd3 'LATIN SMALL LETTER O WITH ACUTE' U+00F3
{ 796, 5, 6, 6, 0, -5 }, // 0xd4 'LATIN SMALL LETTER O WITH CIRCUMFLEX' U+00F4
{ 800, 5, 6, 6, 0, -5 }, // 0xd5 'LATIN SMALL LETTER O WITH TILDE' U+00F5
{ 804, 5, 6, 6, 0, -5 }, // 0xd6 'LATIN SMALL LETTER O WITH DIAERESIS' U+00F6
{ 808, 5, 6, 6, 0, -5 }, // 0xd7 'DIVISION SIGN' U+00F7
{ 812, 5, 6, 6, 0, -5 }, // 0xd8 'LATIN SMALL LETTER O WITH STROKE' U+00F8
{ 816, 5, 6, 6, 0, -5 }, // 0xd9 'LATIN SMALL LETTER U WITH GRAVE' U+00F9
{ 820, 5, 6, 6, 0, -5 }, // 0xda 'LATIN SMALL LETTER U WITH ACUTE' U+00FA
{ 824, 5, 6, 6, 0, -5 }, // 0xdb 'LATIN SMALL LETTER U WITH CIRCUMFLEX' U+00FB
{ 828, 5, 6, 6, 0, -5 }, // 0xdc 'LATIN SMALL LETTER U WITH DIAERESIS' U+00FC
{ 832, 5, 6, 6, 0, -5 }, // 0xdd 'LATIN SMALL LETTER Y WITH ACUTE' U+00FD
{ 836, 5, 6, 6, 0, -5 }, // 0xde 'LATIN SMALL LETTER THORN' U+00FE
{ 840, 5, 6, 6, 0, -5 } }; // 0xdf 'LATIN SMALL LETTER Y WITH DIAERESIS' U+000FF
const GFXfont Atari6px PROGMEM = {
(uint8_t *)Atari6pxBitmaps,
(GFXglyph *)Atari6pxGlyphs,
0x20, 0xDF, 9 };
// Approx. 2195 bytes

View File

@@ -20,7 +20,7 @@ import getopt
import re import re
import json import json
__version__ = "0.2" __version__ = "1.2"
def detect_pages(filename): def detect_pages(filename):
# returns a dictionary with page name and the number of gui fields # returns a dictionary with page name and the number of gui fields
@@ -110,11 +110,10 @@ def create_json(device, no_of_pages, pagedata):
"description": "The display for field {}".format(number_to_text(field_no)), "description": "The display for field {}".format(number_to_text(field_no)),
"category": f"{device.upper()} Page {page_no}", "category": f"{device.upper()} Page {page_no}",
"capabilities": {device.lower(): "true"}, "capabilities": {device.lower(): "true"},
"condition": [ "condition": {
{f"page{page_no}type": page} f"page{page_no}type": [page for page in pages if pagedata[page] >= field_no],
for page in pages "visiblePages": [vp for vp in range(page_no, no_of_pages + 1)]
if pagedata[page] >= field_no },
],
} }
output.append(field_data) output.append(field_data)
@@ -149,12 +148,13 @@ def usage():
print("Command line options") print("Command line options")
print(" -d --device device name to use e.g. obp60") print(" -d --device device name to use e.g. obp60")
print(" -p --pages number of pages to create") print(" -p --pages number of pages to create")
print(" -m --merge json with device config to merge to")
print(" -h show this help") print(" -h show this help")
print() print()
if __name__ == '__main__': if __name__ == '__main__':
try: try:
options, remainder = getopt.getopt(sys.argv[1:], 'd:p:', ['device=','--pages=']) options, remainder = getopt.getopt(sys.argv[1:], 'd:p:m:', ['device=','--pages=','--merge'])
except getopt.GetoptError as err: except getopt.GetoptError as err:
print(err) print(err)
usage() usage()
@@ -162,11 +162,14 @@ if __name__ == '__main__':
device = "obp60" device = "obp60"
no_of_pages = 10 no_of_pages = 10
merge_json = None
for opt, arg in options: for opt, arg in options:
if opt in ('-d', '--device'): if opt in ('-d', '--device'):
device = arg device = arg
elif opt in ('-p', '--pages'): elif opt in ('-p', '--pages'):
no_of_pages = int(arg) no_of_pages = int(arg)
elif opt in ('-m', '--merge'):
merge_json = arg
elif opt == '-h': elif opt == '-h':
usage() usage()
sys.exit(0) sys.exit(0)
@@ -175,5 +178,12 @@ if __name__ == '__main__':
pagedata = detect_pages("obp60task.cpp") pagedata = detect_pages("obp60task.cpp")
json_output = create_json(device, no_of_pages, pagedata) json_output = create_json(device, no_of_pages, pagedata)
# print omitting first line containing [ of JSON array if merge_json and os.path.isfile(merge_json):
print(json_output[1:]) with open(merge_json, 'r') as fh:
device_json = json.load(fh)
page_json = json.loads(json_output)
device_json.extend(page_json)
print(json.dumps(device_json, indent=4))
else:
# print omitting first line containing [ of JSON array
print(json_output[1:])

View File

@@ -1,12 +0,0 @@
git status
git fetch upstream
git diff --name-status upstream/master
git checkout upstream/master platformio.ini
# how to reset my Repo to match norbert'status
git remote add upstream https://github.com/norbert-walter/esp32-nmea2000-obp60
git fetch upstream
git checkout master
git reset --hard upstream/master
git push origin master --force

88
lib/obp60task/hbuffer.cpp Normal file
View File

@@ -0,0 +1,88 @@
/* History Buffer
*
* Storage backed buffer for sensordata
* Permanent storage only supported type: FRAM on I2C-Bus
*
* Values can be 1 to 4 bytes in length
*
* Header: 32 bytes of size
* 0 0x00 HB00 4 magic number
* 4 0x04 xxxxxxxxxxxxxxxx 16 name, space padded
* 20 0x14 n 1 byte size of values in buffer
* 21 0x15 mm 2 buffer size in count of values
* 23 0x17 dd 2 time step in seconds between values
* 25 0x19 tttt 4 unix timestamp of head
* 29 0x1d hh 2 head pointer
* 31 0x1f 0xff 1 header end sign
*
* 32 0x20 ... start of buffer data
*
* Usage example: 7 hours of data collected every 75 seconds
* TODO
*
*/
#include <stdint.h>
#include <time.h>
class HistoryBuffer {
private:
// Header prototype for permanent storage
uint8_t header[32] = {
0x41, 0x48, 0x30, 0x30, // magic: HB00
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // empty name
0x01, // byte size
0x50, 0x01, // value count
0x4b, 0x00, // time step
0x00, 0x00, 0x00, 0x00, // unix time stamp
0x00, 0x00, // head pointer
0xff // end sign
};
uint16_t head = 0; // head pointer to next new value position
time_t timestamp; // last modification time of head
uint16_t delta_t; // time step in seconds
public:
HistoryBuffer(uint16_t size) {
}
~HistoryBuffer() {
// free memory
}
void begin() {
//
}
void finish() {
}
uint16_t add() {
// returns new head value pointer
return 0;
}
uint8_t* get() {
// returns complete buffer in order new to old
return 0;
}
uint8_t getvalue(uint16_t dt) {
// Return a single value delta seconds ago
uint16_t index = head - abs(dt) / delta_t;
return 0;
}
uint8_t getvalue3() {
return 0;
}
bool clear() {
// clears buffer and permanent storage
return true;
}
};
class History {
public:
History() {
}
~History() {
}
void *addSeries() {
return nullptr;
}
};

21
lib/obp60task/hbuffer.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef __HBUFFER_H__
#define __HBUFFER_H__
class HistoryBuffer {
public:
HistoryBuffer(uint16_t size);
void begin();
void finish();
uint16_t add();
uint8_t* get() ;
uint8_t getvalue(uint16_t dt);
uint8_t getvalue3();
void clear();
};
class History {
public:
History();
void *addSeries();
};
#endif

14
lib/obp60task/index.js Normal file
View File

@@ -0,0 +1,14 @@
// Add a new register card in web configuration interface
// This is a Java Script!
(function(){
const api=window.esp32nmea2k;
if (! api) return;
const tabName="Screen";
api.registerListener((id, data) => {
// if (!data.testboard) return; //do nothing if we are not active
let page = api.addTabPage(tabName, "Screen");
api.addEl('button', '', page, 'Screenshot').addEventListener('click', function (ev) {
window.open('/api/user/OBP60Task/screenshot', 'screenshot');
})
}, api.EVENTS.init);
})();

807
lib/obp60task/obp40.conf Normal file
View File

@@ -0,0 +1,807 @@
[DEFAULT]
calibration_fields = AWA, AWS, COG, DBT, HDM, PRPOS, RPOS, SOG, STW, TWA, TWS, TWD, WTemp
[deviceName]
label = system name
type = string
default = OBP40
description = system name, used for the access point and for services
check = checkSystemName
category = system
[timeServer]
label = time server
type = string
default = pool.ntp.org
description = NTP time server. Use only one hostname or IP address
category = wifi client
capabilities = obp40:true
[timeZone]
label = Time Zone
type = number
default = 0.00
description = Time zone [UTC -12...+14]
check = checkMinMax
min = -12.0
max = 14.0
category = OBP40 Settings
capabilities = obp40:true
[homeLAT]
label = Home latitude
type = number
default = 0.00000
description = Latitude of boat home location [-90.0...+90.0]
check = checkMinMax
min = -90.0
max = 90.0
category = OBP40 Settings
capabilities = obp40:true
[homeLON]
label = Home longitude
type = number
default = 0.00000
description = Longitude of boat home location [-180.0...+180.0]
check = checkMinMax
min = -180.0
max = 180.0
category = OBP40 Settings
capabilities = obp40:true
[draft]
label = Boat Draft [m]
type = number
default = 0.00
description = The draft of the boat [0...50m]
check = checkMinMax
min = 0.0
max = 50.0
category = OBP40 Settings
capabilities = obp40:true
[chainLength]
label = Anchor Chain Length [m]
type = number
default = 0
description = The length of the anchor chain [0...255m]
check = checkMinMax
min = 0
max = 255
category = OBP40 Settings
capabilities = obp40:true
[fuelTank]
label = Fuel Tank [l]
type = number
default = 0
description = Fuel tank capacity [0...5000l]
check = checkMinMax
min = 0
max = 5000
category = OBP40 Settings
capabilities = obp40:true
[fuelConsumption]
label = Fuel Consuption [l/h]
type = number
default = 0.00
description = Medium fuel consumption [0...1000l/h]
check = checkMinMax
min = 0.0
max = 1000.0
category = OBP40 Settings
capabilities = obp40:true
[waterTank]
label = Water Tank [l]
type = number
default = 0
description = Water tank capacity [0...5000l]
check = checkMinMax
min = 0
max = 5000
category = OBP40 Settings
capabilities = obp40:true
[wasteTank]
label = Waste Tank [l]
type = number
default = 0
description = Waste tank capacity [0...5000l]
check = checkMinMax
min = 0
max = 5000
category = OBP40 Settings
capabilities = obp40:true
[batteryVoltage]
label = Battery Voltage [V]
type = list
default = 12V
description = Battery Voltage [12V|24V]
list = 12V, 24V
category = OBP40 Settings
capabilities = obp40:true
[batteryType]
label = Battery Type
type = list
default = Pb
description = Type of battery [Pb|Gel|AGM|LiFePo4]
list = Pb, Gel, AGM, LiFePo4
category = OBP40 Settings
capabilities = obp40:true
[batteryCapacity]
label = Battery Capacity [Ah]
type = number
default = 0.0
description = Battery capacity [0...10000Ah]
check = checkMinMax
min = 0.0
max = 10000.0
category = OBP40 Settings
capabilities = obp40:true
[solarPower]
label = Solar Power [W]
type = number
default = 0.0
description = Solar power [0...10000W]
check = checkMinMax
min = 0.0
max = 10000.0
category = OBP40 Settings
capabilities = obp40:true
[genPower]
label = Genarator Power [W]
type = number
default = 0.0
description = Generator power [0...10000W]
check = checkMinMax
min = 0.0
max = 10000.0
category = OBP40 Settings
capabilities = obp40:true
[trackStep]
label = angle [deg]
type = number
default = 3.0
description = track step offset [1...12deg]
check = checkMinMax
min = 1.0
max = 12.0
category = OBP40 Settings
capabilities = obp40:true
[calcTrueWnds]
label = Calculate True Wind
type = boolean
default = false
description = If not available, calculate true wind data from appearant wind and other boat data
category = OBP40 Settings
capabilities = obp40:true
[lengthFormat]
label = Length Format
type = list
default = m
description = Length format [m|ft]
list = m, ft
category = OBP40 Units
capabilities = obp40:true
[distanceFormat]
label = Distance Format
type = list
default = nm
description = Distance format [m|km|nm]
list = m, km, nm
category = OBP40 Units
capabilities = obp40:true
[speedFormat]
label = Speed Format
type = list
default = kn
description = Distance format [m/s|km/h|kn]
list = m/s, km/h, kn
category = OBP40 Units
capabilities = obp40:true
[windspeedFormat]
label = Wind Speed Format
type = list
default = kn
description = Wind speed format [m/s|km/h|kn|bft]
list = m/s, km/h, kn, bft
category = OBP40 Units
capabilities = obp40:true
[tempFormat]
label = Temperature Format
type = list
default = C
description = Temperature format [K|C|F]
list = K, C, F
category = OBP40 Units
capabilities = obp40:true
[dateFormat]
label = Date Format
type = list
default = DE
description = Date format [DE|GB|US|ISO] DE: 31.12.2022, GB: 31/12/2022, US: 12/31/2022, ISO: 2022-12-31
list = DE, GB, US, ISO
category = OBP40 Units
capabilities = obp40:true
[cpuSpeed]
label = CPU Speed [MHz]
type = list
default = 160
description = CPU speed in MHz [80|160|240]
list = 80, 160, 240
category = OBP40 Hardware
capabilities = obp40:true
[useRTC]
label = RTC Modul
type = list
default = off
description = Use RTC module type [off|DS1388]
list = off, DS1388
category = OBP40 Hardware
capabilities = obp40:true
[useGPS]
label = GPS Sensor
type = list
default = off
description = Use internal GPS module type [off|NEO-6M|NEO-M8N|ATGM336H]
list = off, NEO-6M, NEO-M8N, ATGM336H
category = OBP40 Hardware
capabilities = obp40:true
[hdopAccuracy]
label = HDOP Accuracy [m]
type = number
default = 20
description = HDOP ccuracy in m for a valid GPS signal [1...50]
check = checkMinMax
min = 1
max = 50
category = OBP40 Hardware
capabilities = obp40:true
[useEnvSensor]
label = Env. Sensor
type = list
default = off
description = Use internal or external environment sensor via I2C bus [off|BME280|BMP280|BMP180|BMP085|HTU21|SHT21]
list = off, BME280, BMP280, BMP180, BMP085, HTU21, SHT21
category = OBP40 Hardware
capabilities = obp40:true
[usePowSensor1]
label = Battery Sensor
type = list
default = off
description = Use external power management sensor via I2C bus for battery [off|INA219|INA226|]
list = off, INA219, INA226
category = OBP40 Hardware
capabilities = obp40:true
[shunt1]
label = Battery Shunt
type = list
default = 10
description = Shunt current value [10A|50A|100A|200A|300A|400A|500A]
list = 10, 50, 100, 200, 300, 400, 500
category = OBP40 Hardware
capabilities = obp40:true
[usePowSensor2]
label = Solar Sensor
type = list
default = off
description = Use external power management sensor via I2C bus for solar panels [off|INA219|INA226|]
list = off, INA219, INA226
category = OBP40 Hardware
capabilities = obp40:true
[shunt2]
label = Solar Shunt
type = list
default = 10
description = Shunt current value [10A|50A|100A|200A|300A|400A|500A]
list = 10, 50, 100, 200, 300, 400, 500
category = OBP40 Hardware
capabilities = obp40:true
[usePowSensor3]
label = Gen. Sensor
type = list
default = off
description = Use external power management sensor via I2C bus for generator [off|INA219|INA226|]
list = off, INA219, INA226
category = OBP40 Hardware
capabilities = obp40:true
[shunt3]
label = Gen. Shunt
type = list
default = 10
description = Shunt current value [10A|50A|100A|200A|300A|400A|500A] @ 75mV
list = 10, 50, 100, 200, 300, 400, 500
category = OBP40 Hardware
capabilities = obp40:true
[useRotSensor]
label = Rot. Sensor
type = list
default = off
description = Use external rotation sensor via I2C bus [off|AS5600]
list = off, AS5600
category = OBP40 Hardware
capabilities = obp40:true
[rotFunction]
label = Rot. Function
type = list
default = off
description = Function for rotation sensor [off|Rudder|Wind|Mast|Keel|Trim|Boom]
list = off, Rudder, Wind, Mast, Keel, Trim, Boom
category = OBP40 Hardware
capabilities = obp40:true
[rotOffset]
label = Rot. Offset
type = number
default = 0
description = Offset for rotation sensor [-180°...+180°]
check = checkMinMax
min = -180
max = 180
category = OBP40 Hardware
capabilities = obp40:true
[rollLimit]
label = Roll Limit
type = number
default = 25
description = Limit violation for roll angle [-90°...+90°]
check = checkMinMax
min = -90
max = 90
category = OBP40 Hardware
capabilities = obp40:true
[rollOffset]
label = Roll Offset
type = number
default = 0
description = Roll offset angle [-45°...+45°]
check = checkMinMax
min = -45
max = 45
category = OBP40 Hardware
capabilities = obp40:true
[pitchOffset]
label = Pitch Offset
type = number
default = 0
description = Pitch offset angle [-45°...+45°]
check = checkMinMax
min = -45
max = 45
category = OBP40 Hardware
capabilities = obp40:true
[useTempSensor]
label = Temp. Sensor
type = boolean
default = off
description = Use max. 8 external 1Wire devices [off|DS18B20]
list = off, DS18B20
category = OBP40 Hardware
capabilities = obp40:true
[useSDCard]
label = SD Card
type = boolean
default = false
description = Use internal SD card interface [off|on]
category = OBP40 Hardware
capabilities = obp40:true
[powerMode]
label = Power Mode
type = list
default = Max Power
description = Settings for power mode
list = Max Power, Only 5.0V, Min Power
category = OBP40 Hardware
capabilities = obp40:true
[underVoltage]
label = Undervoltage
type = boolean
default = false
description = Switch off device if LiPo voltage drops below 3.65V [on|off]
category = OBP40 Hardware
capabilities = obp40:true
[useSimuData]
label = Simulation Data
type = boolean
default = false
description = Use simulation data when bus data are missing [on|off]
category = OBP40 Hardware
capabilities = obp40:true
[tSensitivity]
label = Touch Sensitivity [%%]
type = number
default = 100
description = Touch sensitivity [0...100%%] for sensor buttons
check = checkMinMax
min = 0
max = 100
category = OBP40 Calibrations
capabilities = obp40:false
[vOffset]
label = VSensor Offset
type = number
default = -1.00
description = Offset for internal voltage sensor (ESP32)
category = OBP40 Calibrations
capabilities = obp40:true
[vSlope]
label = VSensor Slope
type = number
default = 1.00
description = Slope for internal voltage sensor (ESP32)
category = OBP40 Calibrations
capabilities = obp40:true
[calInstance1]
label = Calibration Data Instance 1
type = list
default = ---
description = Data instance for calibration
list = ---, %(calibration_fields)s
category = OBP40 Calibrations
capabilities = obp40:true
[calOffset1]
label = Data Instance 1 Calibration Offset
type = number
default = 0.00
description = Offset for data instance 1
category = OBP40 Calibrations
capabilities = obp40:true
condition = calInstance1 IN %(calibration_fields)s
[calSlope1]
label = Data Instance 1 Calibration Slope
type = number
default = 1.00
description = Slope for data instance 1
category = OBP40 Calibrations
capabilities = obp40:true
condition = calInstance1 IN %(calibration_fields)s
[calSmooth1]
label = Data Instance 1 Smoothing
type = number
default = 0
description = Smoothing factor [0..10]; 0 = no smoothing
check = checkMinMax
min = 0
max = 10
category = OBP40 Calibrations
capabilities = obp40:true
condition = calInstance1 IN %(calibration_fields)s
[calInstance2]
label = Calibration Data Instance 2
type = list
default = ---
description = Data instance for calibration
list = ---, %(calibration_fields)s
category = OBP40 Calibrations
capabilities = obp40:true
[calOffset2]
label = Data Instance 2 Calibration Offset
type = number
default = 0.00
description = Offset for data instance 2
category = OBP40 Calibrations
capabilities = obp40:true
condition = calInstance2 IN %(calibration_fields)s
[calSlope2]
label = Data Instance 2 Calibration Slope
type = number
default = 1.00
description = Slope for data instance 2
category = OBP40 Calibrations
capabilities = obp40:true
condition = calInstance2 IN %(calibration_fields)s
[calSmooth2]
label = Data Instance 2 Smoothing
type = number
default = 0
description = Smoothing factor [0..10]; 0 = no smoothing
check = checkMinMax
min = 0
max = 10
category = OBP40 Calibrations
capabilities = obp40:true
condition = calInstance2 IN %(calibration_fields)s
[calInstance3]
label = Calibration Data Instance 3
type = list
default = ---
description = Data instance for calibration
list = ---, %(calibration_fields)s
category = OBP40 Calibrations
capabilities = obp40:true
[calOffset3]
label = Data Instance 3 Calibration Offset
type = number
default = 0.00
description = Offset for data instance 3
category = OBP40 Calibrations
capabilities = obp40:true
condition = calinstance3 IN %(calibration_fields)s
[calSlope3]
label = Data Instance 3 Calibration Slope
type = number
default = 1.00
description = Slope for data instance 3
category = OBP40 Calibrations
capabilities = obp40:true
condition = calinstance3 IN %(calibration_fields)s
[calSmooth3]
label = Data Instance 3 Smoothing
type = number
default = 0
description = Smoothing factor [0..10]; 0 = no smoothing
check = checkMinMax
min = 0
max = 10
category = OBP40 Calibrations
capabilities = obp40:true
condition = calinstance3 IN %(calibration_fields)s
[display]
label = Display Mode
type = list
default = Logo + QR Code
description = Settings for startup display
list = White Screen, Logo, Logo + QR Code, Off
category = OBP40 Display
capabilities = obp40:true
[displaycolor]
label = Inverted Display Mode
type = list
default = Normal
description = Invert display to white letters on black background [Normal|Inverse]
list = Normal, Inverse
category = OBP40 Display
capabilities = obp40:true
[statusLine]
label = Status Line
type = boolean
default = true
description = Show status line [on|off]
category = OBP40 Display
capabilities = obp40:true
[timeSource]
label = Status Time Source
type = list
default = GPS
description = Data source for date and time display in status line [RTC|iRTC|GPS]
dict =
iRTC:Internal real time clock (iRTC)
RTC:External real time clock (RTC)
GPS:External time via bus (GPS)
category = OBP40 Display
capabilities = obp40:true
[refresh]
label = Refresh
type = boolean
default = true
description = Refresh e-paper display after each new page request to reduce ghost effects [on|off]
category = OBP40 Display
capabilities = obp40:true
[fastRefresh]
label = Fast Refresh
type = boolean
default = false
description = Fast refresh for e-paper display [on|off]
category = OBP40 Display
capabilities = obp40:true
[fullRefreshTime]
label = Full Refresh Time [min]
type = number
default = 1
description = E-Paper full refresh time all [1...10 min]
check = checkMinMax
min = 1
max = 10
category = OBP40 Display
capabilities = obp40:true
[holdvalues]
label = Hold Values
type = boolean
default = false
description = Retain old values when data stream stops [on|off]
category = OBP40 Display
capabilities = obp40:true
[valueprecision]
label = Display value precision
type = list
default = 2
description = Maximum number of decimal places to display [1|2]
list = 1, 2
category = OBP40 Display
capabilities = obp40:true
[headerFormat]
label = Header Format
type = list
default = TEXT
description = Header format: Text or Symbols
dict =
TEXT:Text
ICON:Symbols
category = OBP40 Display
capabilities = obp40:true
[backlight]
label = Backlight Mode
type = list
default = off
description = Settings for automatic backlight mode
list = Off, Control by Sun, Control by Bus, Control by Time, Control by Key, On
category = OBP40 Display
capabilities = obp40:false
[blColor]
label = Backlight Color
type = list
default = Red
description = Backlight color
list = Red, Orange, Yellow, Green, Blue, Aqua, Violet, White
category = OBP40 Display
capabilities = obp40:false
[blBrightness]
label = Brightness [%%]
type = number
default = 50
description = Backlight brightness [20...100%%]
check = checkMinMax
min = 20
max = 100
category = OBP40 Display
capabilities = obp40:false
[flashLED]
label = Flash LED Mode
type = list
default = Off
description = Settings for flash LED
list = Off, Bus Data, GPS Fix Lost, Limit Violation
category = OBP40 Display
capabilities = obp40:false
[buzzerError]
label = Buzzer Error
type = boolean
default = false
description = Sound on error [on|off]
category = OBP40 Buzzer
capabilities = obp40:false
[buzzerGps]
label = Buzzer GPS Fix
type = boolean
default = false
description = Sound on missing or lost GPS fix
category = OBP40 Buzzer
capabilities = obp40:false
[buzzerLim]
label = Buzzer by Limits
type = boolean
default = false
description = Sound on limit violation
category = OBP40 Buzzer
capabilities = obp40:false
[buzzerMode]
label = Buzzer Mode
type = list
default = Off
description = Settings for buzzer behaviour
list = Off, Short Single Beep, Longer Single Beep, Beep until Confirmation
category = OBP40 Buzzer
capabilities = obp40:false
[buzzerPower]
label = Buzzer Power [%%]
type = number
default = 50
description = Buzzer loudness [0...100%%]
check = checkMinMax
min = 0
max = 100
category = OBP40 Buzzer
capabilities = obp40:false
[visiblePages]
label = Number of Pages
type = number
default = 10
description = Number of visible data pages [1...10]
check = checkMinMax
min = 1
max = 10
category = OBP40 Pages
capabilities = obp40:true
[startPage]
label = Start Page
type = number
default = 1
description = First page number to display after device startup
check = checkMinMax
min = 1
max = 10
category = OBP40 Pages
capabilities = obp40:true
[systemPage]
label = System Page
type = boolean
default = false
description = Use wheel button for system page or direct deep sleep mode
category = OBP40 Pages
capabilities = obp40:true
[imageFormat]
label = Screenshot Format
type = list
default = PBM
description = Graphics file format for screenshots [GIF|PBM|BMP]
dict =
GIF:Compressed image (GIF)
PBM:Portable bitmap (PBM)
BMP:Windows bitmap (BMP)
category = OBP40 Pages
capabilities = obp40:true

857
lib/obp60task/obp60.conf Normal file
View File

@@ -0,0 +1,857 @@
[DEFAULT]
calibration_fields = AWA, AWS, COG, DBT, HDM, PRPOS, RPOS, SOG, STW, TWA, TWS, TWD, WTemp
[deviceName]
label = system name
type = string
default = OBP60V2
description = system name, used for the access point and for services
check = checkSystemName
category = system
[timeServer]
label = time server
type = string
default = pool.ntp.org
description = NTP time server. Use only one hostname or IP address
category = wifi client
capabilities = obp60:true
[timeZone]
label = Time Zone
type = number
default = 0.00
description = Time zone [UTC -12...+14]
check = checkMinMax
min = -12.0
max = 14.0
category = OBP60 Settings
capabilities = obp60:true
[homeLAT]
label = Home latitude
type = number
default =
description = Latitude of boat home location [-90.0...+90.0]
check = checkMinMax
min = -90.0
max = 90.0
category = OBP60 Settings
capabilities = obp60:true
[homeLON]
label = Home longitude
type = number
default =
description = Longitude of boat home location [-180.0...+180.0]
check = checkMinMax
min = -180.0
max = 180.0
category = OBP60 Settings
capabilities = obp60:true
[draft]
label = Boat Draft [m]
type = number
default = 0.00
description = The draft of the boat [0...50m]
check = checkMinMax
min = 0.0
max = 50.0
category = OBP60 Settings
capabilities = obp60:true
[chainLength]
label = Anchor Chain Length [m]
type = number
default = 0
description = The length of the anchor chain [0...255m]
check = checkMinMax
min = 0
max = 255
category = OBP60 Settings
capabilities = obp60:true
[fuelTank]
label = Fuel Tank [l]
type = number
default = 0
description = Fuel tank capacity [0...5000l]
check = checkMinMax
min = 0
max = 5000
category = OBP60 Settings
capabilities = obp60:true
[fuelConsumption]
label = Fuel Consuption [l/h]
type = number
default = 0.00
description = Medium fuel consumption [0...1000l/h]
check = checkMinMax
min = 0.0
max = 1000.0
category = OBP60 Settings
capabilities = obp60:true
[waterTank]
label = Water Tank [l]
type = number
default = 0
description = Water tank capacity [0...5000l]
check = checkMinMax
min = 0
max = 5000
category = OBP60 Settings
capabilities = obp60:true
[wasteTank]
label = Waste Tank [l]
type = number
default = 0
description = Waste tank capacity [0...5000l]
check = checkMinMax
min = 0
max = 5000
category = OBP60 Settings
capabilities = obp60:true
[batteryVoltage]
label = Battery Voltage [V]
type = list
default = 12V
description = Battery Voltage [12V|24V]
list = 12V, 24V
category = OBP60 Settings
capabilities = obp60:true
[batteryType]
label = Battery Type
type = list
default = Pb
description = Type of battery [Pb|Gel|AGM|LiFePo4]
list = Pb, Gel, AGM, LiFePo4
category = OBP60 Settings
capabilities = obp60:true
[batteryCapacity]
label = Battery Capacity [Ah]
type = number
default = 0.0
description = Battery capacity [0...10000Ah]
check = checkMinMax
min = 0.0
max = 10000.0
category = OBP60 Settings
capabilities = obp60:true
[solarPower]
label = Solar Power [W]
type = number
default = 0.0
description = Solar power [0...10000W]
check = checkMinMax
min = 0.0
max = 10000.0
category = OBP60 Settings
capabilities = obp60:true
[genPower]
label = Genarator Power [W]
type = number
default = 0.0
description = Generator power [0...10000W]
check = checkMinMax
min = 0.0
max = 10000.0
category = OBP60 Settings
capabilities = obp60:true
[trackStep]
label = angle [deg]
type = number
default = 3.0
description = track step offset [1...12deg]
check = checkMinMax
min = 1.0
max = 12.0
category = OBP60 Settings
capabilities = obp60:true
[calcTrueWnds]
label = Calculate True Wind
type = boolean
default = false
description = If not available, calculate true wind data from appearant wind and other boat data
category = OBP60 Settings
capabilities = obp60:true
[lengthFormat]
label = Length Format
type = list
default = m
description = Length format [m|ft]
list = m, ft
category = OBP60 Units
capabilities = obp60:true
[distanceFormat]
label = Distance Format
type = list
default = nm
description = Distance format [m|km|nm]
list = m, km, nm
category = OBP60 Units
capabilities = obp60:true
[speedFormat]
label = Speed Format
type = list
default = kn
description = Distance format [m/s|km/h|kn]
list = m/s, km/h, kn
category = OBP60 Units
capabilities = obp60:true
[windspeedFormat]
label = Wind Speed Format
type = list
default = kn
description = Wind speed format [m/s|km/h|kn|bft]
list = m/s, km/h, kn, bft
category = OBP60 Units
capabilities = obp60:true
[tempFormat]
label = Temperature Format
type = list
default = C
description = Temperature format [K|C|F]
list = K, C, F
category = OBP60 Units
capabilities = obp60:true
[dateFormat]
label = Date Format
type = list
default = DE
description = Date format [DE|GB|US|ISO] DE: 31.12.2022, GB: 31/12/2022, US: 12/31/2022, ISO: 2022-12-31
list = DE, GB, US, ISO
category = OBP60 Units
capabilities = obp60:true
[cpuSpeed]
label = CPU Speed [MHz]
type = list
default = 160
description = CPU speed in MHz [80|160|240]
list = 80, 160, 240
category = OBP60 Hardware
capabilities = obp60:true
[useRTC]
label = RTC Modul
type = list
default = DS1388
description = Use internal RTC module type [off|DS1388]
list = off, DS1388
category = OBP60 Hardware
capabilities = obp60:true
[useGPS]
label = GPS Sensor
type = list
default = ATGM336H
description = Use internal GPS module type [off|NEO-6M|NEO-M8N|ATGM336H]
list = off, NEO-6M, NEO-M8N, ATGM336H
category = OBP60 Hardware
capabilities = obp60:true
[hdopAccuracy]
label = HDOP Accuracy [m]
type = number
default = 20
description = HDOP ccuracy in m for a valid GPS signal [1...50]
check = checkMinMax
min = 1
max = 50
category = OBP60 Hardware
capabilities = obp60:true
[useEnvSensor]
label = Env. Sensor
type = list
default = BMP280
description = Use internal or external environment sensor via I2C bus [off|BME280|BMP280|BMP180|BMP085|HTU21|SHT21]
list = off, BME280, BMP280, BMP180, BMP085, HTU21, SHT21
category = OBP60 Hardware
capabilities = obp60:true
[usePowSensor1]
label = Battery Sensor
type = list
default = off
description = Use external power management sensor via I2C bus for battery [off|INA219|INA226|]
list = off, INA219, INA226
category = OBP60 Hardware
capabilities = obp60:true
[shunt1]
label = Battery Shunt
type = list
default = 10
description = Shunt current value [10A|50A|100A|200A|300A|400A|500A]
list = 10, 50, 100, 200, 300, 400, 500
category = OBP60 Hardware
capabilities = obp60:true
[usePowSensor2]
label = Solar Sensor
type = list
default = off
description = Use external power management sensor via I2C bus for solar panels [off|INA219|INA226|]
list = off, INA219, INA226
category = OBP60 Hardware
capabilities = obp60:true
[shunt2]
label = Solar Shunt
type = list
default = 10
description = Shunt current value [10A|50A|100A|200A|300A|400A|500A]
list = 10, 50, 100, 200, 300, 400, 500
category = OBP60 Hardware
capabilities = obp60:true
[usePowSensor3]
label = Gen. Sensor
type = list
default = off
description = Use external power management sensor via I2C bus for generator [off|INA219|INA226|]
list = off, INA219, INA226
category = OBP60 Hardware
capabilities = obp60:true
[shunt3]
label = Gen. Shunt
type = list
default = 10
description = Shunt current value [10A|50A|100A|200A|300A|400A|500A] @ 75mV
list = 10, 50, 100, 200, 300, 400, 500
category = OBP60 Hardware
capabilities = obp60:true
[useRotSensor]
label = Rot. Sensor
type = list
default = off
description = Use external rotation sensor via I2C bus [off|AS5600]
list = off, AS5600
category = OBP60 Hardware
capabilities = obp60:true
[rotFunction]
label = Rot. Function
type = list
default = off
description = Function for rotation sensor [off|Rudder|Wind|Mast|Keel|Trim|Boom]
list = off, Rudder, Wind, Mast, Keel, Trim, Boom
category = OBP60 Hardware
capabilities = obp60:true
[rotOffset]
label = Rot. Offset
type = number
default = 0
description = Offset for rotation sensor [-180°...+180°]
check = checkMinMax
min = -180
max = 180
category = OBP60 Hardware
capabilities = obp60:true
[rollLimit]
label = Roll Limit
type = number
default = 25
description = Limit violation for roll angle [-90°...+90°]
check = checkMinMax
min = -90
max = 90
category = OBP60 Hardware
capabilities = obp60:true
[rollOffset]
label = Roll Offset
type = number
default = 0
description = Roll offset angle [-45°...+45°]
check = checkMinMax
min = -45
max = 45
category = OBP60 Hardware
capabilities = obp60:true
[pitchOffset]
label = Pitch Offset
type = number
default = 0
description = Pitch offset angle [-45°...+45°]
check = checkMinMax
min = -45
max = 45
category = OBP60 Hardware
capabilities = obp60:true
[useTempSensor]
label = Temp. Sensor
type = boolean
default = off
description = Use max. 8 external 1Wire devices [off|DS18B20]
list = off, DS18B20
category = OBP60 Hardware
capabilities = obp60:true
[powerMode]
label = Power Mode
type = list
default = Max Power
description = Settings for power mode
list = Max Power, Only 5.0V, Min Power
category = OBP60 Hardware
capabilities = obp60:true
[underVoltage]
label = Undervoltage
type = boolean
default = false
description = Switch off device if voltage drops below 9V [on|off]
category = OBP60 Hardware
capabilities = obp60:true
[useSimuData]
label = Simulation Data
type = boolean
default = false
description = Use simulation data when bus data are missing [on|off]
category = OBP60 Hardware
capabilities = obp60:true
[tSensitivity]
label = Touch Sensitivity [%%]
type = number
default = 100
description = Touch sensitivity [0...100%%] for sensor buttons
check = checkMinMax
min = 0
max = 100
category = OBP60 Calibrations
capabilities = obp60:true
[vOffset]
label = VSensor Offset
type = number
default = -1.00
description = Offset for internal voltage sensor (ESP32)
category = OBP60 Calibrations
capabilities = obp60:true
[vSlope]
label = VSensor Slope
type = number
default = 1.00
description = Slope for internal voltage sensor (ESP32)
category = OBP60 Calibrations
capabilities = obp60:true
[calInstance1]
label = Calibration Data Instance 1
type = list
default = ---
description = Data instance for calibration
list = ---, %(calibration_fields)s
category = OBP60 Calibrations
capabilities = obp60:true
[calOffset1]
label = Data Instance 1 Calibration Offset
type = number
default = 0.00
description = Offset for data instance 1
category = OBP60 Calibrations
capabilities = obp60:true
condition = calInstance1 IN %(calibration_fields)s
[calSlope1]
label = Data Instance 1 Calibration Slope
type = number
default = 1.00
description = Slope for data instance 1
category = OBP60 Calibrations
capabilities = obp60:true
condition = calInstance1 IN %(calibration_fields)s
[calSmooth1]
label = Data Instance 1 Smoothing
type = number
default = 0
description = Smoothing factor [0..10]; 0 = no smoothing
check = checkMinMax
min = 0
max = 10
category = OBP60 Calibrations
capabilities = obp60:true
condition = calInstance1 IN %(calibration_fields)s
[calInstance2]
label = Calibration Data Instance 2
type = list
default = ---
description = Data instance for calibration
list = ---, %(calibration_fields)s
category = OBP60 Calibrations
capabilities = obp60:true
[calOffset2]
label = Data Instance 2 Calibration Offset
type = number
default = 0.00
description = Offset for data instance 2
category = OBP60 Calibrations
capabilities = obp60:true
condition = calInstance2 IN %(calibration_fields)s
[calSlope2]
label = Data Instance 2 Calibration Slope
type = number
default = 1.00
description = Slope for data instance 2
category = OBP60 Calibrations
capabilities = obp60:true
condition = calInstance2 IN %(calibration_fields)s
[calSmooth2]
label = Data Instance 2 Smoothing
type = number
default = 0
description = Smoothing factor [0..10]; 0 = no smoothing
check = checkMinMax
min = 0
max = 10
category = OBP60 Calibrations
capabilities = obp60:true
condition = calInstance2 IN %(calibration_fields)s
[calInstance3]
label = Calibration Data Instance 3
type = list
default = ---
description = Data instance for calibration
list = ---, %(calibration_fields)s
category = OBP60 Calibrations
capabilities = obp60:true
[calOffset3]
label = Data Instance 3 Calibration Offset
type = number
default = 0.00
description = Offset for data instance 3
category = OBP60 Calibrations
capabilities = obp60:true
condition = calinstance3 IN %(calibration_fields)s
[calSlope3]
label = Data Instance 3 Calibration Slope
type = number
default = 1.00
description = Slope for data instance 3
category = OBP60 Calibrations
capabilities = obp60:true
condition = calinstance3 IN %(calibration_fields)s
[calSmooth3]
label = Data Instance 3 Smoothing
type = number
default = 0
description = Smoothing factor [0..10]; 0 = no smoothing
check = checkMinMax
min = 0
max = 10
category = OBP60 Calibrations
capabilities = obp60:true
condition = calinstance3 IN %(calibration_fields)s
[display]
label = Display Mode
type = list
default = Logo + QR Code
description = Settings for startup display
list = White Screen, Logo, Logo + QR Code, Off
category = OBP60 Display
capabilities = obp60:true
[displaycolor]
label = Inverted Display Mode
type = list
default = Normal
description = Invert display to white letters on black background [Normal|Inverse]
list = Normal, Inverse
category = OBP60 Display
capabilities = obp60:true
[statusLine]
label = Status Line
type = boolean
default = true
description = Show status line [on|off]
category = OBP60 Display
capabilities = obp60:true
[timeSource]
label = Status Time Source
type = list
default = GPS
description = Data source for date and time display in status line [RTC|GPS]
dict =
RTC:Real time clock (RTC)
GPS:Time via bus (GPS)
category = OBP60 Display
capabilities = obp60:true
[refresh]
label = Refresh
type = boolean
default = true
description = Refresh e-paper display after each new page request to reduce ghost effects [on|off]
category = OBP60 Display
capabilities = obp60:true
[fastRefresh]
label = Fast Refresh
type = boolean
default = false
description = Fast refresh for e-paper display [on|off]
category = OBP60 Display
capabilities = obp60:true
[fullRefreshTime]
label = Full Refresh Time [min]
type = number
default = 1
description = E-Paper full refresh time all [1...10 min]
check = checkMinMax
min = 1
max = 10
category = OBP60 Display
capabilities = obp60:true
[holdvalues]
label = Hold Values
type = boolean
default = false
description = Retain old values when data stream stops [on|off]
category = OBP60 Display
capabilities = obp60:true
[valueprecision]
label = Display value precision
type = list
default = 2
description = Maximum number of decimal places to display [1|2]
list = 1, 2
category = OBP60 Display
capabilities = obp60:true
[headerFormat]
label = Header Format
type = list
default = TEXT
description = Header format: Text or Symbols
dict =
TEXT:Text
ICON:Symbols
category = OBP60 Pages
capabilities = obp60:true
[backlight]
label = Backlight Mode
type = list
default = Control by Key
description = Settings for automatic backlight mode
list = Off, Control by Sun, Control by Bus, Control by Time, Control by Key, On
category = OBP60 Display
capabilities = obp60:true
[blColor]
label = Backlight Color
type = list
default = Red
description = Backlight color
list = Red, Orange, Yellow, Green, Blue, Aqua, Violet, White
category = OBP60 Display
capabilities = obp60:true
[blBrightness]
label = Brightness [%%]
type = number
default = 50
description = Backlight brightness [20...100%%]
check = checkMinMax
min = 20
max = 100
category = OBP60 Display
capabilities = obp60:true
[flashLED]
label = Flash LED Mode
type = list
default = Limit Violation
description = Settings for flash LED
list = Off, Bus Data, GPS Fix Lost, Limit Violation
category = OBP60 Display
capabilities = obp60:true
[buzzerError]
label = Buzzer Error
type = boolean
default = false
description = Sound on error [on|off]
category = OBP60 Buzzer
capabilities = obp60:true
[buzzerGps]
label = Buzzer GPS Fix
type = boolean
default = false
description = Sound on missing or lost GPS fix
category = OBP60 Buzzer
capabilities = obp60:true
[buzzerLim]
label = Buzzer by Limits
type = boolean
default = false
description = Sound on limit violation
category = OBP60 Buzzer
capabilities = obp60:true
[buzzerMode]
label = Buzzer Mode
type = list
default = Off
description = Settings for buzzer behaviour
list = Off, Short Single Beep, Longer Single Beep, Beep until Confirmation
category = OBP60 Buzzer
capabilities = obp60:true
[buzzerPower]
label = Buzzer Power [%%]
type = number
default = 50
description = Buzzer loudness [0...100%%]
check = checkMinMax
min = 0
max = 100
category = OBP60 Buzzer
capabilities = obp60:true
[visiblePages]
label = Number of Pages
type = number
default = 10
description = Number of visible data pages [1...10]
check = checkMinMax
min = 1
max = 10
category = OBP60 Pages
capabilities = obp60:true
[startPage]
label = Start Page
type = number
default = 1
description = First page number to display after device startup
check = checkMinMax
min = 1
max = 10
category = OBP60 Pages
capabilities = obp60:true
[imageFormat]
label = Screenshot Format
type = list
default = PBM
description = Graphics file format for screenshots [GIF|PBM|BMP]
dict =
GIF:Compressed image (GIF)
PBM:Portable bitmap (PBM)
BMP:Windows bitmap (BMP)
category = OBP60 Pages
capabilities = obp60:true
# WIP
[trackerType]
label = Tracker Type
type = list
default = off
description = Type of tracker to use [OFF|SDCARD|SERVER|HERO]
dict = OFF:No tracker
SDCARD:Log to SD-Card
SERVER:Log to Server
HERO:Connect with Regatta Hero
category = OBP60 Pages
capabilities = obp60:true
[trackerOrganization]
label = Tracker team
type = string
default = demo
description = Tracker organization for login
category = OBP60 Pages
[trackerPasscode]
label = Tracker team
type = password
default = 291758
description = Your tracker team you belong to. E.g. short name of association
category = OBP60 Pages
[trackerTeam]
label = Tracker team
type = string
default = none
description = Your tracker team you belong to. E.g. short name of association
category = OBP60 Pages
[trackerHandicap]
label = Boat handicap
type = number
default = 100
check = checkMinMax
min = 50
max = 1000
description = The handicap value of your boat. E.g. yardstick value
category = OBP60 Pages
[boatName]
label = Boat Name
type = string
default = Unsinkbar II
description = name of your boat
category = OBP60 Pages
[boatClass]
label = Boat Class
type = string
default = One off
description = Class name of your boat if available or "One off"
category = OBP60 Pages
[sailNumber]
label = Sail number
type = string
default = GER 11
description = Identification number on sail
category = OBP60 Pages

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include "GwApi.h" #include "GwApi.h"
//we only compile for some boards //we only compile for some boards
@@ -41,5 +42,24 @@
#ifdef BOARD_OBP40S3 #ifdef BOARD_OBP40S3
DECLARE_CAPABILITY(obp40,true) DECLARE_CAPABILITY(obp40,true)
#endif #endif
DECLARE_STRING_CAPABILITY(HELP_URL, "https://obp60-v2-docu.readthedocs.io/de/latest/"); // Link to help pages #ifdef BOARD_OBP60S3
DECLARE_STRING_CAPABILITY(HELP_URL, "https://obp60-v2-docu.readthedocs.io/en/latest/"); // Link to help pages
#endif
#ifdef BOARD_OBP40S3
DECLARE_STRING_CAPABILITY(HELP_URL, "https://obp40-v1-docu.readthedocs.io/en/latest/"); // Link to help pages
#endif
class BoatValueList{
public:
static const int MAXVALUES=100;
//we create a list containing all our BoatValues
//this is the list we later use to let the api fill all the values
//additionally we put the necessary values into the paga data - see below
GwApi::BoatValue *allBoatValues[MAXVALUES];
int numValues=0;
bool addValueToList(GwApi::BoatValue *v);
//helper to ensure that each BoatValue is only queried once
GwApi::BoatValue *findValueOrCreate(String name);
};
#endif #endif

View File

@@ -8,7 +8,6 @@ default_envs =
obp40_s3 obp40_s3
[env:obp60_s3] [env:obp60_s3]
platform = espressif32@6.8.1
board_build.variants_dir = variants board_build.variants_dir = variants
#board = obp60_s3_n8 #ESP32-S3 N8, 8MB flash, no PSRAM #board = obp60_s3_n8 #ESP32-S3 N8, 8MB flash, no PSRAM
#board = obp60_s3_n16 #ESP32-S3 N16,16MB flash, no PSRAM, zero series #board = obp60_s3_n16 #ESP32-S3 N16,16MB flash, no PSRAM, zero series
@@ -16,20 +15,18 @@ board_build.variants_dir = variants
board = obp60_s3_n16r8 #ESP32-S3 N16R8, 16MB flash, 8MB PSRAM, production series board = obp60_s3_n16r8 #ESP32-S3 N16R8, 16MB flash, 8MB PSRAM, production series
#board_build.partitions = default_8MB.csv #ESP32-S3 N8, 8MB flash #board_build.partitions = default_8MB.csv #ESP32-S3 N8, 8MB flash
board_build.partitions = default_16MB.csv #ESP32-S3 N16, 16MB flash board_build.partitions = default_16MB.csv #ESP32-S3 N16, 16MB flash
board_name = OBP60
framework = arduino framework = arduino
lib_deps = lib_deps =
${basedeps.lib_deps} ${basedeps.lib_deps}
Wire Wire
SPI SPI
ESP32time ESP32time
esphome/AsyncTCP-esphome@2.0.1
robtillaart/PCF8574@0.3.9 robtillaart/PCF8574@0.3.9
adafruit/Adafruit Unified Sensor @ 1.1.13 adafruit/Adafruit Unified Sensor @ 1.1.13
blemasle/MCP23017@2.0.0 blemasle/MCP23017@2.0.0
adafruit/Adafruit BusIO@1.5.0 adafruit/Adafruit BusIO@1.5.0
adafruit/Adafruit GFX Library@1.11.9 adafruit/Adafruit GFX Library@1.11.9
#zinggjm/GxEPD2@1.5.8
#https://github.com/ZinggJM/GxEPD2
https://github.com/thooge/GxEPD2 https://github.com/thooge/GxEPD2
sstaub/Ticker@4.4.0 sstaub/Ticker@4.4.0
adafruit/Adafruit BMP280 Library@2.6.2 adafruit/Adafruit BMP280 Library@2.6.2
@@ -54,6 +51,8 @@ build_flags=
# -D DISPLAY_GYE042A87 #alternativ E-Ink display from Genyo Optical, R10 2.2 ohm - medium # -D DISPLAY_GYE042A87 #alternativ E-Ink display from Genyo Optical, R10 2.2 ohm - medium
# -D DISPLAY_SE0420NQ04 #alternativ E-Ink display from SID Technology, R10 2.2 ohm - bad (burn in effects) # -D DISPLAY_SE0420NQ04 #alternativ E-Ink display from SID Technology, R10 2.2 ohm - bad (burn in effects)
# -D DISPLAY_ZJY400300-042CAAMFGN #alternativ E-Ink display from ZZE Technology, R10 2.2 ohm - very good # -D DISPLAY_ZJY400300-042CAAMFGN #alternativ E-Ink display from ZZE Technology, R10 2.2 ohm - very good
# -D ENABLE_TRUEWIND # calculate true wind data (default off)
# -D ENABLE_CALIBRATION # boat data calibration (default off)
${env.build_flags} ${env.build_flags}
#CONFIG_ESP_TASK_WDT_TIMEOUT_S = 10 #Task Watchdog timeout period (seconds) [1...60] 5 default #CONFIG_ESP_TASK_WDT_TIMEOUT_S = 10 #Task Watchdog timeout period (seconds) [1...60] 5 default
upload_port = /dev/ttyACM0 #OBP60 download via USB-C direct upload_port = /dev/ttyACM0 #OBP60 download via USB-C direct
@@ -62,26 +61,22 @@ upload_speed = 230400
monitor_speed = 115200 monitor_speed = 115200
[env:obp40_s3] [env:obp40_s3]
platform = espressif32@6.8.1
board_build.variants_dir = variants board_build.variants_dir = variants
board = obp40_s3_n8r8 #ESP32-S3 N8R8, 8MB flash, 8MB PSRAM, OBP60 clone (CrowPanel 4.2) board = obp40_s3_n8r8 #ESP32-S3 N8R8, 8MB flash, 8MB PSRAM, OBP60 clone (CrowPanel 4.2)
board_build.partitions = default_8MB.csv #ESP32-S3 N8, 8MB flash board_build.partitions = default_8MB.csv #ESP32-S3 N8, 8MB flash
board_name = OBP40
custom_config = config_obp40.json custom_config = config_obp40.json
framework = arduino framework = arduino
lib_deps = lib_deps =
${basedeps.lib_deps} ${basedeps.lib_deps}
Wire Wire
SPI SPI
SD
ESP32time ESP32time
esphome/AsyncTCP-esphome@2.0.1
robtillaart/PCF8574@0.3.9 robtillaart/PCF8574@0.3.9
adafruit/Adafruit Unified Sensor @ 1.1.13 adafruit/Adafruit Unified Sensor @ 1.1.13
blemasle/MCP23017@2.0.0 blemasle/MCP23017@2.0.0
adafruit/Adafruit BusIO@1.5.0 adafruit/Adafruit BusIO@1.5.0
adafruit/Adafruit GFX Library@1.11.9 adafruit/Adafruit GFX Library@1.11.9
#zinggjm/GxEPD2@1.5.8
#https://github.com/ZinggJM/GxEPD2
https://github.com/thooge/GxEPD2 https://github.com/thooge/GxEPD2
sstaub/Ticker@4.4.0 sstaub/Ticker@4.4.0
adafruit/Adafruit BMP280 Library@2.6.2 adafruit/Adafruit BMP280 Library@2.6.2
@@ -99,8 +94,10 @@ build_flags=
-D HARDWARE_V10 #OBP40 hardware revision V1.0 SKU:DIE07300S V1.1 (CrowPanel 4.2) -D HARDWARE_V10 #OBP40 hardware revision V1.0 SKU:DIE07300S V1.1 (CrowPanel 4.2)
-D DISPLAY_GDEY042T81 #new E-Ink display from Good Display (Waveshare), R10 2.2 ohm - good (contast lost by shunshine) -D DISPLAY_GDEY042T81 #new E-Ink display from Good Display (Waveshare), R10 2.2 ohm - good (contast lost by shunshine)
#-D DISPLAY_ZJY400300-042CAAMFGN #alternativ E-Ink display from ZZE Technology, R10 2.2 ohm - very good #-D DISPLAY_ZJY400300-042CAAMFGN #alternativ E-Ink display from ZZE Technology, R10 2.2 ohm - very good
#-D LIPO_ACCU_1200 #Hardware extension, LiPo accu 3,7V 1200mAh # -D LIPO_ACCU_1200 #Hardware extension, LiPo accu 3,7V 1200mAh
#-D VOLTAGE_SENSOR #Hardware extension, LiPo voltage sensor with two resistors # -D VOLTAGE_SENSOR #Hardware extension, LiPo voltage sensor with two resistors
# -D ENABLE_TRUEWIND # calculate true wind data (default off)
# -D ENABLE_CALIBRATION # boat data calibration (default off)
${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

View File

@@ -0,0 +1,17 @@
import subprocess
# Import("env")
def get_firmware_specifier_build_flag():
#ret = subprocess.run(["git", "describe"], stdout=subprocess.PIPE, text=True) #Uses only annotated tags
ret = subprocess.run(["git", "describe", "--tags"], stdout=subprocess.PIPE, text=True) #Uses any tags
build_version = ret.stdout.strip()
build_flag = "-D AUTO_VERSION=\\\"" + build_version + "\\\""
print ("Firmware Revision: " + build_version)
return (build_flag)
#env.Append(
# BUILD_FLAGS=[get_firmware_specifier_build_flag()]
#)
get_firmware_specifier_build_flag()

172
lib/obp60task/utils/make_json.py Executable file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/python
"""
Convert config file to JSON
The JSON contains array of single fields. There is no hierarchy.
Fields are being grouped in GUI with "category".
A group is shown if a minimum of one field is visible.
Hints
capabilities is a dictionary
field:[true | false]
optional comma separated multiple values
"""
import os
import sys
import getopt
import configparser
from io import StringIO
from pyparsing import alphas, alphanums, Word, Literal, Combine, Group, Forward, ZeroOrMore, delimitedList
__author__ = "Thomas Hooge"
__copyright__ = "Copyleft 2025, all rights reversed"
__version__ = "0.1"
__email__ = "thomas@hoogi.de"
__status__ = "Development"
infile = None
outfile = ""
force = False # overwrite outfile
# Variables for condition parsing
fieldname = Combine(Word(alphas, exact=1) + Word(alphanums, max=15))
fieldvalue = Word(alphanums, max=16)
equals = Literal("=")
in_op = Literal("IN")
and_op = Literal("AND")
or_op = Literal("OR")
comparison = Group(fieldname + equals + fieldvalue) \
| Group(fieldname + in_op + delimitedList(fieldvalue))
expr = Forward()
expr <<= comparison + ZeroOrMore((and_op | or_op) + comparison)
def parse_condition(condition):
try:
result = expr.parseString(condition, parseAll=True)
except Exception as e:
return ""
out = StringIO()
andlist = []
for token in result:
# list: field = value or field IN value [, value ...]
# str: AND, OR
# combine ANDs and output reaching OR
if type(token) == str:
if token == "OR":
andstr = ",\n".join(andlist)
out.write(f'\t\t{{ {andstr} }},\n')
andlist = []
else:
if token[1] == '=':
andlist.append(f'"{token[0]}": "{token[2]}"')
elif token[1] == 'IN':
n = len(token) - 2
if n == 1:
# no list, write single value
andlist.append(f'"{token[0]}": "{token[2]}"')
else:
# write list
inlist = '", "'.join(token[2:])
andlist.append(f'"{token[0]}": [ "{inlist}" ]\n')
if len(andlist) > 0:
out.write("\t\t{{ {} }}".format(", ".join(andlist)))
return out.getvalue()
def create_flist():
flist = []
for field in config.sections():
properties = [f'\t"name": "{field}"']
for prop, val in config.items(field):
if prop in ["label", "type", "default", "description", "category", "check"]:
properties.append(f'\t"{prop}": "{val}"')
elif prop == "capabilities":
# multiple values possible
capas = []
for capa in val.split(','):
k, v = capa.split(':')
capas.append(f'"{k.strip()}":"{v.strip()}"')
capalist = ','.join(capas)
properties.append(f'\t"{prop}": {{{capalist}}}')
elif prop in ("min", "max"):
properties.append(f'\t"{prop}": {val}')
elif prop == "list":
entries = '", "'.join([x.strip() for x in val.split(',')])
properties.append(f'\t"list": ["{entries}"]')
elif prop == "dict":
d = {}
for l in val.splitlines():
if len(l) < 3:
continue
k, v = l.split(':')
d[k.strip()] = v.strip()
lines = []
for k,v in d.items():
lines.append(f'\t\t{{"l":"{v}","v":"{k}"}}')
entries = ",\n".join(lines)
properties.append(f'\t"list": [\n{entries}\n\t]')
elif prop == "condition":
jsoncond = parse_condition(val)
properties.append(f'\t"{prop}": [\n{jsoncond}\n\t]\n')
else:
pass # ignore unknown stuff
fieldprops = ",\n".join(properties)
flist.append(f'{{\n{fieldprops}\n}}')
return flist
def usage():
print("{} v{}".format(os.path.basename(__file__), __version__))
print(__copyright__)
print()
print("Command line options")
print(" -c --config config file name to use")
print(" -j --json json file name to generate")
print(" -f force overwrite of existing json file")
print(" -h show this help")
print()
if __name__ == '__main__':
try:
options, remainder = getopt.getopt(sys.argv[1:], 'c:j:fh', ['config=', 'json='])
except getopt.GetoptError as e:
print(e)
sys.exit(1)
filename = None
for opt, arg in options:
if opt in ('-c', '--config'):
infile = arg
elif opt in ('-j', '--json'):
outfile = arg
elif opt == '-h':
usage()
sys.exit(0)
elif opt == '-f':
force = True
if not infile:
print("Error: config filename missing")
sys.exit(1)
if not os.path.isfile(infile):
print(f"Error: configuration file '{filename} not found'")
sys.exit(1)
if os.path.isfile(outfile) and not force:
print(f"Error: json file '{outfile}' already exists")
sys.exit(1)
config = configparser.ConfigParser()
ret = config.read(infile)
if len(ret) == 0:
print(f"ERROR: Config file '{infile}' not found")
sys.exit(1)
flist = create_flist()
out = "[\n{}\n]\n".format(",\n".join(flist))
if not outfile:
# print to console
print(out)
else:
# write to file
with open(outfile, "w") as fh:
fh.write(out)

View File

@@ -0,0 +1,61 @@
#!/usr/bin/python
#
# Convert a Gimp-created XBM file to bitmap useable by drawBitmap()
#
import os
import sys
import re
from PIL import Image
if len(sys.argv) < 2:
print("Usage: xbmconvert.py <filename>")
sys.exit(1)
xbmfilename = sys.argv[1]
if not os.path.isfile(xbmfilename):
print(f"The file '{xbmfilename}' does not exists.")
sys.exit(1)
im = Image.open(xbmfilename)
imname = "image"
with open(xbmfilename, 'r') as fh:
pattern = r'static\s+unsigned\s+char\s+(\w+)_bits$$$$'
for line in fh:
match = re.search(pattern, line)
if match:
imname = match.group(1)
break
bytecount = int(im.width * im.height / 8)
print(f"#ifndef _{imname.upper()}_H_")
print(f"#define _{imname.upper()}_H_ 1\n")
print(f"#define {imname}_width {im.width}")
print(f"#define {imname}_height {im.height}")
print(f"const unsigned char {imname}_bits[{bytecount}] PROGMEM = {{")
n = 0
print(" ", end='')
f = im.tobytes()
switched_bytes = bytearray()
for i in range(0, len(f), 2):
# Switch LSB and MSB
switched_bytes.append(f[i + 1]) # Append MSB
switched_bytes.append(f[i]) # Append LSB
#for b in im.tobytes():
for b in switched_bytes:
#b2 = 0
#for i in range(8):
# # b2 |= ((b >> i) & 1) << (7 - i)
# b2 <<= 1
# b2 |= b & 1
# b >>= 1
n += 1
print(f"0x{b:02x}", end='')
if n < bytecount:
print(', ', end='')
if n % 12 == 0:
print("\n ", end='')
print("};\n\n#endif")

View File

@@ -216,6 +216,10 @@ public:
{ {
return api->getLogger(); return api->getLogger();
} }
virtual Nmea2kTwai *getNMEA2000()
{
return api->getNMEA2000();
}
virtual GwBoatData *getBoatData() virtual GwBoatData *getBoatData()
{ {
return api->getBoatData(); return api->getBoatData();
@@ -428,4 +432,4 @@ void GwUserCode::handleWebRequest(const String &url,AsyncWebServerRequest *req){
} }
LOG_DEBUG(GwLog::DEBUG,"no task found for web request %s[%s]",url.c_str(),tname.c_str()); LOG_DEBUG(GwLog::DEBUG,"no task found for web request %s[%s]",url.c_str(),tname.c_str());
req->send(404, "text/plain", "not found"); req->send(404, "text/plain", "not found");
} }

View File

@@ -21,8 +21,8 @@ lib_deps =
ttlappalainen/NMEA2000-library @ 4.22.0 ttlappalainen/NMEA2000-library @ 4.22.0
ttlappalainen/NMEA0183 @ 1.10.1 ttlappalainen/NMEA0183 @ 1.10.1
ArduinoJson @ 6.18.5 ArduinoJson @ 6.18.5
AsyncTCP-esphome @ 2.0.1 ESP32Async/AsyncTCP @ 3.4.7
ottowinter/ESPAsyncWebServer-esphome@2.0.1 ESP32Async/ESPAsyncWebServer @ 3.8.0
FS FS
Preferences Preferences
ESPmDNS ESPmDNS
@@ -30,7 +30,7 @@ lib_deps =
Update Update
[env] [env]
platform = espressif32 @ 6.8.1 platform = espressif32 @ 6.11.0
framework = arduino framework = arduino
;platform_packages= ;platform_packages=
; framework-arduinoespressif32 @ 3.20017.0 ; framework-arduinoespressif32 @ 3.20017.0
@@ -56,6 +56,11 @@ lib_ldf_mode = off
monitor_speed = 115200 monitor_speed = 115200
build_flags = build_flags =
-D PIO_ENV_BUILD=$PIOENV -D PIO_ENV_BUILD=$PIOENV
# -D CONFIG_ASYNC_TCP_RUNNING_CORE=1 # async_tcp task core assignment (default is any core)
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096 # reduce the stack size (default is 16K)
-std=gnu++17
build_unflags =
-std=gnu++11
[sensors] [sensors]
; collect the libraries for sensors here ; collect the libraries for sensors here

View File

@@ -302,9 +302,15 @@ public:
if (newValid != list[i]->valid) list[i]->changed=true; if (newValid != list[i]->valid) list[i]->changed=true;
list[i]->valid=newValid; list[i]->valid=newValid;
if (newValid){ if (newValid){
double newValue=item->getDoubleValue(); if (item->getCurrentType() == GWTYPE_STRING) {
if (newValue != list[i]->value) list[i]->changed=true; String newValue=item->getStringValue();
list[i]->value=newValue; if (newValue != list[i]->svalue) list[i]->changed=true;
list[i]->svalue=newValue;
} else {
double newValue=item->getDoubleValue();
if (newValue != list[i]->value) list[i]->changed=true;
list[i]->value=newValue;
}
int newSource=item->getLastSource(); int newSource=item->getLastSource();
if (newSource != list[i]->source){ if (newSource != list[i]->source){
list[i]->source=newSource; list[i]->source=newSource;
@@ -333,6 +339,9 @@ public:
status.n2kTx=countNMEA2KOut.getGlobal(); status.n2kTx=countNMEA2KOut.getGlobal();
channels.fillStatus(status); channels.fillStatus(status);
} }
virtual Nmea2kTwai *getNMEA2000(){
return &NMEA2000;
}
virtual GwBoatData *getBoatData(){ virtual GwBoatData *getBoatData(){
return &boatData; return &boatData;
} }