1
0
mirror of https://github.com/thooge/esp32-nmea2000-obp60.git synced 2025-12-28 13:13:07 +01:00

52 Commits

Author SHA1 Message Date
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
81 changed files with 10397 additions and 7355 deletions

1
.gitignore vendored
View File

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

View File

@@ -501,7 +501,7 @@ def prebuild(env):
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")
version = "dev{}{}".format(datetime.now().strftime("%Y%m%d"), "-ext")
env.Append(CPPDEFINES=[('GWDEVVERSION',version)])
def cleangenerated(source, target, env):

View File

@@ -2,6 +2,7 @@
#define _GWAPI_H
#include "GwMessage.h"
#include "N2kMsg.h"
#include "Nmea2kTwai.h"
#include "NMEA0183Msg.h"
#include "GWConfig.h"
#include "GwBoatData.h"
@@ -23,6 +24,7 @@ class GwApi{
bool formatSet=false;
public:
double value=0;
String svalue="";
bool valid=false;
int source=-1;
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
* you need to use the request pattern as shown in GwExampleTask.cpp
*/
virtual Nmea2kTwai *getNMEA2000()=0;
virtual GwBoatData *getBoatData()=0;
virtual ~GwApi(){}
};

View File

@@ -2,11 +2,6 @@
#include <GwJsonDocument.h>
#include <ArduinoJson/Json/TextFormatter.hpp>
#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
{
@@ -15,7 +10,9 @@ public:
static int getType(const uint16_t &x) { return GWTYPE_UINT16; }
static int getType(const int16_t &x) { return GWTYPE_INT16; }
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 GwAisTargetList &x) { return GWTYPE_USER + 1; }
};
bool GwBoatItemBase::isValid(unsigned long now) const
@@ -252,6 +249,10 @@ static void writeToString(GwTextWriter *writer, const int16_t &value)
{
writer->writeInteger(value);
}
static void writeToString(GwTextWriter *writer, String value)
{
writer->writeString(value.c_str());
}
static void writeToString(GwTextWriter *writer, GwSatInfoList &value)
{
writer->writeInteger(value.getNumSats());
@@ -288,6 +289,8 @@ template class GwBoatItem<double>;
template class GwBoatItem<uint32_t>;
template class GwBoatItem<uint16_t>;
template class GwBoatItem<int16_t>;
template class GwBoatItem<String>;
void GwSatInfoList::houseKeeping(unsigned long ts)
{
if (ts == 0)
@@ -301,6 +304,7 @@ void GwSatInfoList::houseKeeping(unsigned long ts)
}),
sats.end());
}
void GwSatInfoList::update(GwSatInfo entry, unsigned long validTill)
{
entry.validTill = validTill;
@@ -343,6 +347,63 @@ void GwBoatDataSatList::toJsonDoc(GwJsonDocument *doc, unsigned long 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)
{
this->logger = logger;
@@ -512,6 +573,11 @@ bool convertToJson(const GwSatInfoList &si, JsonVariant &variant)
return variant.set(si.getNumSats());
}
bool convertToJson(const GwAisTargetList &si, JsonVariant &variant)
{
return variant.set(si.getNumTargets());
}
#ifdef _UNDEF
#include <ArduinoJson/Json/TextFormatter.hpp>

View File

@@ -9,6 +9,13 @@
#define GW_BOAT_VALUE_LEN 32
#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
//factor to convert from N2k/SI rad/s to current NMEA rad/min
#define ROT_WA_FACTOR 60
@@ -58,6 +65,7 @@ class GwBoatItemBase{
GWSC(formatRot);
GWSC(formatDate);
GWSC(formatTime);
GWSC(formatName);
protected:
int type;
unsigned long lastSet=0;
@@ -92,6 +100,7 @@ class GwBoatItemBase{
virtual int getLastSource(){return lastUpdateSource;}
virtual void refresh(unsigned long ts=0){uls(ts);}
virtual double getDoubleValue()=0;
virtual String getStringValue()=0;
String getName(){return name;}
const String & getFormat() const{return format;}
virtual void setInvalidTime(GwConfigHandler *cfg);
@@ -120,7 +129,17 @@ template<class T> class GwBoatItem : public GwBoatItemBase{
if (! isValid(millis())) return defaultv;
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 toJsonDoc(GwJsonDocument *doc, unsigned long minTime);
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
{
public:
@@ -235,7 +303,9 @@ class GwBoatData{
GWBOATDATA(double,XTE,formatXte) // cross track error
GWBOATDATA(double,WPLat,formatLatitude) // waypoint latitude
GWBOATDATA(double,WPLon,formatLongitude) // waypoint longitude
GWBOATDATA(String,WPName,formatName) // waypoint name
GWSPECBOATDATA(GwBoatDataSatList,SatInfo,GwSatInfoList::toType,formatFixed0);
GWSPECBOATDATA(GwBoatDataAisList,AisTarget,GwAisTargetList::toType,formatFixed0);
public:
GwBoatData(GwLog *logger, GwConfigHandler *cfg);
~GwBoatData();

View File

@@ -355,6 +355,7 @@ private:
AppendN2kRouteWPInfo(n2kMsg,destinationId,rmb.destID,rmb.latitude,rmb.longitude);
send(n2kMsg,msg.sourceId);
}
boatData->WPName->update(String(rmb.destID), msg.sourceId);
}
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());
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;
}
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].smooth = smooth;
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, "stored calibration data: %s, offset: %f, slope: %f, smoothing: %f", instance.c_str(),
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)
@@ -117,7 +117,7 @@ void CalibrationDataList::calibrateInstance(GwApi::BoatValue* boatDataValue, GwL
std::string format = "";
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 found in calibration data list", instance.c_str());
return;
} else if (!boatDataValue->valid) { // no valid boat data value, so we don't want to apply calibration data
calibMap[instance].isCalibrated = false;
@@ -127,7 +127,7 @@ void CalibrationDataList::calibrateInstance(GwApi::BoatValue* boatDataValue, GwL
slope = calibMap[instance].slope;
dataValue = boatDataValue->value;
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
dataValue = (dataValue * slope) + offset;
@@ -156,7 +156,7 @@ void CalibrationDataList::calibrateInstance(GwApi::BoatValue* boatDataValue, GwL
calibrationData.smoothInstance(boatDataValue, logger); // smooth the boat data value
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
return;
} 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;
} else {
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
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

@@ -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

@@ -14,6 +14,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){
uint16_t rt=f1;
@@ -59,12 +83,12 @@ static size_t ledsToBuffer(int numLeds,const Color *leds,uint8_t *buffer){
bool prepareGpio(GwLog *logger, uint8_t pin){
esp_err_t err=gpio_set_direction((gpio_num_t)pin,GPIO_MODE_OUTPUT);
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;
}
err=gpio_set_level((gpio_num_t)pin,0);
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 true;
@@ -90,8 +114,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);
if (err != ESP_OK){
LOG_DEBUG(GwLog::ERROR,"unable to initialize SPI bus %d,mosi=%d, error=%d",
(int)bus,-1,(int)err);
logger->logDebug(GwLog::ERROR, "unable to initialize SPI bus %d,mosi=%d, error=%d",
(int)bus, -1, (int)err);
return false;
}
spi_device_interface_config_t devcfg = {
@@ -109,16 +133,16 @@ bool prepareSpi(GwLog *logger,spi_host_device_t bus,spi_device_handle_t *device)
};
err=spi_bus_add_device(bus,&devcfg,device);
if (err != ESP_OK){
LOG_DEBUG(GwLog::ERROR,"unable to add device to SPI bus %d,mosi=%d, error=%d",
(int)bus,-1,(int)err);
logger->logDebug(GwLog::ERROR, "unable to add device to SPI bus %d,mosi=%d, error=%d",
(int)bus, -1, (int)err);
return false;
}
//slightly speed up the transactions
//as we are the only ones using the bus we can safely acquire it forever
err=spi_device_acquire_bus(*device,portMAX_DELAY);
if (err != ESP_OK){
LOG_DEBUG(GwLog::ERROR,"unable to acquire SPI bus %d,mosi=%d, error=%d",
(int)bus,-1,(int)err);
logger->logDebug(GwLog::ERROR,"unable to acquire SPI bus %d,mosi=%d, error=%d",
(int)bus, -1, (int)err);
return false;
}
return true;
@@ -148,7 +172,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);
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;
}
}
@@ -169,12 +193,12 @@ bool sendToLeds(GwLog *logger, uint8_t pin, int numLeds, Color *leds, spi_host_d
int64_t end = esp_timer_get_time();
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;
}
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)
{
@@ -187,10 +211,10 @@ bool sendToLeds(GwLog *logger, uint8_t pin, int numLeds, Color *leds, spi_host_d
void handleSpiLeds(void *param){
LedTaskData *taskData=(LedTaskData*)param;
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;
bool spiValid=false;
LOG_DEBUG(GwLog::ERROR,"SpiLed task started");
LOG_DEBUG(GwLog::ERROR, "SpiLed task started");
if (! prepareGpio(logger,OBP_FLASH_LED)){
EXIT_TASK;
@@ -209,15 +233,15 @@ void handleSpiLeds(void *param){
LedInterface newLeds=taskData->getLedData();
if (first || current.backlightChanged(newLeds) || current.flasChanged(newLeds)){
first=false;
LOG_DEBUG(GwLog::ERROR,"handle SPI leds");
logger->logDebug(GwLog::ERROR, "handle SPI leds");
if (current.backlightChanged(newLeds) || first){
LOG_DEBUG(GwLog::ERROR,"setting backlight r=%02d,g=%02d,b=%02d",
newLeds.backlight[0].r,newLeds.backlight[0].g,newLeds.backlight[0].b);
logger->logDebug(GwLog::ERROR, "setting backlight r=%02d,g=%02d,b=%02d",
newLeds.backlight[0].r,newLeds.backlight[0].g,newLeds.backlight[0].b);
sendToLeds(logger,OBP_BACKLIGHT_LED,newLeds.backlightLen(),newLeds.backlight,bus,device);
}
if (current.flasChanged(newLeds) || first){
LOG_DEBUG(GwLog::ERROR,"setting flashr=%02d,g=%02d,b=%02d",
newLeds.flash[0].r,newLeds.flash[0].g,newLeds.flash[0].b);
logger->logDebug(GwLog::ERROR, "setting flashr=%02d,g=%02d,b=%02d",
newLeds.flash[0].r,newLeds.flash[0].g,newLeds.flash[0].b);
sendToLeds(logger,OBP_FLASH_LED,newLeds.flashLen(),newLeds.flash,bus,device);
}
current=newLeds;
@@ -229,4 +253,4 @@ void handleSpiLeds(void *param){
void createSpiLedTask(LedTaskData *param){
xTaskCreate(handleSpiLeds,"handleLeds",4000,param,3,NULL);
}
}

View File

@@ -10,7 +10,7 @@ class Color{
uint8_t g;
uint8_t b;
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){}
Color(const Color &o):b(o.b),g(o.g),r(o.r){}
bool equal(const Color &o) const{
@@ -22,6 +22,8 @@ class Color{
bool operator != (const Color &other) const{
return ! equal(other);
}
String toHex();
String toName();
};
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
#include <Arduino.h>
@@ -32,30 +33,20 @@
#ifdef DISPLAY_GDEW042T2
// 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)
// Export display in new funktion
GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> & getdisplay(){return display;}
#endif
#ifdef DISPLAY_GDEY042T81
// 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)
// Export display in new funktion
GxEPD2_BW<GxEPD2_420_GDEY042T81, GxEPD2_420_GDEY042T81::HEIGHT> & getdisplay(){return display;}
#endif
#ifdef DISPLAY_GYE042A87
// 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)
// Export display in new funktion
GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> & getdisplay(){return display;}
#endif
#ifdef DISPLAY_SE0420NQ04
// 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)
// Export display in new funktion
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay(){return display;}
#endif
gxepd2display *epd = &display;
// Horter I2C moduls
PCF8574 pcf8574_Out(PCF8574_I2C_ADDR1); // First digital output modul PCF8574 from Horter
@@ -64,7 +55,14 @@ PCF8574 pcf8574_Out(PCF8574_I2C_ADDR1); // First digital output modul PCF8574 fr
Adafruit_FRAM_I2C fram;
bool hasFRAM = false;
// SD Card
#ifdef BOARD_OBP40S3
sdmmc_card_t *sdcard;
#endif
bool hasSDCard = false;
// Global vars
bool heartbeat = false; // Heartbeat indicator with two different states
bool blinkingLED = false; // Enable / disable blinking flash LED
bool statusLED = false; // Actual status of flash LED on/off
bool statusBacklightLED = false;// Actual status of flash LED on/off
@@ -78,6 +76,9 @@ LedTaskData *ledTaskData=nullptr;
void hardwareInit(GwApi *api)
{
GwLog *logger = api->getLogger();
GwConfigHandler *config = api->getConfig();
Wire.begin();
// Init PCF8574 digital outputs
Wire.setClock(I2C_SPEED); // Set I2C clock on 10 kHz
@@ -87,7 +88,7 @@ void hardwareInit(GwApi *api)
fram = Adafruit_FRAM_I2C();
if (esp_reset_reason() == ESP_RST_POWERON) {
// 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);
}
// FRAM (e.g. MB85RC256V)
@@ -99,11 +100,88 @@ void hardwareInit(GwApi *api)
// Boot counter
uint8_t framcounter = fram.read(0x0000);
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 {
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 +212,17 @@ void deepSleep(CommonData &common){
setFlashLED(false); // Flash LED Off
buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms
// Shutdown EInk display
getdisplay().setFullWindow(); // Set full Refresh
getdisplay().fillScreen(common.bgcolor); // Clear screen
getdisplay().setTextColor(common.fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(85, 150);
getdisplay().print("Sleep Mode");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(65, 175);
getdisplay().print("To wake up press key and wait 5s");
getdisplay().nextPage(); // Update display contents
getdisplay().powerOff(); // Display power off
epd->setFullWindow(); // Set full Refresh
epd->fillScreen(common.bgcolor); // Clear screen
epd->setTextColor(common.fgcolor);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(85, 150);
epd->print("Sleep Mode");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(65, 175);
epd->print("To wake up press key and wait 5s");
epd->nextPage(); // Update display contents
epd->powerOff(); // Display power off
setPortPin(OBP_POWER_50, false); // Power off ePaper display
// Stop system
esp_deep_sleep_start(); // Deep Sleep with weakup via touch pin
@@ -158,18 +236,18 @@ void deepSleep(CommonData &common){
setPortPin(OBP_BACKLIGHT_LED, false); // Backlight Off
setFlashLED(false); // Flash LED Off
// Shutdown EInk display
getdisplay().setFullWindow(); // Set full Refresh
//getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().fillScreen(common.bgcolor); // Clear screen
getdisplay().setTextColor(common.fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(85, 150);
getdisplay().print("Sleep Mode");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(65, 175);
getdisplay().print("To wake up press wheel and wait 5s");
getdisplay().nextPage(); // Partial update
getdisplay().powerOff(); // Display power off
epd->setFullWindow(); // Set full Refresh
//epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
epd->fillScreen(common.bgcolor); // Clear screen
epd->setTextColor(common.fgcolor);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(85, 150);
epd->print("Sleep Mode");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(65, 175);
epd->print("To wake up press wheel and wait 5s");
epd->nextPage(); // Partial update
epd->powerOff(); // Display power off
setPortPin(OBP_POWER_EPD, false); // Power off ePaper display
setPortPin(OBP_POWER_SD, false); // Power off SD card
// Stop system
@@ -214,31 +292,31 @@ void setBacklightLED(uint brightness, const Color &color){
ledTaskData->setLedData(current);
}
void toggleBacklightLED(uint brightness, const Color &color){
void toggleBacklightLED(uint brightness, const Color &color) {
if (ledTaskData == nullptr) return;
statusBacklightLED = !statusBacklightLED;
Color nv=setBrightness(statusBacklightLED?color:COLOR_BLACK,brightness);
LedInterface current=ledTaskData->getLedData();
Color nv = setBrightness(statusBacklightLED ? color : COLOR_BLACK, brightness);
LedInterface current = ledTaskData->getLedData();
current.setBacklight(nv);
ledTaskData->setLedData(current);
}
void setFlashLED(bool status){
void setFlashLED(bool status) {
if (ledTaskData == nullptr) return;
Color c=status?COLOR_RED:COLOR_BLACK;
LedInterface current=ledTaskData->getLedData();
Color c = status ? COLOR_RED : COLOR_BLACK;
LedInterface current = ledTaskData->getLedData();
current.setFlash(c);
ledTaskData->setLedData(current);
}
void blinkingFlashLED(){
if(blinkingLED == true){
void blinkingFlashLED() {
if (blinkingLED == true) {
statusLED = !statusLED; // Toggle LED for each run
setFlashLED(statusLED);
}
}
}
void setBlinkingLED(bool status){
void setBlinkingLED(bool status) {
blinkingLED = status;
}
@@ -276,28 +354,18 @@ String xdrDelete(String 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) {
getdisplay().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[1].x, p4[1].y, p4[2].x, p4[2].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
@@ -347,34 +415,51 @@ std::vector<String> wordwrap(String &line, uint16_t maxwidth) {
void drawTextCenter(int16_t cx, int16_t cy, String text) {
int16_t x1, y1;
uint16_t w, h;
getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h);
getdisplay().setCursor(cx - w / 2, cy + h / 2);
getdisplay().print(text);
epd->getTextBounds(text, 0, 150, &x1, &y1, &w, &h);
epd->setCursor(cx - w / 2, cy + h / 2);
epd->print(text);
}
// Draw right aligned text
void drawTextRalign(int16_t x, int16_t y, String text) {
int16_t x1, y1;
uint16_t w, h;
getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h);
getdisplay().setCursor(x - w, y);
getdisplay().print(text);
epd->getTextBounds(text, 0, 150, &x1, &y1, &w, &h);
epd->setCursor(x - w, y);
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)
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)
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
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 usbTxOld = 0;
static unsigned long serRxOld = 0;
@@ -386,31 +471,65 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
static unsigned long n2kRxOld = 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){
// Show status info
getdisplay().setTextColor(commonData.fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(0, 15);
if(commonData.status.wifiApOn){
getdisplay().print(" AP ");
epd->setTextColor(commonData.fgcolor);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(0, 15);
if (commonData.status.wifiApOn) {
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(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){
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){
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){
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){
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
tcpClRxOld = commonData.status.tcpClRx;
@@ -427,26 +546,26 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
#ifdef HARDWARE_V21
// Display key lock status
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 {
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
#ifdef LIPO_ACCU_1200
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 {
#ifdef VOLTAGE_SENSOR
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) {
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) {
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) {
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 {
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
}
@@ -454,33 +573,33 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
// Heartbeat as page number
if (heartbeat) {
getdisplay().setTextColor(commonData.bgcolor);
getdisplay().fillRect(201, 0, 23, 19, commonData.fgcolor);
epd->setTextColor(commonData.bgcolor);
epd->fillRect(201, 0, 23, 19, commonData.fgcolor);
} else {
getdisplay().setTextColor(commonData.fgcolor);
getdisplay().drawRect(201, 0, 23, 19, commonData.fgcolor);
epd->setTextColor(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));
heartbeat = !heartbeat;
// 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);
double tz = commonData.config->getString(commonData.config->timeZone).toDouble();
getdisplay().setTextColor(commonData.fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(230, 15);
epd->setTextColor(commonData.fgcolor);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(230, 15);
if (timesource == "RTC" or timesource == "iRTC") {
// TODO take DST into account
if (commonData.data.rtcValid) {
time_t tv = mktime(&commonData.data.rtcTime) + (int)(tz * 3600);
struct tm *local_tm = localtime(&tv);
getdisplay().print(formatTime('m', local_tm->tm_hour, local_tm->tm_min, 0));
getdisplay().print(" ");
getdisplay().print(formatDate(fmttype, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday));
getdisplay().print(" ");
getdisplay().print(tz == 0 ? "UTC" : "LOT");
epd->print(formatTime(fmtTime::MMHH, local_tm->tm_hour, local_tm->tm_min, 0));
epd->print(" ");
epd->print(formatDate(fmttype, local_tm->tm_year + 1900, local_tm->tm_mon + 1, local_tm->tm_mday));
epd->print(" ");
epd->print(tz == 0 ? "UTC" : "LOT");
} else {
drawTextRalign(396, 15, "RTC invalid");
}
@@ -488,18 +607,18 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
else if (timesource == "GPS") {
// Show date and time if date present
if(date->valid == true){
String acttime = formatValue(time, commonData).svalue;
String acttime = commonData.fmt->formatValue(time, commonData).svalue;
acttime = acttime.substring(0, 5);
String actdate = formatValue(date, commonData).svalue;
getdisplay().print(acttime);
getdisplay().print(" ");
getdisplay().print(actdate);
getdisplay().print(" ");
getdisplay().print(tz == 0 ? "UTC" : "LOT");
String actdate = commonData.fmt->formatValue(date, commonData).svalue;
epd->print(acttime);
epd->print(" ");
epd->print(actdate);
epd->print(" ");
epd->print(tz == 0 ? "UTC" : "LOT");
}
else{
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{
drawTextRalign(396, 15, "No GPS data");
@@ -507,15 +626,15 @@ void displayHeader(CommonData &commonData, GwApi::BoatValue *date, GwApi::BoatVa
}
} // timesource == "GPS"
else {
getdisplay().print("No time source");
epd->print("No time source");
}
}
}
void displayFooter(CommonData &commonData) {
getdisplay().setFont(&Atari16px);
getdisplay().setTextColor(commonData.fgcolor);
epd->setFont(&Atari16px);
epd->setTextColor(commonData.fgcolor);
#ifdef HARDWARE_V21
// Frame around key icon area
@@ -524,14 +643,14 @@ void displayFooter(CommonData &commonData) {
const uint16_t top = 280;
const uint16_t bottom = 299;
// 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++) {
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
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++) {
uint16_t x, y;
@@ -542,7 +661,7 @@ void displayFooter(CommonData &commonData) {
if (iconmap.find(icon_name) != iconmap.end()) {
x = commonData.keydata[i].x + (commonData.keydata[i].w - icon_width) / 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 {
// icon is missing, use name instead
x = commonData.keydata[i].x + commonData.keydata[i].w / 2;
@@ -557,8 +676,8 @@ void displayFooter(CommonData &commonData) {
}
}
} else {
getdisplay().setCursor(65, 295);
getdisplay().print("Press 1 and 6 fast to unlock keys");
epd->setCursor(65, 295);
epd->print("Press 1 and 6 fast to unlock keys");
}
#endif
#ifdef BOARD_OBP40S3
@@ -569,21 +688,21 @@ void displayFooter(CommonData &commonData) {
uint16_t x0 = (GxEPD_WIDTH - w) / 2 + r * 2;
for (int i = 0; i < commonData.data.maxpage; i++) {
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 {
getdisplay().drawCircle(x0 + i * (r * 2 + space), 290, r, commonData.fgcolor);
epd->drawCircle(x0 + i * (r * 2 + space), 290, r, commonData.fgcolor);
}
}
// key indicators
// left side = top key "menu"
getdisplay().drawLine(0, 280, 10, 280, commonData.fgcolor);
getdisplay().drawLine(55, 280, 65, 280, commonData.fgcolor);
getdisplay().drawLine(65, 280, 65, 299, commonData.fgcolor);
epd->drawLine(0, 280, 10, 280, commonData.fgcolor);
epd->drawLine(55, 280, 65, 280, commonData.fgcolor);
epd->drawLine(65, 280, 65, 299, commonData.fgcolor);
drawTextCenter(33, 291, commonData.keydata[0].label);
// right side = bottom key "exit"
getdisplay().drawLine(390, 280, 399, 280, commonData.fgcolor);
getdisplay().drawLine(335, 280, 345, 280, commonData.fgcolor);
getdisplay().drawLine(335, 280, 335, 399, commonData.fgcolor);
epd->drawLine(390, 280, 399, 280, commonData.fgcolor);
epd->drawLine(335, 280, 345, 280, commonData.fgcolor);
epd->drawLine(335, 280, 335, 399, commonData.fgcolor);
drawTextCenter(366, 291, commonData.keydata[1].label);
#endif
}
@@ -596,31 +715,31 @@ void displayAlarm(CommonData &commonData) {
const uint16_t w = 300;
const uint16_t h = 150;
getdisplay().setFont(&Atari16px);
getdisplay().setTextColor(commonData.fgcolor);
epd->setFont(&Atari16px);
epd->setTextColor(commonData.fgcolor);
// overlay
getdisplay().drawRect(x, y, w, h, commonData.fgcolor);
getdisplay().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, y, w, h, commonData.fgcolor);
epd->fillRect(x + 1, y + 1, w - 1, h - 1, commonData.bgcolor);
epd->drawRect(x + 3, y + 3, w - 5, h - 5, commonData.fgcolor);
// 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
getdisplay().setCursor(x + 64, y + 30);
getdisplay().print("A L A R M");
getdisplay().setCursor(x + 64, y + 48);
getdisplay().print("#" + commonData.alarm.id);
getdisplay().print(" from ");
getdisplay().print(commonData.alarm.source);
epd->setCursor(x + 64, y + 30);
epd->print("A L A R M");
epd->setCursor(x + 64, y + 48);
epd->print("#" + commonData.alarm.id);
epd->print(" from ");
epd->print(commonData.alarm.source);
// message, but maximum 4 lines
std::vector<String> lines = wordwrap (commonData.alarm.message, w - 16 - 8 / 8);
int n = 0;
for (const auto& l : lines) {
getdisplay().setCursor(x + 16, y + 80 + n);
getdisplay().print(l);
epd->setCursor(x + 16, y + 80 + n);
epd->print(l);
n += 16;
if (n > 64) {
break;
@@ -713,20 +832,20 @@ void batteryGraphic(uint x, uint y, float percent, int pcolor, int bcolor){
}
// Battery corpus 100x80 with fill level
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){
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
int xp = xb + 20;
int yp = yb - 15 + t;
getdisplay().fillRect(xp, yp, 20, 15, pcolor);
getdisplay().fillRect(xp+t, yp+t, 20-(2*t), 15-(2*t), bcolor);
epd->fillRect(xp, yp, 20, 15, pcolor);
epd->fillRect(xp+t, yp+t, 20-(2*t), 15-(2*t), bcolor);
// Minus pol 20x15
int xm = xb + 60;
int ym = yb -15 + t;
getdisplay().fillRect(xm, ym, 20, 15, pcolor);
getdisplay().fillRect(xm+t, ym+t, 20-(2*t), 15-(2*t), bcolor);
epd->fillRect(xm, ym, 20, 15, pcolor);
epd->fillRect(xm+t, ym+t, 20-(2*t), 15-(2*t), bcolor);
}
// Solar graphic with fill level
@@ -738,17 +857,17 @@ void solarGraphic(uint x, uint y, int pcolor, int bcolor){
int percent = 0;
// Solar corpus 100x80
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){
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
getdisplay().fillRect(xb, yb+28-t, 100, t, pcolor);
getdisplay().fillRect(xb, yb+54-t, 100, t, pcolor);
epd->fillRect(xb, yb+28-t, 100, t, pcolor);
epd->fillRect(xb, yb+54-t, 100, t, pcolor);
// Draw vertical lines
getdisplay().fillRect(xb+19+t, yb, t, 80, pcolor);
getdisplay().fillRect(xb+39+2*t, yb, t, 80, pcolor);
getdisplay().fillRect(xb+59+3*t, yb, t, 80, pcolor);
epd->fillRect(xb+19+t, yb, t, 80, pcolor);
epd->fillRect(xb+39+2*t, yb, t, 80, pcolor);
epd->fillRect(xb+59+3*t, yb, t, 80, pcolor);
}
@@ -760,13 +879,13 @@ void generatorGraphic(uint x, uint y, int pcolor, int bcolor){
int t = 4; // Line thickness
// Generator corpus with radius 45
getdisplay().fillCircle(xb, yb, 45, pcolor);
getdisplay().fillCircle(xb, yb, 41, bcolor);
epd->fillCircle(xb, yb, 45, pcolor);
epd->fillCircle(xb, yb, 41, bcolor);
// Insert G
getdisplay().setTextColor(pcolor);
getdisplay().setFont(&Ubuntu_Bold32pt8b);
getdisplay().setCursor(xb-22, yb+20);
getdisplay().print("G");
epd->setTextColor(pcolor);
epd->setFont(&Ubuntu_Bold32pt8b);
epd->setCursor(xb-22, yb+20);
epd->print("G");
}
// Function to handle HTTP image request
@@ -780,7 +899,7 @@ void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUM
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
String mimetype;

View File

@@ -1,12 +1,21 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef _OBP60EXTENSIONPORT_H
#define _OBP60EXTENSIONPORT_H
#include <Arduino.h>
#include "OBP60Hardware.h"
#include "OBP60Formatter.h"
#include "LedSpiTask.h"
#include "Graphics.h"
#include <GxEPD2_BW.h> // E-paper lib V2
#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
// 0x0000 - 0x03ff: single variables
#define FRAM_PAGE_NO 0x0002
@@ -15,6 +24,7 @@
#define FRAM_VOLTAGE_AVG 0x000A
#define FRAM_VOLTAGE_TREND 0x000B
#define FRAM_VOLTAGE_MODE 0x000C
// Wind page
#define FRAM_WIND_SIZE 0x000D
#define FRAM_WIND_SRC 0x000E
#define FRAM_WIND_MODE 0x000F
@@ -24,6 +34,12 @@
extern Adafruit_FRAM_I2C fram;
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)
extern const GFXfont DSEG7Classic_BoldItalic16pt7b;
@@ -42,39 +58,33 @@ extern const GFXfont Atari16px;
// Global functions
#ifdef DISPLAY_GDEW042T2
GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> & getdisplay();
typedef GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> gxepd2display;
#endif
#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
#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
#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
extern gxepd2display *epd;
// Page display return values
#define PAGE_OK 0 // all ok, do nothing
#define PAGE_UPDATE 1 // page wants display to update
#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 drawPoly(const std::vector<Point>& points, uint16_t color);
void deepSleep(CommonData &common);
uint8_t getLastPage();
void hardwareInit(GwApi *api);
void powerInit(String powermode);
void setPortPin(uint pin, bool value); // Set port pin for extension port
@@ -96,11 +106,12 @@ String xdrDelete(String input); // Delete xdr prefix from string
void drawTextCenter(int16_t cx, int16_t cy, 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 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 displayAlarm(CommonData &commonData);
@@ -148,24 +159,62 @@ static unsigned char fram_bits[] PROGMEM = {
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,
0xc0, 0x01, 0x20, 0x02, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x01, 0xc0, 0x01,
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,
0x82, 0x08, 0x06, 0x0a, 0x0e, 0x1b, 0x9c, 0x2b, 0x38, 0x2b, 0x74, 0x20,
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 = {
{"LEFT", left_bits},
{"RIGHT", right_bits},
{"LOCK", lock_bits},
{"PLUS", plus_bits},
{"MINUS", minus_bits},
{"DISH", dish_bits},
{"AP", ap_bits}
{"GPS", gps_bits},
{"AP", ap_bits},
{"0183", nmea_bits},
{"N2K", n2k_bits},
{"TCP", tcp_bits},
{"USB", usb_bits},
{"SDCARD", sdcard_bits},
{"BLUE", bluetooth_bits}
};
// Battery

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,170 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef _OBP60FORMATTER_H
#define _OBP60FORMATTER_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
#define OBP_DIRECTION_PIN 8
// 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_SCL 38
// DS1388 RTC
@@ -120,10 +120,10 @@
#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)
// SPI SD-Card
#define SD_SPI_CS 10
#define SD_SPI_MOSI 40
#define SD_SPI_CLK 39
#define SD_SPI_MISO 13
#define SD_SPI_CS GPIO_NUM_10
#define SD_SPI_MOSI GPIO_NUM_40
#define SD_SPI_CLK GPIO_NUM_39
#define SD_SPI_MISO GPIO_NUM_13
// GPS (NEO-6M, NEO-M8N, ATGM336H)
#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
for (uint8_t x = 0; x < qrcode.size; x++) {
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 {
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_y = box_y + box_s;
box_x = init_x;
}
getdisplay().setFont(&Ubuntu_Bold32pt8b);
getdisplay().setTextColor(fgcolor);
getdisplay().setCursor(140, 285);
getdisplay().print("WiFi");
getdisplay().nextPage(); // Full Refresh
epd->setFont(&Ubuntu_Bold32pt8b);
epd->setTextColor(fgcolor);
epd->setCursor(140, 285);
epd->print("WiFi");
epd->nextPage(); // Full Refresh
}
#endif

View File

@@ -77,30 +77,56 @@ void WindUtils::calcTwdSA(const double* AWA, const double* AWS,
// 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
static const double DBL_MIN = std::numeric_limits<double>::lowest();
// Serial.println("\ncalcTrueWind: HDT: " + String(*hdtVal) + ", HDM: " + String(*hdmVal) + ", VAR: " + String(*varVal) + ", SOG: " + String(*sogVal) + ", COG: " + String(*cogVal));
if (*hdmVal != DBL_MIN) {
hdt = *hdmVal + (*varVal != DBL_MIN ? *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_MIN && *sogVal >= minSogVal) {
hdt = *cogVal; // Use COG as fallback if HDT and HDM are not available, and SOG is not data noise
} else {
hdt = DBL_MIN; // Cannot calculate HDT without valid HDM or HDM+VAR or COG
}
return hdt;
}
bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal)
{
double stw, hdt, ctw;
double twd, tws, twa;
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
static const double DBL_MIN = std::numeric_limits<double>::lowest();
if (*hdtVal != DBL_MIN) {
// Serial.println("\ncalcTrueWind: HDT: " + String(*hdtVal) + ", HDM: " + String(*hdmVal) + ", VAR: " + String(*varVal) + ", SOG: " + String(*sogVal) + ", COG: " + String(*cogVal));
/* if (*hdtVal != DBL_MIN) {
hdt = *hdtVal; // Use HDT if available
} else {
if (*hdmVal != DBL_MIN && *varVal != DBL_MIN) {
hdt = *hdmVal + *varVal; // Use corrected HDM if HDT is not available
if (*hdmVal != DBL_MIN) {
hdt = *hdmVal + (*varVal != DBL_MIN ? *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_MIN) {
hdt = *cogVal; // Use COG as fallback if HDT and HDM are not available
} else if (*cogVal != DBL_MIN && *sogVal >= minSogVal) {
hdt = *cogVal; // Use COG as fallback if HDT and HDM are not available, and SOG is not data noise
} else {
return false; // Cannot calculate without valid HDT or HDM+VAR or COG
}
} */
if (*hdtVal != DBL_MIN) {
hdt = *hdtVal; // Use HDT if available
} else {
hdt = calcHDT(hdmVal, varVal, cogVal, sogVal);
}
if (*cogVal != DBL_MIN) {
ctw = *cogVal; // Use COG as CTW if available
// ctw = *cogVal + ((*cogVal - hdt) / 2); // Estimate CTW from COG
if (*cogVal != DBL_MIN && *sogVal >= minSogVal) { // if SOG is data noise, we don't trust COG
ctw = *cogVal; // Use COG for CTW if available
} else {
ctw = hdt; // 2nd approximation for CTW; hdt must exist if we reach this part of the code
}
@@ -113,6 +139,7 @@ bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
// If STW and SOG are not available, we cannot calculate true wind
return false;
}
// Serial.println("\ncalcTrueWind: HDT: " + String(hdt) + ", CTW: " + String(ctw) + ", STW: " + String(stw));
if ((*awaVal == DBL_MIN) || (*awsVal == DBL_MIN)) {
// Cannot calculate true wind without valid AWA, AWS; other checks are done earlier
@@ -126,32 +153,3 @@ bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
return true;
}
}
void HstryBuf::fillWndBufSimData(tBoatHstryData& hstryBufs)
// Fill most part of TWD and TWS history buffer with simulated data
{
double value = 20.0;
int16_t value2 = 0;
for (int i = 0; i < 900; i++) {
value += random(-20, 20);
value = WindUtils::to360(value);
value2 = static_cast<int16_t>(value * DEG_TO_RAD * 1000);
hstryBufs.twdHstry->add(value2);
}
}
/* 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,18 +1,20 @@
#pragma once
#include "GwApi.h"
#include "OBPRingBuffer.h"
#include <Arduino.h>
// #include <Arduino.h>
#include <math.h>
typedef struct {
RingBuffer<int16_t>* twdHstry;
RingBuffer<int16_t>* twsHstry;
RingBuffer<int16_t>* awdHstry;
RingBuffer<int16_t>* awsHstry;
} tBoatHstryData; // Holds pointers to all history buffers for boat data
class HstryBuf {
public:
void fillWndBufSimData(tBoatHstryData& hstryBufs); // Fill most part of the TWD and TWS history buffer with simulated data
};
class WindUtils {
@@ -30,7 +32,8 @@ public:
static void calcTwdSA(const double* AWA, const double* AWS,
const double* CTW, const double* STW, const double* HDT,
double* TWD, double* TWS, double* TWA);
static double calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal);
static bool calcTrueWind(const double* awaVal, const double* awsVal,
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal);
};
};

View File

@@ -32,6 +32,7 @@ public:
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
String getName() const; // Get buffer name
String getFormat() const; // Get buffer data format
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 getFirst() const; // Get the first (oldest) value in buffer
@@ -57,4 +58,4 @@ public:
std::vector<T> getAllValues() const; // Get all current values as a vector
};
#include "OBPRingBuffer.tpp"
#include "OBPRingBuffer.tpp"

View File

@@ -64,6 +64,13 @@ String RingBuffer<T>::getName() const
return dataName;
}
// Get buffer data format
template <typename T>
String RingBuffer<T>::getFormat() const
{
return dataFmt;
}
// Add a new value to buffer
template <typename T>
void RingBuffer<T>::add(const T& value)
@@ -373,4 +380,4 @@ std::vector<T> RingBuffer<T>::getAllValues() const
}
return result;
}
}

View File

@@ -90,16 +90,16 @@ void sensorTask(void *param){
double voffset = (api->getConfig()->getConfigItem(api->getConfig()->vOffset,true)->asString()).toFloat();
double vslope = (api->getConfig()->getConfigItem(api->getConfig()->vSlope,true)->asString()).toFloat();
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
#else
#else
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
#ifdef LIPO_ACCU_1200
#ifdef LIPO_ACCU_1200
sensors.BatteryChargeStatus = 0; // Set to discharging
sensors.batteryLevelLiPo = 0; // Level 0...100%
#endif
#endif
sensors.batteryCurrent = 0;
sensors.batteryPower = 0;
// Fill average arrays with start values
@@ -499,24 +499,24 @@ void sensorTask(void *param){
if(millis() > starttime5 + 1000 && String(powsensor1) == "off"){
starttime5 = millis();
float rawVoltage = 0; // Default value
#ifdef BOARD_OBP40S3
#ifdef BOARD_OBP40S3
sensors.batteryVoltage = 0; // If no sensor then zero voltage
#endif
#if defined(BOARD_OBP40S3) && defined(VOLTAGE_SENSOR)
#endif
#if defined(BOARD_OBP40S3) && defined(VOLTAGE_SENSOR)
rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2; // Vin = 1/2 for OBP40
sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
#endif
#ifdef BOARD_OBP60S3
#endif
#ifdef BOARD_OBP60S3
rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 for OBP60
sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
#endif
#endif
// Save new data in average array
batV.reading(int(sensors.batteryVoltage * 100));
// Calculate the average values for different time lines from integer values
sensors.batteryVoltage10 = batV.getAvg(10) / 100.0;
sensors.batteryVoltage60 = batV.getAvg(60) / 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%
sensors.batteryLevelLiPo = sensors.batteryVoltage60 * 203.8312 -738.1635;
// Limiter
@@ -555,14 +555,14 @@ void sensorTask(void *param){
SetN2kDCBatStatus(N2kMsg, 10, sensors.batteryVoltage, N2kDoubleNA, N2kDoubleNA, 0);
api->sendN2kMessage(N2kMsg);
}
#endif
#ifdef BOARD_OBP60S3
#endif
#ifdef BOARD_OBP60S3
// Send to NMEA200 bus
if(!isnan(sensors.batteryVoltage)){
SetN2kDCBatStatus(N2kMsg, 0, sensors.batteryVoltage, N2kDoubleNA, N2kDoubleNA, 1);
api->sendN2kMessage(N2kMsg);
}
#endif
#endif
}
// Send data from environment sensor all 2s

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

@@ -0,0 +1,175 @@
// 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
*/
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,428 @@
// 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.
*/
#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
#include "Pagedata.h"
@@ -5,13 +6,21 @@
class PageBME280 : public Page
{
public:
PageBME280(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageBME280");
private:
String tempformat;
String useenvsensor;
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
if(key == 11){
commonData->keylock = !commonData->keylock;
@@ -21,8 +30,6 @@ class PageBME280 : public Page
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
double value1 = 0;
double value2 = 0;
@@ -30,13 +37,6 @@ class PageBME280 : public Page
String svalue1 = "";
String svalue2 = "";
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
String name1 = "Temp"; // Value name
@@ -99,82 +99,82 @@ class PageBME280 : public Page
}
// 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
//***********************************************************
// 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 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 55);
getdisplay().print(name1); // Page name
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 55);
epd->print(name1); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 90);
getdisplay().print(unit1); // Unit
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 90);
epd->print(unit1); // Unit
// Switch font if format for any values
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 90);
epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 90);
// Show bus data
getdisplay().print(svalue1); // Real value as formated string
epd->print(svalue1); // Real value as formated string
// ############### Horizontal Line ################
// Horizontal line 3 pix
getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor);
epd->fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 145);
getdisplay().print(name2); // Page name
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 145);
epd->print(name2); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 180);
getdisplay().print(unit2); // Unit
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 180);
epd->print(unit2); // Unit
// Switch font if format for any values
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 180);
epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 180);
// Show bus data
getdisplay().print(svalue2); // Real value as formated string
epd->print(svalue2); // Real value as formated string
// ############### Horizontal Line ################
// Horizontal line 3 pix
getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor);
epd->fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 235);
getdisplay().print(name3); // Page name
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 235);
epd->print(name3); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 270);
getdisplay().print(unit3); // Unit
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 270);
epd->print(unit3); // Unit
// Switch font if format for any values
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(140, 270);
epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(140, 270);
// Show bus data
getdisplay().print(svalue3); // Real value as formated string
epd->print(svalue3); // Real value as formated string
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
#include "Pagedata.h"
@@ -5,20 +6,25 @@
class PageBattery : public Page
{
private:
String powsensor1;
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
public:
PageBattery(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageBattery");
public:
PageBattery(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageBattery");
// Get config data
String powsensor1 = config->getString(config->usePowSensor1);
}
virtual void setupKeys(){
void setupKeys(){
Page::setupKeys();
commonData->keydata[0].label = "AVG";
}
virtual int handleKey(int key){
int handleKey(int key){
// Change average
if(key == 1){
average ++;
@@ -34,10 +40,18 @@ class PageBattery : public Page
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){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function
double value1 = 0;
static String svalue1old = "";
@@ -48,15 +62,7 @@ class PageBattery : public Page
double value3 = 0;
static String svalue3old = "";
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
String name1 = "VBat"; // Value name
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 unit3 = "W"; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// 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
//***********************************************************
// 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
getdisplay().setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold8pt8b);
switch (average) {
case 0:
getdisplay().setCursor(60, 90);
getdisplay().print("Avg: 1s");
getdisplay().setCursor(60, 180);
getdisplay().print("Avg: 1s");
getdisplay().setCursor(60, 270);
getdisplay().print("Avg: 1s");
epd->setCursor(60, 90);
epd->print("Avg: 1s");
epd->setCursor(60, 180);
epd->print("Avg: 1s");
epd->setCursor(60, 270);
epd->print("Avg: 1s");
break;
case 1:
getdisplay().setCursor(60, 90);
getdisplay().print("Avg: 10s");
getdisplay().setCursor(60, 180);
getdisplay().print("Avg: 10s");
getdisplay().setCursor(60, 270);
getdisplay().print("Avg: 10s");
epd->setCursor(60, 90);
epd->print("Avg: 10s");
epd->setCursor(60, 180);
epd->print("Avg: 10s");
epd->setCursor(60, 270);
epd->print("Avg: 10s");
break;
case 2:
getdisplay().setCursor(60, 90);
getdisplay().print("Avg: 60s");
getdisplay().setCursor(60, 180);
getdisplay().print("Avg: 60s");
getdisplay().setCursor(60, 270);
getdisplay().print("Avg: 60s");
epd->setCursor(60, 90);
epd->print("Avg: 60s");
epd->setCursor(60, 180);
epd->print("Avg: 60s");
epd->setCursor(60, 270);
epd->print("Avg: 60s");
break;
case 3:
getdisplay().setCursor(60, 90);
getdisplay().print("Avg: 300s");
getdisplay().setCursor(60, 180);
getdisplay().print("Avg: 300s");
getdisplay().setCursor(60, 270);
getdisplay().print("Avg: 300s");
epd->setCursor(60, 90);
epd->print("Avg: 300s");
epd->setCursor(60, 180);
epd->print("Avg: 300s");
epd->setCursor(60, 270);
epd->print("Avg: 300s");
break;
default:
getdisplay().setCursor(60, 90);
getdisplay().print("Avg: 1s");
getdisplay().setCursor(60, 180);
getdisplay().print("Avg: 1s");
getdisplay().setCursor(60, 270);
getdisplay().print("Avg: 1s");
epd->setCursor(60, 90);
epd->print("Avg: 1s");
epd->setCursor(60, 180);
epd->print("Avg: 1s");
epd->setCursor(60, 270);
epd->print("Avg: 1s");
break;
}
// ############### Value 1 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 55);
getdisplay().print(name1); // Value name
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 55);
epd->print(name1); // Value name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 90);
getdisplay().print(unit1); // Unit
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 90);
epd->print(unit1); // Unit
// Show value
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 90);
epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 90);
// Show bus data
if(String(powsensor1) != "off"){
getdisplay().print(value1,2); // Real value as formated string
epd->print(value1,2); // Real value as formated string
}
else{
getdisplay().print("---"); // No sensor data (sensor is off)
epd->print(commonData->fmt->placeholder); // No sensor data (sensor is off)
}
// ############### Horizontal Line ################
// Horizontal line 3 pix
getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor);
epd->fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 145);
getdisplay().print(name2); // Value name
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 145);
epd->print(name2); // Value name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 180);
getdisplay().print(unit2); // Unit
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 180);
epd->print(unit2); // Unit
// Show value
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 180);
epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 180);
// Show bus data
if(String(powsensor1) != "off"){
getdisplay().print(value2,1); // Real value as formated string
epd->print(value2,1); // Real value as formated string
}
else{
getdisplay().print("---"); // No sensor data (sensor is off)
epd->print(commonData->fmt->placeholder); // No sensor data (sensor is off)
}
// ############### Horizontal Line ################
// Horizontal line 3 pix
getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor);
epd->fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 235);
getdisplay().print(name3); // Value name
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 235);
epd->print(name3); // Value name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 270);
getdisplay().print(unit3); // Unit
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 270);
epd->print(unit3); // Unit
// Show value
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 270);
epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 270);
// Show bus data
if(String(powsensor1) != "off"){
getdisplay().print(value3,1); // Real value as formated string
epd->print(value3,1); // Real value as formated string
}
else{
getdisplay().print("---"); // No sensor data (sensor is off)
epd->print(commonData->fmt->placeholder); // No sensor data (sensor is off)
}
return PAGE_UPDATE;

View File

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

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
@@ -16,30 +17,41 @@
class PageClock : public Page
{
bool simulation = false;
int simtime;
bool keylock = false;
char source = 'R'; // time source (R)TC | (G)PS | (N)TP
char mode = 'A'; // display mode (A)nalog | (D)igital | race (T)imer
char tz = 'L'; // time zone (L)ocal | (U)TC
double timezone = 0; // there are timezones with non int offsets, e.g. 5.5 or 5.75
double homelat;
double homelon;
bool homevalid = false; // homelat and homelon are valid
private:
fmtDate dateformat;
int simtime;
bool keylock = false;
char source = 'R'; // time source (R)TC | (G)PS | (N)TP
char mode = 'A'; // display mode (A)nalog | (D)igital | race (T)imer
char tz = 'L'; // time zone (L)ocal | (U)TC
double timezone = 0; // there are timezones with non int offsets, e.g. 5.5 or 5.75
double homelat;
double homelon;
bool homevalid = false; // homelat and homelon are valid
public:
PageClock(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageClock");
simulation = common.config->getBool(common.config->useSimuData);
timezone = common.config->getString(common.config->timeZone).toDouble();
homelat = common.config->getString(common.config->homeLAT).toDouble();
homelon = common.config->getString(common.config->homeLON).toDouble();
public:
PageClock(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageClock");
// Get config data
dateformat = common.fmt->getDateFormat(config->getString(config->dateFormat));
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;
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();
commonData->keydata[0].label = "SRC";
commonData->keydata[1].label = "MODE";
@@ -47,7 +59,7 @@ bool homevalid = false; // homelat and homelon are valid
}
// Key functions
virtual int handleKey(int key){
int handleKey(int key){
// Time source
if (key == 1) {
if (source == 'G') {
@@ -85,10 +97,17 @@ bool homevalid = false; // homelat and homelon are valid
return key;
}
int displayPage(PageData &pageData)
{
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
static String svalue1old = "";
static String unit1old = "";
@@ -100,16 +119,10 @@ bool homevalid = false; // homelat and homelon are valid
static String svalue5old = "";
static String svalue6old = "";
double value1 = 0;
double value2 = 0;
double value3 = 0;
// 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);
double value1 = 0; // GPS time
double value2 = 0; // GPS date FIXME date defined as uint32_t!
double value3 = 0; // HDOP
bool gpsvalid = false;
// Get boat values for GPS time
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
} // Other simulation data see OBP60Formatter.cpp
bool valid1 = bvalue1->valid; // Valid information
String svalue1 = 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 svalue1 = commonData->fmt->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
if(valid1 == true){
svalue1old = svalue1; // Save old value
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
value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information
String svalue2 = 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 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
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
value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information
String svalue3 = 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 svalue3 = commonData->fmt->formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = commonData->fmt->formatValue(bvalue3, *commonData).unit; // Unit of value
if(valid3 == true){
svalue3old = svalue3; // Save old value
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)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
@@ -163,108 +179,108 @@ bool homevalid = false; // homelat and homelon are valid
// Logging boat values
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
//***********************************************************
// 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;
struct tm *local_tm = localtime(&tv);
// Show values GPS date
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 65);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 65);
if (holdvalues == false) {
if (source == 'G') {
// GPS value
getdisplay().print(svalue2);
epd->print(svalue2);
} else if (commonData->data.rtcValid) {
// RTC value
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 {
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 {
getdisplay().print("---");
epd->print(commonData->fmt->placeholder);
}
} else {
getdisplay().print(svalue2old);
epd->print(svalue2old);
}
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 95);
getdisplay().print("Date"); // Name
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 95);
epd->print("Date"); // Name
// Horizintal separator left
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
epd->fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show values GPS time
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 250);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 250);
if (holdvalues == false) {
if (source == 'G') {
getdisplay().print(svalue1); // Value
epd->print(svalue1); // Value
}
else if (commonData->data.rtcValid) {
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 {
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 {
getdisplay().print("---");
epd->print(commonData->fmt->placeholder);
}
}
else {
getdisplay().print(svalue1old);
epd->print(svalue1old);
}
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 220);
getdisplay().print("Time"); // Name
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 220);
epd->print("Time"); // Name
// Show values sunrise
String sunrise = "---";
if ((valid1 and valid2 and valid3 == true) or (homevalid and commonData->data.rtcValid)) {
String sunrise = commonData->fmt->placeholder;
if (((source == 'G') and gpsvalid) or (homevalid and commonData->data.rtcValid)) {
sunrise = String(commonData->sundata.sunriseHour) + ":" + String(commonData->sundata.sunriseMinute + 100).substring(1);
svalue5old = sunrise;
} else if (simulation) {
sunrise = String("06:42");
}
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 65);
if(holdvalues == false) getdisplay().print(sunrise); // Value
else getdisplay().print(svalue5old);
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 95);
getdisplay().print("SunR"); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 65);
if(holdvalues == false) epd->print(sunrise); // Value
else epd->print(svalue5old);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(335, 95);
epd->print("SunR"); // Name
// Horizintal separator right
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
epd->fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show values sunset
String sunset = "---";
if ((valid1 and valid2 and valid3 == true) or (homevalid and commonData->data.rtcValid)) {
String sunset = commonData->fmt->placeholder;
if (((source == 'G') and gpsvalid) or (homevalid and commonData->data.rtcValid)) {
sunset = String(commonData->sundata.sunsetHour) + ":" + String(commonData->sundata.sunsetMinute + 100).substring(1);
svalue6old = sunset;
} else if (simulation) {
sunset = String("21:03");
}
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 250);
if(holdvalues == false) getdisplay().print(sunset); // Value
else getdisplay().print(svalue6old);
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 220);
getdisplay().print("SunS"); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 250);
if(holdvalues == false) epd->print(sunset); // Value
else epd->print(svalue6old);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(335, 220);
epd->print("SunS"); // Name
//*******************************************************************************************
@@ -272,8 +288,8 @@ bool homevalid = false; // homelat and homelon are valid
int rInstrument = 110; // Radius of clock
float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
epd->fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
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
int16_t x1, y1; // 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
getdisplay().setCursor(x-w/2, y+h/2);
epd->getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
epd->setCursor(x-w/2, y+h/2);
if(i % 30 == 0){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().print(ii);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->print(ii);
}
// Draw sub scale with dots
@@ -314,7 +330,7 @@ bool homevalid = false; // homelat and homelon are valid
if(i % 6 == 0){
float x1c = 200 + rInstrument*sin(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);
cosx=cos(i/180.0*pi);
}
@@ -326,31 +342,31 @@ bool homevalid = false; // homelat and homelon are valid
float xx2 = +dx;
float yy1 = -(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*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*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
}
}
// Print Unit in clock
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(175, 110);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(175, 110);
if(holdvalues == false){
getdisplay().print(tz == 'L' ? "LOT" : "UTC");
epd->print(tz == 'L' ? "LOT" : "UTC");
}
else{
getdisplay().print(unit2old); // date unit
epd->print(unit2old); // date unit
}
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(185, 190);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(185, 190);
if (source == 'G') {
getdisplay().print("GPS");
epd->print("GPS");
} else {
getdisplay().print("RTC");
epd->print("RTC");
}
// Clock values
@@ -380,7 +396,7 @@ bool homevalid = false; // homelat and homelon are valid
if (hour > 12) {
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
float startwidth = 8; // Start width of pointer
@@ -393,7 +409,7 @@ bool homevalid = false; // homelat and homelon are valid
float xx2 = startwidth;
float yy1 = -startwidth;
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*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer
@@ -403,7 +419,7 @@ bool homevalid = false; // homelat and homelon are valid
float ix2 = -endwidth;
float iy1 = -(rInstrument * 0.5);
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*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 yy1 = -startwidth;
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*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer
@@ -429,14 +445,14 @@ bool homevalid = false; // homelat and homelon are valid
float ix2 = -endwidth;
float iy1 = -(rInstrument - 15);
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*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
}
// Center circle
getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
epd->fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
epd->fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
return PAGE_UPDATE;
};

View File

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

View File

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

View File

@@ -0,0 +1,137 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
#include "OBP60Extensions.h"
/*
Electric propulsion
*/
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
#include "Pagedata.h"
@@ -66,25 +67,22 @@ static unsigned char fish_bits[] = {
class PageFluid : public Page
{
bool simulation = false;
private:
double simgoto;
double simval;
double simstep;
bool holdvalues = false;
int fluidtype;
public:
PageFluid(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageFluid");
simulation = common.config->getBool(common.config->useSimuData);
holdvalues = common.config->getBool(common.config->holdvalues);
public:
PageFluid(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageFluid");
simval = double(random(0, 100));
simgoto = double(random(0, 100));
simstep = (simgoto - simval) / 20.0;
}
virtual int handleKey(int key){
int handleKey(int key) {
// Code for keylock
if(key == 11){
commonData->keylock = !commonData->keylock;
@@ -93,27 +91,22 @@ class PageFluid : public Page
return key;
}
virtual void displayNew(PageData &pageData){
fluidtype = commonData->config->getInt("page" + String(pageData.pageNumber) + "fluid", 0);
commonData->logger->logDebug(GwLog::LOG,"New PageFluid: fluidtype=%d", fluidtype);
}
int displayPage(PageData &pageData){
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"){
void displayNew(PageData &pageData) {
fluidtype = config->getInt("page" + String(pageData.pageNumber) + "fluid", 0);
logger->logDebug(GwLog::LOG, "New PageFluid: fluidtype=%d", fluidtype);
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
}
int displayPage(PageData &pageData) {
// Old values for hold function
static double value1old;
GwApi::BoatValue *bvalue1 = pageData.values[0];
String name1 = bvalue1->getName();
@@ -132,23 +125,23 @@ class PageFluid : public Page
}
// 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
//***********************************************************
// 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
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 60);
getdisplay().print("Fluid");
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 60);
epd->print("Fluid");
getdisplay().setCursor(300, 60);
getdisplay().print(xdrDelete(name1).substring(0, 6));
epd->setCursor(300, 60);
epd->print(xdrDelete(name1).substring(0, 6));
// analog instrument
// scale from -120 to 120
@@ -158,11 +151,11 @@ class PageFluid : public Page
uint8_t r = 110;
// circular frame
getdisplay().drawCircle(c.x, c.y, r+5, commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, r+2, commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, r-1, commonData->bgcolor);
epd->drawCircle(c.x, c.y, r+5, commonData->fgcolor);
epd->fillCircle(c.x, c.y, r+2, commonData->fgcolor);
epd->fillCircle(c.x, c.y, r-1, commonData->bgcolor);
// 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
char buffer[6];
@@ -176,32 +169,32 @@ class PageFluid : public Page
// draw symbol (as bitmap)
switch (fluidtype) {
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;
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;
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;
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;
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;
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;
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;
}
Point p, pr;
// scale texts
getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setFont(&Ubuntu_Bold8pt8b);
p = {c.x, c.y - r + 30};
drawTextCenter(p.x, p.y, "1/2");
pr = rotatePoint(c, p, -60);
@@ -210,7 +203,7 @@ class PageFluid : public Page
drawTextCenter(pr.x, pr.y, "3/4");
// empty and full
getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setFont(&Ubuntu_Bold12pt8b);
p = rotatePoint(c, {c.x, c.y - r + 30}, -130);
drawTextCenter(p.x, p.y, "E");
p = rotatePoint(c, {c.x, c.y - r + 30}, 130);
@@ -236,7 +229,7 @@ class PageFluid : public Page
continue;
}
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
@@ -249,7 +242,7 @@ class PageFluid : public Page
};
fillPoly4(rotatePoints(c, pts, -120 + fluidlevel * 2.4), commonData->fgcolor);
// 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;

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
@@ -6,13 +7,19 @@
class PageFourValues : public Page
{
public:
PageFourValues(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageFourValues");
private:
String lengthformat;
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
if(key == 11){
commonData->keylock = !commonData->keylock;
@@ -21,9 +28,17 @@ class PageFourValues : public Page
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){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Old values for hold function
static String svalue1old = "";
@@ -34,14 +49,7 @@ class PageFourValues : public Page
static String unit3old = "";
static String svalue4old = "";
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
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name
@@ -49,8 +57,8 @@ class PageFourValues : public Page
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information
String svalue1 = 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 svalue1 = commonData->fmt->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
// Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
@@ -59,8 +67,8 @@ class PageFourValues : public Page
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information
String svalue2 = 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 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
// Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list
@@ -69,8 +77,8 @@ class PageFourValues : public Page
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information
String svalue3 = 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 svalue3 = commonData->fmt->formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = commonData->fmt->formatValue(bvalue3, *commonData).unit; // Unit of value
// Get boat values #4
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Fourth element in list
@@ -79,64 +87,58 @@ class PageFourValues : public Page
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
double value4 = bvalue4->value; // Value as double in SI unit
bool valid4 = bvalue4->valid; // Valid information
String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
String svalue4 = commonData->fmt->formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = commonData->fmt->formatValue(bvalue4, *commonData).unit; // Unit of value
// Logging boat values
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
//***********************************************************
// 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 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(20, 45);
getdisplay().print(name1); // Page name
epd->setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(20, 45);
epd->print(name1); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 65);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(20, 65);
if(holdvalues == false){
getdisplay().print(unit1); // Unit
epd->print(unit1); // Unit
}
else{
getdisplay().print(unit1old);
epd->print(unit1old);
}
// Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(120, 55);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(120, 55);
}
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(150, 58);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(150, 58);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(180, 65);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(180, 65);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue1); // Real value as formated string
epd->print(svalue1); // Real value as formated string
}
else{
getdisplay().print(svalue1old); // Old value as formated string
epd->print(svalue1old); // Old value as formated string
}
if(valid1 == true){
svalue1old = svalue1; // Save the old value
@@ -146,45 +148,45 @@ class PageFourValues : public Page
// ############### Horizontal Line ################
// Horizontal line 3 pix
getdisplay().fillRect(0, 80, 400, 3, commonData->fgcolor);
epd->fillRect(0, 80, 400, 3, commonData->fgcolor);
// ############### Value 2 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(20, 113);
getdisplay().print(name2); // Page name
epd->setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(20, 113);
epd->print(name2); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 133);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(20, 133);
if(holdvalues == false){
getdisplay().print(unit2); // Unit
epd->print(unit2); // Unit
}
else{
getdisplay().print(unit2old);
epd->print(unit2old);
}
// Switch font if format for any values
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(120, 123);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(120, 123);
}
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(150, 123);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(150, 123);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(180, 133);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(180, 133);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue2); // Real value as formated string
epd->print(svalue2); // Real value as formated string
}
else{
getdisplay().print(svalue2old); // Old value as formated string
epd->print(svalue2old); // Old value as formated string
}
if(valid2 == true){
svalue2old = svalue2; // Save the old value
@@ -194,45 +196,45 @@ class PageFourValues : public Page
// ############### Horizontal Line ################
// Horizontal line 3 pix
getdisplay().fillRect(0, 146, 400, 3, commonData->fgcolor);
epd->fillRect(0, 146, 400, 3, commonData->fgcolor);
// ############### Value 3 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(20, 181);
getdisplay().print(name3); // Page name
epd->setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(20, 181);
epd->print(name3); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 201);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(20, 201);
if(holdvalues == false){
getdisplay().print(unit3); // Unit
epd->print(unit3); // Unit
}
else{
getdisplay().print(unit3old);
epd->print(unit3old);
}
// Switch font if format for any values
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(120, 191);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(120, 191);
}
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(150, 191);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(150, 191);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(180, 201);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(180, 201);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue3); // Real value as formated string
epd->print(svalue3); // Real value as formated string
}
else{
getdisplay().print(svalue3old); // Old value as formated string
epd->print(svalue3old); // Old value as formated string
}
if(valid3 == true){
svalue3old = svalue3; // Save the old value
@@ -242,45 +244,45 @@ class PageFourValues : public Page
// ############### Horizontal Line ################
// Horizontal line 3 pix
getdisplay().fillRect(0, 214, 400, 3, commonData->fgcolor);
epd->fillRect(0, 214, 400, 3, commonData->fgcolor);
// ############### Value 4 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(20, 249);
getdisplay().print(name4); // Page name
epd->setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(20, 249);
epd->print(name4); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 269);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(20, 269);
if(holdvalues == false){
getdisplay().print(unit4); // Unit
epd->print(unit4); // Unit
}
else{
getdisplay().print(unit4old);
epd->print(unit4old);
}
// Switch font if format for any values
if(bvalue4->getFormat() == "formatLatitude" || bvalue4->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(120, 259);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(120, 259);
}
else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(150, 259);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(150, 259);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(180, 269);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(180, 269);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue4); // Real value as formated string
epd->print(svalue4); // Real value as formated string
}
else{
getdisplay().print(svalue4old); // Old value as formated string
epd->print(svalue4old); // Old value as formated string
}
if(valid4 == true){
svalue4old = svalue4; // Save the old value
@@ -289,8 +291,15 @@ class PageFourValues : public Page
return PAGE_UPDATE;
};
void leavePage(PageData &pageData) {
logger->logDebug(GwLog::LOG, "Leaving PageFourvalues");
}
};
static Page *createPage(CommonData &common){
return new PageFourValues(common);
}/**

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
@@ -6,13 +7,19 @@
class PageFourValues2 : public Page
{
public:
PageFourValues2(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageFourValues2");
private:
String lengthformat;
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
if(key == 11){
commonData->keylock = !commonData->keylock; // Toggle keylock
@@ -21,9 +28,17 @@ class PageFourValues2 : public Page
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
// Old values for hold function
static String svalue1old = "";
@@ -34,14 +49,7 @@ class PageFourValues2 : public Page
static String unit3old = "";
static String svalue4old = "";
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
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name
@@ -49,8 +57,8 @@ class PageFourValues2 : public Page
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information
String svalue1 = 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 svalue1 = commonData->fmt->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
// Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list (only one value by PageOneValue)
@@ -59,8 +67,8 @@ class PageFourValues2 : public Page
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information
String svalue2 = 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 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
// Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Second element in list (only one value by PageOneValue)
@@ -69,8 +77,8 @@ class PageFourValues2 : public Page
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information
String svalue3 = 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 svalue3 = commonData->fmt->formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = commonData->fmt->formatValue(bvalue3, *commonData).unit; // Unit of value
// Get boat values #4
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Second element in list (only one value by PageOneValue)
@@ -79,64 +87,58 @@ class PageFourValues2 : public Page
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
double value4 = bvalue4->value; // Value as double in SI unit
bool valid4 = bvalue4->valid; // Valid information
String svalue4 = formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = formatValue(bvalue4, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
String svalue4 = commonData->fmt->formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = commonData->fmt->formatValue(bvalue4, *commonData).unit; // Unit of value
// Logging boat values
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
//***********************************************************
// 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 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 55);
getdisplay().print(name1); // Page name
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 55);
epd->print(name1); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 90);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 90);
if(holdvalues == false){
getdisplay().print(unit1); // Unit
epd->print(unit1); // Unit
}
else{
getdisplay().print(unit1old);
epd->print(unit1old);
}
// Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(100, 90);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(100, 90);
}
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(180, 77);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(180, 77);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 90);
epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 90);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue1); // Real value as formated string
epd->print(svalue1); // Real value as formated string
}
else{
getdisplay().print(svalue1old); // Old value as formated string
epd->print(svalue1old); // Old value as formated string
}
if(valid1 == true){
svalue1old = svalue1; // Save the old value
@@ -146,45 +148,45 @@ class PageFourValues2 : public Page
// ############### Horizontal Line ################
// Horizontal line 3 pix
getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor);
epd->fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 145);
getdisplay().print(name2); // Page name
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 145);
epd->print(name2); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 180);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 180);
if(holdvalues == false){
getdisplay().print(unit2); // Unit
epd->print(unit2); // Unit
}
else{
getdisplay().print(unit2old);
epd->print(unit2old);
}
// Switch font if format for any values
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(100, 180);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(100, 180);
}
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(180, 158);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(180, 158);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 180);
epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 180);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue2); // Real value as formated string
epd->print(svalue2); // Real value as formated string
}
else{
getdisplay().print(svalue2old); // Old value as formated string
epd->print(svalue2old); // Old value as formated string
}
if(valid2 == true){
svalue2old = svalue2; // Save the old value
@@ -194,45 +196,45 @@ class PageFourValues2 : public Page
// ############### Horizontal Line ################
// Horizontal line 3 pix
getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor);
epd->fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 220);
getdisplay().print(name3); // Page name
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 220);
epd->print(name3); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(20, 240);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(20, 240);
if(holdvalues == false){
getdisplay().print(unit3); // Unit
epd->print(unit3); // Unit
}
else{
getdisplay().print(unit3old);
epd->print(unit3old);
}
// Switch font if format for any values
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(50, 240);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(50, 240);
}
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(100, 240);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(100, 240);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(80, 270);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(80, 270);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue3); // Real value as formated string
epd->print(svalue3); // Real value as formated string
}
else{
getdisplay().print(svalue3old); // Old value as formated string
epd->print(svalue3old); // Old value as formated string
}
if(valid3 == true){
svalue3old = svalue3; // Save the old value
@@ -242,45 +244,45 @@ class PageFourValues2 : public Page
// ############### Vertical Line ################
// Vertical line 3 pix
getdisplay().fillRect(200, 195, 3, 75, commonData->fgcolor);
epd->fillRect(200, 195, 3, 75, commonData->fgcolor);
// ############### Value 4 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(220, 220);
getdisplay().print(name4); // Page name
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(220, 220);
epd->print(name4); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(220, 240);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(220, 240);
if(holdvalues == false){
getdisplay().print(unit4); // Unit
epd->print(unit4); // Unit
}
else{
getdisplay().print(unit4old);
epd->print(unit4old);
}
// Switch font if format for any values
if(bvalue4->getFormat() == "formatLatitude" || bvalue4->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(250, 240);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(250, 240);
}
else if(bvalue4->getFormat() == "formatTime" || bvalue4->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(300, 240);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(300, 240);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(280, 270);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(280, 270);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue4); // Real value as formated string
epd->print(svalue4); // Real value as formated string
}
else{
getdisplay().print(svalue4old); // Old value as formated string
epd->print(svalue4old); // Old value as formated string
}
if(valid4 == true){
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
#include "Pagedata.h"
@@ -6,12 +7,23 @@
class PageGenerator : public Page
{
private:
String batVoltage;
int genPower;
String powerSensor;
public:
PageGenerator(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageGenerator");
PageGenerator(CommonData &common) : Page(common)
{
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
if(key == 11){
commonData->keylock = !commonData->keylock;
@@ -20,20 +32,8 @@ public:
return key;
}
int displayPage(PageData &pageData)
{
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
int displayPage(PageData &pageData) {
// 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 value2 = 0; // Solar current
double value3 = 0; // Solar output power
@@ -60,100 +60,95 @@ public:
bool valid1 = true;
// Optical warning by limit violation
if(String(flashLED) == "Limit Violation"){
// Over voltage
if(value1 > 14.8 && batVoltage == "12V"){
setBlinkingLED(true);
}
if(value1 <= 14.8 && batVoltage == "12V"){
if (flashLED == "Limit Violation") {
// Over voltage?
if (batVoltage == "12V") {
setBlinkingLED(value1 > 14.8);
} else if (batVoltage == "24V") {
setBlinkingLED(value1 > 29.6);
} else {
setBlinkingLED(false);
}
if(value1 > 29.6 && batVoltage == "24V"){
setBlinkingLED(true);
}
if(value1 <= 29.6 && batVoltage == "24V"){
setBlinkingLED(false);
}
}
// 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
//***********************************************************
// 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
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(10, 65);
getdisplay().print("Power");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(12, 82);
getdisplay().print("Generator");
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(10, 65);
epd->print("Power");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(12, 82);
epd->print("Generator");
// Show voltage type
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 140);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 140);
int bvoltage = 0;
if(String(batVoltage) == "12V") bvoltage = 12;
else bvoltage = 24;
getdisplay().print(bvoltage);
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V");
epd->print(bvoltage);
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("V");
// Show solar power
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 200);
if(genPower <= 999) getdisplay().print(genPower, 0);
if(genPower > 999) getdisplay().print(float(genPower/1000.0), 1);
getdisplay().setFont(&Ubuntu_Bold16pt8b);
if(genPower <= 999) getdisplay().print("W");
if(genPower > 999) getdisplay().print("kW");
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 200);
if(genPower <= 999) epd->print(genPower, 0);
if(genPower > 999) epd->print(float(genPower/1000.0), 1);
epd->setFont(&Ubuntu_Bold16pt8b);
if(genPower <= 999) epd->print("W");
if(genPower > 999) epd->print("kW");
// Show info
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 235);
getdisplay().print("Installed");
getdisplay().setCursor(10, 255);
getdisplay().print("Power Modul");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 235);
epd->print("Installed");
epd->setCursor(10, 255);
epd->print("Power Modul");
// Show generator
generatorGraphic(200, 95, commonData->fgcolor, commonData->bgcolor);
// Show load level in percent
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 200);
getdisplay().print(genPercentage);
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("%");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(150, 235);
getdisplay().print("Load");
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(150, 200);
epd->print(genPercentage);
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("%");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(150, 235);
epd->print("Load");
// Show sensor type info
String i2cAddr = "";
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(270, 60);
if(powerSensor == "off") getdisplay().print("Internal");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(270, 60);
if(powerSensor == "off") epd->print("Internal");
if(powerSensor == "INA219"){
getdisplay().print("INA219");
epd->print("INA219");
i2cAddr = " (0x" + String(INA219_I2C_ADDR3, HEX) + ")";
}
if(powerSensor == "INA226"){
getdisplay().print("INA226");
epd->print("INA226");
i2cAddr = " (0x" + String(INA226_I2C_ADDR3, HEX) + ")";
}
getdisplay().print(i2cAddr);
getdisplay().setCursor(270, 80);
getdisplay().print("Sensor Modul");
epd->print(i2cAddr);
epd->setCursor(270, 80);
epd->print("Sensor Modul");
// Reading bus data or using simulation data
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 140);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 140);
if(simulation == true){
if(batVoltage == "12V"){
value1 = 12.0;
@@ -162,46 +157,59 @@ public:
value1 = 24.0;
}
value1 += float(random(0, 5)) / 10; // Simulation data
getdisplay().print(value1,1);
epd->print(value1,1);
}
else{
// Check for valid real data, display also if hold values activated
if(valid1 == true || holdvalues == true){
// Resolution switching
if(value1 <= 9.9) getdisplay().print(value1, 2);
if(value1 > 9.9 && value1 <= 99.9)getdisplay().print(value1, 1);
if(value1 > 99.9) getdisplay().print(value1, 0);
if(value1 <= 9.9) epd->print(value1, 2);
if(value1 > 9.9 && value1 <= 99.9)epd->print(value1, 1);
if(value1 > 99.9) epd->print(value1, 0);
}
else{
getdisplay().print("---"); // Missing bus data
epd->print(commonData->fmt->placeholder); // Missing bus data
}
}
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V");
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("V");
// Show actual current in A
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 200);
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
if(value2 <= 9.9) getdisplay().print(value2, 2);
if(value2 > 9.9 && value2 <= 99.9)getdisplay().print(value2, 1);
if(value2 > 99.9) getdisplay().print(value2, 0);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 200);
if ((powerSensor == "INA219" || powerSensor == "INA226") && (simulation == false)) {
// TODO use formatter for this?
if (value2 <= 9.9) {
epd->print(value2, 2);
} else if (value2 <= 99.9) {
epd->print(value2, 1);
} else {
epd->print(value2, 0);
}
}
else getdisplay().print("---");
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("A");
else {
epd->print(commonData->fmt->placeholder);
}
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("A");
// Show actual consumption in W
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 260);
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
if(value3 <= 9.9) getdisplay().print(value3, 2);
if(value3 > 9.9 && value3 <= 99.9)getdisplay().print(value3, 1);
if(value3 > 99.9) getdisplay().print(value3, 0);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 260);
if ((powerSensor == "INA219" || powerSensor == "INA226") && (simulation == false)) {
if(value3 <= 9.9) {
epd->print(value3, 2);
} else if (value3 <= 99.9) {
epd->print(value3, 1);
} else {
epd->print(value3, 0);
}
}
else getdisplay().print("---");
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("W");
else {
epd->print(commonData->fmt->placeholder);
}
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("W");
return PAGE_UPDATE;
};

View File

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

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
@@ -6,13 +7,19 @@
class PageOneValue : public Page
{
public:
PageOneValue(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageOneValue");
private:
String lengthformat;
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
if(key == 11){
commonData->keylock = !commonData->keylock;
@@ -21,20 +28,22 @@ class PageOneValue : public Page
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
// Old values for hold function
static String svalue1old = "";
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
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
@@ -43,61 +52,55 @@ class PageOneValue : public Page
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
String svalue1 = commonData->fmt->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
// Logging boat values
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
//***********************************************************
/// 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
getdisplay().setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold32pt8b);
getdisplay().setCursor(20, 100);
getdisplay().print(name1); // Page name
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold32pt8b);
epd->setCursor(20, 100);
epd->print(name1); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(270, 100);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(270, 100);
if(holdvalues == false){
getdisplay().print(unit1); // Unit
epd->print(unit1); // Unit
}
else{
getdisplay().print(unit1old);
epd->print(unit1old);
}
// Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 180);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 180);
}
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold32pt8b);
getdisplay().setCursor(20, 200);
epd->setFont(&Ubuntu_Bold32pt8b);
epd->setCursor(20, 200);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b);
getdisplay().setCursor(20, 240);
epd->setFont(&DSEG7Classic_BoldItalic60pt7b);
epd->setCursor(20, 240);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue1); // Real value as formated string
epd->print(svalue1); // Real value as formated string
}
else{
getdisplay().print(svalue1old); // Old value as formated string
epd->print(svalue1old); // Old value as formated string
}
if(valid1 == true){
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
#include "Pagedata.h"
@@ -5,14 +6,30 @@
class PageRollPitch : public Page
{
private:
String lengthformat;
int rolllimit;
String roffset;
double rolloffset;
String poffset;
double pitchoffset;
public:
PageRollPitch(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageRollPitch");
PageRollPitch(CommonData &common) : Page(common)
{
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
virtual int handleKey(int key){
int handleKey(int key){
// Code for keylock
if(key == 11){
commonData->keylock = !commonData->keylock;
@@ -21,9 +38,7 @@ public:
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
int displayPage(PageData &pageData) {
double value1 = 0;
double value2 = 0;
@@ -32,19 +47,6 @@ public:
String svalue2 = "";
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
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (xdrRoll)
String name1 = xdrDelete(bvalue1->getName()); // Value name
@@ -97,71 +99,70 @@ public:
}
// Optical warning by limit violation
if(String(flashLED) == "Limit Violation"){
if (flashLED == "Limit Violation") {
// 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);
setFlashLED(false);
}
else{
} else {
setBlinkingLED(true);
}
}
// Logging boat values
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
//***********************************************************
// 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
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 65);
getdisplay().print(rolllimit); // Value
//getdisplay().print(svalue1); // Value
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 65);
epd->print(rolllimit); // Value
//epd->print(svalue1); // Value
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 95);
getdisplay().print("Limit"); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 115);
getdisplay().print("DEG");
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 95);
epd->print("Limit"); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 115);
epd->print("DEG");
// Horizintal separator left
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
epd->fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show roll value
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 270);
if(holdvalues == false) getdisplay().print(svalue1); // Value
else getdisplay().print(svalue1old);
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 220);
getdisplay().print(name1); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 190);
getdisplay().print("Deg");
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 270);
if(holdvalues == false) epd->print(svalue1); // Value
else epd->print(svalue1old);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 220);
epd->print(name1); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 190);
epd->print("Deg");
// Horizintal separator right
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
epd->fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show pitch value
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 270);
if(holdvalues == false) getdisplay().print(svalue2); // Value
else getdisplay().print(svalue2old);
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 220);
getdisplay().print(name2); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 190);
getdisplay().print("Deg");
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(295, 270);
if(holdvalues == false) epd->print(svalue2); // Value
else epd->print(svalue2old);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(335, 220);
epd->print(name2); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 190);
epd->print("Deg");
//*******************************************************************************************
@@ -169,8 +170,8 @@ public:
int rInstrument = 100; // Radius of instrument
float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
epd->fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
for(int i=0; i<360; i=i+10)
{
@@ -194,17 +195,17 @@ public:
// Print text centered on position x, y
int16_t x1, y1; // 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
getdisplay().setCursor(x-w/2, y+h/2);
epd->getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
epd->setCursor(x-w/2, y+h/2);
if(i % 20 == 0){
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().print(ii);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->print(ii);
}
// Draw sub scale with dots
float x1c = 200 + rInstrument*sin(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 cosx=cos(i/180.0*M_PI);
@@ -215,10 +216,10 @@ public:
float xx2 = +dx;
float yy1 = -(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*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*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
}
@@ -239,7 +240,7 @@ public:
float xx2 = startwidth;
float yy1 = -startwidth;
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*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer
@@ -249,28 +250,28 @@ public:
float ix2 = -endwidth;
float iy1 = -(rInstrument * 0.7);
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*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
// 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
getdisplay().fillCircle(200, 150, startwidth + 22, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 20, commonData->fgcolor); // Boat circle
epd->fillCircle(200, 150, startwidth + 22, commonData->bgcolor);
epd->fillCircle(200, 150, startwidth + 20, commonData->fgcolor); // Boat circle
int x0 = 200;
int y0 = 150;
int x1 = x0 + 50*cos(value1);
int y1 = y0 + 50*sin(value1);
int x2 = x0 + 50*cos(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);
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)
getdisplay().fillRect(150, 160, 100, 4, commonData->fgcolor); // Water line
epd->fillTriangle(x0, y0, x1, y1, x2, y2, commonData->bgcolor); // Clear half top side of boat circle (left triangle)
epd->fillRect(150, 160, 100, 4, commonData->fgcolor); // Water line
// Draw roll pointer
startwidth = 4; // Start width of pointer
@@ -283,7 +284,7 @@ public:
float xx2 = startwidth;
float yy1 = -startwidth;
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*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer
@@ -293,15 +294,15 @@ public:
float ix2 = -endwidth;
float iy1 = -(rInstrument - 15);
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*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
}
else{
// Print sensor info
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(145, 200);
getdisplay().print("No sensor data"); // Info missing sensor
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(145, 200);
epd->print("No sensor data"); // Info missing sensor
}
return PAGE_UPDATE;

View File

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

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
@@ -14,13 +15,13 @@ const int HowManyValues = 6;
class PageSixValues : public Page
{
public:
PageSixValues(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageSixValues");
public:
PageSixValues(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageSixValues");
}
virtual int handleKey(int key){
int handleKey(int key) {
// Code for keylock
if(key == 11){
commonData->keylock = !commonData->keylock;
@@ -29,130 +30,131 @@ class PageSixValues : public Page
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// 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
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;
};
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
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
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] = 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){
return new PageSixValues(common);
}/**

View File

@@ -0,0 +1,191 @@
// 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");
epd->setFont(&Ubuntu_Bold8pt8b);
// show satellites in "map"
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);
}
// Signal / Noise bars
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
#include "Pagedata.h"
@@ -6,12 +7,23 @@
class PageSolar : public Page
{
private:
String batVoltage;
int solPower;
String powerSensor;
public:
PageSolar(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageSolar");
PageSolar(CommonData &common) : Page(common)
{
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
if(key == 11){
commonData->keylock = !commonData->keylock;
@@ -20,19 +32,8 @@ public:
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
int displayPage(PageData &pageData) {
// 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 value2 = 0; // Solar current
double value3 = 0; // Solar output power
@@ -58,98 +59,93 @@ public:
bool valid1 = true;
// Optical warning by limit violation
if(String(flashLED) == "Limit Violation"){
// Over voltage
if(value1 > 14.8 && batVoltage == "12V"){
setBlinkingLED(true);
}
if(value1 <= 14.8 && batVoltage == "12V"){
// Optical warning by limit violation
if (flashLED == "Limit Violation") {
// Over voltage?
if (batVoltage == "12V") {
setBlinkingLED(value1 > 14.8);
} else if (batVoltage == "24V") {
setBlinkingLED(value1 > 29.6);
} else {
setBlinkingLED(false);
}
if(value1 > 29.6 && batVoltage == "24V"){
setBlinkingLED(true);
}
if(value1 <= 29.6 && batVoltage == "24V"){
setBlinkingLED(false);
}
}
// 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
//***********************************************************
// 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
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(10, 65);
getdisplay().print("Solar");
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(10, 65);
epd->print("Solar");
// Show voltage type
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 140);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 140);
int bvoltage = 0;
if(String(batVoltage) == "12V") bvoltage = 12;
else bvoltage = 24;
getdisplay().print(bvoltage);
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V");
epd->print(bvoltage);
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("V");
// Show solar power
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 200);
if(solPower <= 999) getdisplay().print(solPower, 0);
if(solPower > 999) getdisplay().print(float(solPower/1000.0), 1);
getdisplay().setFont(&Ubuntu_Bold16pt8b);
if(solPower <= 999) getdisplay().print("W");
if(solPower > 999) getdisplay().print("kW");
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 200);
if(solPower <= 999) epd->print(solPower, 0);
if(solPower > 999) epd->print(float(solPower/1000.0), 1);
epd->setFont(&Ubuntu_Bold16pt8b);
if(solPower <= 999) epd->print("W");
if(solPower > 999) epd->print("kW");
// Show info
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 235);
getdisplay().print("Installed");
getdisplay().setCursor(10, 255);
getdisplay().print("Solar Modul");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 235);
epd->print("Installed");
epd->setCursor(10, 255);
epd->print("Solar Modul");
// Show solar panel
solarGraphic(150, 45, commonData->fgcolor, commonData->bgcolor);
// Show load level in percent
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 200);
getdisplay().print(solPercentage);
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("%");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(150, 235);
getdisplay().print("Load");
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(150, 200);
epd->print(solPercentage);
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("%");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(150, 235);
epd->print("Load");
// Show sensor type info
String i2cAddr = "";
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(270, 60);
if(powerSensor == "off") getdisplay().print("Internal");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(270, 60);
if(powerSensor == "off") epd->print("Internal");
if(powerSensor == "INA219"){
getdisplay().print("INA219");
epd->print("INA219");
i2cAddr = " (0x" + String(INA219_I2C_ADDR2, HEX) + ")";
}
if(powerSensor == "INA226"){
getdisplay().print("INA226");
epd->print("INA226");
i2cAddr = " (0x" + String(INA226_I2C_ADDR2, HEX) + ")";
}
getdisplay().print(i2cAddr);
getdisplay().setCursor(270, 80);
getdisplay().print("Sensor Modul");
epd->print(i2cAddr);
epd->setCursor(270, 80);
epd->print("Sensor Modul");
// Reading bus data or using simulation data
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 140);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 140);
if(simulation == true){
if(batVoltage == "12V"){
value1 = 12.0;
@@ -158,46 +154,62 @@ public:
value1 = 24.0;
}
value1 += float(random(0, 5)) / 10; // Simulation data
getdisplay().print(value1,1);
epd->print(value1,1);
}
else{
// Check for valid real data, display also if hold values activated
if(valid1 == true || holdvalues == true){
// Resolution switching
if(value1 <= 9.9) getdisplay().print(value1, 2);
if(value1 > 9.9 && value1 <= 99.9)getdisplay().print(value1, 1);
if(value1 > 99.9) getdisplay().print(value1, 0);
if (value1 <= 9.9) {
epd->print(value1, 2);
} else if (value1 <= 99.9) {
epd->print(value1, 1);
} else {
epd->print(value1, 0);
}
}
else{
getdisplay().print("---"); // Missing bus data
else {
epd->print(commonData->fmt->placeholder); // Missing bus data
}
}
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("V");
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("V");
// Show actual current in A
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 200);
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
if(value2 <= 9.9) getdisplay().print(value2, 2);
if(value2 > 9.9 && value2 <= 99.9)getdisplay().print(value2, 1);
if(value2 > 99.9) getdisplay().print(value2, 0);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 200);
if ((powerSensor == "INA219" || powerSensor == "INA226") && (simulation == false)) {
if (value2 <= 9.9) {
epd->print(value2, 2);
} else if (value2 <= 99.9) {
epd->print(value2, 1);
} else {
epd->print(value2, 0);
}
}
else getdisplay().print("---");
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("A");
else {
epd->print(commonData->fmt->placeholder);
}
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("A");
// Show actual consumption in W
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(260, 260);
if((powerSensor == "INA219" || powerSensor == "INA226") && simulation == false){
if(value3 <= 9.9) getdisplay().print(value3, 2);
if(value3 > 9.9 && value3 <= 99.9)getdisplay().print(value3, 1);
if(value3 > 99.9) getdisplay().print(value3, 0);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(260, 260);
if ((powerSensor == "INA219" || powerSensor == "INA226") && (simulation == false)) {
if (value3 <= 9.9) {
epd->print(value3, 2);
} else if (value3 <= 99.9) {
epd->print(value3, 1);
} else {
epd->print(value3, 0);
}
}
else getdisplay().print("---");
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().print("W");
else {
epd->print(commonData->fmt->placeholder);
}
epd->setFont(&Ubuntu_Bold16pt8b);
epd->print("W");
return PAGE_UPDATE;
};

View File

@@ -1,14 +1,38 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#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
* 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 "OBP60Extensions.h"
#include "ConfigMenu.h"
#include "images/logo64.xbm"
#include <esp32/clk.h>
#include "qrcode.h"
#include "Nmea2kTwai.h"
#ifdef BOARD_OBP40S3
#include <SD.h>
#include <FS.h>
#include "dirent.h"
#endif
#define STRINGIZE_IMPL(x) #x
@@ -19,64 +43,531 @@
#define DISPLAYINFO STRINGIZE(EPDTYPE)
#define GXEPD2INFO STRINGIZE(GXEPD2VERS)
/*
* 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
{
uint64_t chipid;
bool simulation;
bool sdcard;
String buzzer_mode;
uint8_t buzzer_power;
String cpuspeed;
String rtc_module;
String gps_module;
String env_module;
private:
// NVRAM config options
String flashLED;
String batt_sensor;
String solar_sensor;
String gen_sensor;
String rot_sensor;
double homelat;
double homelon;
// Generic data access
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:
PageSystem(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageSystem");
if (hasFRAM) {
mode = fram.read(FRAM_SYSTEM_MODE);
String batt_sensor;
String solar_sensor;
String gen_sensor;
String rot_sensor;
double homelat;
double homelon;
Nmea2kTwai *NMEA2000;
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();
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();
if (hasFRAM) fram.write(FRAM_SYSTEM_MODE, mode);
}
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
int 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);
epd->print("Work in progress...");
#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
IMAGES/
CHARTS/
LOGS/
DATA/
*/
// Simple test for one file in root
epd->setCursor(x0, y0 + 32);
String file_test = MOUNT_POINT "/IMG/icon-settings2.pbm";
logger->logDebug(GwLog::LOG, "Testfile: %s", file_test.c_str());
struct stat st;
if (stat(file_test.c_str(), &st) == 0) {
epd->printf("File %s exists", file_test.c_str());
} else {
epd->printf("File %s not found", file_test.c_str());
}
// Root directory check
DIR* dir = opendir(MOUNT_POINT);
if (dir != NULL) {
logger->logDebug(GwLog::LOG, "Root directory: %s", MOUNT_POINT);
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
logger->logDebug(GwLog::LOG, " %s type %d", entry->d_name, entry->d_type);
// type 1 is file
// type 2 is dir
}
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 "/IMG/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() {
// NMEA2000 device list
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));
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[1].label = "MODE";
commonData->keydata[2].label = "";
@@ -85,25 +576,12 @@ public:
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
// Switch display mode
commonData->logger->logDebug(GwLog::LOG, "System keyboard handler");
logger->logDebug(GwLog::LOG, "System keyboard handler");
if (key == 2) {
if (mode == 'N') {
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);
incMode();
return 0;
}
#ifdef BOARD_OBP60S3
@@ -117,7 +595,8 @@ public:
}
// standby / deep sleep
if (key == 5) {
deepSleep(*commonData);
logger->logDebug(GwLog::LOG, "System going into deep sleep mode...");
deepSleep(*commonData);
}
// Code for keylock
if (key == 11) {
@@ -126,12 +605,18 @@ public:
}
#endif
#ifdef BOARD_OBP40S3
// grab cursor keys to disable page navigation
if (key == 9 or key == 10) {
// use cursor keys for local mode navigation
if (key == 9) {
incMode();
return 0;
}
if (key == 10) {
decMode();
return 0;
}
// standby / deep sleep
if (key == 12) {
logger->logDebug(GwLog::LOG, "System going into deep sleep mode...");
deepSleep(*commonData);
}
#endif
@@ -155,7 +640,7 @@ public:
for (uint8_t j = 0; j < qrcode.size; j++) {
for (uint8_t i = 0; i < qrcode.size; i++) {
if (qrcode_getModule(&qrcode, i, j)) {
getdisplay().fillRect(x, y, s, s, commonData->fgcolor);
epd->fillRect(x, y, s, s, commonData->fgcolor);
}
x += s;
}
@@ -164,233 +649,48 @@ public:
}
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Get config data
String flashLED = config->getString(config->flashLED);
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
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
LOG_DEBUG(GwLog::LOG,"Drawing at PageSystem");
int displayPage(PageData &pageData) {
// Draw page
//***********************************************************
uint16_t x0 = 8; // left column
uint16_t y0 = 48; // data table starts here
// Logging page information
logger->logDebug(GwLog::LOG, "Drawing at PageSystem, Mode=%c", 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') {
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48);
getdisplay().print("System Information");
getdisplay().drawXBitmap(320, 25, logo64_bits, logo64_width, logo64_height, commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold8pt8b);
y0 = 155;
char ssid[13];
snprintf(ssid, 13, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);
displayBarcode(String(ssid), 320, 200, 2);
getdisplay().setCursor(8, 70);
getdisplay().print(String("MCUDEVICE-") + String(ssid));
getdisplay().setCursor(8, 95);
getdisplay().print("Firmware version: ");
getdisplay().setCursor(150, 95);
getdisplay().print(VERSINFO);
getdisplay().setCursor(8, 113);
getdisplay().print("Board version: ");
getdisplay().setCursor(150, 113);
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));
// call current system page
switch (mode) {
case 'N':
displayModeNormal();
break;
case 'S':
displayModeSettings();
break;
case 'C':
displayModeConfig();
break;
case 'A':
displayModeSDCard();
break;
case 'D':
displayModeDevicelist();
break;
}
// Update display
getdisplay().nextPage(); // Partial update (fast)
epd->nextPage(); // Partial update (fast)
return PAGE_OK;
};
};

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
@@ -6,13 +7,19 @@
class PageThreeValues : public Page
{
public:
PageThreeValues(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageThreeValue");
private:
String lengthformat;
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
if(key == 11){
commonData->keylock = !commonData->keylock;
@@ -21,9 +28,17 @@ class PageThreeValues : public Page
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
// Old values for hold function
static String svalue1old = "";
@@ -33,13 +48,6 @@ class PageThreeValues : public Page
static String svalue3old = "";
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
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name
@@ -47,8 +55,8 @@ class PageThreeValues : public Page
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information
String svalue1 = 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 svalue1 = commonData->fmt->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
// Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
@@ -57,8 +65,8 @@ class PageThreeValues : public Page
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information
String svalue2 = 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 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
// Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list
@@ -67,63 +75,57 @@ class PageThreeValues : public Page
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information
String svalue3 = formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = formatValue(bvalue3, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
String svalue3 = commonData->fmt->formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = commonData->fmt->formatValue(bvalue3, *commonData).unit; // Unit of value
// 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);
logger->logDebug(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
//***********************************************************
/// 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 ################
// Show name
getdisplay().setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 55);
getdisplay().print(name1); // Page name
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 55);
epd->print(name1); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 90);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 90);
if(holdvalues == false){
getdisplay().print(unit1); // Unit
epd->print(unit1); // Unit
}
else{
getdisplay().print(unit1old);
epd->print(unit1old);
}
// Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 90);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(50, 90);
}
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 68);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(170, 68);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 90);
epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 90);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue1); // Real value as formated string
epd->print(svalue1); // Real value as formated string
}
else{
getdisplay().print(svalue1old); // Old value as formated string
epd->print(svalue1old); // Old value as formated string
}
if(valid1 == true){
svalue1old = svalue1; // Save the old value
@@ -133,45 +135,45 @@ class PageThreeValues : public Page
// ############### Horizontal Line ################
// Horizontal line 3 pix
getdisplay().fillRect(0, 105, 400, 3, commonData->fgcolor);
epd->fillRect(0, 105, 400, 3, commonData->fgcolor);
// ############### Value 2 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 145);
getdisplay().print(name2); // Page name
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 145);
epd->print(name2); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 180);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 180);
if(holdvalues == false){
getdisplay().print(unit2); // Unit
epd->print(unit2); // Unit
}
else{
getdisplay().print(unit2old);
epd->print(unit2old);
}
// Switch font if format for any values
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 180);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(50, 180);
}
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 158);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(170, 158);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 180);
epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 180);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue2); // Real value as formated string
epd->print(svalue2); // Real value as formated string
}
else{
getdisplay().print(svalue2old); // Old value as formated string
epd->print(svalue2old); // Old value as formated string
}
if(valid2 == true){
svalue2old = svalue2; // Save the old value
@@ -181,45 +183,45 @@ class PageThreeValues : public Page
// ############### Horizontal Line ################
// Horizontal line 3 pix
getdisplay().fillRect(0, 195, 400, 3, commonData->fgcolor);
epd->fillRect(0, 195, 400, 3, commonData->fgcolor);
// ############### Value 3 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 235);
getdisplay().print(name3); // Page name
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 235);
epd->print(name3); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 270);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 270);
if(holdvalues == false){
getdisplay().print(unit3); // Unit
epd->print(unit3); // Unit
}
else{
getdisplay().print(unit3old);
epd->print(unit3old);
}
// Switch font if format for any values
if(bvalue3->getFormat() == "formatLatitude" || bvalue3->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 270);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(50, 270);
}
else if(bvalue3->getFormat() == "formatTime" || bvalue3->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 248);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(170, 248);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
getdisplay().setCursor(180, 270);
epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setCursor(180, 270);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue3); // Real value as formated string
epd->print(svalue3); // Real value as formated string
}
else{
getdisplay().print(svalue3old); // Old value as formated string
epd->print(svalue3old); // Old value as formated string
}
if(valid3 == true){
svalue3old = svalue3; // 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
#include "Pagedata.h"
@@ -6,13 +7,19 @@
class PageTwoValues : public Page
{
public:
PageTwoValues(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageTwoValue");
private:
String lengthformat;
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
if(key == 11){
commonData->keylock = !commonData->keylock;
@@ -21,9 +28,17 @@ class PageTwoValues : public Page
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
// Old values for hold function
static String svalue1old = "";
@@ -31,12 +46,6 @@ class PageTwoValues : public Page
static String svalue2old = "";
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
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
@@ -45,8 +54,8 @@ class PageTwoValues : public Page
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information
String svalue1 = 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 svalue1 = commonData->fmt->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
// Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
@@ -55,63 +64,57 @@ class PageTwoValues : public Page
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
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
// Logging boat values
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
//***********************************************************
// 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 ################
// Show name
getdisplay().setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 80);
getdisplay().print(name1); // Page name
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 80);
epd->print(name1); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 130);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 130);
if(holdvalues == false){
getdisplay().print(unit1); // Unit
epd->print(unit1); // Unit
}
else{
getdisplay().print(unit1old);
epd->print(unit1old);
}
// Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 130);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(50, 130);
}
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 105);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(170, 105);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b);
getdisplay().setCursor(180, 130);
epd->setFont(&DSEG7Classic_BoldItalic42pt7b);
epd->setCursor(180, 130);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue1); // Real value as formated string
epd->print(svalue1); // Real value as formated string
}
else{
getdisplay().print(svalue1old); // Old value as formated string
epd->print(svalue1old); // Old value as formated string
}
if(valid1 == true){
svalue1old = svalue1; // Save the old value
@@ -121,45 +124,45 @@ class PageTwoValues : public Page
// ############### Horizontal Line ################
// Horizontal line 3 pix
getdisplay().fillRect(0, 145, 400, 3, commonData->fgcolor);
epd->fillRect(0, 145, 400, 3, commonData->fgcolor);
// ############### Value 2 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 190);
getdisplay().print(name2); // Page name
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 190);
epd->print(name2); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 240);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(20, 240);
if(holdvalues == false){
getdisplay().print(unit2); // Unit
epd->print(unit2); // Unit
}
else{
getdisplay().print(unit2old);
epd->print(unit2old);
}
// Switch font if format for any values
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 240);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(50, 240);
}
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 215);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(170, 215);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b);
getdisplay().setCursor(180, 240);
epd->setFont(&DSEG7Classic_BoldItalic42pt7b);
epd->setCursor(180, 240);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue2); // Real value as formated string
epd->print(svalue2); // Real value as formated string
}
else{
getdisplay().print(svalue2old); // Old value as formated string
epd->print(svalue2old); // Old value as formated string
}
if(valid2 == true){
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
#include "Pagedata.h"
@@ -6,16 +7,24 @@
class PageVoltage : public Page
{
bool init = false; // Marker for init done
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
private:
String batVoltage;
String batType;
bool init = false; // Marker for init done
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:
PageVoltage(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageVoltage");
PageVoltage(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageVoltage");
// Get config data
batVoltage = config->getString(config->batteryVoltage);
batType = config->getString(config->batteryType);
if (hasFRAM) {
average = fram.read(FRAM_VOLTAGE_AVG);
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();
commonData->keydata[0].label = "AVG";
commonData->keydata[1].label = "MODE";
commonData->keydata[4].label = "TRD";
}
virtual int handleKey(int key){
int handleKey(int key) {
// Change average
if(key == 1){
average ++;
@@ -66,51 +79,41 @@ public:
}
void printAvg(int avg, uint16_t x, uint16_t y, bool prefix) {
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(x, y);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(x, y);
if (prefix) {
getdisplay().print("Avg: ");
epd->print("Avg: ");
}
switch (average) {
case 0:
getdisplay().print("1s");
epd->print("1s");
break;
case 1:
getdisplay().print("10s");
epd->print("10s");
break;
case 2:
getdisplay().print("60s");
epd->print("60s");
break;
case 3:
getdisplay().print("300s");
epd->print("300s");
break;
default:
getdisplay().print("1s");
epd->print("1s");
break;
}
}
void printVoltageSymbol(uint16_t x, uint16_t y, uint16_t color) {
getdisplay().setFont(&Ubuntu_Bold16pt8b);
getdisplay().setCursor(x, y);
getdisplay().print("V");
getdisplay().fillRect(x, y + 6, 22, 3, color);
getdisplay().fillRect(x, y + 11, 6, 3, color);
getdisplay().fillRect(x + 8, y + 11, 6, 3, color);
getdisplay().fillRect(x + 16, y + 11, 6, 3, color);
epd->setFont(&Ubuntu_Bold16pt8b);
epd->setCursor(x, y);
epd->print("V");
epd->fillRect(x, y + 6, 22, 3, color);
epd->fillRect(x, y + 11, 6, 3, color);
epd->fillRect(x + 8, y + 11, 6, 3, color);
epd->fillRect(x + 16, y + 11, 6, 3, color);
}
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);
int displayPage(PageData &pageData) {
double value1 = 0;
double valueTrend = 0; // Average over 10 values
@@ -151,92 +154,76 @@ public:
bool valid1 = true;
// Optical warning by limit violation
if(String(flashLED) == "Limit Violation"){
// Limits for Pb battery
if(String(batType) == "Pb" && (raw < 11.8 || raw > 14.8)){
if (flashLED == "Limit Violation") {
bool violation = false;
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);
}
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)){
} else {
setBlinkingLED(false);
setFlashLED(false);
}
}
// 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
//***********************************************************
// 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') {
// Display mode digital
// Show name
getdisplay().setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold32pt8b);
getdisplay().setCursor(20, 100);
getdisplay().print(name1); // Value name
epd->setTextColor(commonData->fgcolor);
epd->setFont(&Ubuntu_Bold32pt8b);
epd->setCursor(20, 100);
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
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(185, 100);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(185, 100);
if(commonData->data.BatteryChargeStatus == true){
getdisplay().print("Charge");
epd->print("Charge");
}
else{
getdisplay().print("Discharge");
epd->print("Discharge");
}
#endif
#endif
// Show unit
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(270, 100);
getdisplay().print("V");
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(270, 100);
epd->print("V");
// Show battery type
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(295, 100);
#ifdef BOARD_OBP60S3
getdisplay().print(batType);
#endif
#if defined BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR
getdisplay().print("LiPo");
#endif
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(295, 100);
#ifdef BOARD_OBP60S3
epd->print(batType);
#endif
#if defined BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR
epd->print("LiPo");
#endif
// Show average settings
printAvg(average, 320, 84, true);
// Reading bus data or using simulation data
getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b);
getdisplay().setCursor(20, 240);
epd->setFont(&DSEG7Classic_BoldItalic60pt7b);
epd->setCursor(20, 240);
if(simulation == true){
if(batVoltage == "12V"){
value1 = 12.0;
@@ -245,30 +232,30 @@ public:
value1 = 24.0;
}
value1 += float(random(0, 5)) / 10; // Simulation data
getdisplay().print(value1,1);
epd->print(value1,1);
}
else{
// Check for valid real data, display also if hold values activated
if(valid1 == true || holdvalues == true){
// Resolution switching
if(value1 < 10){
getdisplay().print(value1,2);
epd->print(value1,2);
}
if(value1 >= 10 && value1 < 100){
getdisplay().print(value1,1);
epd->print(value1,1);
}
if(value1 >= 100){
getdisplay().print(value1,0);
epd->print(value1,0);
}
}
else{
getdisplay().print("---"); // Missing bus data
epd->print(commonData->fmt->placeholder); // Missing bus data
}
}
// Show trend indicator
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)){
displayTrendHigh(320, 174, 11, commonData->fgcolor); // Show high indicator
}
@@ -289,9 +276,9 @@ public:
std::vector<Point> pts;
// Instrument
getdisplay().drawCircleHelper(c.x, c.y, r + 2, 0x01, commonData->fgcolor);
getdisplay().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 + 2, 0x01, commonData->fgcolor);
epd->drawCircleHelper(c.x, c.y, r + 1, 0x01, commonData->fgcolor);
epd->drawCircleHelper(c.x, c.y, r , 0x01, commonData->fgcolor);
// Scale
// angle to voltage scale mapping
@@ -304,7 +291,7 @@ public:
{c.x - r + 12, 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) {
if (angle % 15 == 0) {
fillPoly4(rotatePoints(c, pts, angle), commonData->fgcolor);
@@ -314,7 +301,7 @@ public:
else {
p1 = rotatePoint(c, {c.x - r, 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);
// base
getdisplay().fillCircle(c.x, c.y, 7, commonData->fgcolor);
getdisplay().fillCircle(c.x, c.y, 4, commonData->bgcolor);
epd->fillCircle(c.x, c.y, 7, commonData->fgcolor);
epd->fillCircle(c.x, c.y, 4, commonData->bgcolor);
// Symbol
printVoltageSymbol(40, 60, commonData->fgcolor);
// Additional information at right side
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(300, 60);
getdisplay().print("Source:");
getdisplay().setCursor(300, 80);
getdisplay().print(name1);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(300, 60);
epd->print("Source:");
epd->setCursor(300, 80);
epd->print(name1);
getdisplay().setCursor(300, 110);
getdisplay().print("Type:");
getdisplay().setCursor(300, 130);
getdisplay().print(batType);
epd->setCursor(300, 110);
epd->print("Type:");
epd->setCursor(300, 130);
epd->print(batType);
getdisplay().setCursor(300, 160);
getdisplay().print("Avg:");
epd->setCursor(300, 160);
epd->print("Avg:");
printAvg(average, 300, 180, false);
// FRAM indicator
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
#include "Pagedata.h"
@@ -13,16 +14,17 @@
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:
PageWhite(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageWhite");
PageWhite(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageWhite");
refreshtime = 15000;
}
virtual int handleKey(int key) {
int handleKey(int key) {
// Change display mode
if (key == 1) {
if (mode == 'W') {
@@ -37,21 +39,20 @@ public:
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// Get config data
String flashLED = config->getString(config->flashLED);
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
// Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageWhite");
logger->logDebug(GwLog::LOG, "Drawing at PageWhite");
// Draw page
//***********************************************************
@@ -61,19 +62,19 @@ public:
// Set display in partial refresh mode
if (mode == 'W') {
getdisplay().setFullWindow();
epd->setFullWindow();
} 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') {
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') {
#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
#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
}

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
@@ -214,15 +215,21 @@ static unsigned char front_bits[] PROGMEM = {
class PageWind : public Page
{
bool keylock = false; // Keylock
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
private:
String lengthformat;
bool keylock = false; // Keylock
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:
PageWind(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageWind");
PageWind(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageWind");
// Get config data
lengthformat = config->getString(config->lengthFormat);
if (hasFRAM) {
lp = fram.read(FRAM_WIND_SIZE);
source = fram.read(FRAM_WIND_SRC);
@@ -230,7 +237,7 @@ public:
}
}
virtual void setupKeys(){
void setupKeys() {
Page::setupKeys();
commonData->keydata[0].label = "MODE";
if (mode == 'X') {
@@ -242,7 +249,7 @@ public:
}
// Key functions
virtual int handleKey(int key){
int handleKey(int key) {
if(key == 1){ // Mode switch
if(mode == 'N'){
@@ -296,23 +303,23 @@ public:
return key;
}
int displayPage(PageData &pageData)
{
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
static String svalue1old = "";
static String unit1old = "";
static String svalue2old = "";
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 *bvalue2; // Value 2 for angle on bottom
@@ -327,8 +334,8 @@ public:
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit
// bool valid1 = bvalue1->valid; // Valid information
String svalue1 = 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 svalue1 = commonData->fmt->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
// Get boat values for angle (AWD/TWD)
if (source == 'A') {
@@ -344,68 +351,62 @@ public:
if (simulation) {
value2 = 0.62731; // some random value
}
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
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
// Logging boat values
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
//***********************************************************
// 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') {
// Original example code with scaling circle
// Show values AWS/TWS
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 50);
getdisplay().print(name1); // Value name
getdisplay().print(": ");
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 50);
epd->print(name1); // Value name
epd->print(": ");
if(holdvalues == false){
getdisplay().print(svalue1); // Value
getdisplay().print(" ");
getdisplay().print(unit1); // Unit
epd->print(svalue1); // Value
epd->print(" ");
epd->print(unit1); // Unit
}
else{
getdisplay().print(svalue1old); // Value old
getdisplay().print(" ");
getdisplay().print(unit1old); // Unit old
epd->print(svalue1old); // Value old
epd->print(" ");
epd->print(unit1old); // Unit old
}
// Show values AWD/TWD
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 260);
getdisplay().print(name2); // Value name
getdisplay().print(": ");
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(20, 260);
epd->print(name2); // Value name
epd->print(": ");
if(holdvalues == false){
getdisplay().print(svalue2); // Value
getdisplay().print(" ");
getdisplay().print(unit2); // Unit
epd->print(svalue2); // Value
epd->print(" ");
epd->print(unit2); // Unit
}
else{
getdisplay().print(svalue2old); // Value old
getdisplay().print(" ");
getdisplay().print(unit2old); // Unit old
epd->print(svalue2old); // Value old
epd->print(" ");
epd->print(unit2old); // Unit old
}
Point c = {200, 145};
// Draw instrument
getdisplay().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 + 5, commonData->fgcolor);
epd->fillCircle(c.x, c.y, lp + 1, commonData->bgcolor);
// Wind pointer
if (bvalue2->valid or simulation) {
@@ -420,7 +421,7 @@ public:
};
fillPoly4(rotatePoints(c, pts, RadToDeg(value2)), commonData->fgcolor);
} else {
getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setFont(&Ubuntu_Bold12pt8b);
drawTextCenter(c.x, c.y, "no data");
}
@@ -438,7 +439,7 @@ public:
};
int angle;
getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setFont(&Ubuntu_Bold12pt8b);
// starbord
// text with line
@@ -453,7 +454,7 @@ public:
for (int i = 30; i < 138; i += 6) {
if (i % 15 != 0) {
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 +471,17 @@ public:
for (int i = 228; i < 330; i += 6) {
if (i % 15 != 0) {
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
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 50);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 50);
if (source == 'A') {
getdisplay().print("APP");
epd->print("APP");
} else {
getdisplay().print("TRUE");
epd->print("TRUE");
}
// Wind pointer (angle)
@@ -500,7 +501,7 @@ public:
alpha *= -1;
}
getdisplay().fillCircle(c.x, c.y, 8, commonData->fgcolor);
epd->fillCircle(c.x, c.y, 8, commonData->fgcolor);
pts = {
{c.x - 1, c.y - (r - 20)},
{c.x + 1, c.y - (r - 20)},
@@ -508,39 +509,39 @@ public:
{c.x - 6, c.y + 15}
};
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 {
getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setFont(&Ubuntu_Bold12pt8b);
drawTextCenter(c.x, c.y, "no data");
}
// Wind speed as decimal number
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 250);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(150, 250);
if (holdvalues == false) {
getdisplay().print(svalue1);
epd->print(svalue1);
} else {
getdisplay().print(svalue1old);
epd->print(svalue1old);
}
// unit
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(220, 265);
getdisplay().print("kts");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(220, 265);
epd->print("kts");
}
else {
// Normal mode
// data source
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 50);
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(8, 50);
if (source == 'A') {
getdisplay().print("APP");
epd->print("APP");
} else {
getdisplay().print("TRUE");
epd->print("TRUE");
}
// 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};
uint16_t r = 150;
@@ -565,7 +566,7 @@ public:
for (int i = 30; i < 150; i += 10) {
if (i % 30 != 0) {
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 +583,27 @@ public:
for (int i = 210; i < 340; i += 10) {
if (i % 30 != 0) {
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
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(150, 250);
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(150, 250);
if (holdvalues == false) {
getdisplay().print(svalue1);
epd->print(svalue1);
} else {
getdisplay().print(svalue1old);
epd->print(svalue1old);
}
// unit
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(220, 265);
getdisplay().print("kts");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(220, 265);
epd->print("kts");
// Wind pointer (angle)
if (bvalue2->valid or simulation) {
float alpha = RadToDeg(value2);
getdisplay().fillCircle(c.x, c.y, 8, commonData->fgcolor);
epd->fillCircle(c.x, c.y, 8, commonData->fgcolor);
pts = {
{c.x - 1, c.y - (r - 20)},
{c.x + 1, c.y - (r - 20)},
@@ -610,9 +611,9 @@ public:
{c.x - 6, c.y + 15}
};
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 {
getdisplay().setFont(&Ubuntu_Bold12pt8b);
epd->setFont(&Ubuntu_Bold12pt8b);
drawTextCenter(c.x, c.y, "no data");
}

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "BoatDataCalibration.h"
@@ -8,13 +9,40 @@
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)
{
int minVal = windDirHstry.getMinVal();
size_t count = windDirHstry.getCurrentSize();
if (windDirHstry.isEmpty() || amount <= 0) {
return 0;
}
if (amount > count)
amount = count;
int16_t midWndDir, minWndDir, maxWndDir = 0;
int wndCenter = 0;
midWndDir = windDirHstry.getMid(amount);
if (midWndDir != INT16_MIN) {
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
int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
{
int minVal = windDirHstry.getMinVal();
size_t count = windDirHstry.getCurrentSize();
// size_t capacity = windDirHstry.getCapacity();
// size_t last = windDirHstry.getLastIdx();
if (windDirHstry.isEmpty() || amount <= 0) {
return minVal;
@@ -27,7 +55,6 @@ int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
int maxRng = minVal;
// Start from the newest value (last) and go backwards x times
for (size_t i = 0; i < amount; i++) {
// value = windDirHstry.get(((last - i) % capacity + capacity) % capacity);
value = windDirHstry.get(count - 1 - i);
if (value == minVal) {
@@ -48,31 +75,32 @@ int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
// ****************************************************************
class PageWindPlot : public Page {
private:
bool keylock = false; // Keylock
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
int dataIntv = 1; // Update interval for wind 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:
PageWindPlot(CommonData& common)
PageWindPlot(CommonData& common) : Page(common)
{
commonData = &common;
common.logger->logDebug(GwLog::LOG, "Instantiate PageWindPlot");
logger->logDebug(GwLog::LOG, "Instantiate PageWindPlot");
}
virtual void setupKeys()
{
void setupKeys() {
Page::setupKeys();
// 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[4].label = "TWS";
#endif
}
// Key functions
virtual int handleKey(int key)
{
int handleKey(int key) {
// Set chart mode TWD | TWS -> to be implemented
if (key == 1) {
if (chrtMode == 'D') {
@@ -85,8 +113,18 @@ public:
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) {
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) {
dataIntv = 2;
} else if (dataIntv == 2) {
@@ -99,12 +137,6 @@ public:
return 0; // Commit the key
}
// Switch TWS on/off
if (key == 5) {
showTWS = !showTWS;
return 0; // Commit the key
}
// Keylock function
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
@@ -113,19 +145,42 @@ public:
return key;
}
int displayPage(PageData& pageData)
{
GwConfigHandler* config = commonData->config;
GwLog* logger = commonData->logger;
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
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
static String twdName, twdUnit; // TWD name and unit
static int updFreq; // Update frequency for TWD
static int16_t twdLowest, twdHighest; // TWD range
// static int16_t twdBufMinVal; // lowest possible twd buffer value; used for non-set data
wndSrc = commonData->config->getString("page" + String(pageData.pageNumber) + "wndsrc");
if (wndSrc =="True wind") {
showTruW = true;
} else {
showTruW = false; // Wind source is apparant wind
}
commonData->logger->logDebug(GwLog::LOG,"New PageWindPlot: wind source=%s", wndSrc);
// current boat data values; TWD only for validation test, TWS for display of current value
const int numBoatData = 2;
#endif
};
int displayPage(PageData& pageData) {
static RingBuffer<int16_t>* wdHstry; // Wind direction data buffer
static RingBuffer<int16_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 int updFreq; // Update frequency for wind direction
static int16_t wdLowest, wdHighest; // Wind direction range
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, TWS/AWS for display of current value
const int numBoatData = 4;
GwApi::BoatValue* bvalue;
String BDataName[numBoatData];
double BDataValue[numBoatData];
@@ -137,8 +192,6 @@ public:
static bool isInitialized = false; // Flag to indicate that page is initialized
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 bool simulation = false;
static bool holdValues = false;
static int width; // Screen width
static int height; // Screen height
@@ -155,16 +208,15 @@ public:
static size_t lastIdx; // Last index of TWD history buffer
static size_t lastAddedIdx = 0; // Last index of TWD history buffer when new data was added
static int oldDataIntv; // remember recent user selection of data interval
static bool oldShowTruW; // remember recent user selection of wind data type
static int wndCenter; // chart wind center value position
static int wndLeft; // chart wind left 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
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
static int simTwd; // Simulation value for TWD
static float simTws; // Simulation value for TWS
int x, y; // x and y coordinates for drawing
static int prevX, prevY; // Last x and y coordinates for drawing
@@ -172,29 +224,27 @@ public:
int chrtVal; // Current wind value
static int chrtPrevVal; // Last wind value in chart area for check if value crosses 180 degree line
LOG_DEBUG(GwLog::LOG, "Display page WindPlot");
// 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);
logger->logDebug(GwLog::LOG, "Display PageWindPlot");
if (!isInitialized) {
width = getdisplay().width();
height = getdisplay().height();
width = epd->width();
height = epd->height();
xCenter = width / 2;
cHeight = height - yOffset - 22;
bufSize = pageData.boatHstry.twdHstry->getCapacity();
numNoData = 0;
simTwd = pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg;
simTws = 0;
twsValue = 0;
bufStart = 0;
oldDataIntv = 0;
oldShowTruW = false; // we want to initialize wind buffers at 1st time routine runs
wdHstry = pageData.boatHstry.twdHstry;
bufSize = wdHstry->getCapacity();
wsHstry = pageData.boatHstry.twsHstry;
bufSize = wsHstry->getCapacity();
wdHstry->getMetaData(wdName, wdFormat, updFreq, wdLowest, wdHighest);
wsHstry->getMetaData(wsName, wsFormat, updFreq, wdLowest, wdHighest);
wsValue = 0;
wsBVal->setFormat(wsHstry->getFormat());
numAddedBufVals, currIdx, lastIdx = 0;
lastAddedIdx = pageData.boatHstry.twdHstry->getLastIdx();
pageData.boatHstry.twdHstry->getMetaData(twdName, twdUnit, updFreq, twdLowest, twdHighest);
lastAddedIdx = wdHstry->getLastIdx();
wndCenter = INT_MIN;
midWndDir = 0;
diffRng = dfltRng;
@@ -211,8 +261,8 @@ public:
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;
BDataText[i] = formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
BDataUnit[i] = formatValue(bvalue, *commonData).unit;
BDataText[i] = commonData->fmt->formatValue(bvalue, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
BDataUnit[i] = commonData->fmt->formatValue(bvalue, *commonData).unit;
BDataFormat[i] = bvalue->getFormat(); // Unit of value
}
@@ -222,9 +272,25 @@ public:
setFlashLED(false);
}
if (showTruW != oldShowTruW) {
if (showTruW) {
wdHstry = pageData.boatHstry.twdHstry;
wsHstry = pageData.boatHstry.twsHstry;
} else {
wdHstry = pageData.boatHstry.awdHstry;
wsHstry = pageData.boatHstry.awsHstry;
}
wdHstry->getMetaData(wdName, wdFormat, updFreq, wdLowest, wdHighest);
wsHstry->getMetaData(wsName, wsFormat, updFreq, wdLowest, wdHighest);
bufSize = wdHstry->getCapacity();
wsBVal->setFormat(wsHstry->getFormat());
oldShowTruW = showTruW;
}
// Identify buffer size and buffer start position for chart
count = pageData.boatHstry.twdHstry->getCurrentSize();
currIdx = pageData.boatHstry.twdHstry->getLastIdx();
count = wdHstry->getCurrentSize();
currIdx = wdHstry->getLastIdx();
numAddedBufVals = (currIdx - lastAddedIdx + bufSize) % bufSize; // Number of values added to buffer since last display
if (dataIntv != oldDataIntv || count == 1) {
// new data interval selected by user
@@ -240,29 +306,25 @@ public:
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",
count, pageData.boatHstry.twdHstry->getLast() / 1000.0 * radToDeg, pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384, BDataValid[0],
intvBufSize, numWndVals, bufStart, numAddedBufVals, pageData.boatHstry.twdHstry->getLastIdx(), oldDataIntv, dataIntv);
logger->logDebug(GwLog::DEBUG, "PageWindPlot Dataset: count: %d, xWD: %.0f, xWS: %.1f, xWD_valid? %d, intvBufSize: %d, numWndVals: %d, bufStart: %d, numAddedBufVals: %d, lastIdx: %d, wind source: %s",
count, wdHstry->getLast() / 1000.0 * radToDeg, wsHstry->getLast() / 10.0 * 1.94384, BDataValid[0], intvBufSize, numWndVals, bufStart, numAddedBufVals, wdHstry->getLastIdx(),
showTruW ? "True" : "App");
// Set wndCenter from 1st real buffer value
if (wndCenter == INT_MIN || (wndCenter == 0 && count == 1)) {
midWndDir = pageData.boatHstry.twdHstry->getMid(numWndVals);
if (midWndDir != INT16_MIN) {
midWndDir = midWndDir / 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);
wndCenter = getCntr(*wdHstry, numWndVals);
logger->logDebug(GwLog::DEBUG, "PageWindPlot Range Init: count: %d, xWD: %.0f, wndCenter: %d, diffRng: %d, chrtRng: %d, Min: %.0f, Max: %.0f", count, wdHstry->getLast() / 1000.0 * radToDeg,
wndCenter, diffRng, chrtRng, wdHstry->getMin(numWndVals) / 1000.0 * radToDeg, wdHstry->getMax(numWndVals) / 1000.0 * radToDeg);
} else {
// 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);
if (diffRng > chrtRng) {
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
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
@@ -277,38 +339,38 @@ public:
//***********************************************************************
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, width, height); // Set partial update
getdisplay().setTextColor(commonData->fgcolor);
epd->setPartialWindow(0, 0, width, height); // Set partial update
epd->setTextColor(commonData->fgcolor);
// chart lines
getdisplay().fillRect(0, yOffset, width, 2, commonData->fgcolor);
getdisplay().fillRect(xCenter, yOffset, 1, cHeight, commonData->fgcolor);
epd->fillRect(0, yOffset, width, 2, commonData->fgcolor);
epd->fillRect(xCenter, yOffset, 1, cHeight, commonData->fgcolor);
// chart labels
char sWndLbl[4]; // char buffer for Wind angle label
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(xCenter - 88, yOffset - 3);
getdisplay().print("TWD"); // Wind data name
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(xCenter - 88, yOffset - 3);
epd->print(wdName); // Wind data name
snprintf(sWndLbl, 4, "%03d", (wndCenter < 0) ? (wndCenter + 360) : wndCenter);
drawTextCenter(xCenter, yOffset - 11, sWndLbl);
getdisplay().drawCircle(xCenter + 25, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
getdisplay().drawCircle(xCenter + 25, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
getdisplay().setCursor(1, yOffset - 3);
epd->drawCircle(xCenter + 25, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
epd->drawCircle(xCenter + 25, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
epd->setCursor(1, yOffset - 3);
snprintf(sWndLbl, 4, "%03d", (wndLeft < 0) ? (wndLeft + 360) : wndLeft);
getdisplay().print(sWndLbl); // Wind left value
getdisplay().drawCircle(46, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
getdisplay().drawCircle(46, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
getdisplay().setCursor(width - 50, yOffset - 3);
epd->print(sWndLbl); // Wind left value
epd->drawCircle(46, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
epd->drawCircle(46, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
epd->setCursor(width - 50, yOffset - 3);
snprintf(sWndLbl, 4, "%03d", (wndRight < 0) ? (wndRight + 360) : wndRight);
getdisplay().print(sWndLbl); // Wind right value
getdisplay().drawCircle(width - 5, yOffset - 17, 2, commonData->fgcolor); // <degree> symbol
getdisplay().drawCircle(width - 5, yOffset - 17, 3, commonData->fgcolor); // <degree> symbol
epd->print(sWndLbl); // Wind right value
epd->drawCircle(width - 5, yOffset - 17, 2, 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() == wdHstry->getMinVal()) {
// only <INT16_MIN> values in buffer -> no valid wind data available
wndDataValid = false;
} else if (!BDataValid[0]) {
// currently no valid TWD data available
} else if (!BDataValid[0] && !simulation) {
// currently no valid TWD data available and no simulation mode
numNoData++;
wndDataValid = true;
if (numNoData > 3) {
@@ -323,7 +385,7 @@ public:
//***********************************************************************
if (wndDataValid) {
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) {
chrtPrevVal = INT16_MIN;
} else {
@@ -331,9 +393,8 @@ public:
x = ((chrtVal - wndLeft + 360) % 360) * chrtScl;
y = yOffset + cHeight - i; // Position in chart area
// if (i >= (numWndVals / dataIntv) - 10)
if (i >= (numWndVals / dataIntv) - 1)
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Chart: i: %d, chrtVal: %d, bufStart: %d, count: %d, linesToShow: %d", i, chrtVal, bufStart, count, (numWndVals / dataIntv));
if (i >= (numWndVals / dataIntv) - 1) // log chart data of 1 line (adjust for test purposes)
logger->logDebug(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)) {
// just a dot for 1st chart point or after some invalid values
@@ -347,15 +408,15 @@ public:
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
int xSplit = (((chrtPrevVal180 > 0 ? wndRight : wndLeft) - wndLeft + 360) % 360) * chrtScl;
getdisplay().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, xSplit, 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;
}
}
// Draw line with 2 pixels width + make sure vertical line are drawn correctly
getdisplay().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, x, y, commonData->fgcolor);
epd->drawLine(prevX, prevY - 1, ((x != prevX) ? x : x - 1), ((x != prevX) ? y - 1 : y), commonData->fgcolor);
chrtPrevVal = chrtVal;
prevX = x;
prevY = y;
@@ -364,41 +425,27 @@ public:
if (i >= (cHeight - 1)) {
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 maxWndDir = pageData.boatHstry.twdHstry->getMax(numWndVals) / 1000.0 * radToDeg;
LOG_DEBUG(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))) {
// Check if all wind value are left or right of center value -> optimize chart range
midWndDir = pageData.boatHstry.twdHstry->getMid(numWndVals) / 1000.0 * radToDeg;
if (midWndDir != INT16_MIN) {
wndCenter = int((midWndDir + (midWndDir >= 0 ? 5 : -5)) / 10) * 10; // Set new center value; round to nearest 10 degree value
}
int minWndDir = wdHstry->getMin(numWndVals) / 1000.0 * radToDeg;
int maxWndDir = wdHstry->getMax(numWndVals) / 1000.0 * radToDeg;
logger->logDebug(GwLog::DEBUG, "PageWindPlot FreeTop: Minimum: %d, Maximum: %d, OldwndCenter: %d", minWndDir, maxWndDir, wndCenter);
// 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 center
wndCenter = getCntr(*wdHstry, numWndVals);
}
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;
}
}
} else {
// 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) {
// Print wind speed value
int currentZone;
static int lastZone = 0;
static bool flipTws = false;
int xPosTws;
static const int yPosTws = yOffset + 40;
twsValue = pageData.boatHstry.twsHstry->getLast() / 10.0 * 1.94384; // TWS value in knots
xPosTws = flipTws ? 20 : width - 138;
xPosTws = flipTws ? 20 : width - 145;
currentZone = (y >= yPosTws - 38) && (y <= yPosTws + 6) && (x >= xPosTws - 4) && (x <= xPosTws + 146) ? 1 : 0; // Define current zone for TWS value
if (currentZone != lastZone) {
// Only flip when x moves to a different zone
@@ -409,39 +456,47 @@ public:
}
lastZone = currentZone;
getdisplay().fillRect(xPosTws - 4, yPosTws - 38, 142, 44, commonData->bgcolor); // Clear area for TWS value
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setCursor(xPosTws, yPosTws);
if (!BDataValid[1]) {
getdisplay().print("--.-");
wsValue = wsHstry->getLast();
wsBVal->value = wsValue; // temp variable to retreive data unit from OBP60Formater
wsBVal->valid = (static_cast<int16_t>(wsValue) != wsHstry->getMinVal());
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);
if (!wsBVal->valid) {
epd->print("--.-");
} else {
double dbl = BDataValue[1] * 3.6 / 1.852;
if (dbl < 10.0) {
getdisplay().printf("!%3.1f", dbl); // Value, round to 1 decimal
wsValue = wsValue / 10.0 * 1.94384; // Wind speed value in knots
if (wsValue < 10.0) {
epd->printf("!%3.1f", wsValue); // Value, round to 1 decimal
} 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);
getdisplay().setCursor(xPosTws + 82, yPosTws - 14);
// getdisplay().print("TWS"); // Name
getdisplay().print(BDataName[1]); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
// getdisplay().setCursor(xPosTws + 78, yPosTws + 1);
getdisplay().setCursor(xPosTws + 82, yPosTws + 1);
// getdisplay().printf(" kn"); // Unit
getdisplay().print(BDataUnit[1]); // Unit
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(xPosTws + 82, yPosTws - 14);
epd->print(wsName); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(xPosTws + 82, yPosTws + 1);
epd->print(wsUnit); // Unit
} else {
// 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
int yPos;
int chrtLbl;
getdisplay().setFont(&Ubuntu_Bold8pt8b);
epd->setFont(&Ubuntu_Bold8pt8b);
for (int i = 1; i <= 3; i++) {
yPos = yOffset + (i * 60);
getdisplay().fillRect(0, yPos, width, 1, commonData->fgcolor);
getdisplay().fillRect(0, yPos - 8, 24, 16, commonData->bgcolor); // Clear small area to remove potential chart lines
getdisplay().setCursor(1, yPos + 4);
epd->fillRect(0, yPos, width, 1, commonData->fgcolor);
epd->fillRect(0, yPos - 8, 24, 16, commonData->bgcolor); // Clear small area to remove potential chart lines
epd->setCursor(1, yPos + 4);
if (count >= intvBufSize) {
// 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
@@ -449,7 +504,7 @@ public:
int j = 3 - i;
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
}
return PAGE_UPDATE;
@@ -460,20 +515,18 @@ static Page* createPage(CommonData& 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 define which function is to be called
* 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(
"WindPlot", // Page name
createPage, // Action
0, // Number of bus values depends on selection in Web configuration
{ "TWD", "TWS" }, // Bus values we need in the page
// {}, // Bus values we need in the page
{ "TWD", "TWS", "AWD", "AWS" }, // Bus values we need in the page
true // Show display header on/off
);
#endif
#endif

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
@@ -6,16 +7,21 @@
class PageWindRose : public Page
{
int16_t lp = 80; // Pointer length
private:
String lengthformat;
int16_t lp = 80; // Pointer length
public:
PageWindRose(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageWindRose");
PageWindRose(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageWindRose");
// Get config data
String lengthformat = config->getString(config->lengthFormat);
}
// Key functions
virtual int handleKey(int key){
int handleKey(int key) {
// Code for keylock
if(key == 11){
commonData->keylock = !commonData->keylock;
@@ -24,9 +30,17 @@ public:
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
static String svalue1old = "";
static String unit1old = "";
@@ -41,13 +55,6 @@ public:
static String svalue6old = "";
static String unit6old = "";
// 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 value for AWA
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name
@@ -55,9 +62,9 @@ public:
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information
value1 = formatValue(bvalue1, *commonData).value;// Format only nesaccery for simulation data for pointer
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
value1 = commonData->fmt->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 unit1 = commonData->fmt->formatValue(bvalue1, *commonData).unit; // Unit of value
if(valid1 == true){
svalue1old = svalue1; // Save old value
unit1old = unit1; // Save old unit
@@ -70,8 +77,8 @@ public:
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information
String svalue2 = 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 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
unit2old = unit2; // Save old unit
@@ -84,8 +91,8 @@ public:
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information
String svalue3 = 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 svalue3 = commonData->fmt->formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = commonData->fmt->formatValue(bvalue3, *commonData).unit; // Unit of value
if(valid3 == true){
svalue3old = svalue3; // Save old value
unit3old = unit3; // Save old unit
@@ -98,8 +105,8 @@ public:
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
double value4 = bvalue4->value; // Value as double in SI unit
bool valid4 = bvalue4->valid; // Valid information
String svalue4 = 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 svalue4 = commonData->fmt->formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = commonData->fmt->formatValue(bvalue4, *commonData).unit; // Unit of value
if(valid4 == true){
svalue4old = svalue4; // Save old value
unit4old = unit4; // Save old unit
@@ -112,8 +119,8 @@ public:
calibrationData.calibrateInstance(bvalue5, logger); // Check if boat data value is to be calibrated
double value5 = bvalue5->value; // Value as double in SI unit
bool valid5 = bvalue5->valid; // Valid information
String svalue5 = 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 svalue5 = commonData->fmt->formatValue(bvalue5, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit5 = commonData->fmt->formatValue(bvalue5, *commonData).unit; // Unit of value
if(valid5 == true){
svalue5old = svalue5; // Save old value
unit5old = unit5; // Save old unit
@@ -126,109 +133,83 @@ public:
calibrationData.calibrateInstance(bvalue6, logger); // Check if boat data value is to be calibrated
double value6 = bvalue6->value; // Value as double in SI unit
bool valid6 = bvalue6->valid; // Valid information
String svalue6 = 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 svalue6 = commonData->fmt->formatValue(bvalue6, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit6 = commonData->fmt->formatValue(bvalue6, *commonData).unit; // Unit of value
if(valid6 == true){
svalue6old = svalue6; // Save old value
unit6old = unit6; // Save old unit
}
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
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);
logger->logDebug(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
//***********************************************************
// 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 values AWA
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 65);
getdisplay().print(svalue1); // Value
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 95);
getdisplay().print(name1); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 115);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit1); // Unit
}
else{
getdisplay().print(unit1old); // Unit
}
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 65);
epd->print(svalue1); // Value
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 95);
epd->print(name1); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 115);
epd->print(" ");
epd->print(holdvalues ? unit1old : unit1);
// Horizintal separator left
getdisplay().fillRect(0, 149, 60, 3, commonData->fgcolor);
epd->fillRect(0, 149, 60, 3, commonData->fgcolor);
// Show values AWS
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 270);
getdisplay().print(svalue2); // Value
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 220);
getdisplay().print(name2); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 190);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit2); // Unit
}
else{
getdisplay().print(unit2old); // Unit
}
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 270);
epd->print(svalue2); // Value
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 220);
epd->print(name2); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 190);
epd->print(" ");
epd->print(holdvalues ? unit2old : unit2);
// Show values TWD
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 65);
if(valid3 == true){
getdisplay().print(abs(value3 * 180 / PI), 0); // Value
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(295, 65);
if (valid3 == true) {
epd->print(abs(value3 * 180 / PI), 0); // Value
}
else{
getdisplay().print("---"); // Value
}
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
else {
epd->print(commonData->fmt->placeholder);
}
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(335, 95);
epd->print(name3); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 115);
epd->print(" ");
epd->print(holdvalues ? unit3old : unit3);
// Horizintal separator right
getdisplay().fillRect(340, 149, 80, 3, commonData->fgcolor);
epd->fillRect(340, 149, 80, 3, commonData->fgcolor);
// Show values TWS
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 270);
getdisplay().print(svalue4); // Value
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(335, 220);
getdisplay().print(name4); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(335, 190);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit4); // Unit
}
else{
getdisplay().print(unit4old); // Unit
}
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(295, 270);
epd->print(svalue4); // Value
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(335, 220);
epd->print(name4); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(335, 190);
epd->print(" ");
epd->print(holdvalues ? unit4old : unit4);
//*******************************************************************************************
@@ -236,10 +217,10 @@ public:
int rInstrument = 110; // Radius of grafic instrument
float pi = 3.141592;
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument - 10, commonData->fgcolor); // Inner circle
getdisplay().fillCircle(200, 150, rInstrument - 13, commonData->bgcolor); // Inner circle
epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
epd->fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
epd->fillCircle(200, 150, rInstrument - 10, commonData->fgcolor); // Inner circle
epd->fillCircle(200, 150, rInstrument - 13, commonData->bgcolor); // Inner circle
for(int i=0; i<360; i=i+10)
{
@@ -247,37 +228,36 @@ public:
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
const char *ii = "";
switch (i)
{
case 0: ii="0"; break;
case 30 : ii="30"; break;
case 60 : ii="60"; break;
case 90 : ii="90"; break;
case 120 : ii="120"; break;
case 150 : ii="150"; break;
case 180 : ii="180"; break;
case 210 : ii="210"; break;
case 240 : ii="240"; break;
case 270 : ii="270"; break;
case 300 : ii="300"; break;
case 330 : ii="330"; break;
default: break;
switch (i) {
case 0: ii="0"; break;
case 30 : ii="30"; break;
case 60 : ii="60"; break;
case 90 : ii="90"; break;
case 120 : ii="120"; break;
case 150 : ii="150"; break;
case 180 : ii="180"; break;
case 210 : ii="210"; break;
case 240 : ii="240"; break;
case 270 : ii="270"; break;
case 300 : ii="300"; break;
case 330 : ii="330"; break;
default: break;
}
// Print text centered on position x, y
int16_t x1, y1; // 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
getdisplay().setCursor(x-w/2, y+h/2);
if(i % 30 == 0){
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().print(ii);
epd->getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
epd->setCursor(x-w/2, y+h/2);
if (i % 30 == 0) {
epd->setFont(&Ubuntu_Bold8pt8b);
epd->print(ii);
}
// Draw sub scale with dots
float x1c = 200 + rInstrument*sin(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 cosx=cos(i/180.0*pi);
@@ -288,10 +268,10 @@ public:
float xx2 = +dx;
float yy1 = -(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*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*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
}
@@ -308,7 +288,7 @@ public:
float xx2 = startwidth;
float yy1 = -startwidth;
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*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer
@@ -318,44 +298,34 @@ public:
float ix2 = -endwidth;
float iy1 = -(rInstrument-15);
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*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
}
// Center circle
getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
epd->fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
epd->fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
//*******************************************************************************************
// Show values DBT
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setCursor(160, 200);
getdisplay().print(svalue5); // Value
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(190, 215);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit5); // Unit
}
else{
getdisplay().print(unit5old); // Unit
}
epd->setFont(&DSEG7Classic_BoldItalic16pt7b);
epd->setCursor(160, 200);
epd->print(svalue5); // Value
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(190, 215);
epd->print(" ");
epd->print(holdvalues ? unit5old : unit5);
// Show values STW
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
getdisplay().setCursor(160, 130);
getdisplay().print(svalue6); // Value
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(190, 90);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit6); // Unit
}
else{
getdisplay().print(unit6old); // Unit
}
epd->setFont(&DSEG7Classic_BoldItalic16pt7b);
epd->setCursor(160, 130);
epd->print(svalue6); // Value
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(190, 90);
epd->print(" ");
epd->print(holdvalues ? unit6old : unit6);
return PAGE_UPDATE;
};

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
@@ -6,16 +7,28 @@
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:
PageWindRoseFlex(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageWindRoseFlex");
PageWindRoseFlex(CommonData &common) : Page(common)
{
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
virtual int handleKey(int key){
int handleKey(int key) {
// Code for keylock
if(key == 11){
commonData->keylock = !commonData->keylock;
@@ -24,9 +37,17 @@ public:
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
void displayNew(PageData &pageData) {
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
};
int displayPage(PageData &pageData) {
static String svalue1old = "";
static String unit1old = "";
@@ -41,206 +62,186 @@ public:
static String svalue6old = "";
static String unit6old = "";
// 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 angle
GwApi::BoatValue *bvalue2; // Value 2 for speed
// Get boat values #1
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name
// Get boat value for wind angle (AWA/TWA), shown by pointer
if (source == 'A') {
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
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information
value1 = formatValue(bvalue1, *commonData).value;// Format only nesaccery for simulation data for pointer
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
if(valid1 == true){
String svalue1 = commonData->fmt->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
if (valid1 == true) {
svalue1old = svalue1; // Save old value
unit1old = unit1; // Save old unit
}
// Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name
// Get boat value for wind speed (AWS/TWS), shown in top left corner
if (source == 'A') {
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
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
if(valid2 == true){
if (simulation) {
value2 = 0.62731; // some random value
}
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
unit2old = unit2; // Save old unit
}
// Get boat values #3
GwApi::BoatValue *bvalue3 = pageData.values[2]; // Third element in list
// Get boat value for bottom left corner
GwApi::BoatValue *bvalue3 = pageData.values[0];
String name3 = xdrDelete(bvalue3->getName()); // Value name
name3 = name3.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue3, logger); // Check if boat data value is to be calibrated
double value3 = bvalue3->value; // Value as double in SI unit
bool valid3 = bvalue3->valid; // Valid information
String svalue3 = 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 svalue3 = commonData->fmt->formatValue(bvalue3, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit3 = commonData->fmt->formatValue(bvalue3, *commonData).unit; // Unit of value
if(valid3 == true){
svalue3old = svalue3; // Save old value
unit3old = unit3; // Save old unit
}
// Get boat values #4
GwApi::BoatValue *bvalue4 = pageData.values[3]; // Fourth element in list
// Get boat value for top right corner
GwApi::BoatValue *bvalue4 = pageData.values[1];
String name4 = xdrDelete(bvalue4->getName()); // Value name
name4 = name4.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue4, logger); // Check if boat data value is to be calibrated
double value4 = bvalue4->value; // Value as double in SI unit
bool valid4 = bvalue4->valid; // Valid information
String svalue4 = 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 svalue4 = commonData->fmt->formatValue(bvalue4, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit4 = commonData->fmt->formatValue(bvalue4, *commonData).unit; // Unit of value
if(valid4 == true){
svalue4old = svalue4; // Save old value
unit4old = unit4; // Save old unit
}
// Get boat values #5
GwApi::BoatValue *bvalue5 = pageData.values[4]; // Fifth element in list
// Get boat value for bottom right corner
GwApi::BoatValue *bvalue5 = pageData.values[2];
String name5 = xdrDelete(bvalue5->getName()); // Value name
name5 = name5.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue5, logger); // Check if boat data value is to be calibrated
double value5 = bvalue5->value; // Value as double in SI unit
bool valid5 = bvalue5->valid; // Valid information
String svalue5 = 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 svalue5 = commonData->fmt->formatValue(bvalue5, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit5 = commonData->fmt->formatValue(bvalue5, *commonData).unit; // Unit of value
if(valid5 == true){
svalue5old = svalue5; // Save old value
unit5old = unit5; // Save old unit
}
// Get boat values #5
GwApi::BoatValue *bvalue6 = pageData.values[5]; // Sixth element in list
// Get boat value for center
GwApi::BoatValue *bvalue6 = pageData.values[3];
String name6 = xdrDelete(bvalue6->getName()); // Value name
name6 = name6.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue6, logger); // Check if boat data value is to be calibrated
double value6 = bvalue6->value; // Value as double in SI unit
bool valid6 = bvalue6->valid; // Valid information
String svalue6 = 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 svalue6 = commonData->fmt->formatValue(bvalue6, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit6 = commonData->fmt->formatValue(bvalue6, *commonData).unit; // Unit of value
if(valid6 == true){
svalue6old = svalue6; // Save old value
unit6old = unit6; // Save old unit
}
// Optical warning by limit violation (unused)
if(String(flashLED) == "Limit Violation"){
setBlinkingLED(false);
setFlashLED(false);
}
// Logging boat values
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
//***********************************************************
// 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)
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 65);
getdisplay().print(svalue2); // Value
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 95);
getdisplay().print(name2); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 115);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit2); // Unit
}
else{
getdisplay().print(unit2old); // Unit
}
// Show AWS or TWS top left
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 65);
epd->print(svalue2); // Value
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 95);
epd->print(name2); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 115);
epd->print(" ");
epd->print(holdvalues ? unit2old : unit2);
// 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
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(10, 270);
getdisplay().print(svalue3); // Value
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(10, 220);
getdisplay().print(name3); // Name
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(10, 190);
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit3); // Unit
}
else{
getdisplay().print(unit3old); // Unit
}
// Show value 3 (=first user-configured parameter) at bottom left
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(10, 270);
epd->print(svalue3); // Value
epd->setFont(&Ubuntu_Bold12pt8b);
epd->setCursor(10, 220);
epd->print(name3); // Name
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(10, 190);
epd->print(" ");
epd->print(holdvalues ? unit3old : unit3);
// Show value 4 at top right
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 65);
// Show value 4 (=second user-configured parameter) at top right
epd->setFont(&DSEG7Classic_BoldItalic20pt7b);
epd->setCursor(295, 65);
if(valid3 == true){
// getdisplay().print(abs(value3 * 180 / M_PI), 0); // Value
getdisplay().print(svalue4); // Value
epd->print(svalue4); // Value
}
else{
getdisplay().print("---"); // Value
}
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->print(commonData->fmt->placeholder);
}
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
getdisplay().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
}
epd->fillRect(340, 149, 80, 3, commonData->fgcolor);
// 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
int rInstrument = 110; // Radius of grafic instrument
getdisplay().fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
getdisplay().fillCircle(200, 150, rInstrument - 10, commonData->fgcolor); // Inner circle
getdisplay().fillCircle(200, 150, rInstrument - 13, commonData->bgcolor); // Inner circle
epd->fillCircle(200, 150, rInstrument + 10, commonData->fgcolor); // Outer circle
epd->fillCircle(200, 150, rInstrument + 7, commonData->bgcolor); // Outer circle
epd->fillCircle(200, 150, rInstrument - 10, commonData->fgcolor); // Inner circle
epd->fillCircle(200, 150, rInstrument - 13, commonData->bgcolor); // Inner circle
for(int i=0; i<360; i=i+10)
{
@@ -267,17 +268,17 @@ public:
// Print text centered on position x, y
int16_t x1, y1; // 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
getdisplay().setCursor(x-w/2, y+h/2);
epd->getTextBounds(ii, int(x), int(y), &x1, &y1, &w, &h); // Calc width of new string
epd->setCursor(x-w/2, y+h/2);
if(i % 30 == 0){
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().print(ii);
epd->setFont(&Ubuntu_Bold8pt8b);
epd->print(ii);
}
// Draw sub scale with dots
float x1c = 200 + rInstrument*sin(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 cosx=cos(i/180.0*M_PI);
@@ -288,10 +289,10 @@ public:
float xx2 = +dx;
float yy1 = -(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*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*xx2-sinx*yy2),150+(int)(sinx*xx2+cosx*yy2),commonData->fgcolor);
}
@@ -308,7 +309,7 @@ public:
float xx2 = startwidth;
float yy1 = -startwidth;
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*0-sinx*yy2),150+(int)(sinx*0+cosx*yy2),commonData->fgcolor);
// Inverted pointer
@@ -318,37 +319,48 @@ public:
float ix2 = -endwidth;
float iy1 = -(rInstrument-15);
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*0-sinx*iy2),150+(int)(sinx*0+cosx*iy2),commonData->fgcolor);
}
// Center circle
getdisplay().fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
getdisplay().fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
epd->fillCircle(200, 150, startwidth + 6, commonData->bgcolor);
epd->fillCircle(200, 150, startwidth + 4, commonData->fgcolor);
//*******************************************************************************************
// Show value6, so that it does not collide with the wind pointer
getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b);
if (cos(value1) > 0){
getdisplay().setCursor(160, 200);
getdisplay().print(svalue6); // Value
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(190, 215);
} else{
getdisplay().setCursor(160, 130);
getdisplay().print(svalue6); // Value
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(190, 90);
// Show value6 (=fourth user-configured parameter) and ssource, so that they do not collide with the wind pointer
if (cos(value1) > 0) {
epd->setFont(&DSEG7Classic_BoldItalic16pt7b);
epd->setCursor(160, 200);
epd->print(svalue6); // Value
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(190, 215);
epd->print(" ");
epd->print(holdvalues ? unit6old : unit6);
if (sin(value1) > 0) {
epd->setCursor(160, 130);
} else {
epd->setCursor(220, 130);
}
getdisplay().print(" ");
if(holdvalues == false){
getdisplay().print(unit6); // Unit
}
else{
getdisplay().print(unit6old); // Unit
epd->print(ssource); // true or app.
}
else {
epd->setFont(&DSEG7Classic_BoldItalic16pt7b);
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, 130);
} else {
epd->setCursor(220, 130);
}
epd->print(ssource); //true or app.
}
return PAGE_UPDATE;
};
@@ -361,13 +373,14 @@ static Page *createPage(CommonData &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 (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
*/
PageDescription registerPageWindRoseFlex(
"WindRoseFlex", // Page name
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
);

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "Pagedata.h"
@@ -28,15 +29,18 @@ static unsigned char ship_bits[] PROGMEM = {
class PageXTETrack : public Page
{
bool simulation = false;
bool holdvalues = false;
private:
String trackStep;
double seg_step;
public:
PageXTETrack(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageXTETrack");
simulation = common.config->getBool(common.config->useSimuData);
holdvalues = common.config->getBool(common.config->holdvalues);
public:
PageXTETrack(CommonData &common) : Page(common)
{
logger->logDebug(GwLog::LOG, "Instantiate PageXTETrack");
// 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,
@@ -45,18 +49,18 @@ class PageXTETrack : public Page
if (fill == true) {
// no primitive for quadrangular object
// we create it from 2 triangles
getdisplay().fillTriangle(x0, y0, x1, y1, x3, y3, color);
getdisplay().fillTriangle(x1, y1, x2, y2, x3, y3, color);
epd->fillTriangle(x0, y0, x1, y1, x3, y3, color);
epd->fillTriangle(x1, y1, x2, y2, x3, y3, color);
} else {
// draw outline
getdisplay().drawLine(x0, y0, x1, y1, color);
getdisplay().drawLine(x1, y1, x2, y2, color);
getdisplay().drawLine(x2, y2, x3, y3, color);
getdisplay().drawLine(x3, y3, x0, y0, color);
epd->drawLine(x0, y0, x1, y1, color);
epd->drawLine(x1, y1, x2, y2, color);
epd->drawLine(x2, y2, x3, y3, color);
epd->drawLine(x3, y3, x0, y0, color);
}
}
virtual int handleKey(int key){
int handleKey(int key) {
// Code for keylock
if(key == 11){
commonData->keylock = !commonData->keylock;
@@ -65,96 +69,93 @@ class PageXTETrack : public Page
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
// 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"){
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
LOG_DEBUG(GwLog::LOG,"Drawing at PageXTETrack");
logger->logDebug(GwLog::LOG, "Drawing at PageXTETrack");
// Draw page
//***********************************************************
// 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
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(50, 188);
getdisplay().print("Cross-track error");
getdisplay().setCursor(270, 188);
getdisplay().print("Track");
getdisplay().setCursor(45, 275);
getdisplay().print("Distance to waypoint");
getdisplay().setCursor(260, 275);
getdisplay().print("Bearing");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(50, 188);
epd->print("Cross-track error");
epd->setCursor(270, 188);
epd->print("Track");
epd->setCursor(45, 275);
epd->print("Distance to waypoint");
epd->setCursor(260, 275);
epd->print("Bearing");
// values
getdisplay().setFont(&DSEG7Classic_BoldItalic30pt7b);
epd->setFont(&DSEG7Classic_BoldItalic30pt7b);
int16_t x, y;
uint16_t w, h;
GwApi::BoatValue *bv_xte = pageData.values[0]; // XTE
String sval_xte = formatValue(bv_xte, *commonData).svalue;
getdisplay().getTextBounds(sval_xte, 0, 0, &x, &y, &w, &h);
getdisplay().setCursor(160-w, 170);
getdisplay().print(sval_xte);
String sval_xte = commonData->fmt->formatValue(bv_xte, *commonData).svalue;
epd->getTextBounds(sval_xte, 0, 0, &x, &y, &w, &h);
epd->setCursor(160-w, 170);
epd->print(sval_xte);
GwApi::BoatValue *bv_cog = pageData.values[1]; // COG
String sval_cog = formatValue(bv_cog, *commonData).svalue;
getdisplay().getTextBounds(sval_cog, 0, 0, &x, &y, &w, &h);
getdisplay().setCursor(360-w, 170);
getdisplay().print(sval_cog);
String sval_cog = commonData->fmt->formatValue(bv_cog, *commonData).svalue;
epd->getTextBounds(sval_cog, 0, 0, &x, &y, &w, &h);
epd->setCursor(360-w, 170);
epd->print(sval_cog);
GwApi::BoatValue *bv_dtw = pageData.values[2]; // DTW
String sval_dtw = formatValue(bv_dtw, *commonData).svalue;
getdisplay().getTextBounds(sval_dtw, 0, 0, &x, &y, &w, &h);
getdisplay().setCursor(160-w, 257);
getdisplay().print(sval_dtw);
String sval_dtw = commonData->fmt->formatValue(bv_dtw, *commonData).svalue;
epd->getTextBounds(sval_dtw, 0, 0, &x, &y, &w, &h);
epd->setCursor(160-w, 257);
epd->print(sval_dtw);
GwApi::BoatValue *bv_btw = pageData.values[3]; // BTW
String sval_btw = formatValue(bv_btw, *commonData).svalue;
getdisplay().getTextBounds(sval_btw, 0, 0, &x, &y, &w, &h);
getdisplay().setCursor(360-w, 257);
getdisplay().print(sval_btw);
String sval_btw = commonData->fmt->formatValue(bv_btw, *commonData).svalue;
epd->getTextBounds(sval_btw, 0, 0, &x, &y, &w, &h);
epd->setCursor(360-w, 257);
epd->print(sval_btw);
GwApi::BoatValue *bv_wpname = pageData.values[4]; // WPName
bool valid = bv_cog->valid && bv_btw->valid;
// XTETrack view
// 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
String sval_wpname = "no data";
if (valid) {
sval_wpname = "Tonne 122";
sval_wpname = bv_wpname->svalue;
}
getdisplay().setFont(&Ubuntu_Bold10pt8b);
getdisplay().getTextBounds(sval_wpname, 0, 150, &x, &y, &w, &h);
epd->setFont(&Ubuntu_Bold10pt8b);
epd->getTextBounds(sval_wpname, 0, 150, &x, &y, &w, &h);
// TODO if text don't fix use smaller font size.
// if smallest size does not fit use 2 lines
// last resort: clip with ellipsis
getdisplay().setCursor(200 - w / 2, 60);
getdisplay().print(sval_wpname);
epd->setCursor(200 - w / 2, 60);
epd->print(sval_wpname);
// draw course segments
@@ -226,7 +227,7 @@ PageDescription registerPageXTETrack(
"XTETrack", // Page name
createPage, // Action
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
);

View File

@@ -1,3 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <Arduino.h>
#include "GwApi.h"
@@ -12,6 +14,7 @@
typedef std::vector<GwApi::BoatValue *> ValueList;
typedef struct{
GwApi *api;
String pageName;
uint8_t pageNumber; // page number in sequence of visible pages
//the values will always contain the user defined values first
@@ -99,30 +102,55 @@ typedef struct{
} AlarmData;
typedef struct{
GwApi::Status status;
GwLog *logger=NULL;
GwConfigHandler *config=NULL;
SensorData data;
SunData sundata;
TouchKeyData keydata[6];
BacklightData backlight;
AlarmData alarm;
GwApi::BoatValue *time=NULL;
GwApi::BoatValue *date=NULL;
uint16_t fgcolor;
uint16_t bgcolor;
bool keylock = false;
String powermode;
int voltage = 0;
} AvgData;
class Formatter; // forward declaration
typedef struct{
GwApi::Status status;
GwLog *logger = nullptr;
GwConfigHandler *config = nullptr;
Formatter *fmt = nullptr;
SensorData data;
SunData sundata;
TouchKeyData keydata[6];
BacklightData backlight;
AlarmData alarm;
AvgData avgdata;
GwApi::BoatValue *time = nullptr;
GwApi::BoatValue *date = nullptr;
uint16_t fgcolor;
uint16_t bgcolor;
bool keylock = false;
String powermode;
} CommonData;
//a base class that all pages must inherit from
class Page{
protected:
protected:
// TODO Future: GwApi *api;
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;
virtual int displayPage(PageData &pageData)=0;
virtual void displayNew(PageData &pageData){}
virtual void leavePage(PageData &pageData){}
virtual void setupKeys() {
#ifdef HARDWARE_V21
commonData->keydata[0].label = "";
@@ -181,23 +209,7 @@ class PageDescription{
class PageStruct{
public:
Page *page=NULL;
Page *page = nullptr;
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)])
print("added hardware info to CPPDEFINES")
print("friendly board name is '{}'".format(env.GetProjectOption("board_name")))

View File

@@ -20,7 +20,7 @@ import getopt
import re
import json
__version__ = "0.2"
__version__ = "1.2"
def detect_pages(filename):
# 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)),
"category": f"{device.upper()} Page {page_no}",
"capabilities": {device.lower(): "true"},
"condition": [
{f"page{page_no}type": page}
for page in pages
if pagedata[page] >= field_no
],
"condition": {
f"page{page_no}type": [page for page in pages if pagedata[page] >= field_no],
"visiblePages": [vp for vp in range(page_no, no_of_pages + 1)]
},
}
output.append(field_data)
@@ -149,12 +148,13 @@ def usage():
print("Command line options")
print(" -d --device device name to use e.g. obp60")
print(" -p --pages number of pages to create")
print(" -m --merge json with device config to merge to")
print(" -h show this help")
print()
if __name__ == '__main__':
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:
print(err)
usage()
@@ -162,11 +162,14 @@ if __name__ == '__main__':
device = "obp60"
no_of_pages = 10
merge_json = None
for opt, arg in options:
if opt in ('-d', '--device'):
device = arg
elif opt in ('-p', '--pages'):
no_of_pages = int(arg)
elif opt in ('-m', '--merge'):
merge_json = arg
elif opt == '-h':
usage()
sys.exit(0)
@@ -175,5 +178,12 @@ if __name__ == '__main__':
pagedata = detect_pages("obp60task.cpp")
json_output = create_json(device, no_of_pages, pagedata)
# print omitting first line containing [ of JSON array
print(json_output[1:])
if merge_json and os.path.isfile(merge_json):
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

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

@@ -0,0 +1,84 @@
/* 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
}
uint8_t* get() {
// returns complete buffer in order new to old
}
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() {
}
bool clear() {
// clears buffer and permanent storage
return true;
}
};
class History {
public:
History() {
}
~History() {
}
void *addSeries() {
}
};

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

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

@@ -0,0 +1,12 @@
(function(){
const api=window.esp32nmea2k;
if (! api) return;
const tabName="OBP60";
api.registerListener((id, data) => {
// if (!data.testboard) return; //do nothing if we are not active
let page = api.addTabPage(tabName, "OBP60");
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

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

@@ -0,0 +1,790 @@
[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

View File

@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include "obp60task.h"
#include "Pagedata.h" // Data exchange for pages
#include "OBP60Formatter.h" // Data formatting for boat values
#include "OBP60Hardware.h" // PIN definitions
#include <Wire.h> // I2C connections
#include <MCP23017.h> // MCP23017 extension Port
@@ -18,23 +20,15 @@
#ifdef BOARD_OBP40S3
#include "driver/rtc_io.h" // Needs for weakup from deep sleep
#include <FS.h> // SD-Card access
#include <SD.h>
#include <SPI.h>
#endif
// True type character sets includes
// See OBP60ExtensionPort.cpp
// Pictures
//#include GxEPD_BitmapExamples // Example picture
#include "MFD_OBP60_400x300_sw.h" // MFD with logo
#include "Logo_OBP_400x300_sw.h" // OBP Logo
#include "images/OBP_400x300.xbm" // OBP Logo
#include "images/unknown.xbm" // unknown page indicator
#include "OBP60QRWiFi.h" // Functions lib for WiFi QR code
#include "OBPSensorTask.h" // Functions lib for sensor data
// Global vars
bool initComplete = false; // Initialization complete
int taskRunCounter = 0; // Task couter for loop section
@@ -47,63 +41,23 @@ void OBP60Init(GwApi *api){
GwConfigHandler *config = api->getConfig();
// Set a new device name and hidden the original name in the main config
String devicename = api->getConfig()->getConfigItem(api->getConfig()->deviceName,true)->asString();
api->getConfig()->setValue(GwConfigDefinitions::systemName, devicename, GwConfigInterface::ConfigType::HIDDEN);
String devicename = config->getConfigItem(config->deviceName, true)->asString();
config->setValue(GwConfigDefinitions::systemName, devicename, GwConfigInterface::ConfigType::HIDDEN);
logger->prefix = devicename + ":";
logger->logDebug(GwLog::LOG,"obp60init running");
api->getLogger()->logDebug(GwLog::LOG,"obp60init running");
// Check I2C devices
// Init power
String powermode = config->getConfigItem(config->powerMode,true)->asString();
logger->logDebug(GwLog::DEBUG, "Power Mode is: %s", powermode.c_str());
powerInit(powermode);
// Init hardware
hardwareInit(api);
// Init power rail 5.0V
String powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode,true)->asString();
api->getLogger()->logDebug(GwLog::DEBUG,"Power Mode is: %s", powermode.c_str());
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{
#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
}
#ifdef BOARD_OBP40S3
bool sdcard = config->getBool(config->useSDCard);
if (sdcard) {
SPIClass SD_SPI = SPIClass(HSPI);
SD_SPI.begin(SD_SPI_CLK, SD_SPI_MISO, SD_SPI_MOSI);
if (SD.begin(SD_SPI_CS, SD_SPI, 80000000)) {
String sdtype = "unknown";
uint8_t cardType = SD.cardType();
switch (cardType) {
case CARD_MMC:
sdtype = "MMC";
break;
case CARD_SD:
sdtype = "SDSC";
break;
case CARD_SDHC:
sdtype = "SDHC";
break;
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
LOG_DEBUG(GwLog::LOG,"SD card type %s of size %d MB detected", sdtype, cardSize);
}
}
#ifdef BOARD_OBP40S3
// Deep sleep wakeup configuration
esp_sleep_enable_ext0_wakeup(OBP_WAKEWUP_PIN, 0); // 1 = High, 0 = Low
rtc_gpio_pullup_en(OBP_WAKEWUP_PIN); // Activate pullup resistor
@@ -111,15 +65,15 @@ void OBP60Init(GwApi *api){
#endif
// Settings for e-paper display
String fastrefresh = api->getConfig()->getConfigItem(api->getConfig()->fastRefresh,true)->asString();
api->getLogger()->logDebug(GwLog::DEBUG,"Fast Refresh Mode is: %s", fastrefresh.c_str());
#ifdef DISPLAY_GDEY042T81
String fastrefresh = config->getConfigItem(config->fastRefresh,true)->asString();
logger->logDebug(GwLog::DEBUG, "Fast Refresh Mode is: %s", fastrefresh.c_str());
#ifdef DISPLAY_GDEY042T81
if(fastrefresh == "true"){
static const bool useFastFullUpdate = true; // Enable fast full display update only for GDEY042T81
}
#endif
#endif
#ifdef BOARD_OBP60S3
#ifdef BOARD_OBP60S3
touchSleepWakeUpEnable(TP1, 45); // TODO sensitivity should be configurable via web interface
touchSleepWakeUpEnable(TP2, 45);
touchSleepWakeUpEnable(TP3, 45);
@@ -127,31 +81,31 @@ void OBP60Init(GwApi *api){
touchSleepWakeUpEnable(TP5, 45);
touchSleepWakeUpEnable(TP6, 45);
esp_sleep_enable_touchpad_wakeup();
#endif
#endif
// Get CPU speed
int freq = getCpuFrequencyMhz();
api->getLogger()->logDebug(GwLog::LOG,"CPU speed at boot: %i MHz", freq);
logger->logDebug(GwLog::LOG,"CPU speed at boot: %i MHz", freq);
// Settings for backlight
String backlightMode = api->getConfig()->getConfigItem(api->getConfig()->backlight,true)->asString();
api->getLogger()->logDebug(GwLog::DEBUG,"Backlight Mode is: %s", backlightMode.c_str());
uint brightness = uint(api->getConfig()->getConfigItem(api->getConfig()->blBrightness,true)->asInt());
String backlightColor = api->getConfig()->getConfigItem(api->getConfig()->blColor,true)->asString();
if(String(backlightMode) == "On"){
setBacklightLED(brightness, colorMapping(backlightColor));
String backlightMode = config->getConfigItem(config->backlight,true)->asString();
logger->logDebug(GwLog::DEBUG, "Backlight Mode is: %s", backlightMode.c_str());
uint brightness = uint(config->getConfigItem(config->blBrightness,true)->asInt());
String backlightColor = config->getConfigItem(config->blColor,true)->asString();
if (backlightMode == "On") {
setBacklightLED(brightness, colorMapping(backlightColor));
}
else if(String(backlightMode) == "Off"){
setBacklightLED(0, COLOR_BLACK); // Backlight LEDs off (blue without britghness)
else if (backlightMode == "Off") {
setBacklightLED(0, COLOR_BLACK); // Backlight LEDs off (blue without britghness)
}
else if(String(backlightMode) == "Control by Key"){
setBacklightLED(0, COLOR_BLUE); // Backlight LEDs off (blue without britghness)
else if (backlightMode == "Control by Key") {
setBacklightLED(0, COLOR_BLUE); // Backlight LEDs off (blue without britghness)
}
// Settings flash LED mode
String ledMode = api->getConfig()->getConfigItem(api->getConfig()->flashLED,true)->asString();
api->getLogger()->logDebug(GwLog::DEBUG,"LED Mode is: %s", ledMode.c_str());
if(String(ledMode) == "Off"){
String ledMode = config->getConfigItem(config->flashLED,true)->asString();
logger->logDebug(GwLog::DEBUG,"LED Mode is: %s", ledMode.c_str());
if (ledMode == "Off") {
setBlinkingLED(false);
}
@@ -160,19 +114,19 @@ void OBP60Init(GwApi *api){
initComplete = true;
// Buzzer tone for initialization finish
setBuzzerPower(uint(api->getConfig()->getConfigItem(api->getConfig()->buzzerPower,true)->asInt()));
setBuzzerPower(uint(config->getConfigItem(config->buzzerPower,true)->asInt()));
buzzer(TONE4, 500);
}
typedef struct {
int page0=0;
QueueHandle_t queue;
GwLog* logger = NULL;
int page0 = 0;
QueueHandle_t queue;
GwLog* logger = nullptr;
// GwApi* api = NULL;
uint sensitivity = 100;
bool use_syspage = true;
} MyData;
uint sensitivity = 100;
bool use_syspage = true;
} MyData;
// Keyboard Task
void keyboardTask(void *param){
@@ -202,7 +156,7 @@ class BoatValueList{
//additionally we put the necessary values into the paga data - see below
GwApi::BoatValue *allBoatValues[MAXVALUES];
int numValues=0;
bool addValueToList(GwApi::BoatValue *v){
for (int i=0;i<numValues;i++){
if (allBoatValues[i] == v){
@@ -232,7 +186,6 @@ class BoatValueList{
//this way each page can easily be added here
//needs some minor tricks for the safe static initialization
typedef std::vector<PageDescription*> Pages;
//the page list class
class PageList{
public:
Pages pages;
@@ -254,12 +207,13 @@ class PageList{
* each page should have defined a registerXXXPage variable of type
* PageData that describes what it needs
*/
void registerAllPages(PageList &list){
void registerAllPages(GwLog *logger, PageList &list){
//the next line says that this variable is defined somewhere else
//in our case in a separate C++ source file
//this way this separate source file can be compiled by it's own
//and has no access to any of our data except the one that we
//give as a parameter to the page function
logger->logDebug(GwLog::LOG, "Memory before registering pages: stack=%d, heap=%d", uxTaskGetStackHighWaterMark(NULL), ESP.getFreeHeap());
extern PageDescription registerPageSystem;
//we add the variable to our list
list.add(&registerPageSystem);
@@ -282,7 +236,7 @@ void registerAllPages(PageList &list){
extern PageDescription registerPageWindRose;
list.add(&registerPageWindRose);
extern PageDescription registerPageWindRoseFlex;
list.add(&registerPageWindRoseFlex); //
list.add(&registerPageWindRoseFlex);
extern PageDescription registerPageVoltage;
list.add(&registerPageVoltage);
extern PageDescription registerPageDST810;
@@ -313,72 +267,78 @@ void registerAllPages(PageList &list){
list.add(&registerPageXTETrack);
extern PageDescription registerPageFluid;
list.add(&registerPageFluid);
extern PageDescription registerPageSkyView;
list.add(&registerPageSkyView);
extern PageDescription registerPageAnchor;
list.add(&registerPageAnchor);
extern PageDescription registerPageAIS;
list.add(&registerPageAIS);
extern PageDescription registerPageBarograph;
list.add(&registerPageBarograph);
logger->logDebug(GwLog::LOG,"Memory after registering pages: stack=%d, heap=%d", uxTaskGetStackHighWaterMark(NULL), ESP.getFreeHeap());
}
// Undervoltage detection for shutdown display
void underVoltageDetection(GwApi *api, CommonData &common){
// Read settings
double voffset = (api->getConfig()->getConfigItem(api->getConfig()->vOffset,true)->asString()).toFloat();
double vslope = (api->getConfig()->getConfigItem(api->getConfig()->vSlope,true)->asString()).toFloat();
// Read supply voltage
#if defined VOLTAGE_SENSOR && defined LIPO_ACCU_1200
float actVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2; // Vin = 1/2 for OBP40
float minVoltage = 3.65; // Absolut minimum volatge for 3,7V LiPo accu
#else
float actVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 for OBP60
float minVoltage = MIN_VOLTAGE;
#endif
double calVoltage = actVoltage * vslope + voffset; // Calibration
if(calVoltage < minVoltage){
#if defined VOLTAGE_SENSOR && defined LIPO_ACCU_1200
// Switch off all power lines
setPortPin(OBP_BACKLIGHT_LED, false); // Backlight Off
setFlashLED(false); // Flash LED Off
buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms
// Shutdown EInk display
getdisplay().setFullWindow(); // Set full Refresh
//getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().fillScreen(common.bgcolor);// Clear screen
getdisplay().setTextColor(common.fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(65, 150);
getdisplay().print("Undervoltage");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(65, 175);
getdisplay().print("Charge battery and restart system");
getdisplay().nextPage(); // Partial update
getdisplay().powerOff(); // Display power off
setPortPin(OBP_POWER_EPD, false); // Power off ePaper display
setPortPin(OBP_POWER_SD, false); // Power off SD card
#else
// Switch off all power lines
setPortPin(OBP_BACKLIGHT_LED, false); // Backlight Off
setFlashLED(false); // Flash LED Off
buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms
setPortPin(OBP_POWER_50, false); // Power rail 5.0V Off
// Shutdown EInk display
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().fillScreen(common.bgcolor);// Clear screen
getdisplay().setTextColor(common.fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(65, 150);
getdisplay().print("Undervoltage");
getdisplay().setFont(&Ubuntu_Bold8pt8b);
getdisplay().setCursor(65, 175);
getdisplay().print("To wake up repower system");
getdisplay().nextPage(); // Partial update
getdisplay().powerOff(); // Display power off
#endif
// Stop system
while(true){
esp_deep_sleep_start(); // Deep Sleep without weakup. Weakup only after power cycle (restart).
}
void underVoltageError(CommonData &common) {
#if defined VOLTAGE_SENSOR && defined LIPO_ACCU_1200
// Switch off all power lines
setPortPin(OBP_BACKLIGHT_LED, false); // Backlight Off
setFlashLED(false); // Flash LED Off
buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms
// Shutdown EInk display
epd->setFullWindow(); // Set full Refresh
//epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
epd->fillScreen(common.bgcolor);// Clear screen
epd->setTextColor(common.fgcolor);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(65, 150);
epd->print("Undervoltage");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(65, 175);
epd->print("Charge battery and restart system");
epd->nextPage(); // Partial update
epd->powerOff(); // Display power off
setPortPin(OBP_POWER_EPD, false); // Power off ePaper display
setPortPin(OBP_POWER_SD, false); // Power off SD card
#else
// Switch off all power lines
setPortPin(OBP_BACKLIGHT_LED, false); // Backlight Off
setFlashLED(false); // Flash LED Off
buzzer(TONE4, 20); // Buzzer tone 4kHz 20ms
setPortPin(OBP_POWER_50, false); // Power rail 5.0V Off
// Shutdown EInk display
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
epd->fillScreen(common.bgcolor);// Clear screen
epd->setTextColor(common.fgcolor);
epd->setFont(&Ubuntu_Bold20pt8b);
epd->setCursor(65, 150);
epd->print("Undervoltage");
epd->setFont(&Ubuntu_Bold8pt8b);
epd->setCursor(65, 175);
epd->print("To wake up repower system");
epd->nextPage(); // Partial update
epd->powerOff(); // Display power off
#endif
while (true) {
esp_deep_sleep_start(); // Deep Sleep without wakeup. Wakeup only after power cycle (restart).
}
}
//bool addTrueWind(GwApi* api, BoatValueList* boatValues, double *twd, double *tws, double *twa) {
inline bool underVoltageDetection(float voffset, float vslope) {
// Read supply voltage
#if defined VOLTAGE_SENSOR && defined LIPO_ACCU_1200
float actVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2; // Vin = 1/2 for OBP40
float minVoltage = 3.65; // Absolut minimum volatge for 3,7V LiPo accu
#else
float actVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20 for OBP60
float minVoltage = MIN_VOLTAGE;
#endif
float calVoltage = actVoltage * vslope + voffset; // Calibration
return (calVoltage < minVoltage);
}
// Calculate true wind data and add to obp60task boat data list
bool addTrueWind(GwApi* api, BoatValueList* boatValues) {
// Calculate true wind data and add to obp60task boat data list
double awaVal, awsVal, cogVal, stwVal, sogVal, hdtVal, hdmVal, varVal;
double twd, tws, twa;
@@ -404,7 +364,7 @@ bool addTrueWind(GwApi* api, BoatValueList* boatValues) {
hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MIN;
hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MIN;
varVal = varBVal->valid ? varBVal->value : DBL_MIN;
api->getLogger()->logDebug(GwLog::DEBUG,"obp60task addTrueWind: AWA %.1f, AWS %.1f, COG %.1f, STW %.1f, SOG %.1f, HDT %.1f, HDM %.1f, VAR %.1f", awaBVal->value * RAD_TO_DEG, awsBVal->value * 3.6 / 1.852,
api->getLogger()->logDebug(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 = WindUtils::calcTrueWind(&awaVal, &awsVal, &cogVal, &stwVal, &sogVal, &hdtVal, &hdmVal, &varVal, &twd, &tws, &twa);
@@ -423,31 +383,42 @@ bool addTrueWind(GwApi* api, BoatValueList* boatValues) {
twaBVal->valid = true;
}
}
api->getLogger()->logDebug(GwLog::DEBUG,"obp60task addTrueWind: TWD_Valid %d, isCalculated %d, TWD %.1f, TWA %.1f, TWS %.1f", twdBVal->valid, isCalculated, twdBVal->value * RAD_TO_DEG,
api->getLogger()->logDebug(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;
}
// Init history buffers for selected boat data
void initHstryBuf(GwApi* api, BoatValueList* boatValues, tBoatHstryData hstryBufList) {
// Init history buffers for TWD, TWS
GwApi::BoatValue *calBVal; // temp variable just for data calibration -> we don't want to calibrate the original data here
const double DBL_MIN = std::numeric_limits<double>::lowest();
int hstryUpdFreq = 1000; // Update frequency for history buffers in ms
int hstryMinVal = 0; // Minimum value for these history buffers
int twdHstryMax = 6283; // Max value for wind direction (TWD) in rad (0...2*PI), shifted by 1000 for 3 decimals
int twsHstryMax = 1000; // Max value for wind speed (TWS) in m/s, shifted by 10 for 1 decimal
int twdHstryMax = 6283; // Max value for wind direction (TWD, AWD) in rad (0...2*PI), shifted by 1000 for 3 decimals
int twsHstryMax = 1000; // Max value for wind speed (TWS, AWS) in m/s, shifted by 10 for 1 decimal
// 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
GwApi::BoatValue *twdBVal = boatValues->findValueOrCreate(hstryBufList.twdHstry->getName());
GwApi::BoatValue *twsBVal = boatValues->findValueOrCreate(hstryBufList.twsHstry->getName());
GwApi::BoatValue *twaBVal = boatValues->findValueOrCreate("TWA");
GwApi::BoatValue *awdBVal = boatValues->findValueOrCreate(hstryBufList.awdHstry->getName());
GwApi::BoatValue *awsBVal = boatValues->findValueOrCreate(hstryBufList.awsHstry->getName());
if (!awdBVal->valid) { // AWD usually does not exist
awdBVal->setFormat(hstryBufList.awdHstry->getFormat());
awdBVal->value = DBL_MIN;
}
}
void handleHstryBuf(GwApi* api, BoatValueList* boatValues, tBoatHstryData hstryBufList) {
void handleHstryBuf(GwApi* api, BoatValueList* boatValues, tBoatHstryData hstryBufList, bool useSimuData) {
// Handle history buffers for TWD, TWS
GwLog *logger = api->getLogger();
@@ -456,42 +427,110 @@ void handleHstryBuf(GwApi* api, BoatValueList* boatValues, tBoatHstryData hstryB
int16_t twdHstryMax = hstryBufList.twdHstry->getMaxVal();
int16_t twsHstryMin = hstryBufList.twsHstry->getMinVal();
int16_t twsHstryMax = hstryBufList.twsHstry->getMaxVal();
int16_t twdBuf, twsBuf;
int16_t awdHstryMin = hstryBufList.awdHstry->getMinVal();
int16_t awdHstryMax = hstryBufList.awdHstry->getMaxVal();
int16_t awsHstryMin = hstryBufList.awsHstry->getMinVal();
int16_t awsHstryMax = hstryBufList.awsHstry->getMaxVal();
static int16_t twd, 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
GwApi::BoatValue *twdBVal = boatValues->findValueOrCreate(hstryBufList.twdHstry->getName());
GwApi::BoatValue *twsBVal = boatValues->findValueOrCreate(hstryBufList.twsHstry->getName());
GwApi::BoatValue *twaBVal = boatValues->findValueOrCreate("TWA");
GwApi::BoatValue *awdBVal = boatValues->findValueOrCreate(hstryBufList.awdHstry->getName());
GwApi::BoatValue *awsBVal = boatValues->findValueOrCreate(hstryBufList.awsHstry->getName());
GwApi::BoatValue *awaBVal = boatValues->findValueOrCreate("AWA");
GwApi::BoatValue *hdtBVal = boatValues->findValueOrCreate("HDT");
GwApi::BoatValue *hdmBVal = boatValues->findValueOrCreate("HDM");
GwApi::BoatValue *varBVal = boatValues->findValueOrCreate("VAR");
GwApi::BoatValue *cogBVal = boatValues->findValueOrCreate("COG");
GwApi::BoatValue *sogBVal = boatValues->findValueOrCreate("SOG");
api->getLogger()->logDebug(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);
api->getLogger()->logDebug(GwLog::DEBUG,"obp60task handleHstryBuf: twdBVal: %.1f, twaBVal: %.1f, twsBVal: %.1f, TWD_isValid? %d", twdBVal->value * RAD_TO_DEG,
twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852, twdBVal->valid);
calBVal = new GwApi::BoatValue("TWD"); // temporary solution for calibration of history buffer values
calBVal->setFormat(twdBVal->getFormat());
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
twdBuf = static_cast<int16_t>(std::round(calBVal->value * 1000));
if (twdBuf >= twdHstryMin && twdBuf <= twdHstryMax) {
hstryBufList.twdHstry->add(twdBuf);
twd = static_cast<int16_t>(std::round(calBVal->value * 1000));
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));
}
delete calBVal;
calBVal = nullptr;
calBVal = new GwApi::BoatValue("TWS"); // temporary solution for calibration of history buffer values
calBVal->setFormat(twsBVal->getFormat());
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
twsBuf = static_cast<int16_t>(std::round(calBVal->value * 10));
if (twsBuf >= twsHstryMin && twsBuf <= twsHstryMax) {
hstryBufList.twsHstry->add(twsBuf);
tws = static_cast<int16_t>(std::round(calBVal->value * 10));
if (tws >= twsHstryMin && tws <= twsHstryMax) {
hstryBufList.twsHstry->add(tws);
}
delete calBVal;
calBVal = nullptr;
} else if (useSimuData) {
tws += random(-50, 50); // TWS value in m/s; expands to 1 decimal
tws = constrain(tws, 0, 250); // 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);
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 * 10);
if (aws >= awsHstryMin && aws <= awsHstryMax) {
hstryBufList.awsHstry->add(static_cast<int16_t>(aws));
}
delete calBVal;
calBVal = nullptr;
} else if (useSimuData) {
aws += random(-50, 50); // TWS value in m/s; expands to 1 decimal
aws = constrain(aws, 0, 250); // Limit TWS to [0..25] m/s
hstryBufList.awsHstry->add(aws);
}
delete calBVal;
calBVal = nullptr;
}
// OBP60 Task
@@ -499,16 +538,17 @@ void handleHstryBuf(GwApi* api, BoatValueList* boatValues, tBoatHstryData hstryB
void OBP60Task(GwApi *api){
// vTaskDelete(NULL);
// return;
GwLog *logger=api->getLogger();
GwConfigHandler *config=api->getConfig();
GwLog *logger = api->getLogger();
GwConfigHandler *config = api->getConfig();
#ifdef HARDWARE_V21
startLedTask(api);
#endif
PageList allPages;
registerAllPages(allPages);
registerAllPages(logger, allPages);
CommonData commonData;
commonData.logger=logger;
commonData.config=config;
commonData.logger = logger;
commonData.config = config;
commonData.fmt = new Formatter(config);
#ifdef HARDWARE_V21
// Keyboard coordinates for page footer
@@ -523,8 +563,8 @@ void OBP60Task(GwApi *api){
}
// Init E-Ink display
String displaymode = api->getConfig()->getConfigItem(api->getConfig()->display,true)->asString();
String displaycolor = api->getConfig()->getConfigItem(api->getConfig()->displaycolor,true)->asString();
String displaymode = config->getConfigItem(config->display,true)->asString();
String displaycolor = config->getConfigItem(config->displaycolor,true)->asString();
if (displaycolor == "Normal") {
commonData.fgcolor = GxEPD_BLACK;
commonData.bgcolor = GxEPD_WHITE;
@@ -533,47 +573,48 @@ void OBP60Task(GwApi *api){
commonData.fgcolor = GxEPD_WHITE;
commonData.bgcolor = GxEPD_BLACK;
}
String systemname = api->getConfig()->getConfigItem(api->getConfig()->systemName,true)->asString();
String wifipass = api->getConfig()->getConfigItem(api->getConfig()->apPassword,true)->asString();
bool refreshmode = api->getConfig()->getConfigItem(api->getConfig()->refresh,true)->asBoolean();
String fastrefresh = api->getConfig()->getConfigItem(api->getConfig()->fastRefresh,true)->asString();
uint fullrefreshtime = uint(api->getConfig()->getConfigItem(api->getConfig()->fullRefreshTime,true)->asInt());
#ifdef BOARD_OBP40S3
String systemname = config->getConfigItem(config->systemName, true)->asString();
String wifipass = config->getConfigItem(config->apPassword, true)->asString();
bool refreshmode = config->getConfigItem(config->refresh, true)->asBoolean();
bool symbolmode = (config->getString(config->headerFormat) == "ICON");
String fastrefresh = config->getConfigItem(config->fastRefresh, true)->asString();
uint fullrefreshtime = uint(config->getConfigItem(config->fullRefreshTime, true)->asInt());
#ifdef BOARD_OBP40S3
bool syspage_enabled = config->getBool(config->systemPage);
#endif
#endif
#ifdef DISPLAY_GDEY042T81
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else
getdisplay().init(115200); // Init for normal displays
#endif
#ifdef DISPLAY_GDEY042T81
epd->init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else
epd->init(115200); // Init for normal displays
#endif
getdisplay().setRotation(0); // Set display orientation (horizontal)
getdisplay().setFullWindow(); // Set full Refresh
getdisplay().firstPage(); // set first page
getdisplay().fillScreen(commonData.bgcolor);
getdisplay().setTextColor(commonData.fgcolor);
getdisplay().nextPage(); // Full Refresh
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().fillScreen(commonData.bgcolor);
getdisplay().nextPage(); // Fast Refresh
getdisplay().nextPage(); // Fast Refresh
epd->setRotation(0); // Set display orientation (horizontal)
epd->setFullWindow(); // Set full Refresh
epd->firstPage(); // set first page
epd->fillScreen(commonData.bgcolor);
epd->setTextColor(commonData.fgcolor);
epd->nextPage(); // Full Refresh
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
epd->fillScreen(commonData.bgcolor);
epd->nextPage(); // Fast Refresh
epd->nextPage(); // Fast Refresh
if(String(displaymode) == "Logo + QR Code" || String(displaymode) == "Logo"){
getdisplay().fillScreen(commonData.bgcolor);
getdisplay().drawBitmap(0, 0, gImage_Logo_OBP_400x300_sw, getdisplay().width(), getdisplay().height(), commonData.fgcolor); // Draw start logo
getdisplay().nextPage(); // Fast Refresh
getdisplay().nextPage(); // Fast Refresh
epd->fillScreen(commonData.bgcolor);
epd->drawXBitmap(0, 0, OBP_400x300_bits, OBP_400x300_width, OBP_400x300_height, commonData.fgcolor);
epd->nextPage(); // Fast Refresh
epd->nextPage(); // Fast Refresh
delay(SHOW_TIME); // Logo show time
if(String(displaymode) == "Logo + QR Code"){
getdisplay().fillScreen(commonData.bgcolor);
epd->fillScreen(commonData.bgcolor);
qrWiFi(systemname, wifipass, commonData.fgcolor, commonData.bgcolor); // Show QR code for WiFi connection
getdisplay().nextPage(); // Fast Refresh
getdisplay().nextPage(); // Fast Refresh
epd->nextPage(); // Fast Refresh
epd->nextPage(); // Fast Refresh
delay(SHOW_TIME); // QR code show time
}
getdisplay().fillScreen(commonData.bgcolor);
getdisplay().nextPage(); // Fast Refresh
getdisplay().nextPage(); // Fast Refresh
epd->fillScreen(commonData.bgcolor);
epd->nextPage(); // Fast Refresh
epd->nextPage(); // Fast Refresh
}
// Init pages
@@ -608,9 +649,11 @@ void OBP60Task(GwApi *api){
//add all necessary data to common data
// Create ring buffers for history storage of some boat data
RingBuffer<int16_t> twdHstry(960); // Circular buffer to store wind direction values; store 960 TWD values for 16 minutes history
RingBuffer<int16_t> twsHstry(960); // Circular buffer to store wind speed values (TWS)
tBoatHstryData hstryBufList = {&twdHstry, &twsHstry};
RingBuffer<int16_t> twdHstry(960); // Circular buffer to store true wind direction values; store 960 TWD values for 16 minutes history
RingBuffer<int16_t> twsHstry(960); // Circular buffer to store true wind speed values (TWS)
RingBuffer<int16_t> awdHstry(960); // Circular buffer to store appearant wind direction values; store 960 AWD values for 16 minutes history
RingBuffer<int16_t> awsHstry(960); // Circular buffer to store appearant xwind speed values (AWS)
tBoatHstryData hstryBufList = {&twdHstry, &twsHstry, &awdHstry, &awsHstry};
//fill the page data from config
numPages=config->getInt(config->visiblePages,1);
@@ -632,6 +675,7 @@ void OBP60Task(GwApi *api){
pages[i].page=description->creator(commonData);
pages[i].parameters.pageName=pageType;
pages[i].parameters.pageNumber = i + 1;
pages[i].parameters.api = api;
LOG_DEBUG(GwLog::DEBUG,"found page %s for number %d",pageType.c_str(),i);
//fill in all the user defined parameters
for (int uid=0;uid<description->userParam;uid++){
@@ -663,7 +707,7 @@ void OBP60Task(GwApi *api){
// Check user setting for true wind calculation
bool calcTrueWnds = api->getConfig()->getBool(api->getConfig()->calcTrueWnds, false);
// bool simulation = api->getConfig()->getBool(api->getConfig()->useSimuData, false);
bool simulation = api->getConfig()->getBool(api->getConfig()->useSimuData, false);
// Initialize history buffer for certain boat data
initHstryBuf(api, &boatValues, hstryBufList);
@@ -681,9 +725,9 @@ void OBP60Task(GwApi *api){
allParameters.page0=3;
allParameters.queue=xQueueCreate(10,sizeof(int));
allParameters.sensitivity= api->getConfig()->getInt(GwConfigDefinitions::tSensitivity);
#ifdef BOARD_OBP40S3
#ifdef BOARD_OBP40S3
allParameters.use_syspage = syspage_enabled;
#endif
#endif
xTaskCreate(keyboardTask,"keyboard",2000,&allParameters,configMAX_PRIORITIES-1,NULL);
SharedData *shared=new SharedData(api);
createSensorTask(shared);
@@ -692,26 +736,28 @@ void OBP60Task(GwApi *api){
//####################################################################################
// Configuration values for main loop
String gpsFix = api->getConfig()->getConfigItem(api->getConfig()->flashLED,true)->asString();
String gpsOn=api->getConfig()->getConfigItem(api->getConfig()->useGPS,true)->asString();
float tz = api->getConfig()->getConfigItem(api->getConfig()->timeZone,true)->asFloat();
String gpsFix = config->getConfigItem(config->flashLED,true)->asString();
String gpsOn = config->getConfigItem(config->useGPS,true)->asString();
float tz = config->getConfigItem(config->timeZone,true)->asFloat();
commonData.backlight.mode = backlightMapping(config->getConfigItem(config->backlight,true)->asString());
commonData.backlight.color = colorMapping(config->getConfigItem(config->blColor,true)->asString());
commonData.backlight.brightness = 2.55 * uint(config->getConfigItem(config->blBrightness,true)->asInt());
commonData.powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode,true)->asString();
commonData.backlight.mode = backlightMapping(config->getConfigItem(config->backlight, true)->asString());
commonData.backlight.color = colorMapping(config->getConfigItem(config->blColor, true)->asString());
commonData.backlight.brightness = 2.55 * uint(config->getConfigItem(config->blBrightness, true)->asInt());
commonData.powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode, true)->asString();
bool uvoltage = api->getConfig()->getConfigItem(api->getConfig()->underVoltage,true)->asBoolean();
String cpuspeed = api->getConfig()->getConfigItem(api->getConfig()->cpuSpeed,true)->asString();
uint hdopAccuracy = uint(api->getConfig()->getConfigItem(api->getConfig()->hdopAccuracy,true)->asInt());
bool uvoltage = config->getConfigItem(config->underVoltage, true)->asBoolean();
float voffset = (config->getConfigItem(config->vOffset,true)->asString()).toFloat();
float vslope = (config->getConfigItem(config->vSlope,true)->asString()).toFloat();
String cpuspeed = config->getConfigItem(config->cpuSpeed, true)->asString();
uint hdopAccuracy = uint(config->getConfigItem(config->hdopAccuracy, true)->asInt());
double homelat = commonData.config->getString(commonData.config->homeLAT).toDouble();
double homelon = commonData.config->getString(commonData.config->homeLON).toDouble();
double homelat = config->getString(config->homeLAT).toDouble();
double homelon = config->getString(config->homeLON).toDouble();
bool homevalid = homelat >= -180.0 and homelat <= 180 and homelon >= -90.0 and homelon <= 90.0;
if (homevalid) {
LOG_DEBUG(GwLog::LOG, "Home location set to %f : %f", homelat, homelon);
logger->logDebug(GwLog::LOG, "Home location set to lat=%f, lon=%f", homelat, homelon);
} else {
LOG_DEBUG(GwLog::LOG, "No valid home location found");
logger->logDebug(GwLog::LOG, "No valid home location found");
}
// refreshmode defined in init section
@@ -743,14 +789,18 @@ void OBP60Task(GwApi *api){
//####################################################################################
bool systemPage = false;
bool systemPageNew = false;
Page *currentPage;
while (true){
delay(100); // Delay 100ms (loop time)
bool keypressed = false;
// Undervoltage detection
if(uvoltage == true){
underVoltageDetection(api, commonData);
if (uvoltage == true) {
if (underVoltageDetection(voffset, vslope)) {
LOG_DEBUG(GwLog::ERROR, "Undervoltage detected, shutting down!");
underVoltageError(commonData);
}
}
// Set CPU speed after boot after 1min
@@ -795,6 +845,7 @@ void OBP60Task(GwApi *api){
systemPage = true; // System page is out of band
syspage->setupKeys();
keyboardMessage = 0;
systemPageNew = true;
}
else {
currentPage = pages[pageNumber].page;
@@ -821,12 +872,12 @@ void OBP60Task(GwApi *api){
toggleBacklightLED(commonData.backlight.brightness, commonData.backlight.color);
}
}
#ifdef BOARD_OBP40S3
#ifdef BOARD_OBP40S3
// #3 Deep sleep mode for OBP40
if ((keyboardMessage == 3) and !syspage_enabled){
deepSleep(commonData);
}
#endif
#endif
// #9 Swipe right or #4 key right
if ((keyboardMessage == 9) or (keyboardMessage == 4))
{
@@ -884,23 +935,23 @@ void OBP60Task(GwApi *api){
if(millis() > starttime4 + 4000 && delayedDisplayUpdate == true){
starttime1 = millis();
starttime2 = millis();
getdisplay().setFullWindow(); // Set full update
epd->setFullWindow(); // Set full update
if(fastrefresh == "true"){
getdisplay().nextPage(); // Full update
epd->nextPage(); // Full update
}
else{
getdisplay().fillScreen(commonData.fgcolor); // Clear display
#ifdef DISPLAY_GDEY042T81
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else
getdisplay().init(115200); // Init for normal displays
#endif
getdisplay().firstPage(); // Full update
getdisplay().nextPage(); // Full update
// getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
// getdisplay().fillScreen(commonData.bgcolor); // Clear display
// getdisplay().nextPage(); // Partial update
// getdisplay().nextPage(); // Partial update
epd->fillScreen(commonData.fgcolor); // Clear display
#ifdef DISPLAY_GDEY042T81
epd->init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else
epd->init(115200); // Init for normal displays
#endif
epd->firstPage(); // Full update
epd->nextPage(); // Full update
// epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
// epd->fillScreen(commonData.bgcolor); // Clear display
// epd->nextPage(); // Partial update
// epd->nextPage(); // Partial update
}
delayedDisplayUpdate = false;
}
@@ -911,23 +962,23 @@ void OBP60Task(GwApi *api){
starttime1 = millis();
starttime2 = millis();
LOG_DEBUG(GwLog::DEBUG,"E-Ink full refresh first 5 min");
getdisplay().setFullWindow(); // Set full update
epd->setFullWindow(); // Set full update
if(fastrefresh == "true"){
getdisplay().nextPage(); // Full update
epd->nextPage(); // Full update
}
else{
getdisplay().fillScreen(commonData.fgcolor); // Clear display
#ifdef DISPLAY_GDEY042T81
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else
getdisplay().init(115200); // Init for normal displays
#endif
getdisplay().firstPage(); // Full update
getdisplay().nextPage(); // Full update
// getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
// getdisplay().fillScreen(commonData.bgcolor); // Clear display
// getdisplay().nextPage(); // Partial update
// getdisplay().nextPage(); // Partial update
epd->fillScreen(commonData.fgcolor); // Clear display
#ifdef DISPLAY_GDEY042T81
epd->init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else
epd->init(115200); // Init for normal displays
#endif
epd->firstPage(); // Full update
epd->nextPage(); // Full update
// epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
// epd->fillScreen(commonData.bgcolor); // Clear display
// epd->nextPage(); // Partial update
// epd->nextPage(); // Partial update
}
}
@@ -935,23 +986,23 @@ void OBP60Task(GwApi *api){
if(millis() > starttime2 + fullrefreshtime * 60 * 1000){
starttime2 = millis();
LOG_DEBUG(GwLog::DEBUG,"E-Ink full refresh");
getdisplay().setFullWindow(); // Set full update
epd->setFullWindow(); // Set full update
if(fastrefresh == "true"){
getdisplay().nextPage(); // Full update
epd->nextPage(); // Full update
}
else{
getdisplay().fillScreen(commonData.fgcolor); // Clear display
#ifdef DISPLAY_GDEY042T81
getdisplay().init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else
getdisplay().init(115200); // Init for normal displays
#endif
getdisplay().firstPage(); // Full update
getdisplay().nextPage(); // Full update
// getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
// getdisplay().fillScreen(commonData.bgcolor); // Clear display
// getdisplay().nextPage(); // Partial update
// getdisplay().nextPage(); // Partial update
epd->fillScreen(commonData.fgcolor); // Clear display
#ifdef DISPLAY_GDEY042T81
epd->init(115200, true, 2, false); // Init for Waveshare boards with "clever" reset circuit, 2ms reset pulse
#else
epd->init(115200); // Init for normal displays
#endif
epd->firstPage(); // Full update
epd->nextPage(); // Full update
// epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
// epd->fillScreen(commonData.bgcolor); // Clear display
// epd->nextPage(); // Partial update
// epd->nextPage(); // Partial update
}
}
@@ -974,42 +1025,48 @@ void OBP60Task(GwApi *api){
addTrueWind(api, &boatValues);
}
// Handle history buffers for TWD, TWS for wind plot page and other usage
handleHstryBuf(api, &boatValues, hstryBufList);
handleHstryBuf(api, &boatValues, hstryBufList, simulation);
// Clear display
// getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);
getdisplay().fillScreen(commonData.bgcolor); // Clear display
// epd->fillRect(0, 0, epd->width(), epd->height(), commonData.bgcolor);
epd->fillScreen(commonData.bgcolor); // Clear display
// Show header if enabled
if (pages[pageNumber].description && pages[pageNumber].description->header or systemPage){
// build header using commonData
displayHeader(commonData, date, time, hdop); // Show page header
displayHeader(commonData, symbolmode, date, time, hdop); // Show page header
}
// Call the particular page
if (systemPage) {
displayFooter(commonData);
PageData sysparams; // empty
sysparams.api = api;
if (systemPageNew) {
syspage->displayNew(sysparams);
systemPageNew = false;
}
syspage->displayPage(sysparams);
}
else {
if (currentPage == NULL){
LOG_DEBUG(GwLog::ERROR,"page number %d not found", pageNumber);
// Error handling for missing page
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().fillScreen(commonData.bgcolor); // Clear display
getdisplay().drawXBitmap(200 - unknown_width / 2, 150 - unknown_height / 2, unknown_bits, unknown_width, unknown_height, commonData.fgcolor);
getdisplay().setCursor(140, 250);
getdisplay().setFont(&Atari16px);
getdisplay().print("Here be dragons!");
getdisplay().nextPage(); // Partial update (fast)
epd->setPartialWindow(0, 0, epd->width(), epd->height()); // Set partial update
epd->fillScreen(commonData.bgcolor); // Clear display
epd->drawXBitmap(200 - unknown_width / 2, 150 - unknown_height / 2, unknown_bits, unknown_width, unknown_height, commonData.fgcolor);
epd->setCursor(140, 250);
epd->setFont(&Atari16px);
epd->print("Here be dragons!");
epd->nextPage(); // Partial update (fast)
}
else{
else {
if (lastPage != pageNumber){
if (hasFRAM) fram.write(FRAM_PAGE_NO, pageNumber); // remember page for device restart
pages[lastPage].page->leavePage(pages[lastPage].parameters); // call page cleanup code
if (hasFRAM) fram.write(FRAM_PAGE_NO, pageNumber); // remember new page for device restart
currentPage->setupKeys();
currentPage->displayNew(pages[pageNumber].parameters);
lastPage=pageNumber;
lastPage = pageNumber;
}
//call the page code
LOG_DEBUG(GwLog::DEBUG,"calling page %d",pageNumber);
@@ -1022,10 +1079,10 @@ void OBP60Task(GwApi *api){
displayAlarm(commonData);
}
if (ret & PAGE_UPDATE) {
getdisplay().nextPage(); // Partial update (fast)
epd->nextPage(); // Partial update (fast)
}
if (ret & PAGE_HIBERNATE) {
getdisplay().hibernate();
epd->hibernate();
}
}

View File

@@ -41,5 +41,10 @@
#ifdef BOARD_OBP40S3
DECLARE_CAPABILITY(obp40,true)
#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
#endif

View File

@@ -8,7 +8,6 @@ default_envs =
obp40_s3
[env:obp60_s3]
platform = espressif32@6.8.1
board_build.variants_dir = variants
#board = obp60_s3_n8 #ESP32-S3 N8, 8MB flash, no PSRAM
#board = obp60_s3_n16 #ESP32-S3 N16,16MB flash, no PSRAM, zero series
@@ -16,6 +15,7 @@ board_build.variants_dir = variants
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_16MB.csv #ESP32-S3 N16, 16MB flash
board_name = OBP60
framework = arduino
lib_deps =
${basedeps.lib_deps}
@@ -62,17 +62,16 @@ upload_speed = 230400
monitor_speed = 115200
[env:obp40_s3]
platform = espressif32@6.8.1
board_build.variants_dir = variants
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_name = OBP40
custom_config = config_obp40.json
framework = arduino
lib_deps =
${basedeps.lib_deps}
Wire
SPI
SD
ESP32time
esphome/AsyncTCP-esphome@2.0.1
robtillaart/PCF8574@0.3.9

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

@@ -216,6 +216,10 @@ public:
{
return api->getLogger();
}
virtual Nmea2kTwai *getNMEA2000()
{
return api->getNMEA2000();
}
virtual GwBoatData *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());
req->send(404, "text/plain", "not found");
}
}

View File

@@ -30,7 +30,7 @@ lib_deps =
Update
[env]
platform = espressif32 @ 6.8.1
platform = espressif32 @ 6.11.0
framework = arduino
;platform_packages=
; framework-arduinoespressif32 @ 3.20017.0
@@ -56,6 +56,9 @@ lib_ldf_mode = off
monitor_speed = 115200
build_flags =
-D PIO_ENV_BUILD=$PIOENV
-std=gnu++17
build_unflags =
-std=gnu++11
[sensors]
; collect the libraries for sensors here

View File

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