mirror of
https://github.com/thooge/esp32-nmea2000-obp60.git
synced 2025-12-24 03:03:06 +01:00
Compare commits
34 Commits
barograph
...
2fd11c72b6
| Author | SHA1 | Date | |
|---|---|---|---|
| 2fd11c72b6 | |||
| 235cfd1c9c | |||
| 16e7b80230 | |||
| 5f5c520714 | |||
|
|
47db247471 | ||
|
|
6ffa974354 | ||
|
|
d2ee05a353 | ||
|
|
574deac6f8 | ||
|
|
bbdbe47972 | ||
|
|
9fc090a5a8 | ||
|
|
71673e78bd | ||
| 970d05191c | |||
|
|
695013cb88 | ||
| 81a6c05b55 | |||
|
|
bedb8338ab | ||
| 8d43189140 | |||
| be48929c40 | |||
|
|
1a43fe29e9 | ||
|
|
9930769073 | ||
|
|
83f3e6f24b | ||
|
|
a8d1080429 | ||
|
|
b142470c1d | ||
|
|
b91f345d25 | ||
| 6637b80400 | |||
|
|
707ac9b84f | ||
|
|
2808c56a37 | ||
| 319f3f3e68 | |||
| 6e256e136a | |||
| daaefc7eba | |||
| 3bc0082bb8 | |||
| 0274b5d755 | |||
|
|
df5ff1c91a | ||
| c7b6bafde8 | |||
| 21ae96c8d7 |
56
boards/obp60_s3_light_n8r8.json
Normal file
56
boards/obp60_s3_light_n8r8.json
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino":{
|
||||||
|
"ldscript": "esp32s3_out.ld",
|
||||||
|
"partitions": "default_8MB.csv",
|
||||||
|
"memory_type": "qio_opi"
|
||||||
|
},
|
||||||
|
"core": "esp32",
|
||||||
|
"extra_flags": [
|
||||||
|
"-DBOARD_HAS_PSRAM",
|
||||||
|
"-DARDUINO_ESP32S3_DEV",
|
||||||
|
"-DARDUINO_USB_MODE=1",
|
||||||
|
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||||
|
"-DARDUINO_RUNNING_CORE=1",
|
||||||
|
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||||
|
],
|
||||||
|
"f_cpu": "240000000L",
|
||||||
|
"f_flash": "80000000L",
|
||||||
|
"flash_mode": "qio",
|
||||||
|
"hwids": [
|
||||||
|
[
|
||||||
|
"0x303A",
|
||||||
|
"0x1001"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"mcu": "esp32s3",
|
||||||
|
"variant": "obp60s3_light"
|
||||||
|
},
|
||||||
|
"connectivity": [
|
||||||
|
"bluetooth",
|
||||||
|
"wifi"
|
||||||
|
],
|
||||||
|
"debug": {
|
||||||
|
"default_tool": "esp-builtin",
|
||||||
|
"onboard_tools": [
|
||||||
|
"esp-builtin"
|
||||||
|
],
|
||||||
|
"openocd_target": "esp32s3.cfg"
|
||||||
|
},
|
||||||
|
"frameworks": [
|
||||||
|
"arduino",
|
||||||
|
"espidf"
|
||||||
|
],
|
||||||
|
"name": "OBP60 Light ESP32-S3-N8R8 (8 MB QD, 8 MB PSRAM)",
|
||||||
|
"upload": {
|
||||||
|
"flash_size": "8MB",
|
||||||
|
"maximum_ram_size": 327680,
|
||||||
|
"maximum_size": 8388608,
|
||||||
|
"use_1200bps_touch": true,
|
||||||
|
"wait_for_upload_port": true,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"speed": 460800
|
||||||
|
},
|
||||||
|
"url": "https://open-boat-projects.org/en/diy-multifunktionsdisplay-obp-60/",
|
||||||
|
"vendor": "Open Boat Projects"
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
#include "GwApi.h"
|
#include "GwApi.h"
|
||||||
#include "OBP60Hardware.h"
|
#include "OBP60Hardware.h"
|
||||||
|
|
||||||
|
|
||||||
class Color{
|
class Color{
|
||||||
public:
|
public:
|
||||||
uint8_t r;
|
uint8_t r;
|
||||||
@@ -92,5 +91,4 @@ class LedTaskData{
|
|||||||
//task function
|
//task function
|
||||||
void createSpiLedTask(LedTaskData *param);
|
void createSpiLedTask(LedTaskData *param);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
#ifdef BOARD_OBP60S3
|
#ifdef BOARD_OBP60S3
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#define FASTLED_ALL_PINS_HARDWARE_SPI
|
|
||||||
#define FASTLED_ESP32_SPI_BUS FSPI
|
|
||||||
#define FASTLED_ESP32_FLASH_LOCK 1
|
|
||||||
#include <PCF8574.h> // Driver for PCF8574 output modul from Horter
|
#include <PCF8574.h> // Driver for PCF8574 output modul from Horter
|
||||||
#include <Wire.h> // I2C
|
#include <Wire.h> // I2C
|
||||||
#include <RTClib.h> // Driver for DS1388 RTC
|
#include <RTClib.h> // Driver for DS1388 RTC
|
||||||
@@ -11,6 +8,7 @@
|
|||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
#include "OBP60Hardware.h"
|
#include "OBP60Hardware.h"
|
||||||
#include "OBP60Extensions.h"
|
#include "OBP60Extensions.h"
|
||||||
|
#include "imglib.h"
|
||||||
|
|
||||||
// Character sets
|
// Character sets
|
||||||
#include "Ubuntu_Bold8pt7b.h"
|
#include "Ubuntu_Bold8pt7b.h"
|
||||||
@@ -60,6 +58,10 @@ GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay(){r
|
|||||||
// Horter I2C moduls
|
// Horter I2C moduls
|
||||||
PCF8574 pcf8574_Out(PCF8574_I2C_ADDR1); // First digital output modul PCF8574 from Horter
|
PCF8574 pcf8574_Out(PCF8574_I2C_ADDR1); // First digital output modul PCF8574 from Horter
|
||||||
|
|
||||||
|
// FRAM
|
||||||
|
Adafruit_FRAM_I2C fram;
|
||||||
|
bool hasFRAM = false;
|
||||||
|
|
||||||
// Global vars
|
// Global vars
|
||||||
bool blinkingLED = false; // Enable / disable blinking flash LED
|
bool blinkingLED = false; // Enable / disable blinking flash LED
|
||||||
bool statusLED = false; // Actual status of flash LED on/off
|
bool statusLED = false; // Actual status of flash LED on/off
|
||||||
@@ -69,7 +71,7 @@ int uvDuration = 0; // Under voltage duration in n x 100ms
|
|||||||
|
|
||||||
LedTaskData *ledTaskData=nullptr;
|
LedTaskData *ledTaskData=nullptr;
|
||||||
|
|
||||||
void hardwareInit()
|
void hardwareInit(GwApi *api)
|
||||||
{
|
{
|
||||||
Wire.begin();
|
Wire.begin();
|
||||||
// Init PCF8574 digital outputs
|
// Init PCF8574 digital outputs
|
||||||
@@ -77,12 +79,27 @@ void hardwareInit()
|
|||||||
if(pcf8574_Out.begin()){ // Initialize PCF8574
|
if(pcf8574_Out.begin()){ // Initialize PCF8574
|
||||||
pcf8574_Out.write8(255); // Clear all outputs
|
pcf8574_Out.write8(255); // Clear all outputs
|
||||||
}
|
}
|
||||||
|
fram = Adafruit_FRAM_I2C();
|
||||||
|
if (esp_reset_reason() == ESP_RST_POWERON) {
|
||||||
|
// help initialize FRAM
|
||||||
|
api->getLogger()->logDebug(GwLog::LOG,"Delaying I2C init for 250ms due to cold boot");
|
||||||
|
delay(250);
|
||||||
|
}
|
||||||
|
// FRAM (e.g. MB85RC256V)
|
||||||
|
if (fram.begin(FRAM_I2C_ADDR)) {
|
||||||
|
hasFRAM = true;
|
||||||
|
uint16_t manufacturerID;
|
||||||
|
uint16_t productID;
|
||||||
|
fram.getDeviceID(&manufacturerID, &productID);
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hasFRAM = false;
|
||||||
|
api->getLogger()->logDebug(GwLog::LOG,"NO FRAM detected");
|
||||||
}
|
}
|
||||||
|
|
||||||
void startLedTask(GwApi *api){
|
|
||||||
ledTaskData=new LedTaskData(api);
|
|
||||||
createSpiLedTask(ledTaskData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPortPin(uint pin, bool value){
|
void setPortPin(uint pin, bool value){
|
||||||
@@ -95,6 +112,11 @@ void togglePortPin(uint pin){
|
|||||||
digitalWrite(pin, !digitalRead(pin));
|
digitalWrite(pin, !digitalRead(pin));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void startLedTask(GwApi *api){
|
||||||
|
ledTaskData=new LedTaskData(api);
|
||||||
|
createSpiLedTask(ledTaskData);
|
||||||
|
}
|
||||||
|
|
||||||
// Valid colors see hue
|
// Valid colors see hue
|
||||||
Color colorMapping(const String &colorString){
|
Color colorMapping(const String &colorString){
|
||||||
Color color = COLOR_RED;
|
Color color = COLOR_RED;
|
||||||
@@ -179,6 +201,48 @@ String xdrDelete(String input){
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Point rotatePoint(const Point& origin, const Point& p, double angle) {
|
||||||
|
// rotate poind around origin by degrees
|
||||||
|
Point rotated;
|
||||||
|
double phi = angle * M_PI / 180.0;
|
||||||
|
double dx = p.x - origin.x;
|
||||||
|
double dy = p.y - origin.y;
|
||||||
|
rotated.x = origin.x + cos(phi) * dx - sin(phi) * dy;
|
||||||
|
rotated.y = origin.y + sin(phi) * dx + cos(phi) * dy;
|
||||||
|
return rotated;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle) {
|
||||||
|
std::vector<Point> rotatedPoints;
|
||||||
|
for (const auto& p : pts) {
|
||||||
|
rotatedPoints.push_back(rotatePoint(origin, p, angle));
|
||||||
|
}
|
||||||
|
return rotatedPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillPoly4(const std::vector<Point>& p4, uint16_t color) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw centered text
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
// Show a triangle for trend direction high (x, y is the left edge)
|
// Show a triangle for trend direction high (x, y is the left edge)
|
||||||
void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color){
|
void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color){
|
||||||
getdisplay().fillTriangle(x, y, x+size*2, y, x+size, y-size*2, color);
|
getdisplay().fillTriangle(x, y, x+size*2, y, x+size, y-size*2, color);
|
||||||
@@ -404,4 +468,45 @@ void generatorGraphic(uint x, uint y, int pcolor, int bcolor){
|
|||||||
getdisplay().print("G");
|
getdisplay().print("G");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to handle HTTP image request
|
||||||
|
// http://192.168.15.1/api/user/OBP60Task/screenshot
|
||||||
|
void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request) {
|
||||||
|
GwLog *logger = api->getLogger();
|
||||||
|
|
||||||
|
String imgformat = api->getConfig()->getConfigItem(api->getConfig()->imageFormat,true)->asString();
|
||||||
|
imgformat.toLowerCase();
|
||||||
|
String filename = "Page" + String(*pageno) + "_" + pages[*pageno].description->pageName + "." + imgformat;
|
||||||
|
|
||||||
|
logger->logDebug(GwLog::LOG,"handle image request [%s]: %s", imgformat, filename);
|
||||||
|
|
||||||
|
uint8_t *fb = getdisplay().getBuffer(); // EPD framebuffer
|
||||||
|
std::vector<uint8_t> imageBuffer; // image in webserver transferbuffer
|
||||||
|
String mimetype;
|
||||||
|
|
||||||
|
if (imgformat == "gif") {
|
||||||
|
// GIF is commpressed with LZW, so small
|
||||||
|
mimetype = "image/gif";
|
||||||
|
if (!createGIF(fb, &imageBuffer, GxEPD_WIDTH, GxEPD_HEIGHT)) {
|
||||||
|
logger->logDebug(GwLog::LOG,"GIF creation failed: Hashtable init error!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (imgformat == "bmp") {
|
||||||
|
// Microsoft BMP bitmap
|
||||||
|
mimetype = "image/bmp";
|
||||||
|
createBMP(fb, &imageBuffer, GxEPD_WIDTH, GxEPD_HEIGHT);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// PBM simple portable bitmap
|
||||||
|
mimetype = "image/x-portable-bitmap";
|
||||||
|
createPBM(fb, &imageBuffer, GxEPD_WIDTH, GxEPD_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWebServerResponse *response = request->beginResponse_P(200, mimetype, (const uint8_t*)imageBuffer.data(), imageBuffer.size());
|
||||||
|
response->addHeader("Content-Disposition", "inline; filename=" + filename);
|
||||||
|
request->send(response);
|
||||||
|
|
||||||
|
imageBuffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -3,11 +3,24 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "OBP60Hardware.h"
|
#include "OBP60Hardware.h"
|
||||||
#define FASTLED_ALL_PINS_HARDWARE_SPI
|
|
||||||
#define FASTLED_ESP32_SPI_BUS FSPI
|
|
||||||
#define FASTLED_ESP32_FLASH_LOCK 1
|
|
||||||
#include "LedSpiTask.h"
|
#include "LedSpiTask.h"
|
||||||
#include <GxEPD2_BW.h> // E-paper lib V2
|
#include <GxEPD2_BW.h> // E-paper lib V2
|
||||||
|
#include <Adafruit_FRAM_I2C.h> // I2C FRAM
|
||||||
|
|
||||||
|
// FRAM address reservations 32kB: 0x0000 - 0x7FFF
|
||||||
|
// 0x0000 - 0x03ff: single variables
|
||||||
|
#define FRAM_VOLTAGE_AVG 0x000A
|
||||||
|
#define FRAM_VOLTAGE_TREND 0x000B
|
||||||
|
#define FRAM_VOLTAGE_MODE 0x000C
|
||||||
|
#define FRAM_WIND_SIZE 0x000D
|
||||||
|
#define FRAM_WIND_SRC 0x000E
|
||||||
|
#define FRAM_WIND_MODE 0x000F
|
||||||
|
// Barograph history data
|
||||||
|
#define FRAM_BAROGRAPH_START 0x0400
|
||||||
|
#define FRAM_BAROGRAPH_END 0x13FF
|
||||||
|
|
||||||
|
extern Adafruit_FRAM_I2C fram;
|
||||||
|
extern bool hasFRAM;
|
||||||
|
|
||||||
// Fonts declarations for display (#inclues see OBP60Extensions.cpp)
|
// Fonts declarations for display (#inclues see OBP60Extensions.cpp)
|
||||||
extern const GFXfont Ubuntu_Bold8pt7b;
|
extern const GFXfont Ubuntu_Bold8pt7b;
|
||||||
@@ -39,7 +52,15 @@ GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> & getdisplay();
|
|||||||
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay();
|
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void hardwareInit();
|
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 hardwareInit(GwApi *api);
|
||||||
|
|
||||||
void setPortPin(uint pin, bool value); // Set port pin for extension port
|
void setPortPin(uint pin, bool value); // Set port pin for extension port
|
||||||
|
|
||||||
@@ -58,6 +79,9 @@ void setBuzzerPower(uint power); // Set buzzer power
|
|||||||
|
|
||||||
String xdrDelete(String input); // Delete xdr prefix from string
|
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 displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color);
|
void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color);
|
||||||
void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color);
|
void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color);
|
||||||
|
|
||||||
@@ -70,4 +94,13 @@ void solarGraphic(uint x, uint y, int pcolor, int bcolor); // S
|
|||||||
void generatorGraphic(uint x, uint y, int pcolor, int bcolor); // Generator graphic with fill level
|
void generatorGraphic(uint x, uint y, int pcolor, int bcolor); // Generator graphic with fill level
|
||||||
void startLedTask(GwApi *api);
|
void startLedTask(GwApi *api);
|
||||||
|
|
||||||
|
void doImageRequest(GwApi *api, int *pageno, const PageStruct pages[MAX_PAGE_NUMBER], AsyncWebServerRequest *request);
|
||||||
|
|
||||||
|
#define fram_width 16
|
||||||
|
#define fram_height 16
|
||||||
|
static unsigned char fram_bits[] = {
|
||||||
|
0xf8, 0x1f, 0xff, 0xff, 0x9f, 0xff, 0x98, 0x1f, 0xf8, 0x1f, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xf8, 0x1f, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f,
|
||||||
|
0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f };
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// General hardware definitions
|
// General hardware definitions
|
||||||
// CAN and RS485 bus pin definitions see obp60task.h
|
// CAN and RS485 bus pin definitions see obp60task.h
|
||||||
|
|
||||||
|
#ifdef HARDWARE_V21
|
||||||
// Direction pin for RS485 NMEA0183
|
// Direction pin for RS485 NMEA0183
|
||||||
#define OBP_DIRECTION_PIN 18
|
#define OBP_DIRECTION_PIN 18
|
||||||
// I2C
|
// I2C
|
||||||
@@ -30,6 +31,8 @@
|
|||||||
#define INA226_I2C_ADDR3 0x45 // Addr. 0x45 (fix A0 = 5V, A1 = 5V) for generator
|
#define INA226_I2C_ADDR3 0x45 // Addr. 0x45 (fix A0 = 5V, A1 = 5V) for generator
|
||||||
// Horter modules
|
// Horter modules
|
||||||
#define PCF8574_I2C_ADDR1 0x20 // First digital out module
|
#define PCF8574_I2C_ADDR1 0x20 // First digital out module
|
||||||
|
// FRAM (e.g. MB85RC256V)
|
||||||
|
#define FRAM_I2C_ADDR 0x50
|
||||||
// SPI (E-Ink display, Extern Bus)
|
// SPI (E-Ink display, Extern Bus)
|
||||||
#define OBP_SPI_CS 39
|
#define OBP_SPI_CS 39
|
||||||
#define OBP_SPI_DC 40
|
#define OBP_SPI_DC 40
|
||||||
@@ -39,11 +42,6 @@
|
|||||||
#define OBP_SPI_DIN 48
|
#define OBP_SPI_DIN 48
|
||||||
#define SHOW_TIME 6000 // Show time in [ms] for logo and WiFi QR code
|
#define SHOW_TIME 6000 // Show time in [ms] for logo and WiFi QR code
|
||||||
#define FULL_REFRESH_TIME 600 // Refresh cycle time in [s][600...3600] for full display update (very important healcy function)
|
#define FULL_REFRESH_TIME 600 // Refresh cycle time in [s][600...3600] for full display update (very important healcy function)
|
||||||
#define MAX_PAGE_NUMBER 10 // Max number of pages for show data
|
|
||||||
#define FONT1 "Ubuntu_Bold8pt7b"
|
|
||||||
#define FONT2 "Ubuntu_Bold24pt7b"
|
|
||||||
#define FONT3 "Ubuntu_Bold32pt7b"
|
|
||||||
#define FONT4 "DSEG7Classic_BoldItalic80pt7b"
|
|
||||||
|
|
||||||
// GPS (NEO-6M, NEO-M8N, ATGM336H)
|
// GPS (NEO-6M, NEO-M8N, ATGM336H)
|
||||||
#define OBP_GPS_RX 2
|
#define OBP_GPS_RX 2
|
||||||
@@ -57,7 +55,7 @@
|
|||||||
#define TONE3 3500 // 3500Hz
|
#define TONE3 3500 // 3500Hz
|
||||||
#define TONE4 4000 // 4000Hz
|
#define TONE4 4000 // 4000Hz
|
||||||
// Analog Input
|
// Analog Input
|
||||||
#define OBP_ANALOG0 4 // Analog input for voltage power supplay
|
#define OBP_ANALOG0 4 // Analog input for voltage power supply
|
||||||
#define MIN_VOLTAGE 10.0 // Min voltage for under voltage detection (then goto deep sleep)
|
#define MIN_VOLTAGE 10.0 // Min voltage for under voltage detection (then goto deep sleep)
|
||||||
#define POWER_FAIL_TIME 2 // in [ms] Accept min voltage until 2 x 1ms (for under voltage gaps by engine start)
|
#define POWER_FAIL_TIME 2 // in [ms] Accept min voltage until 2 x 1ms (for under voltage gaps by engine start)
|
||||||
// Touch buttons
|
// Touch buttons
|
||||||
@@ -72,9 +70,92 @@
|
|||||||
#define NUM_FLASH_LED 1 // Number of flash LED
|
#define NUM_FLASH_LED 1 // Number of flash LED
|
||||||
#define OBP_FLASH_LED 7 // GPIO port
|
#define OBP_FLASH_LED 7 // GPIO port
|
||||||
// Backlight LEDs (6x WS2812B)
|
// Backlight LEDs (6x WS2812B)
|
||||||
#define NUM_BACKLIGHT_LED 6 // Numebr of Backlight LEDs
|
#define NUM_BACKLIGHT_LED 6 // Number of Backlight LEDs
|
||||||
#define OBP_BACKLIGHT_LED 15 // GPIO port
|
#define OBP_BACKLIGHT_LED 15 // GPIO port
|
||||||
// Power Rail
|
// Power Rail
|
||||||
#define OBP_POWER_50 5 // 5.0V power rail
|
#define OBP_POWER_50 5 // 5.0V power rail
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Hardware configuration for OBP60 LIGHT
|
||||||
|
|
||||||
|
#ifdef HARDWARE_LIGHT
|
||||||
|
// Direction pin for RS485 NMEA0183
|
||||||
|
#define OBP_DIRECTION_PIN 8
|
||||||
|
// I2C
|
||||||
|
#define I2C_SPEED 10000UL // 10kHz clock speed on I2C bus
|
||||||
|
#define OBP_I2C_SDA 21
|
||||||
|
#define OBP_I2C_SCL 38
|
||||||
|
// DS1388 RTC
|
||||||
|
#define DS1388_I2C_ADDR 0x68 // Addr. 0x68
|
||||||
|
// BME280
|
||||||
|
#define BME280_I2C_ADDR 0x76 // Addr. 0x76 (0x77)
|
||||||
|
// BMP280
|
||||||
|
#define BMP280_I2C_ADDR 0x77 // Addr. 0x77 (0x76) Attention: Pull up resistor
|
||||||
|
// BMP085 / BMP180
|
||||||
|
#define BMP180_I2C_ADDR 0x77 // Addr. 0x77 (fix)
|
||||||
|
// SHT21 / HUT21
|
||||||
|
#define SHT21_I2C_ADDR 0x40 // Addr. 0x40 (fix)
|
||||||
|
// AS5600
|
||||||
|
#define AS5600_I2C_ADDR 0x36 // Addr. 0x36 (fix)
|
||||||
|
// INA219
|
||||||
|
#define SHUNT_VOLTAGE 0.075 // Shunt voltage in V by max. current (75mV)
|
||||||
|
#define INA219_I2C_ADDR1 0x40 // Addr. 0x41 (fix A0 = 5V, A1 = GND) for battery
|
||||||
|
#define INA219_I2C_ADDR2 0x41 // Addr. 0x44 (fix A0 = GND, A1 = 5V) for solar panels
|
||||||
|
#define INA219_I2C_ADDR3 0x45 // Addr. 0x45 (fix A0 = 5V, A1 = 5V) for generator
|
||||||
|
// INA226
|
||||||
|
#define INA226_I2C_ADDR1 0x41 // Addr. 0x41 (fix A0 = 5V, A1 = GND) for battery
|
||||||
|
#define INA226_I2C_ADDR2 0x44 // Addr. 0x44 (fix A0 = GND, A1 = 5V) for solar panels
|
||||||
|
#define INA226_I2C_ADDR3 0x45 // Addr. 0x45 (fix A0 = 5V, A1 = 5V) for generator
|
||||||
|
// Horter modules
|
||||||
|
#define PCF8574_I2C_ADDR1 0x20 // First digital out module
|
||||||
|
// FRAM (e.g. MB85RC256V)
|
||||||
|
#define FRAM_I2C_ADDR 0x50
|
||||||
|
// SPI (E-Ink display, Extern Bus)
|
||||||
|
#define OBP_SPI_CS 45
|
||||||
|
#define OBP_SPI_DC 46
|
||||||
|
#define OBP_SPI_RST 47
|
||||||
|
#define OBP_SPI_BUSY 48
|
||||||
|
#define OBP_SPI_CLK 12
|
||||||
|
#define OBP_SPI_DIN 11
|
||||||
|
#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
|
||||||
|
|
||||||
|
// GPS (NEO-6M, NEO-M8N, ATGM336H)
|
||||||
|
#define OBP_GPS_RX 19
|
||||||
|
#define OBP_GPS_TX 20
|
||||||
|
// 1Wire (DS18B20)
|
||||||
|
#define OBP_1WIRE 17 // External 1Wire
|
||||||
|
// Buzzer
|
||||||
|
#define OBP_BUZZER 18
|
||||||
|
#define TONE1 1500 // 1500Hz
|
||||||
|
#define TONE2 2500 // 2500Hz
|
||||||
|
#define TONE3 3500 // 3500Hz
|
||||||
|
#define TONE4 4000 // 4000Hz
|
||||||
|
// Analog Input
|
||||||
|
#define OBP_ANALOG0 3 // Analog input for voltage power supply
|
||||||
|
#define MIN_VOLTAGE 10.0 // Min voltage for under voltage detection (then goto deep sleep)
|
||||||
|
#define POWER_FAIL_TIME 2 // in [ms] Accept min voltage until 2 x 1ms (for under voltage gaps by engine start)
|
||||||
|
// Buttons
|
||||||
|
#define UP 6 // Wheel up
|
||||||
|
#define DOWN 4 // Wheel down
|
||||||
|
#define CONF 5 // Wheel press
|
||||||
|
#define MENUE 2 // Button top
|
||||||
|
#define EXIT 1 // Button bottom
|
||||||
|
|
||||||
|
// Flash LED (1x WS2812B)
|
||||||
|
#define NUM_FLASH_LED 1 // Number of flash LED
|
||||||
|
#define OBP_FLASH_LED 10 // GPIO port
|
||||||
|
// Backlight LEDs (6x WS2812B)
|
||||||
|
#define NUM_BACKLIGHT_LED 6 // Number of Backlight LEDs
|
||||||
|
#define OBP_BACKLIGHT_LED 40 // GPIO port
|
||||||
|
// Power Rail
|
||||||
|
#define OBP_POWER_50 41 // Power LED
|
||||||
|
#define OBP_POWER_EPD 7 // ePaper power
|
||||||
|
#define OBP_POWER_SD 42 // SD card power
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,14 @@ int keycode = 0; // Keycode of pressed key [0...8], 0 = nothing touched
|
|||||||
int keycode2 = 0; // Keycode of very short pressed key [0...8], 0 = nothing touched
|
int keycode2 = 0; // Keycode of very short pressed key [0...8], 0 = nothing touched
|
||||||
int keycodeold = 0; // Old keycode
|
int keycodeold = 0; // Old keycode
|
||||||
int keycodeold2 = 0; // Old keycode for short pressed key
|
int keycodeold2 = 0; // Old keycode for short pressed key
|
||||||
|
int keystatus = 0; // Status of key [0...11]
|
||||||
bool keyoff = false; // Disable all keys
|
bool keyoff = false; // Disable all keys
|
||||||
int keydelay = 250; // Delay after key pressed in [ms]
|
int keydelay = 250; // Delay after key pressed in [ms]
|
||||||
bool keylock = false; // Key lock after pressed key is valid (repeat protection by conginous pressing)
|
bool keylock = false; // Key lock after pressed key is valid (repeat protection by conginous pressing)
|
||||||
long starttime = 0; // Start time point for pressed key
|
long starttime = 0; // Start time point for pressed key
|
||||||
|
|
||||||
|
#ifdef HARDWARE_V21
|
||||||
|
// Keypad functions for original OBP60 hardware
|
||||||
int readKeypad(uint thSensitivity) {
|
int readKeypad(uint thSensitivity) {
|
||||||
|
|
||||||
// Touch sensor values
|
// Touch sensor values
|
||||||
@@ -29,7 +31,7 @@ int readKeypad(uint thSensitivity) {
|
|||||||
// 170000 - Strong touched
|
// 170000 - Strong touched
|
||||||
uint32_t touchthreshold = (thSensitivity * -1200) + 170000; // thSensitivity 0...100%
|
uint32_t touchthreshold = (thSensitivity * -1200) + 170000; // thSensitivity 0...100%
|
||||||
|
|
||||||
int keystatus = 0; // Status of key [0...11], 0 = processed, 1...8 = key 1..8, 9 = right swipe , 10 = left swipe, 11 keys disabled
|
keystatus = 0; // Status of key [0...11], 0 = processed, 1...8 = key 1..8, 9 = right swipe , 10 = left swipe, 11 keys disabled
|
||||||
keycode = 0;
|
keycode = 0;
|
||||||
|
|
||||||
// Read key code
|
// Read key code
|
||||||
@@ -176,5 +178,59 @@ int readKeypad(uint thSensitivity) {
|
|||||||
|
|
||||||
return keystatus;
|
return keystatus;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HARDWARE_LIGHT
|
||||||
|
// Keypad functions for OBP60 clone (thSensitivity is inactiv)
|
||||||
|
int readKeypad(uint thSensitivity) {
|
||||||
|
pinMode(UP, INPUT);
|
||||||
|
pinMode(DOWN, INPUT);
|
||||||
|
pinMode(CONF, INPUT);
|
||||||
|
pinMode(MENUE, INPUT);
|
||||||
|
pinMode(EXIT, INPUT);
|
||||||
|
|
||||||
|
// Read key code
|
||||||
|
if(digitalRead(UP) == LOW){
|
||||||
|
keycode = 10; // Left swipe
|
||||||
|
}
|
||||||
|
else if(digitalRead(DOWN) == LOW){
|
||||||
|
keycode = 9; // Right swipe
|
||||||
|
}
|
||||||
|
else if(digitalRead(CONF) == LOW){
|
||||||
|
keycode = 3; // Key 3
|
||||||
|
}
|
||||||
|
else if(digitalRead(MENUE) == LOW){
|
||||||
|
keycode = 1; // Key 1
|
||||||
|
}
|
||||||
|
else if(digitalRead(EXIT) == LOW){
|
||||||
|
keycode = 2; // Key 2
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
keycode = 0; // No key activ
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect key
|
||||||
|
if (keycode > 0 ){
|
||||||
|
if(keycode != keycodeold){
|
||||||
|
starttime = millis(); // Start key pressed
|
||||||
|
keycodeold = keycode;
|
||||||
|
}
|
||||||
|
// If key pressed longer than 200ms
|
||||||
|
if(millis() > starttime + 200 && keycode == keycodeold) {
|
||||||
|
keystatus = keycode;
|
||||||
|
// Copy keycode
|
||||||
|
keycodeold = keycode;
|
||||||
|
delay(keydelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
keycode = 0;
|
||||||
|
keycodeold = 0;
|
||||||
|
keystatus = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return keystatus;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -456,7 +456,7 @@ void sensorTask(void *param){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send supplay voltage value all 1s
|
// Send supply voltage value all 1s
|
||||||
if(millis() > starttime5 + 1000 && String(powsensor1) == "off"){
|
if(millis() > starttime5 + 1000 && String(powsensor1) == "off"){
|
||||||
starttime5 = millis();
|
starttime5 = millis();
|
||||||
sensors.batteryVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20
|
sensors.batteryVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20
|
||||||
|
|||||||
@@ -1,192 +0,0 @@
|
|||||||
#ifdef BOARD_OBP60S3
|
|
||||||
|
|
||||||
#include "Pagedata.h"
|
|
||||||
#include "OBP60Extensions.h"
|
|
||||||
|
|
||||||
class PageApparentWind : public Page
|
|
||||||
{
|
|
||||||
bool keylock = false; // Keylock
|
|
||||||
int16_t lp = 80; // Pointer length
|
|
||||||
|
|
||||||
public:
|
|
||||||
PageApparentWind(CommonData &common){
|
|
||||||
common.logger->logDebug(GwLog::LOG,"Show PageApparentWind");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key functions
|
|
||||||
virtual int handleKey(int key){
|
|
||||||
// Reduce instrument size
|
|
||||||
if(key == 2){ // Code for reduce
|
|
||||||
lp = lp - 10;
|
|
||||||
if(lp < 10){
|
|
||||||
lp = 10;
|
|
||||||
}
|
|
||||||
return 0; // Commit the key
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enlarge instrument size
|
|
||||||
if(key == 5){ // Code for enlarge
|
|
||||||
lp = lp + 10;
|
|
||||||
if(lp > 80){
|
|
||||||
lp = 80;
|
|
||||||
}
|
|
||||||
return 0; // Commit the key
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keylock function
|
|
||||||
if(key == 11){ // Code for keylock
|
|
||||||
keylock = !keylock; // Toggle keylock
|
|
||||||
return 0; // Commit the key
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void displayPage(CommonData &commonData, PageData &pageData)
|
|
||||||
{
|
|
||||||
GwConfigHandler *config = commonData.config;
|
|
||||||
GwLog *logger=commonData.logger;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Get boat values for AWS
|
|
||||||
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
|
|
||||||
String name1 = bvalue1->getName().c_str(); // 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
|
|
||||||
|
|
||||||
// Get boat values for AWD
|
|
||||||
GwApi::BoatValue *bvalue2 = pageData.values[1]; // First element in list (only one value by PageOneValue)
|
|
||||||
String name2 = bvalue2->getName().c_str(); // Value name
|
|
||||||
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
|
|
||||||
|
|
||||||
// Optical warning by limit violation (unused)
|
|
||||||
if(String(flashLED) == "Limit Violation"){
|
|
||||||
setBlinkingLED(false);
|
|
||||||
setFlashLED(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logging boat values
|
|
||||||
if (bvalue1 == NULL) return;
|
|
||||||
LOG_DEBUG(GwLog::LOG,"Drawing at PageApparentWind, %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
|
|
||||||
|
|
||||||
getdisplay().setTextColor(commonData.fgcolor);
|
|
||||||
|
|
||||||
// Show values AWS
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt7b);
|
|
||||||
getdisplay().setCursor(20, 50);
|
|
||||||
if(holdvalues == false){
|
|
||||||
getdisplay().print(name1); // Value name
|
|
||||||
getdisplay().print(": ");
|
|
||||||
getdisplay().print(svalue1); // Value
|
|
||||||
getdisplay().print(" ");
|
|
||||||
getdisplay().print(unit1); // Unit
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
getdisplay().print(name1); // Value name
|
|
||||||
getdisplay().print(": ");
|
|
||||||
getdisplay().print(svalue1old); // Value old
|
|
||||||
getdisplay().print(" ");
|
|
||||||
getdisplay().print(unit1old); // Unit old
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show values AWD
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt7b);
|
|
||||||
getdisplay().setCursor(20, 260);
|
|
||||||
if(holdvalues == false){
|
|
||||||
getdisplay().print(name2); // Value name
|
|
||||||
getdisplay().print(": ");
|
|
||||||
getdisplay().print(svalue2); // Value
|
|
||||||
getdisplay().print(" ");
|
|
||||||
getdisplay().print(unit2); // Unit
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
getdisplay().print(name2); // Value name
|
|
||||||
getdisplay().print(": ");
|
|
||||||
getdisplay().print(svalue2old); // Value old
|
|
||||||
getdisplay().print(" ");
|
|
||||||
getdisplay().print(unit2old); // Unit old
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw wind pointer
|
|
||||||
static int16_t x0 = 200; // Center point
|
|
||||||
static int16_t y0 = 145;
|
|
||||||
static int16_t x1 = x0; // Start point for pointer
|
|
||||||
static int16_t y1 = y0;
|
|
||||||
static int16_t x2 = x0; // End point for pointer
|
|
||||||
static int16_t y2 = y0;
|
|
||||||
|
|
||||||
//Draw instrument
|
|
||||||
getdisplay().fillCircle(x0, y0, lp + 5, commonData.fgcolor);
|
|
||||||
getdisplay().fillCircle(x0, y0, lp + 1, commonData.bgcolor);
|
|
||||||
|
|
||||||
// Calculation end point of pointer
|
|
||||||
value2 = value2 - 3.14 / 2;
|
|
||||||
x1 = x0 + cos(value2) * lp * 0.6;
|
|
||||||
y1 = y0 + sin(value2) * lp * 0.6;
|
|
||||||
x2 = x0 + cos(value2) * lp;
|
|
||||||
y2 = y0 + sin(value2) * lp;
|
|
||||||
getdisplay().drawLine(x1, y1, x2, y2, commonData.fgcolor);
|
|
||||||
|
|
||||||
// Key Layout
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt7b);
|
|
||||||
if(keylock == false){
|
|
||||||
getdisplay().setCursor(130, 290);
|
|
||||||
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
|
|
||||||
if(String(backlightMode) == "Control by Key"){ // Key for illumination
|
|
||||||
getdisplay().setCursor(343, 290);
|
|
||||||
getdisplay().print("[ILUM]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
getdisplay().setCursor(130, 290);
|
|
||||||
getdisplay().print(" [ Keylock active ]");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update display
|
|
||||||
getdisplay().nextPage(); // Partial update (fast)
|
|
||||||
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
static Page *createPage(CommonData &common){
|
|
||||||
return new PageApparentWind(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 will will provide the names of the fixed values we need
|
|
||||||
*/
|
|
||||||
PageDescription registerPageApparentWind(
|
|
||||||
"ApparentWind", // Page name
|
|
||||||
createPage, // Action
|
|
||||||
0, // Number of bus values depends on selection in Web configuration
|
|
||||||
{"AWS","AWA"}, // Bus values we need in the page
|
|
||||||
true // Show display header on/off
|
|
||||||
);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -22,43 +22,6 @@ TODO
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct Point {
|
|
||||||
double x;
|
|
||||||
double y;
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawTextCentered(int16_t tx, int16_t ty, String text) {
|
|
||||||
int16_t x, y;
|
|
||||||
uint16_t w, h;
|
|
||||||
getdisplay().getTextBounds(text, 0, 0, &x, &y, &w, &h);
|
|
||||||
getdisplay().setCursor(tx - w / 2, ty + h / 2);
|
|
||||||
getdisplay().print(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define fuel_width 16
|
#define fuel_width 16
|
||||||
#define fuel_height 16
|
#define fuel_height 16
|
||||||
static unsigned char fuel_bits[] = {
|
static unsigned char fuel_bits[] = {
|
||||||
@@ -96,12 +59,13 @@ static unsigned char gasoline_bits[] = {
|
|||||||
|
|
||||||
class PageFluid : public Page{
|
class PageFluid : public Page{
|
||||||
bool keylock = false; // Keylock
|
bool keylock = false; // Keylock
|
||||||
|
bool holdvalues = false;
|
||||||
int fluidtype;
|
int fluidtype;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageFluid(CommonData &common){
|
PageFluid(CommonData &common){
|
||||||
common.logger->logDebug(GwLog::LOG,"Show PageFluid");
|
common.logger->logDebug(GwLog::LOG,"Instantiate PageFluid");
|
||||||
fluidtype = common.config->getInt("page" + String(common.data.actpage) + "fluid", 0);
|
holdvalues = common.config->getBool(common.config->holdvalues);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int handleKey(int key){
|
virtual int handleKey(int key){
|
||||||
@@ -112,10 +76,18 @@ class PageFluid : public Page{
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void displayNew(CommonData &commonData, PageData &pageData){
|
||||||
|
fluidtype = commonData.config->getInt("page" + String(pageData.pageNumber) + "fluid", 0);
|
||||||
|
commonData.logger->logDebug(GwLog::LOG,"New PageFluid: fluidtype=%d", fluidtype);
|
||||||
|
}
|
||||||
|
|
||||||
virtual void displayPage(CommonData &commonData, PageData &pageData){
|
virtual void displayPage(CommonData &commonData, PageData &pageData){
|
||||||
GwConfigHandler *config = commonData.config;
|
GwConfigHandler *config = commonData.config;
|
||||||
GwLog *logger=commonData.logger;
|
GwLog *logger=commonData.logger;
|
||||||
|
|
||||||
|
// Old values for hold function
|
||||||
|
static double value1old;
|
||||||
|
|
||||||
// Get config data
|
// Get config data
|
||||||
String flashLED = config->getString(config->flashLED);
|
String flashLED = config->getString(config->flashLED);
|
||||||
String backlightMode = config->getString(config->backlight);
|
String backlightMode = config->getString(config->backlight);
|
||||||
@@ -126,13 +98,14 @@ class PageFluid : public Page{
|
|||||||
setFlashLED(false);
|
setFlashLED(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logging boat values
|
|
||||||
LOG_DEBUG(GwLog::LOG,"Drawing at PageFluid");
|
|
||||||
|
|
||||||
GwApi::BoatValue *bvalue1 = pageData.values[0];
|
GwApi::BoatValue *bvalue1 = pageData.values[0];
|
||||||
String name1 = bvalue1->getName();
|
String name1 = bvalue1->getName();
|
||||||
double value1 = bvalue1->value;
|
if (holdvalues and bvalue1->valid) {
|
||||||
bool valid1 = bvalue1->valid;
|
value1old = bvalue1->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logging boat values
|
||||||
|
LOG_DEBUG(GwLog::LOG,"Drawing at PageFluid: value=%f", bvalue1->value);
|
||||||
|
|
||||||
// Draw page
|
// Draw page
|
||||||
//***********************************************************
|
//***********************************************************
|
||||||
@@ -171,7 +144,7 @@ class PageFluid : public Page{
|
|||||||
} else {
|
} else {
|
||||||
strcpy(buffer, "---");
|
strcpy(buffer, "---");
|
||||||
}
|
}
|
||||||
drawTextCentered(c.x, c.y + r - 20, String(buffer));
|
drawTextCenter(c.x, c.y + r - 20, String(buffer));
|
||||||
|
|
||||||
// draw symbol (as bitmap)
|
// draw symbol (as bitmap)
|
||||||
switch (fluidtype) {
|
switch (fluidtype) {
|
||||||
@@ -197,18 +170,18 @@ class PageFluid : public Page{
|
|||||||
// scale texts
|
// scale texts
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt7b);
|
getdisplay().setFont(&Ubuntu_Bold8pt7b);
|
||||||
p = {c.x, c.y - r + 30};
|
p = {c.x, c.y - r + 30};
|
||||||
drawTextCentered(p.x, p.y, "1/2");
|
drawTextCenter(p.x, p.y, "1/2");
|
||||||
pr = rotatePoint(c, p, -60);
|
pr = rotatePoint(c, p, -60);
|
||||||
drawTextCentered(pr.x, pr.y, "1/4");
|
drawTextCenter(pr.x, pr.y, "1/4");
|
||||||
pr = rotatePoint(c, p, 60);
|
pr = rotatePoint(c, p, 60);
|
||||||
drawTextCentered(pr.x, pr.y, "3/4");
|
drawTextCenter(pr.x, pr.y, "3/4");
|
||||||
|
|
||||||
// empty and full
|
// empty and full
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt7b);
|
getdisplay().setFont(&Ubuntu_Bold12pt7b);
|
||||||
p = rotatePoint(c, {c.x, c.y - r + 30}, -130);
|
p = rotatePoint(c, {c.x, c.y - r + 30}, -130);
|
||||||
drawTextCentered(p.x, p.y, "E");
|
drawTextCenter(p.x, p.y, "E");
|
||||||
p = rotatePoint(c, {c.x, c.y - r + 30}, 130);
|
p = rotatePoint(c, {c.x, c.y - r + 30}, 130);
|
||||||
drawTextCentered(p.x, p.y, "F");
|
drawTextCenter(p.x, p.y, "F");
|
||||||
|
|
||||||
// lines
|
// lines
|
||||||
std::vector<Point> pts = {
|
std::vector<Point> pts = {
|
||||||
|
|||||||
@@ -342,9 +342,9 @@ static Page *createPage(CommonData &common){
|
|||||||
PageDescription registerPageRollPitch(
|
PageDescription registerPageRollPitch(
|
||||||
"RollPitch", // Page name
|
"RollPitch", // Page name
|
||||||
createPage, // Action
|
createPage, // Action
|
||||||
0, // Number of bus values depends on selection in Web configuration
|
2, // Number of bus values depends on selection in Web configuration
|
||||||
// {"xdrROLL", "xdrPTCH"},// Bus values we need in the page
|
// {"xdrROLL", "xdrPTCH"},// Bus values we need in the page
|
||||||
{"xdrRoll", "xdrPitch"},// Bus values we need in the page
|
//{"xdrRoll", "xdrPitch"},// Bus values we need in the page
|
||||||
true // Show display header on/off
|
true // Show display header on/off
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -8,25 +8,44 @@ class PageVoltage : public Page
|
|||||||
{
|
{
|
||||||
bool init = false; // Marker for init done
|
bool init = false; // Marker for init done
|
||||||
bool keylock = false; // Keylock
|
bool keylock = false; // Keylock
|
||||||
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
|
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
|
bool trend = true; // Trend indicator [0|1], 0=off, 1=on
|
||||||
double raw = 0;
|
double raw = 0;
|
||||||
|
char mode = 'D'; // display mode (A)nalog | (D)igital
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageVoltage(CommonData &common){
|
PageVoltage(CommonData &common){
|
||||||
common.logger->logDebug(GwLog::LOG,"Show PageVoltage");
|
common.logger->logDebug(GwLog::LOG,"Instantiate PageVoltage");
|
||||||
|
if (hasFRAM) {
|
||||||
|
average = fram.read(FRAM_VOLTAGE_AVG);
|
||||||
|
trend = fram.read(FRAM_VOLTAGE_TREND);
|
||||||
|
mode = fram.read(FRAM_VOLTAGE_MODE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
virtual int handleKey(int key){
|
virtual int handleKey(int key){
|
||||||
// Change average
|
// Change average
|
||||||
if(key == 1){
|
if(key == 1){
|
||||||
average ++;
|
average ++;
|
||||||
average = average % 4; // Modulo 4
|
average = average % 4; // Modulo 4
|
||||||
|
if (hasFRAM) fram.write(FRAM_VOLTAGE_AVG, average);
|
||||||
return 0; // Commit the key
|
return 0; // Commit the key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Switch display mode
|
||||||
|
if (key == 2) {
|
||||||
|
if (mode == 'A') {
|
||||||
|
mode = 'D';
|
||||||
|
} else {
|
||||||
|
mode = 'A';
|
||||||
|
}
|
||||||
|
if (hasFRAM) fram.write(FRAM_VOLTAGE_MODE, mode);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Trend indicator
|
// Trend indicator
|
||||||
if(key == 5){
|
if(key == 5){
|
||||||
trend = !trend;
|
trend = !trend;
|
||||||
|
if (hasFRAM) fram.write(FRAM_VOLTAGE_TREND, trend);
|
||||||
return 0; // Commit the key
|
return 0; // Commit the key
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +57,41 @@ public:
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void printAvg(int avg, uint16_t x, uint16_t y, bool prefix) {
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold8pt7b);
|
||||||
|
getdisplay().setCursor(x, y);
|
||||||
|
if (prefix) {
|
||||||
|
getdisplay().print("Avg: ");
|
||||||
|
}
|
||||||
|
switch (average) {
|
||||||
|
case 0:
|
||||||
|
getdisplay().print("1s");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
getdisplay().print("10s");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
getdisplay().print("60s");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
getdisplay().print("300s");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
getdisplay().print("1s");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void printVoltageSymbol(uint16_t x, uint16_t y, uint16_t color) {
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold16pt7b);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
virtual void displayPage(CommonData &commonData, PageData &pageData)
|
virtual void displayPage(CommonData &commonData, PageData &pageData)
|
||||||
{
|
{
|
||||||
GwConfigHandler *config = commonData.config;
|
GwConfigHandler *config = commonData.config;
|
||||||
@@ -135,6 +189,9 @@ public:
|
|||||||
// Set display in partial refresh mode
|
// Set display in partial refresh mode
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
||||||
|
|
||||||
|
if (mode == 'D') {
|
||||||
|
// Display mode digital
|
||||||
|
|
||||||
// Show name
|
// Show name
|
||||||
getdisplay().setTextColor(commonData.fgcolor);
|
getdisplay().setTextColor(commonData.fgcolor);
|
||||||
getdisplay().setFont(&Ubuntu_Bold32pt7b);
|
getdisplay().setFont(&Ubuntu_Bold32pt7b);
|
||||||
@@ -152,25 +209,7 @@ public:
|
|||||||
getdisplay().print(batType);
|
getdisplay().print(batType);
|
||||||
|
|
||||||
// Show average settings
|
// Show average settings
|
||||||
getdisplay().setFont(&Ubuntu_Bold8pt7b);
|
printAvg(average, 320, 84, true);
|
||||||
getdisplay().setCursor(320, 84);
|
|
||||||
switch (average) {
|
|
||||||
case 0:
|
|
||||||
getdisplay().print("Avg: 1s");
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
getdisplay().print("Avg: 10s");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
getdisplay().print("Avg: 60s");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
getdisplay().print("Avg: 300s");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
getdisplay().print("Avg: 1s");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reading bus data or using simulation data
|
// Reading bus data or using simulation data
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b);
|
getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b);
|
||||||
@@ -221,6 +260,111 @@ public:
|
|||||||
getdisplay().fillRect(310, 240, 40, 120, commonData.bgcolor); // Clear area
|
getdisplay().fillRect(310, 240, 40, 120, commonData.bgcolor); // Clear area
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Display mode analog
|
||||||
|
|
||||||
|
// center
|
||||||
|
Point c = {260, 270};
|
||||||
|
uint8_t r = 240;
|
||||||
|
|
||||||
|
Point p1, p2;
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Scale
|
||||||
|
// angle to voltage scale mapping
|
||||||
|
std::map<int, String> mapping = {
|
||||||
|
{15, "10"}, {30, "11"}, {45, "12"}, {60, "13"}, {75, "14"}
|
||||||
|
};
|
||||||
|
pts = {
|
||||||
|
{c.x - r, c.y - 1},
|
||||||
|
{c.x - r + 12, c.y - 1},
|
||||||
|
{c.x - r + 12, c.y + 1},
|
||||||
|
{c.x - r, c.y + 1}
|
||||||
|
};
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold10pt7b);
|
||||||
|
for (int angle = 3; angle < 90; angle += 3) {
|
||||||
|
if (angle % 15 == 0) {
|
||||||
|
fillPoly4(rotatePoints(c, pts, angle), commonData.fgcolor);
|
||||||
|
p1 = rotatePoint(c, {c.x - r + 30, c.y}, angle);
|
||||||
|
drawTextCenter(p1.x, p1.y, mapping[angle]);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pointer rotation and limits
|
||||||
|
double angle;
|
||||||
|
if (not valid1) {
|
||||||
|
angle = -0.5;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (value1 > 15.0) {
|
||||||
|
angle = 91;
|
||||||
|
}
|
||||||
|
else if (value1 <= 9) {
|
||||||
|
angle = -0.5;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
angle = (value1 - 9) * 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pointer
|
||||||
|
// thick part
|
||||||
|
pts = {
|
||||||
|
{c.x - 2, c.y + 3},
|
||||||
|
{c.x - r + 38, c.y + 2},
|
||||||
|
{c.x - r + 38, c.y - 2},
|
||||||
|
{c.x - 2, c.y - 3}
|
||||||
|
};
|
||||||
|
fillPoly4(rotatePoints(c, pts, angle), commonData.fgcolor);
|
||||||
|
// thin part
|
||||||
|
pts = {
|
||||||
|
{c.x - r + 40, c.y + 1},
|
||||||
|
{c.x - r + 5, c.y + 1},
|
||||||
|
{c.x - r + 5, c.y -1},
|
||||||
|
{c.x - r + 40, c.y - 1},
|
||||||
|
};
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Symbol
|
||||||
|
printVoltageSymbol(40, 60, commonData.fgcolor);
|
||||||
|
|
||||||
|
// Additional information at right side
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold8pt7b);
|
||||||
|
getdisplay().setCursor(300, 60);
|
||||||
|
getdisplay().print("Source:");
|
||||||
|
getdisplay().setCursor(300, 80);
|
||||||
|
getdisplay().print(name1);
|
||||||
|
|
||||||
|
getdisplay().setCursor(300, 110);
|
||||||
|
getdisplay().print("Type:");
|
||||||
|
getdisplay().setCursor(300, 130);
|
||||||
|
getdisplay().print(batType);
|
||||||
|
|
||||||
|
getdisplay().setCursor(300, 160);
|
||||||
|
getdisplay().print("Avg:");
|
||||||
|
printAvg(average, 300, 180, false);
|
||||||
|
|
||||||
|
// FRAM indicator
|
||||||
|
if (hasFRAM) {
|
||||||
|
getdisplay().drawXBitmap(300, 240, fram_bits, fram_width, fram_height, commonData.fgcolor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Key Layout
|
// Key Layout
|
||||||
getdisplay().setTextColor(commonData.fgcolor);
|
getdisplay().setTextColor(commonData.fgcolor);
|
||||||
@@ -228,6 +372,8 @@ public:
|
|||||||
if(keylock == false){
|
if(keylock == false){
|
||||||
getdisplay().setCursor(10, 290);
|
getdisplay().setCursor(10, 290);
|
||||||
getdisplay().print("[AVG]");
|
getdisplay().print("[AVG]");
|
||||||
|
getdisplay().setCursor(62, 290);
|
||||||
|
getdisplay().print("[MODE]");
|
||||||
getdisplay().setCursor(130, 290);
|
getdisplay().setCursor(130, 290);
|
||||||
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
|
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
|
||||||
getdisplay().setCursor(293, 290);
|
getdisplay().setCursor(293, 290);
|
||||||
|
|||||||
650
lib/obp60task/PageWind.cpp
Normal file
650
lib/obp60task/PageWind.cpp
Normal file
@@ -0,0 +1,650 @@
|
|||||||
|
#ifdef BOARD_OBP60S3
|
||||||
|
|
||||||
|
#include "Pagedata.h"
|
||||||
|
#include "OBP60Extensions.h"
|
||||||
|
#include "N2kMessages.h"
|
||||||
|
|
||||||
|
#define front_width 120
|
||||||
|
#define front_height 162
|
||||||
|
static unsigned char front_bits[] PROGMEM = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x80, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xc0, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0x03,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xf0, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xf0, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x0f, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x1f,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xfc, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xfe, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf7, 0x7f,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||||
|
0xff, 0xf3, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xc0, 0xff, 0xe1, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xc1, 0xff, 0x01, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0x80, 0xff,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0,
|
||||||
|
0x7f, 0x80, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xf0, 0x7f, 0x00, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0xfe, 0x0f, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0xfe,
|
||||||
|
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc,
|
||||||
|
0x1f, 0x00, 0xfc, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xfc, 0x0f, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x0f, 0x00, 0xf8, 0x3f, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 0x00, 0xf0,
|
||||||
|
0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
|
||||||
|
0x03, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x80, 0xff, 0x03, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x01, 0x00, 0xc0, 0xff, 0x01, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0x00, 0x00, 0x80,
|
||||||
|
0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff,
|
||||||
|
0x00, 0x00, 0x80, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xf0, 0x7f, 0x00, 0x00, 0x00, 0xfe, 0x07, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00,
|
||||||
|
0xfe, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f,
|
||||||
|
0x00, 0x00, 0x00, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00, 0x00,
|
||||||
|
0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x07,
|
||||||
|
0x00, 0x00, 0x00, 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xfe, 0x03, 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0xc0, 0xff, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x80, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3f, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xf0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xf8, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3f,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80,
|
||||||
|
0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x1f, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0xe0,
|
||||||
|
0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
|
||||||
|
0x07, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
|
||||||
|
0x1f, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xfc,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x7f, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x3f,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xfc, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x80, 0x0f,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00,
|
||||||
|
0x00, 0x80, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xf0, 0x03, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x03,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00,
|
||||||
|
0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xe0, 0x07, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0xf0, 0x01,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00,
|
||||||
|
0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x80, 0x0f, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x78, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00,
|
||||||
|
0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x3e, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3c, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00,
|
||||||
|
0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x7c, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x0e, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00,
|
||||||
|
0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xf8, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x07, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01,
|
||||||
|
0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xe0, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x80, 0x03, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03,
|
||||||
|
0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xc0, 0x03, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x01, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07,
|
||||||
|
0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x80, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0xe0, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
|
||||||
|
0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x0f, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x70, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e,
|
||||||
|
0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x0e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
|
||||||
|
0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x1c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x18, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
|
||||||
|
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x1c, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
|
||||||
|
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70,
|
||||||
|
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x70, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x0e, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0,
|
||||||
|
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xc0 };
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
public:
|
||||||
|
PageWind(CommonData &common){
|
||||||
|
common.logger->logDebug(GwLog::LOG,"Instantiate PageWind");
|
||||||
|
if (hasFRAM) {
|
||||||
|
lp = fram.read(FRAM_WIND_SIZE);
|
||||||
|
source = fram.read(FRAM_WIND_SRC);
|
||||||
|
mode = fram.read(FRAM_WIND_MODE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key functions
|
||||||
|
virtual int handleKey(int key){
|
||||||
|
|
||||||
|
if(key == 1){ // Mode switch
|
||||||
|
if(mode == 'N'){
|
||||||
|
mode = 'L';
|
||||||
|
} else if (mode == 'L') {
|
||||||
|
mode = 'X';
|
||||||
|
} else {
|
||||||
|
mode = 'N';
|
||||||
|
}
|
||||||
|
if (hasFRAM) fram.write(FRAM_WIND_MODE, mode);
|
||||||
|
return 0; // Commit the key
|
||||||
|
}
|
||||||
|
|
||||||
|
if(key == 3){ // Source switch
|
||||||
|
if(source == 'A'){
|
||||||
|
source = 'T';
|
||||||
|
} else {
|
||||||
|
source = 'A';
|
||||||
|
}
|
||||||
|
if (hasFRAM) fram.write(FRAM_WIND_SRC, source);
|
||||||
|
return 0; // Commit the key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce instrument size
|
||||||
|
if(key == 2 && mode == 'X'){ // Code for reduce
|
||||||
|
lp = lp - 10;
|
||||||
|
if(lp < 10){
|
||||||
|
lp = 10;
|
||||||
|
}
|
||||||
|
if (hasFRAM) fram.write(FRAM_WIND_SIZE, lp);
|
||||||
|
return 0; // Commit the key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enlarge instrument size
|
||||||
|
if(key == 5 && mode == 'X'){ // Code for enlarge
|
||||||
|
lp = lp + 10;
|
||||||
|
if(lp > 80){
|
||||||
|
lp = 80;
|
||||||
|
}
|
||||||
|
if (hasFRAM) fram.write(FRAM_WIND_SIZE, lp);
|
||||||
|
return 0; // Commit the key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keylock function
|
||||||
|
if(key == 11){ // Code for keylock
|
||||||
|
keylock = !keylock; // Toggle keylock
|
||||||
|
return 0; // Commit the key
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void displayPage(CommonData &commonData, PageData &pageData)
|
||||||
|
{
|
||||||
|
GwConfigHandler *config = commonData.config;
|
||||||
|
GwLog *logger=commonData.logger;
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Get boat values for speed (AWS/TWS)
|
||||||
|
if (source == 'A') {
|
||||||
|
bvalue1 = pageData.values[0];
|
||||||
|
} else {
|
||||||
|
bvalue1 = pageData.values[2];
|
||||||
|
}
|
||||||
|
String name1 = bvalue1->getName().c_str(); // 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
|
||||||
|
|
||||||
|
// Get boat values for angle (AWD/TWD)
|
||||||
|
if (source == 'A') {
|
||||||
|
bvalue2 = pageData.values[1];
|
||||||
|
} else {
|
||||||
|
bvalue2 = pageData.values[3];
|
||||||
|
}
|
||||||
|
String name2 = bvalue2->getName().c_str(); // Value name
|
||||||
|
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
|
||||||
|
|
||||||
|
// Optical warning by limit violation (unused)
|
||||||
|
if(String(flashLED) == "Limit Violation"){
|
||||||
|
setBlinkingLED(false);
|
||||||
|
setFlashLED(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logging boat values
|
||||||
|
if (bvalue1 == NULL) return;
|
||||||
|
LOG_DEBUG(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
|
||||||
|
|
||||||
|
getdisplay().setTextColor(commonData.fgcolor);
|
||||||
|
|
||||||
|
|
||||||
|
if (mode == 'X') {
|
||||||
|
// Original example code with scaling circle
|
||||||
|
|
||||||
|
// Show values AWS/TWS
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold20pt7b);
|
||||||
|
getdisplay().setCursor(20, 50);
|
||||||
|
getdisplay().print(name1); // Value name
|
||||||
|
getdisplay().print(": ");
|
||||||
|
if(holdvalues == false){
|
||||||
|
getdisplay().print(svalue1); // Value
|
||||||
|
getdisplay().print(" ");
|
||||||
|
getdisplay().print(unit1); // Unit
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
getdisplay().print(svalue1old); // Value old
|
||||||
|
getdisplay().print(" ");
|
||||||
|
getdisplay().print(unit1old); // Unit old
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show values AWD/TWD
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold20pt7b);
|
||||||
|
getdisplay().setCursor(20, 260);
|
||||||
|
getdisplay().print(name2); // Value name
|
||||||
|
getdisplay().print(": ");
|
||||||
|
if(holdvalues == false){
|
||||||
|
getdisplay().print(svalue2); // Value
|
||||||
|
getdisplay().print(" ");
|
||||||
|
getdisplay().print(unit2); // Unit
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
getdisplay().print(svalue2old); // Value old
|
||||||
|
getdisplay().print(" ");
|
||||||
|
getdisplay().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);
|
||||||
|
|
||||||
|
// Wind pointer
|
||||||
|
if (bvalue2->valid) {
|
||||||
|
uint8_t lp0 = lp * 0.6; // effective pointer outside size
|
||||||
|
uint8_t lp1 = lp * 0.4; // effective pointer inside size
|
||||||
|
// zero position
|
||||||
|
std::vector<Point> pts = {
|
||||||
|
{c.x, c.y - lp},
|
||||||
|
{c.x - 7, c.y - lp + lp0},
|
||||||
|
{c.x, c.y - lp + lp1},
|
||||||
|
{c.x + 7, c.y - lp + lp0}
|
||||||
|
};
|
||||||
|
fillPoly4(rotatePoints(c, pts, RadToDeg(value2)), commonData.fgcolor);
|
||||||
|
} else {
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold12pt7b);
|
||||||
|
drawTextCenter(c.x, c.y, "no data");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (mode == 'L') { // Mode (L)ens
|
||||||
|
|
||||||
|
Point c = {200, 155};
|
||||||
|
uint16_t r = 150;
|
||||||
|
|
||||||
|
Point p;
|
||||||
|
std::vector<Point> pts = { // polygon lines
|
||||||
|
{c.x - 2, c.y - r},
|
||||||
|
{c.x + 2, c.y - r},
|
||||||
|
{c.x + 2, c.y - (r - 16)},
|
||||||
|
{c.x - 2, c.y - (r - 16)}
|
||||||
|
};
|
||||||
|
int angle;
|
||||||
|
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold12pt7b);
|
||||||
|
|
||||||
|
// starbord
|
||||||
|
// text with line
|
||||||
|
angle = 20;
|
||||||
|
for (int i = 30; i < 150; i += 30) {
|
||||||
|
p = rotatePoint(c, {c.x, c.y - r + 40}, i);
|
||||||
|
drawTextCenter(p.x, p.y, String(angle));
|
||||||
|
angle += 10;
|
||||||
|
fillPoly4(rotatePoints(c, pts, i), commonData.fgcolor);
|
||||||
|
}
|
||||||
|
// dots
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// port
|
||||||
|
angle = 50;
|
||||||
|
// text with line
|
||||||
|
for (int i = 240; i <= 330; i += 30) {
|
||||||
|
p = rotatePoint(c, {c.x, c.y - r + 40}, i);
|
||||||
|
drawTextCenter(p.x, p.y, String(angle));
|
||||||
|
angle -= 10;
|
||||||
|
fillPoly4(rotatePoints(c, pts, i), commonData.fgcolor);
|
||||||
|
}
|
||||||
|
// dots
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// data source
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold12pt7b);
|
||||||
|
getdisplay().setCursor(8, 50);
|
||||||
|
if (source == 'A') {
|
||||||
|
getdisplay().print("APP");
|
||||||
|
} else {
|
||||||
|
getdisplay().print("TRUE");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wind pointer (angle)
|
||||||
|
if (bvalue2->valid) {
|
||||||
|
float alpha = RadToDeg(value2);
|
||||||
|
bool port = (alpha > 180);
|
||||||
|
if (port) {
|
||||||
|
alpha = 360 - alpha;
|
||||||
|
}
|
||||||
|
if (alpha < 15) {
|
||||||
|
alpha = 15; // stop at start of scale
|
||||||
|
} else if (alpha > 55) {
|
||||||
|
alpha = 55; // stop at end of scale
|
||||||
|
}
|
||||||
|
alpha = 3 * alpha - 30; // convert to lens scale
|
||||||
|
if (port) {
|
||||||
|
alpha *= -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getdisplay().fillCircle(c.x, c.y, 8, commonData.fgcolor);
|
||||||
|
pts = {
|
||||||
|
{c.x - 1, c.y - (r - 20)},
|
||||||
|
{c.x + 1, c.y - (r - 20)},
|
||||||
|
{c.x + 6, c.y + 15},
|
||||||
|
{c.x - 6, c.y + 15}
|
||||||
|
};
|
||||||
|
fillPoly4(rotatePoints(c, pts, alpha), commonData.fgcolor);
|
||||||
|
getdisplay().fillCircle(c.x, c.y, 6, commonData.bgcolor);
|
||||||
|
} else {
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold12pt7b);
|
||||||
|
drawTextCenter(c.x, c.y, "no data");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wind speed as decimal number
|
||||||
|
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
||||||
|
getdisplay().setCursor(150, 250);
|
||||||
|
if (holdvalues == false) {
|
||||||
|
getdisplay().print(svalue1);
|
||||||
|
} else {
|
||||||
|
getdisplay().print(svalue1old);
|
||||||
|
}
|
||||||
|
// unit
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold8pt7b);
|
||||||
|
getdisplay().setCursor(220, 265);
|
||||||
|
getdisplay().print("kts");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Normal mode
|
||||||
|
|
||||||
|
// data source
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold12pt7b);
|
||||||
|
getdisplay().setCursor(8, 50);
|
||||||
|
if (source == 'A') {
|
||||||
|
getdisplay().print("APP");
|
||||||
|
} else {
|
||||||
|
getdisplay().print("TRUE");
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw ship front symbol (as bitmap)
|
||||||
|
getdisplay().drawXBitmap(140, 30, front_bits, front_width, front_height, commonData.fgcolor);
|
||||||
|
|
||||||
|
Point c = {200, 155};
|
||||||
|
uint16_t r = 150;
|
||||||
|
|
||||||
|
Point p;
|
||||||
|
std::vector<Point> pts = { // polygon lines
|
||||||
|
{c.x - 2, c.y - r},
|
||||||
|
{c.x + 2, c.y - r},
|
||||||
|
{c.x + 2, c.y - (r - 16)},
|
||||||
|
{c.x - 2, c.y - (r - 16)}
|
||||||
|
};
|
||||||
|
int angle;
|
||||||
|
|
||||||
|
// starbord
|
||||||
|
// text with line
|
||||||
|
for (int i = 30; i < 150; i += 30) {
|
||||||
|
p = rotatePoint(c, {c.x, c.y - r + 40}, i);
|
||||||
|
drawTextCenter(p.x, p.y, String(i));
|
||||||
|
fillPoly4(rotatePoints(c, pts, i), commonData.fgcolor);
|
||||||
|
}
|
||||||
|
// dots
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// port
|
||||||
|
// text with line
|
||||||
|
angle = 120;
|
||||||
|
for (int i = 240; i <= 330; i += 30) {
|
||||||
|
p = rotatePoint(c, {c.x, c.y - r + 40}, i);
|
||||||
|
drawTextCenter(p.x, p.y, String(angle));
|
||||||
|
angle -= 30;
|
||||||
|
fillPoly4(rotatePoints(c, pts, i), commonData.fgcolor);
|
||||||
|
}
|
||||||
|
// dots
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wind speed as decimal number
|
||||||
|
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
||||||
|
getdisplay().setCursor(150, 250);
|
||||||
|
if (holdvalues == false) {
|
||||||
|
getdisplay().print(svalue1);
|
||||||
|
} else {
|
||||||
|
getdisplay().print(svalue1old);
|
||||||
|
}
|
||||||
|
// unit
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold8pt7b);
|
||||||
|
getdisplay().setCursor(220, 265);
|
||||||
|
getdisplay().print("kts");
|
||||||
|
|
||||||
|
// Wind pointer (angle)
|
||||||
|
if (bvalue2->valid) {
|
||||||
|
float alpha = RadToDeg(value2);
|
||||||
|
getdisplay().fillCircle(c.x, c.y, 8, commonData.fgcolor);
|
||||||
|
pts = {
|
||||||
|
{c.x - 1, c.y - (r - 20)},
|
||||||
|
{c.x + 1, c.y - (r - 20)},
|
||||||
|
{c.x + 6, c.y + 15},
|
||||||
|
{c.x - 6, c.y + 15}
|
||||||
|
};
|
||||||
|
fillPoly4(rotatePoints(c, pts, alpha), commonData.fgcolor);
|
||||||
|
getdisplay().fillCircle(c.x, c.y, 6, commonData.bgcolor);
|
||||||
|
} else {
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold12pt7b);
|
||||||
|
drawTextCenter(c.x, c.y, "no data");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key Layout
|
||||||
|
getdisplay().setFont(&Ubuntu_Bold8pt7b);
|
||||||
|
if(keylock == false){
|
||||||
|
getdisplay().setCursor(10, 290);
|
||||||
|
getdisplay().print("[MODE]");
|
||||||
|
getdisplay().setCursor(130, 290);
|
||||||
|
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
|
||||||
|
|
||||||
|
if (mode == 'X') {
|
||||||
|
getdisplay().setCursor(85, 290);
|
||||||
|
getdisplay().print("[ - ]");
|
||||||
|
getdisplay().setCursor(295, 290);
|
||||||
|
getdisplay().print("[ + ]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(String(backlightMode) == "Control by Key"){ // Key for illumination
|
||||||
|
getdisplay().setCursor(343, 290);
|
||||||
|
getdisplay().print("[ILUM]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
getdisplay().setCursor(130, 290);
|
||||||
|
getdisplay().print(" [ Keylock active ]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update display
|
||||||
|
getdisplay().nextPage(); // Partial update (fast)
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static Page *createPage(CommonData &common){
|
||||||
|
return new PageWind(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 will will provide the names of the fixed values we need
|
||||||
|
*/
|
||||||
|
PageDescription registerPageWind(
|
||||||
|
"Wind", // Page name
|
||||||
|
createPage, // Action
|
||||||
|
0, // Number of bus values depends on selection in Web configuration
|
||||||
|
{"AWS","AWA", "TWS", "TWA"}, // Bus values we need in the page
|
||||||
|
true // Show display header on/off
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -186,7 +186,8 @@ public:
|
|||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
|
||||||
getdisplay().setCursor(295, 65);
|
getdisplay().setCursor(295, 65);
|
||||||
if(valid3 == true){
|
if(valid3 == true){
|
||||||
getdisplay().print(abs(value3 * 180 / PI), 0); // Value
|
// getdisplay().print(abs(value3 * 180 / PI), 0); // Value
|
||||||
|
getdisplay().print(svalue3); // Value
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
getdisplay().print("---"); // Value
|
getdisplay().print("---"); // Value
|
||||||
|
|||||||
@@ -4,9 +4,12 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#define MAX_PAGE_NUMBER 10 // Max number of pages for show data
|
||||||
|
|
||||||
typedef std::vector<GwApi::BoatValue *> ValueList;
|
typedef std::vector<GwApi::BoatValue *> ValueList;
|
||||||
typedef struct{
|
typedef struct{
|
||||||
String pageName;
|
String pageName;
|
||||||
|
uint8_t pageNumber; // page number in sequence of visible pages
|
||||||
//the values will always contain the user defined values first
|
//the values will always contain the user defined values first
|
||||||
ValueList values;
|
ValueList values;
|
||||||
} PageData;
|
} PageData;
|
||||||
@@ -114,6 +117,13 @@ class PageDescription{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PageStruct{
|
||||||
|
public:
|
||||||
|
Page *page=NULL;
|
||||||
|
PageData parameters;
|
||||||
|
PageDescription *description=NULL;
|
||||||
|
};
|
||||||
|
|
||||||
// Structure for formated boat values
|
// Structure for formated boat values
|
||||||
typedef struct{
|
typedef struct{
|
||||||
double value;
|
double value;
|
||||||
|
|||||||
11
lib/obp60task/README
Normal file
11
lib/obp60task/README
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
OBP40
|
||||||
|
=====
|
||||||
|
|
||||||
|
Important information:
|
||||||
|
|
||||||
|
***************************************************
|
||||||
|
THIS BRANCH IS NOT INTENDED TO MERGE INTO "master"!
|
||||||
|
***************************************************
|
||||||
|
|
||||||
|
platformio.ini adapted to compile directly
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
|||||||
#!/bin/perl -w
|
#!/bin/perl -w
|
||||||
#A tool to generate the part of config.json that deals with pages and fields.
|
#A tool to generate the part of config.json that deals with pages and fields.
|
||||||
|
#DEPRECATED, moved to get_set.py
|
||||||
|
die "Please use gen_set.py instead";
|
||||||
#List of all pages and the number of parameters they expect.
|
#List of all pages and the number of parameters they expect.
|
||||||
%NoOfFieldsPerPage=qw(
|
%NoOfFieldsPerPage=qw(
|
||||||
ApparentWind 0
|
ApparentWind 0
|
||||||
@@ -81,7 +82,7 @@ for ($PageNo=1;$PageNo<=$NoOfPages;$PageNo++){
|
|||||||
print "\t",'"condition":[';
|
print "\t",'"condition":[';
|
||||||
foreach $page (@Pages) {
|
foreach $page (@Pages) {
|
||||||
if($NoOfFieldsPerPage{$page}>=$FieldNo){
|
if($NoOfFieldsPerPage{$page}>=$FieldNo){
|
||||||
print '{"page1type":"',$page,'"},';
|
print '{"page',$PageNo,'type":"',$page,'"},';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print "\b],\n";
|
print "\b],\n";
|
||||||
|
|||||||
123
lib/obp60task/gen_set.py
Executable file
123
lib/obp60task/gen_set.py
Executable file
@@ -0,0 +1,123 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# A tool to generate that part of config.json that deals with pages and fields.
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
# List of all pages and the number of parameters they expect.
|
||||||
|
no_of_fields_per_page = {
|
||||||
|
"ApparentWind": 0,
|
||||||
|
"XTETrack": 0,
|
||||||
|
"Battery2": 0,
|
||||||
|
"Battery": 0,
|
||||||
|
"BME280": 0,
|
||||||
|
"Clock": 0,
|
||||||
|
"DST810": 0,
|
||||||
|
"Fluid": 0,
|
||||||
|
"FourValues2": 4,
|
||||||
|
"FourValues": 4,
|
||||||
|
"Generator": 0,
|
||||||
|
"KeelPosition": 0,
|
||||||
|
"OneValue": 1,
|
||||||
|
"RollPitch": 2,
|
||||||
|
"RudderPosition": 0,
|
||||||
|
"Solar": 0,
|
||||||
|
"ThreeValues": 3,
|
||||||
|
"TwoValues": 2,
|
||||||
|
"Voltage": 0,
|
||||||
|
"White": 0,
|
||||||
|
"WindRose": 0,
|
||||||
|
"WindRoseFlex": 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
# No changes needed beyond this point
|
||||||
|
# max number of pages supported by OBP60
|
||||||
|
no_of_pages = 10
|
||||||
|
# Default selection for each page
|
||||||
|
default_pages = [
|
||||||
|
"Voltage",
|
||||||
|
"WindRose",
|
||||||
|
"OneValue",
|
||||||
|
"TwoValues",
|
||||||
|
"ThreeValues",
|
||||||
|
"FourValues",
|
||||||
|
"FourValues2",
|
||||||
|
"Clock",
|
||||||
|
"RollPitch",
|
||||||
|
"Battery2",
|
||||||
|
]
|
||||||
|
numbers = [
|
||||||
|
"one",
|
||||||
|
"two",
|
||||||
|
"three",
|
||||||
|
"four",
|
||||||
|
"five",
|
||||||
|
"six",
|
||||||
|
"seven",
|
||||||
|
"eight",
|
||||||
|
"nine",
|
||||||
|
"ten",
|
||||||
|
]
|
||||||
|
pages = sorted(no_of_fields_per_page.keys())
|
||||||
|
max_no_of_fields_per_page = max(no_of_fields_per_page.values())
|
||||||
|
|
||||||
|
output = []
|
||||||
|
|
||||||
|
for page_no in range(1, no_of_pages + 1):
|
||||||
|
page_data = {
|
||||||
|
"name": f"page{page_no}type",
|
||||||
|
"label": "Type",
|
||||||
|
"type": "list",
|
||||||
|
"default": default_pages[page_no - 1],
|
||||||
|
"description": f"Type of page for page {page_no}",
|
||||||
|
"list": pages,
|
||||||
|
"category": f"OBP60 Page {page_no}",
|
||||||
|
"capabilities": {"obp60": "true"},
|
||||||
|
"condition": [{"visiblePages": vp} for vp in range(page_no, no_of_pages + 1)],
|
||||||
|
#"fields": [],
|
||||||
|
}
|
||||||
|
output.append(page_data)
|
||||||
|
|
||||||
|
for field_no in range(1, max_no_of_fields_per_page + 1):
|
||||||
|
field_data = {
|
||||||
|
"name": f"page{page_no}value{field_no}",
|
||||||
|
"label": f"Field {field_no}",
|
||||||
|
"type": "boatData",
|
||||||
|
"default": "",
|
||||||
|
"description": f"The display for field {numbers[field_no - 1]}",
|
||||||
|
"category": f"OBP60 Page {page_no}",
|
||||||
|
"capabilities": {"obp60": "true"},
|
||||||
|
"condition": [
|
||||||
|
{f"page{page_no}type": page}
|
||||||
|
for page in pages
|
||||||
|
if no_of_fields_per_page[page] >= field_no
|
||||||
|
],
|
||||||
|
}
|
||||||
|
output.append(field_data)
|
||||||
|
|
||||||
|
fluid_data ={
|
||||||
|
"name": f"page{page_no}fluid",
|
||||||
|
"label": "Fluid type",
|
||||||
|
"type": "list",
|
||||||
|
"default": "0",
|
||||||
|
"list": [
|
||||||
|
{"l":"Fuel (0)","v":"0"},
|
||||||
|
{"l":"Water (1)","v":"1"},
|
||||||
|
{"l":"Gray Water (2)","v":"2"},
|
||||||
|
{"l":"Live Well (3)","v":"3"},
|
||||||
|
{"l":"Oil (4)","v":"4"},
|
||||||
|
{"l":"Black Water (5)","v":"5"},
|
||||||
|
{"l":"Fuel Gasoline (6)","v":"6"}
|
||||||
|
],
|
||||||
|
"description": "Fluid type in tank",
|
||||||
|
"category": f"OBP60 Page {page_no}",
|
||||||
|
"capabilities": {
|
||||||
|
"obp60":"true"
|
||||||
|
},
|
||||||
|
"condition":[{f"page{page_no}type":"Fluid"}]
|
||||||
|
}
|
||||||
|
output.append(fluid_data)
|
||||||
|
|
||||||
|
json_output = json.dumps(output, indent=4)
|
||||||
|
# print omitting first and last line containing [ ] of JSON array
|
||||||
|
print(json_output[1:-1])
|
||||||
|
# print(",")
|
||||||
347
lib/obp60task/imglib.cpp
Normal file
347
lib/obp60task/imglib.cpp
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Image functions:
|
||||||
|
* - Convert a 1bit framebuffer in RAM to
|
||||||
|
* - GIF, compressed, based on giflib and gif_hash
|
||||||
|
* - PBM, portable bitmap, very simple copy
|
||||||
|
* - BMP, bigger with a little bit fiddling around
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include <Arduino.h> // needed for PROGMEM
|
||||||
|
#include <vector>
|
||||||
|
#include "GwLog.h" // needed for logger
|
||||||
|
#include "imglib.h"
|
||||||
|
|
||||||
|
GifFilePrivateType gifprivate;
|
||||||
|
|
||||||
|
void ClearHashTable(GifHashTableType *HashTable) {
|
||||||
|
memset(HashTable->HTable, 0xFF, HT_SIZE * sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
GifHashTableType *InitHashTable(void) {
|
||||||
|
GifHashTableType *HashTable;
|
||||||
|
if ((HashTable = (GifHashTableType *)ps_malloc(sizeof(GifHashTableType))) == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ClearHashTable(HashTable);
|
||||||
|
return HashTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int KeyItem(uint32_t Item) {
|
||||||
|
return ((Item >> 12) ^ Item) & HT_KEY_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code) {
|
||||||
|
int HKey = KeyItem(Key);
|
||||||
|
uint32_t *HTable = HashTable->HTable;
|
||||||
|
while (HT_GET_KEY(HTable[HKey]) != 0xFFFFFL) {
|
||||||
|
HKey = (HKey + 1) & HT_KEY_MASK;
|
||||||
|
}
|
||||||
|
HTable[HKey] = HT_PUT_KEY(Key) | HT_PUT_CODE(Code);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ExistsHashTable(GifHashTableType *HashTable, uint32_t Key) {
|
||||||
|
int HKey = KeyItem(Key);
|
||||||
|
uint32_t *HTable = HashTable->HTable, HTKey;
|
||||||
|
while ((HTKey = HT_GET_KEY(HTable[HKey])) != 0xFFFFFL) {
|
||||||
|
if (Key == HTKey) {
|
||||||
|
return HT_GET_CODE(HTable[HKey]);
|
||||||
|
}
|
||||||
|
HKey = (HKey + 1) & HT_KEY_MASK;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Put 2 bytes (a word) into the given file in little-endian order:
|
||||||
|
******************************************************************************/
|
||||||
|
void GifPutWord(std::vector<uint8_t>* gifBuffer, uint16_t Word) {
|
||||||
|
/*cuint8_t c[2];
|
||||||
|
[0] = LOBYTE(Word);
|
||||||
|
c[1] = HIBYTE(Word);
|
||||||
|
gifBuffer->push_back(c[0]);
|
||||||
|
gifBuffer->push_back(c[1]); */
|
||||||
|
gifBuffer->push_back(LOBYTE(Word));
|
||||||
|
gifBuffer->push_back(HIBYTE(Word));
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
This routines buffers the given characters until 255 characters are ready
|
||||||
|
to be output. If Code is equal to -1 the buffer is flushed (EOF).
|
||||||
|
The buffer is Dumped with first byte as its size, as GIF format requires.
|
||||||
|
******************************************************************************/
|
||||||
|
void GifBufferedOutput(std::vector<uint8_t>* gifBuffer, GifByteType *Buf, int c) {
|
||||||
|
if (c == FLUSH_OUTPUT) {
|
||||||
|
// Flush everything out.
|
||||||
|
for (int i = 0; i < Buf[0] + 1; i++) {
|
||||||
|
gifBuffer->push_back(Buf[i]);
|
||||||
|
}
|
||||||
|
// Mark end of compressed data, by an empty block (see GIF doc):
|
||||||
|
Buf[0] = 0;
|
||||||
|
gifBuffer->push_back(0);
|
||||||
|
} else {
|
||||||
|
if (Buf[0] == 255) {
|
||||||
|
// Dump out this buffer - it is full:
|
||||||
|
for (int i = 0; i < Buf[0] + 1; i++) {
|
||||||
|
gifBuffer->push_back(Buf[i]);
|
||||||
|
}
|
||||||
|
Buf[0] = 0;
|
||||||
|
}
|
||||||
|
Buf[++Buf[0]] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
The LZ compression output routine:
|
||||||
|
This routine is responsible for the compression of the bit stream into
|
||||||
|
8 bits (bytes) packets.
|
||||||
|
******************************************************************************/
|
||||||
|
void GifCompressOutput(std::vector<uint8_t>* gifBuffer, const int Code) {
|
||||||
|
|
||||||
|
if (Code == FLUSH_OUTPUT) {
|
||||||
|
while (gifprivate.CrntShiftState > 0) {
|
||||||
|
// Get Rid of what is left in DWord, and flush it.
|
||||||
|
GifBufferedOutput(gifBuffer, gifprivate.Buf, gifprivate.CrntShiftDWord & 0xff);
|
||||||
|
gifprivate.CrntShiftDWord >>= 8;
|
||||||
|
gifprivate.CrntShiftState -= 8;
|
||||||
|
}
|
||||||
|
gifprivate.CrntShiftState = 0; // For next time.
|
||||||
|
GifBufferedOutput(gifBuffer, gifprivate.Buf, FLUSH_OUTPUT);
|
||||||
|
} else {
|
||||||
|
gifprivate.CrntShiftDWord |= ((long)Code) << gifprivate.CrntShiftState;
|
||||||
|
gifprivate.CrntShiftState += gifprivate.RunningBits;
|
||||||
|
while (gifprivate.CrntShiftState >= 8) {
|
||||||
|
// Dump out full bytes:
|
||||||
|
GifBufferedOutput(gifBuffer, gifprivate.Buf, gifprivate.CrntShiftDWord & 0xff);
|
||||||
|
gifprivate.CrntShiftDWord >>= 8;
|
||||||
|
gifprivate.CrntShiftState -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If code cannt fit into RunningBits bits, must raise its size. Note */
|
||||||
|
/* however that codes above LZ_MAX_CODE are used for special signaling. */
|
||||||
|
if (gifprivate.RunningCode >= gifprivate.MaxCode1 && Code <= LZ_MAX_CODE) {
|
||||||
|
gifprivate.MaxCode1 = 1 << ++gifprivate.RunningBits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Setup the LZ compression for this image:
|
||||||
|
******************************************************************************/
|
||||||
|
void GifSetupCompress(std::vector<uint8_t>* gifBuffer) {
|
||||||
|
gifBuffer->push_back(0x02);// Bits per pixel wit minimum 2
|
||||||
|
|
||||||
|
gifprivate.Buf[0] = 0; // Nothing was output yet
|
||||||
|
gifprivate.BitsPerPixel = 2; // Minimum is 2
|
||||||
|
gifprivate.ClearCode = (1 << 2);
|
||||||
|
gifprivate.EOFCode = gifprivate.ClearCode + 1;
|
||||||
|
gifprivate.RunningCode = gifprivate.EOFCode + 1;
|
||||||
|
gifprivate.RunningBits = 2 + 1; // Number of bits per code
|
||||||
|
gifprivate.MaxCode1 = 1 << gifprivate.RunningBits; // Max. code + 1
|
||||||
|
gifprivate.CrntCode = FIRST_CODE; // Signal that this is first one!
|
||||||
|
gifprivate.CrntShiftState = 0; // No information in CrntShiftDWord
|
||||||
|
gifprivate.CrntShiftDWord = 0;
|
||||||
|
|
||||||
|
GifCompressOutput(gifBuffer, gifprivate.ClearCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void createGifHeader(std::vector<uint8_t>* gifBuffer, uint16_t width, uint16_t height) {
|
||||||
|
|
||||||
|
// SCREEN DESCRIPTOR
|
||||||
|
gifBuffer->push_back('G');
|
||||||
|
gifBuffer->push_back('I');
|
||||||
|
gifBuffer->push_back('F');
|
||||||
|
gifBuffer->push_back('8');
|
||||||
|
gifBuffer->push_back('7');
|
||||||
|
gifBuffer->push_back('a');
|
||||||
|
|
||||||
|
GifPutWord(gifBuffer, width);
|
||||||
|
GifPutWord(gifBuffer, height);
|
||||||
|
|
||||||
|
gifBuffer->push_back(0x80 | (1 << 4));
|
||||||
|
gifBuffer->push_back(0x00); // Index into the ColorTable for background color
|
||||||
|
gifBuffer->push_back(0x00); // Pixel Aspect Ratio
|
||||||
|
|
||||||
|
// Colormap
|
||||||
|
gifBuffer->push_back(0xff); // Color 0
|
||||||
|
gifBuffer->push_back(0xff);
|
||||||
|
gifBuffer->push_back(0xff);
|
||||||
|
gifBuffer->push_back(0x00); // Color 1
|
||||||
|
gifBuffer->push_back(0x00);
|
||||||
|
gifBuffer->push_back(0x00);
|
||||||
|
|
||||||
|
// IMAGE DESCRIPTOR
|
||||||
|
gifBuffer->push_back(DESCRIPTOR_INTRODUCER);
|
||||||
|
|
||||||
|
GifPutWord(gifBuffer, 0);
|
||||||
|
GifPutWord(gifBuffer, 0);
|
||||||
|
GifPutWord(gifBuffer, width);
|
||||||
|
GifPutWord(gifBuffer, height);
|
||||||
|
|
||||||
|
gifBuffer->push_back(0x00); // No colormap here , we use the global one
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
The LZ compression routine:
|
||||||
|
This version compresses the given buffer Line of length LineLen.
|
||||||
|
This routine can be called a few times (one per scan line, for example), in
|
||||||
|
order to complete the whole image.
|
||||||
|
******************************************************************************/
|
||||||
|
void GifCompressLine(std::vector<uint8_t>* gifBuffer, const GifPixelType *Line, const int LineLen) {
|
||||||
|
int i = 0, CrntCode;
|
||||||
|
GifHashTableType *HashTable;
|
||||||
|
|
||||||
|
HashTable = gifprivate.HashTable;
|
||||||
|
|
||||||
|
if (gifprivate.CrntCode == FIRST_CODE) { // Its first time!
|
||||||
|
CrntCode = Line[i++];
|
||||||
|
} else {
|
||||||
|
CrntCode =
|
||||||
|
gifprivate.CrntCode; // Get last code in compression
|
||||||
|
}
|
||||||
|
while (i < LineLen) { // Decode LineLen items
|
||||||
|
GifPixelType Pixel = Line[i++]; // Get next pixel from stream.
|
||||||
|
/* Form a new unique key to search hash table for the code
|
||||||
|
* combines CrntCode as Prefix string with Pixel as postfix
|
||||||
|
* char.
|
||||||
|
*/
|
||||||
|
int NewCode;
|
||||||
|
unsigned long NewKey = (((uint32_t)CrntCode) << 8) + Pixel;
|
||||||
|
if ((NewCode = ExistsHashTable(HashTable, NewKey)) >= 0) {
|
||||||
|
/* This Key is already there, or the string is old one,
|
||||||
|
* so simple take new code as our CrntCode:
|
||||||
|
*/
|
||||||
|
CrntCode = NewCode;
|
||||||
|
} else {
|
||||||
|
/* Put it in hash table, output the prefix code, and
|
||||||
|
* make our CrntCode equal to Pixel.
|
||||||
|
*/
|
||||||
|
GifCompressOutput(gifBuffer, CrntCode);
|
||||||
|
CrntCode = Pixel;
|
||||||
|
|
||||||
|
/* If however the HashTable if full, we send a clear
|
||||||
|
* first and Clear the hash table.
|
||||||
|
*/
|
||||||
|
if (gifprivate.RunningCode >= LZ_MAX_CODE) {
|
||||||
|
// Time to do some clearance:
|
||||||
|
GifCompressOutput(gifBuffer, gifprivate.ClearCode);
|
||||||
|
gifprivate.RunningCode = gifprivate.EOFCode + 1;
|
||||||
|
gifprivate.RunningBits = gifprivate.BitsPerPixel + 1;
|
||||||
|
gifprivate.MaxCode1 = 1 << gifprivate.RunningBits;
|
||||||
|
ClearHashTable(HashTable);
|
||||||
|
} else {
|
||||||
|
// Put this unique key with its relative Code in hash table:
|
||||||
|
InsertHashTable(HashTable, NewKey, gifprivate.RunningCode++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preserve the current state of the compression algorithm:
|
||||||
|
gifprivate.CrntCode = CrntCode;
|
||||||
|
|
||||||
|
if (gifprivate.PixelCount == 0) {
|
||||||
|
// We are done - output last Code and flush output buffers:
|
||||||
|
GifCompressOutput(gifBuffer, CrntCode);
|
||||||
|
GifCompressOutput(gifBuffer, gifprivate.EOFCode);
|
||||||
|
GifCompressOutput(gifBuffer, FLUSH_OUTPUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool createBMP(uint8_t *frameBuffer, std::vector<uint8_t>* imageBuffer, uint16_t width, uint16_t height) {
|
||||||
|
// For BMP the line size has to be a multiple of 4 bytes.
|
||||||
|
// So padding is needed. Also the lines have to be in reverded
|
||||||
|
// order compared to plain buffer
|
||||||
|
|
||||||
|
// BMP header for black-and-white image (1 bit per pixel)
|
||||||
|
const uint8_t bmp_header[] PROGMEM = {
|
||||||
|
// BITMAPFILEHEADER (14 Bytes)
|
||||||
|
0x42, 0x4D, // bfType: 'BM' signature
|
||||||
|
0x2e, 0x3d, 0x00, 0x00, // bfSize: file size in bytes
|
||||||
|
0x00, 0x00, // bfReserved1
|
||||||
|
0x00, 0x00, // bfReserved2
|
||||||
|
0x3e, 0x00, 0x00, 0x00, // bfOffBits: offset in bytes to pixeldata
|
||||||
|
// BITMAPINFOHEADER (40 Bytes)
|
||||||
|
0x28, 0x00, 0x00, 0x00, // biSize: DIB header size
|
||||||
|
(uint8_t)LOBYTE(width), (uint8_t)HIBYTE(width), 0x00, 0x00, // biWidth
|
||||||
|
(uint8_t)LOBYTE(height), (uint8_t)HIBYTE(height), 0x00, 0x00, // biHeight
|
||||||
|
0x01, 0x00, // biPlanes: Number of color planes (1)
|
||||||
|
0x01, 0x00, // biBitCount: Color depth (1 bit per pixel)
|
||||||
|
0x00, 0x00, 0x00, 0x00, // biCompression: Compression (none)
|
||||||
|
0xf0, 0x3c, 0x00, 0x00, // biSizeImage: Image data size (calculate)
|
||||||
|
0x13, 0x0b, 0x00, 0x00, // biXPelsPerMeter: Horizontal resolution (2835 pixels/meter)
|
||||||
|
0x13, 0x0b, 0x00, 0x00, // biYPelsPerMeter: Vertical resolution (2835 pixels/meter)
|
||||||
|
0x02, 0x00, 0x00, 0x00, // biClrUsed: Colors in color palette (2)
|
||||||
|
0x00, 0x00, 0x00, 0x00, // biClrImportant: Important colors (all)
|
||||||
|
// PALETTE: COLORTRIPLES of RGBQUAD (n * 4 Bytes)
|
||||||
|
0x00, 0x00, 0x00, 0x00, // Color palette: Black
|
||||||
|
0xff, 0xff, 0xff, 0x00 // Color palette: White
|
||||||
|
};
|
||||||
|
size_t bmp_headerSize = sizeof(bmp_header);
|
||||||
|
|
||||||
|
size_t lineSize = (width / 8);
|
||||||
|
size_t paddingSize = 0;
|
||||||
|
if (lineSize % 4 != 0) {
|
||||||
|
paddingSize = 4 - lineSize % 4;
|
||||||
|
}
|
||||||
|
size_t imageSize = bmp_headerSize + (lineSize + paddingSize) * height;
|
||||||
|
|
||||||
|
imageBuffer->resize(imageSize);
|
||||||
|
memcpy(imageBuffer->data(), bmp_header, bmp_headerSize);
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
uint8_t* srcRow = frameBuffer + (y * lineSize);
|
||||||
|
uint8_t* destRow = imageBuffer->data() + bmp_headerSize + ((height - 1 - y) * (lineSize + paddingSize));
|
||||||
|
memcpy(destRow, srcRow, lineSize);
|
||||||
|
for (int j = 0; j < paddingSize; j++) {
|
||||||
|
destRow[lineSize + j] = 0x00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool createPBM(uint8_t *frameBuffer, std::vector<uint8_t>* imageBuffer, uint16_t width, uint16_t height) {
|
||||||
|
// creates binary PBM image inside imagebuffer
|
||||||
|
// returns bytesize of created image
|
||||||
|
const char pbm_header[] PROGMEM = "P4\n#Created by OBP60\n400 300\n";
|
||||||
|
size_t pbm_headerSize = sizeof(pbm_header) - 1; // We don't want trailing zero
|
||||||
|
size_t imageSize = pbm_headerSize + width / 8 * height;
|
||||||
|
imageBuffer->resize(imageSize);
|
||||||
|
memcpy(imageBuffer->data(), pbm_header, pbm_headerSize);
|
||||||
|
memcpy(imageBuffer->data() + pbm_headerSize, frameBuffer, width / 8 * height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool createGIF(uint8_t *framebuffer, std::vector<uint8_t>* gifBuffer, uint16_t width, uint16_t height) {
|
||||||
|
|
||||||
|
size_t imageSize = 0;
|
||||||
|
uint16_t bufOffset = 0; // Offset into imageBuffer for next write access
|
||||||
|
|
||||||
|
gifprivate.HashTable = InitHashTable();
|
||||||
|
if (gifprivate.HashTable == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
gifprivate.PixelCount = width * height;
|
||||||
|
|
||||||
|
createGifHeader(gifBuffer, width, height);
|
||||||
|
|
||||||
|
// Reset compress algorithm parameters.
|
||||||
|
GifSetupCompress(gifBuffer);
|
||||||
|
|
||||||
|
gifBuffer->reserve(4096); // to avoid lots of alloactions
|
||||||
|
GifPixelType line[width];
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
// convert uint8_t pixels to single pixels
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
int byteIndex = (y * width + x) / 8;
|
||||||
|
uint8_t bitIndex = 7 - ((y * width + x) % 8);
|
||||||
|
line[x] = (framebuffer[byteIndex] & (uint8_t)(1 << bitIndex)) == 0;
|
||||||
|
}
|
||||||
|
gifprivate.PixelCount -= width;
|
||||||
|
GifCompressLine(gifBuffer, line, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
gifBuffer->push_back(TERMINATOR_INTRODUCER);
|
||||||
|
free((GifHashTableType *)gifprivate.HashTable);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
70
lib/obp60task/imglib.h
Normal file
70
lib/obp60task/imglib.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
*
|
||||||
|
* imglib.h
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef _IMGLIB_H_
|
||||||
|
#define _IMGLIB_H_ 1
|
||||||
|
|
||||||
|
// extract bytes from an unsigned word
|
||||||
|
#define LOBYTE(x) ((x)&0xff)
|
||||||
|
#define HIBYTE(x) (((x) >> 8) & 0xff)
|
||||||
|
|
||||||
|
// GIF encoding constants
|
||||||
|
#define DESCRIPTOR_INTRODUCER 0x2c
|
||||||
|
#define TERMINATOR_INTRODUCER 0x3b
|
||||||
|
|
||||||
|
#define LZ_MAX_CODE 4095 // Biggest code possible in 12 bits
|
||||||
|
#define LZ_BITS 12
|
||||||
|
|
||||||
|
#define FLUSH_OUTPUT 4096 // Impossible code, to signal flush
|
||||||
|
#define FIRST_CODE 4097 // Impossible code, to signal first
|
||||||
|
#define NO_SUCH_CODE 4098 // Impossible code, to signal empty
|
||||||
|
|
||||||
|
// magfic constants and declarations for GIF LZW
|
||||||
|
|
||||||
|
#define HT_SIZE 8192 /* 12bits = 4096 or twice as big! */
|
||||||
|
#define HT_KEY_MASK 0x1FFF /* 13bits keys */
|
||||||
|
#define HT_KEY_NUM_BITS 13 /* 13bits keys */
|
||||||
|
#define HT_MAX_KEY 8191 /* 13bits - 1, maximal code possible */
|
||||||
|
#define HT_MAX_CODE 4095 /* Biggest code possible in 12 bits. */
|
||||||
|
|
||||||
|
/* The 32 bits of the long are divided into two parts for the key & code: */
|
||||||
|
/* 1. The code is 12 bits as our compression algorithm is limited to 12bits */
|
||||||
|
/* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits. */
|
||||||
|
/* The key is the upper 20 bits. The code is the lower 12. */
|
||||||
|
#define HT_GET_KEY(l) (l >> 12)
|
||||||
|
#define HT_GET_CODE(l) (l & 0x0FFF)
|
||||||
|
#define HT_PUT_KEY(l) (l << 12)
|
||||||
|
#define HT_PUT_CODE(l) (l & 0x0FFF)
|
||||||
|
|
||||||
|
typedef unsigned char GifPixelType;
|
||||||
|
typedef unsigned char GifByteType;
|
||||||
|
|
||||||
|
typedef struct GifHashTableType {
|
||||||
|
uint32_t HTable[HT_SIZE];
|
||||||
|
} GifHashTableType;
|
||||||
|
|
||||||
|
typedef struct GifFilePrivateType {
|
||||||
|
uint8_t BitsPerPixel; // Bits per pixel (Codes uses at least this + 1)
|
||||||
|
uint16_t ClearCode; // The CLEAR LZ code
|
||||||
|
uint16_t EOFCode; // The EOF LZ code
|
||||||
|
uint16_t RunningCode; // The next code algorithm can generate
|
||||||
|
uint16_t RunningBits; // The number of bits required to represent RunningCode
|
||||||
|
uint16_t MaxCode1; // 1 bigger than max. possible code, in RunningBits bits
|
||||||
|
uint16_t LastCode; // The code before the current code.
|
||||||
|
uint16_t CrntCode; // Current algorithm code
|
||||||
|
uint16_t CrntShiftState; // Number of bits in CrntShiftDWord
|
||||||
|
uint32_t CrntShiftDWord; // For bytes decomposition into codes
|
||||||
|
uint32_t PixelCount; // Number of pixels in image
|
||||||
|
GifByteType Buf[256]; // Compressed input is buffered here
|
||||||
|
GifHashTableType *HashTable;
|
||||||
|
} GifFilePrivateType;
|
||||||
|
|
||||||
|
bool createGIF(uint8_t *framebuffer, std::vector<uint8_t>* imageBuffer, uint16_t width, uint16_t height);
|
||||||
|
bool createBMP(uint8_t *framebuffer, std::vector<uint8_t>* imageBuffer, uint16_t width, uint16_t height);
|
||||||
|
bool createPBM(uint8_t *framebuffer, std::vector<uint8_t>* gifBuffer, uint16_t width, uint16_t height);
|
||||||
|
|
||||||
|
#endif /* _IMGLIB_H */
|
||||||
@@ -13,6 +13,10 @@
|
|||||||
#include "OBP60Extensions.h" // Functions lib for extension board
|
#include "OBP60Extensions.h" // Functions lib for extension board
|
||||||
#include "OBP60Keypad.h" // Functions for keypad
|
#include "OBP60Keypad.h" // Functions for keypad
|
||||||
|
|
||||||
|
#include <FS.h> // SD-Card access
|
||||||
|
#include <SD.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
// True type character sets includes
|
// True type character sets includes
|
||||||
// See OBP60ExtensionPort.cpp
|
// See OBP60ExtensionPort.cpp
|
||||||
|
|
||||||
@@ -43,16 +47,26 @@ void OBP60Init(GwApi *api){
|
|||||||
|
|
||||||
|
|
||||||
// Init hardware
|
// Init hardware
|
||||||
hardwareInit();
|
hardwareInit(api);
|
||||||
|
|
||||||
// Init power rail 5.0V
|
// Init power rail 5.0V
|
||||||
String powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode,true)->asString();
|
String powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode,true)->asString();
|
||||||
api->getLogger()->logDebug(GwLog::DEBUG,"Power Mode is: %s", powermode.c_str());
|
api->getLogger()->logDebug(GwLog::DEBUG,"Power Mode is: %s", powermode.c_str());
|
||||||
if(powermode == "Max Power" || powermode == "Only 5.0V"){
|
if(powermode == "Max Power" || powermode == "Only 5.0V"){
|
||||||
|
#ifdef HARDWARE_V21
|
||||||
setPortPin(OBP_POWER_50, true); // Power on 5.0V rail
|
setPortPin(OBP_POWER_50, true); // Power on 5.0V rail
|
||||||
|
#endif
|
||||||
|
#ifdef HARDWARE_LIGHT
|
||||||
|
setPortPin(OBP_POWER_EPD, true);// Power on ePaper display
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
#ifdef HARDWARE_V21
|
||||||
setPortPin(OBP_POWER_50, false); // Power off 5.0V rail
|
setPortPin(OBP_POWER_50, false); // Power off 5.0V rail
|
||||||
|
#endif
|
||||||
|
#ifdef HARDWARE_LIGHT
|
||||||
|
setPortPin(OBP_POWER_EPD, false);// Power off ePaper display
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings for e-paper display
|
// Settings for e-paper display
|
||||||
@@ -183,13 +197,6 @@ class PageList{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class PageStruct{
|
|
||||||
public:
|
|
||||||
Page *page=NULL;
|
|
||||||
PageData parameters;
|
|
||||||
PageDescription *description=NULL;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* this function will add all the pages we know to the pagelist
|
* this function will add all the pages we know to the pagelist
|
||||||
* each page should have defined a registerXXXPage variable of type
|
* each page should have defined a registerXXXPage variable of type
|
||||||
@@ -212,8 +219,8 @@ void registerAllPages(PageList &list){
|
|||||||
list.add(®isterPageFourValues);
|
list.add(®isterPageFourValues);
|
||||||
extern PageDescription registerPageFourValues2;
|
extern PageDescription registerPageFourValues2;
|
||||||
list.add(®isterPageFourValues2);
|
list.add(®isterPageFourValues2);
|
||||||
extern PageDescription registerPageApparentWind;
|
extern PageDescription registerPageWind;
|
||||||
list.add(®isterPageApparentWind);
|
list.add(®isterPageWind);
|
||||||
extern PageDescription registerPageWindRose;
|
extern PageDescription registerPageWindRose;
|
||||||
list.add(®isterPageWindRose);
|
list.add(®isterPageWindRose);
|
||||||
extern PageDescription registerPageWindRoseFlex;
|
extern PageDescription registerPageWindRoseFlex;
|
||||||
@@ -253,7 +260,7 @@ void underVoltageDetection(GwApi *api, CommonData &common){
|
|||||||
// Read settings
|
// Read settings
|
||||||
float vslope = uint(api->getConfig()->getConfigItem(api->getConfig()->vSlope,true)->asFloat());
|
float vslope = uint(api->getConfig()->getConfigItem(api->getConfig()->vSlope,true)->asFloat());
|
||||||
float voffset = uint(api->getConfig()->getConfigItem(api->getConfig()->vOffset,true)->asFloat());
|
float voffset = uint(api->getConfig()->getConfigItem(api->getConfig()->vOffset,true)->asFloat());
|
||||||
// Read supplay voltage
|
// Read supply voltage
|
||||||
float actVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // V = 1/20 * Vin
|
float actVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // V = 1/20 * Vin
|
||||||
actVoltage = actVoltage * vslope + voffset;
|
actVoltage = actVoltage * vslope + voffset;
|
||||||
if(actVoltage < MIN_VOLTAGE){
|
if(actVoltage < MIN_VOLTAGE){
|
||||||
@@ -353,6 +360,10 @@ void OBP60Task(GwApi *api){
|
|||||||
// Init pages
|
// Init pages
|
||||||
int numPages=1;
|
int numPages=1;
|
||||||
PageStruct pages[MAX_PAGE_NUMBER];
|
PageStruct pages[MAX_PAGE_NUMBER];
|
||||||
|
// Set start page
|
||||||
|
int pageNumber = int(api->getConfig()->getConfigItem(api->getConfig()->startPage,true)->asInt()) - 1;
|
||||||
|
int lastPage=pageNumber;
|
||||||
|
|
||||||
BoatValueList boatValues; //all the boat values for the api query
|
BoatValueList boatValues; //all the boat values for the api query
|
||||||
//commonData.distanceformat=config->getString(xxx);
|
//commonData.distanceformat=config->getString(xxx);
|
||||||
//add all necessary data to common data
|
//add all necessary data to common data
|
||||||
@@ -376,6 +387,7 @@ void OBP60Task(GwApi *api){
|
|||||||
pages[i].description=description;
|
pages[i].description=description;
|
||||||
pages[i].page=description->creator(commonData);
|
pages[i].page=description->creator(commonData);
|
||||||
pages[i].parameters.pageName=pageType;
|
pages[i].parameters.pageName=pageType;
|
||||||
|
pages[i].parameters.pageNumber = i + 1;
|
||||||
LOG_DEBUG(GwLog::DEBUG,"found page %s for number %d",pageType.c_str(),i);
|
LOG_DEBUG(GwLog::DEBUG,"found page %s for number %d",pageType.c_str(),i);
|
||||||
//fill in all the user defined parameters
|
//fill in all the user defined parameters
|
||||||
for (int uid=0;uid<description->userParam;uid++){
|
for (int uid=0;uid<description->userParam;uid++){
|
||||||
@@ -395,6 +407,33 @@ void OBP60Task(GwApi *api){
|
|||||||
pages[i].parameters.values.push_back(value);
|
pages[i].parameters.values.push_back(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Display screenshot handler for HTTP request
|
||||||
|
// http://192.168.15.1/api/user/OBP60Task/screenshot
|
||||||
|
api->registerRequestHandler("screenshot", [api, &pageNumber, pages](AsyncWebServerRequest *request) {
|
||||||
|
doImageRequest(api, &pageNumber, pages, request);
|
||||||
|
});
|
||||||
|
|
||||||
|
// SD-Card: init an check
|
||||||
|
SPI.begin(SD_SPI_CLK, SD_SPI_MISO, SD_SPI_MOSI, SD_SPI_CS);
|
||||||
|
if (SD.begin(SD_SPI_CS)) {
|
||||||
|
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::DEBUG,"SD card type %s of size %d MB detected", sdtype, cardSize);
|
||||||
|
}
|
||||||
|
|
||||||
//now we have prepared the page data
|
//now we have prepared the page data
|
||||||
//we start a separate task that will fetch our keys...
|
//we start a separate task that will fetch our keys...
|
||||||
MyData allParameters;
|
MyData allParameters;
|
||||||
@@ -431,9 +470,6 @@ void OBP60Task(GwApi *api){
|
|||||||
GwApi::BoatValue *hdop = boatValues.findValueOrCreate("HDOP"); // Load GpsHDOP
|
GwApi::BoatValue *hdop = boatValues.findValueOrCreate("HDOP"); // Load GpsHDOP
|
||||||
|
|
||||||
LOG_DEBUG(GwLog::LOG,"obp60task: start mainloop");
|
LOG_DEBUG(GwLog::LOG,"obp60task: start mainloop");
|
||||||
// Set start page
|
|
||||||
int pageNumber = int(api->getConfig()->getConfigItem(api->getConfig()->startPage,true)->asInt()) - 1;
|
|
||||||
int lastPage=pageNumber;
|
|
||||||
|
|
||||||
commonData.time = boatValues.findValueOrCreate("GPST"); // Load GpsTime
|
commonData.time = boatValues.findValueOrCreate("GPST"); // Load GpsTime
|
||||||
commonData.date = boatValues.findValueOrCreate("GPSD"); // Load GpsTime
|
commonData.date = boatValues.findValueOrCreate("GPSD"); // Load GpsTime
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
//we only compile for some boards
|
//we only compile for some boards
|
||||||
#ifdef BOARD_OBP60S3
|
#ifdef BOARD_OBP60S3
|
||||||
#define USBSerial Serial
|
#define USBSerial Serial
|
||||||
|
#ifdef HARDWARE_V21
|
||||||
// CAN NMEA2000
|
// CAN NMEA2000
|
||||||
#define ESP32_CAN_TX_PIN 46
|
#define ESP32_CAN_TX_PIN 46
|
||||||
#define ESP32_CAN_RX_PIN 3
|
#define ESP32_CAN_RX_PIN 3
|
||||||
@@ -12,6 +13,18 @@
|
|||||||
#define GWSERIAL_TX 17
|
#define GWSERIAL_TX 17
|
||||||
#define GWSERIAL_RX 8
|
#define GWSERIAL_RX 8
|
||||||
#define GWSERIAL_MODE "UNI"
|
#define GWSERIAL_MODE "UNI"
|
||||||
|
#endif
|
||||||
|
#ifdef HARDWARE_LIGHT
|
||||||
|
// CAN NMEA2000
|
||||||
|
#define ESP32_CAN_TX_PIN 15
|
||||||
|
#define ESP32_CAN_RX_PIN 16
|
||||||
|
// Bus load in 50mA steps
|
||||||
|
#define N2K_LOAD_LEVEL 2 // 5x50mA = 100mA max bus load with back light on
|
||||||
|
// RS485 NMEA0183
|
||||||
|
#define GWSERIAL_TX 9
|
||||||
|
#define GWSERIAL_RX 14
|
||||||
|
#define GWSERIAL_MODE "UNI"
|
||||||
|
#endif
|
||||||
// Allowed to set a new password for access point
|
// Allowed to set a new password for access point
|
||||||
#define FORCE_AP_PWCHANGE
|
#define FORCE_AP_PWCHANGE
|
||||||
|
|
||||||
|
|||||||
@@ -9,22 +9,25 @@ board_build.variants_dir = variants
|
|||||||
#board = obp60_s3_n8 #ESP32-S3 N8, 8MB flash, no PSRAM
|
#board = obp60_s3_n8 #ESP32-S3 N8, 8MB flash, no PSRAM
|
||||||
#board = obp60_s3_n16 #ESP32-S3 N16,16MB flash, no PSRAM, zero series
|
#board = obp60_s3_n16 #ESP32-S3 N16,16MB flash, no PSRAM, zero series
|
||||||
#board = obp60_s3_n8r8 #ESP32-S3 N8R8, 8MB flash, 8MB PSRAM
|
#board = obp60_s3_n8r8 #ESP32-S3 N8R8, 8MB flash, 8MB PSRAM
|
||||||
board = obp60_s3_n16r8 #ESP32-S3 N16R8, 16MB flash, 8MB PSRAM, production series
|
#board = obp60_s3_n16r8 #ESP32-S3 N16R8, 16MB flash, 8MB PSRAM, production series
|
||||||
#board_build.partitions = default_8MB.csv #ESP32-S3 N8, 8MB flash
|
board = obp60_s3_light_n8r8 #ESP32-S3 N8R8, 8MB flash, 8MB PSRAM, OBP60 clone
|
||||||
board_build.partitions = default_16MB.csv #ESP32-S3 N16, 16MB flash
|
board_build.partitions = default_8MB.csv #ESP32-S3 N8, 8MB flash
|
||||||
|
#board_build.partitions = default_16MB.csv #ESP32-S3 N16, 16MB flash
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${basedeps.lib_deps}
|
${basedeps.lib_deps}
|
||||||
Wire
|
Wire
|
||||||
SPI
|
SPI
|
||||||
|
SD
|
||||||
esphome/AsyncTCP-esphome@2.0.1
|
esphome/AsyncTCP-esphome@2.0.1
|
||||||
robtillaart/PCF8574@0.3.9
|
robtillaart/PCF8574@0.3.9
|
||||||
adafruit/Adafruit Unified Sensor @ 1.1.13
|
adafruit/Adafruit Unified Sensor @ 1.1.13
|
||||||
blemasle/MCP23017@2.0.0
|
blemasle/MCP23017@2.0.0
|
||||||
adafruit/Adafruit BusIO@1.5.0
|
adafruit/Adafruit BusIO@1.5.0
|
||||||
adafruit/Adafruit GFX Library@1.11.9
|
adafruit/Adafruit GFX Library@1.11.9
|
||||||
zinggjm/GxEPD2@1.5.8
|
#zinggjm/GxEPD2@1.5.8
|
||||||
#https://github.com/ZinggJM/GxEPD2
|
#https://github.com/ZinggJM/GxEPD2
|
||||||
|
https://github.com/thooge/GxEPD2
|
||||||
sstaub/Ticker@4.4.0
|
sstaub/Ticker@4.4.0
|
||||||
adafruit/Adafruit BMP280 Library@2.6.2
|
adafruit/Adafruit BMP280 Library@2.6.2
|
||||||
adafruit/Adafruit BME280 Library@2.2.2
|
adafruit/Adafruit BME280 Library@2.2.2
|
||||||
@@ -34,23 +37,26 @@ lib_deps =
|
|||||||
paulstoffregen/OneWire@2.3.8
|
paulstoffregen/OneWire@2.3.8
|
||||||
milesburton/DallasTemperature@3.11.0
|
milesburton/DallasTemperature@3.11.0
|
||||||
signetica/SunRise@2.0.2
|
signetica/SunRise@2.0.2
|
||||||
|
adafruit/Adafruit FRAM I2C@^2.0.3
|
||||||
build_flags=
|
build_flags=
|
||||||
#https://thingpulse.com/usb-settings-for-logging-with-the-esp32-s3-in-platformio/?srsltid=AfmBOopGskbkr4GoeVkNlFaZXe_zXkLceKF6Rn-tmoXABCeAR2vWsdHL
|
#https://thingpulse.com/usb-settings-for-logging-with-the-esp32-s3-in-platformio/?srsltid=AfmBOopGskbkr4GoeVkNlFaZXe_zXkLceKF6Rn-tmoXABCeAR2vWsdHL
|
||||||
# -D ARDUINO_USB_MODE=1 #0=OTG (to implement other external devices), 1=CDC (is a serial device)
|
# -D ARDUINO_USB_MODE=1 #0=OTG (to implement other external devices), 1=CDC (is a serial device)
|
||||||
# -D ARDUINO_USB_CDC_ON_BOOT=1 #0=JTAG, 1=CDC (serial device)
|
-D ARDUINO_USB_CDC_ON_BOOT=0 #0=JTAG, 1=CDC (serial device)
|
||||||
# -D CORE_DEBUG_LEVEL=1 #Debug level for CPU core via CDC (seral device)
|
# -D CORE_DEBUG_LEVEL=1 #Debug level for CPU core via CDC (seral device)
|
||||||
# -D TIME=$UNIX_TIME #Set PC time for RTC (only settable via VSC)
|
# -D TIME=$UNIX_TIME #Set PC time for RTC (only settable via VSC)
|
||||||
-D DISABLE_DIAGNOSTIC_OUTPUT #Disable diagnostic output for GxEPD2 lib
|
-D DISABLE_DIAGNOSTIC_OUTPUT #Disable diagnostic output for GxEPD2 lib
|
||||||
-D BOARD_OBP60S3 #Board OBP60 V2.1 with ESP32S3
|
-D BOARD_OBP60S3 #Board OBP60 V2.1 with ESP32S3
|
||||||
# -D HARDWARE_V20 #Hardware revision V2.0
|
# -D HARDWARE_V20 #OBP60 hardware revision V2.0
|
||||||
-D HARDWARE_V21 #Hardware revision V2.1
|
# -D HARDWARE_V21 #OBP60 hardware revision V2.1
|
||||||
|
-D HARDWARE_LIGHT #OBP60 hardware clone
|
||||||
# -D DISPLAY_GDEW042T2 #old E-Ink display from Waveshare, R10 0.47 ohm
|
# -D DISPLAY_GDEW042T2 #old E-Ink display from Waveshare, R10 0.47 ohm
|
||||||
-D DISPLAY_GDEY042T81 #new E-Ink display from Waveshare, R10 2.2 ohm
|
-D DISPLAY_GDEY042T81 #new E-Ink display from Waveshare, R10 2.2 ohm
|
||||||
# -D DISPLAY_GYE042A87 #alternativ E-Ink display from Genyo Optical, R10 2.2 ohm
|
# -D DISPLAY_GYE042A87 #alternativ E-Ink display from Genyo Optical, R10 2.2 ohm
|
||||||
# -D DISPLAY_SE0420NQ04 #alternativ E-Ink display from SID Technology, R10 2.2 ohm
|
# -D DISPLAY_SE0420NQ04 #alternativ E-Ink display from SID Technology, R10 2.2 ohm
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
#CONFIG_ESP_TASK_WDT_TIMEOUT_S = 10 #Task Watchdog timeout period (seconds) [1...60] 5 default
|
#CONFIG_ESP_TASK_WDT_TIMEOUT_S = 10 #Task Watchdog timeout period (seconds) [1...60] 5 default
|
||||||
upload_port = /dev/ttyACM0
|
#upload_port = /dev/ttyACM0 #OBP60 original
|
||||||
|
upload_port = /dev/ttyUSB0 #OBP60 clone
|
||||||
upload_protocol = esptool #firmware upload via USB OTG seriell, by first upload need to set the ESP32-S3 in the upload mode with shortcut GND to Pin27
|
upload_protocol = esptool #firmware upload via USB OTG seriell, by first upload need to set the ESP32-S3 in the upload mode with shortcut GND to Pin27
|
||||||
upload_speed = 230400
|
upload_speed = 230400
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
|
|||||||
61
lib/obp60task/platformio.ini.light
Normal file
61
lib/obp60task/platformio.ini.light
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
[platformio]
|
||||||
|
#if you want a pio run to only build
|
||||||
|
#your special environments you can set this here
|
||||||
|
#by uncommenting the next line
|
||||||
|
default_envs = obp60_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
|
||||||
|
#board = obp60_s3_n8r8 #ESP32-S3 N8R8, 8MB flash, 8MB PSRAM
|
||||||
|
#board = obp60_s3_n16r8 #ESP32-S3 N16R8, 16MB flash, 8MB PSRAM, production series
|
||||||
|
board = obp60_s3_light_n8r8 #ESP32-S3 N8R8, 8MB flash, 8MB PSRAM, OBP60 clone
|
||||||
|
board_build.partitions = default_8MB.csv #ESP32-S3 N8, 8MB flash
|
||||||
|
#board_build.partitions = default_16MB.csv #ESP32-S3 N16, 16MB flash
|
||||||
|
framework = arduino
|
||||||
|
lib_deps =
|
||||||
|
${basedeps.lib_deps}
|
||||||
|
Wire
|
||||||
|
SPI
|
||||||
|
esphome/AsyncTCP-esphome@2.0.1
|
||||||
|
robtillaart/PCF8574@0.3.9
|
||||||
|
adafruit/Adafruit Unified Sensor @ 1.1.13
|
||||||
|
blemasle/MCP23017@2.0.0
|
||||||
|
adafruit/Adafruit BusIO@1.5.0
|
||||||
|
adafruit/Adafruit GFX Library@1.11.9
|
||||||
|
#zinggjm/GxEPD2@1.5.8
|
||||||
|
#https://github.com/ZinggJM/GxEPD2
|
||||||
|
https://github.com/thooge/GxEPD2
|
||||||
|
sstaub/Ticker@4.4.0
|
||||||
|
adafruit/Adafruit BMP280 Library@2.6.2
|
||||||
|
adafruit/Adafruit BME280 Library@2.2.2
|
||||||
|
adafruit/Adafruit BMP085 Library@1.2.1
|
||||||
|
enjoyneering/HTU21D@1.2.1
|
||||||
|
robtillaart/INA226@0.2.0
|
||||||
|
paulstoffregen/OneWire@2.3.8
|
||||||
|
milesburton/DallasTemperature@3.11.0
|
||||||
|
signetica/SunRise@2.0.2
|
||||||
|
adafruit/Adafruit FRAM I2C@^2.0.3
|
||||||
|
build_flags=
|
||||||
|
#https://thingpulse.com/usb-settings-for-logging-with-the-esp32-s3-in-platformio/?srsltid=AfmBOopGskbkr4GoeVkNlFaZXe_zXkLceKF6Rn-tmoXABCeAR2vWsdHL
|
||||||
|
# -D ARDUINO_USB_MODE=1 #0=OTG (to implement other external devices), 1=CDC (is a serial device)
|
||||||
|
# -D ARDUINO_USB_CDC_ON_BOOT=1 #0=JTAG, 1=CDC (serial device)
|
||||||
|
# -D CORE_DEBUG_LEVEL=1 #Debug level for CPU core via CDC (seral device)
|
||||||
|
# -D TIME=$UNIX_TIME #Set PC time for RTC (only settable via VSC)
|
||||||
|
-D DISABLE_DIAGNOSTIC_OUTPUT #Disable diagnostic output for GxEPD2 lib
|
||||||
|
-D BOARD_OBP60S3 #Board OBP60 V2.1 with ESP32S3
|
||||||
|
# -D HARDWARE_V20 #OBP60 hardware revision V2.0
|
||||||
|
# -D HARDWARE_V21 #OBP60 hardware revision V2.1
|
||||||
|
-D HARDWARE_LIGHT #OBP60 hardware clone
|
||||||
|
# -D DISPLAY_GDEW042T2 #old E-Ink display from Waveshare, R10 0.47 ohm
|
||||||
|
-D DISPLAY_GDEY042T81 #new E-Ink display from Waveshare, R10 2.2 ohm
|
||||||
|
# -D DISPLAY_GYE042A87 #alternativ E-Ink display from Genyo Optical, R10 2.2 ohm
|
||||||
|
# -D DISPLAY_SE0420NQ04 #alternativ E-Ink display from SID Technology, R10 2.2 ohm
|
||||||
|
${env.build_flags}
|
||||||
|
#CONFIG_ESP_TASK_WDT_TIMEOUT_S = 10 #Task Watchdog timeout period (seconds) [1...60] 5 default
|
||||||
|
#upload_port = /dev/ttyACM0 #OBP60 original
|
||||||
|
upload_port = /dev/ttyUSB0 #OBP60 clone
|
||||||
|
upload_protocol = esptool #firmware upload via USB OTG seriell, by first upload need to set the ESP32-S3 in the upload mode with shortcut GND to Pin27
|
||||||
|
upload_speed = 230400
|
||||||
|
monitor_speed = 115200
|
||||||
61
lib/obp60task/platformio.ini.orig
Normal file
61
lib/obp60task/platformio.ini.orig
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
[platformio]
|
||||||
|
#if you want a pio run to only build
|
||||||
|
#your special environments you can set this here
|
||||||
|
#by uncommenting the next line
|
||||||
|
default_envs = obp60_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
|
||||||
|
#board = obp60_s3_n8r8 #ESP32-S3 N8R8, 8MB flash, 8MB PSRAM
|
||||||
|
board = obp60_s3_n16r8 #ESP32-S3 N16R8, 16MB flash, 8MB PSRAM, production series
|
||||||
|
#board = obp60_s3_light_n8r8 #ESP32-S3 N8R8, 8MB flash, 8MB PSRAM, OBP60 clone
|
||||||
|
#board_build.partitions = default_8MB.csv #ESP32-S3 N8, 8MB flash
|
||||||
|
board_build.partitions = default_16MB.csv #ESP32-S3 N16, 16MB flash
|
||||||
|
framework = arduino
|
||||||
|
lib_deps =
|
||||||
|
${basedeps.lib_deps}
|
||||||
|
Wire
|
||||||
|
SPI
|
||||||
|
esphome/AsyncTCP-esphome@2.0.1
|
||||||
|
robtillaart/PCF8574@0.3.9
|
||||||
|
adafruit/Adafruit Unified Sensor @ 1.1.13
|
||||||
|
blemasle/MCP23017@2.0.0
|
||||||
|
adafruit/Adafruit BusIO@1.5.0
|
||||||
|
adafruit/Adafruit GFX Library@1.11.9
|
||||||
|
#zinggjm/GxEPD2@1.5.8
|
||||||
|
#https://github.com/ZinggJM/GxEPD2
|
||||||
|
https://github.com/thooge/GxEPD2
|
||||||
|
sstaub/Ticker@4.4.0
|
||||||
|
adafruit/Adafruit BMP280 Library@2.6.2
|
||||||
|
adafruit/Adafruit BME280 Library@2.2.2
|
||||||
|
adafruit/Adafruit BMP085 Library@1.2.1
|
||||||
|
enjoyneering/HTU21D@1.2.1
|
||||||
|
robtillaart/INA226@0.2.0
|
||||||
|
paulstoffregen/OneWire@2.3.8
|
||||||
|
milesburton/DallasTemperature@3.11.0
|
||||||
|
signetica/SunRise@2.0.2
|
||||||
|
adafruit/Adafruit FRAM I2C@^2.0.3
|
||||||
|
build_flags=
|
||||||
|
#https://thingpulse.com/usb-settings-for-logging-with-the-esp32-s3-in-platformio/?srsltid=AfmBOopGskbkr4GoeVkNlFaZXe_zXkLceKF6Rn-tmoXABCeAR2vWsdHL
|
||||||
|
# -D ARDUINO_USB_MODE=1 #0=OTG (to implement other external devices), 1=CDC (is a serial device)
|
||||||
|
# -D ARDUINO_USB_CDC_ON_BOOT=1 #0=JTAG, 1=CDC (serial device)
|
||||||
|
# -D CORE_DEBUG_LEVEL=1 #Debug level for CPU core via CDC (seral device)
|
||||||
|
# -D TIME=$UNIX_TIME #Set PC time for RTC (only settable via VSC)
|
||||||
|
-D DISABLE_DIAGNOSTIC_OUTPUT #Disable diagnostic output for GxEPD2 lib
|
||||||
|
-D BOARD_OBP60S3 #Board OBP60 V2.1 with ESP32S3
|
||||||
|
# -D HARDWARE_V20 #OBP60 hardware revision V2.0
|
||||||
|
-D HARDWARE_V21 #OBP60 hardware revision V2.1
|
||||||
|
# -D HARDWARE_LIGHT #OBP60 hardware clone
|
||||||
|
# -D DISPLAY_GDEW042T2 #old E-Ink display from Waveshare, R10 0.47 ohm
|
||||||
|
-D DISPLAY_GDEY042T81 #new E-Ink display from Waveshare, R10 2.2 ohm
|
||||||
|
# -D DISPLAY_GYE042A87 #alternativ E-Ink display from Genyo Optical, R10 2.2 ohm
|
||||||
|
# -D DISPLAY_SE0420NQ04 #alternativ E-Ink display from SID Technology, R10 2.2 ohm
|
||||||
|
${env.build_flags}
|
||||||
|
#CONFIG_ESP_TASK_WDT_TIMEOUT_S = 10 #Task Watchdog timeout period (seconds) [1...60] 5 default
|
||||||
|
upload_port = /dev/ttyACM0 #OBP60 original
|
||||||
|
#upload_port = /dev/ttyUSB0 #OBP60 clone
|
||||||
|
upload_protocol = esptool #firmware upload via USB OTG seriell, by first upload need to set the ESP32-S3 in the upload mode with shortcut GND to Pin27
|
||||||
|
upload_speed = 230400
|
||||||
|
monitor_speed = 115200
|
||||||
74
variants/obp60s3_light/pins_arduino.h
Normal file
74
variants/obp60s3_light/pins_arduino.h
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#ifndef Pins_Arduino_h
|
||||||
|
#define Pins_Arduino_h
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
|
||||||
|
#define USB_VID 0x303a
|
||||||
|
#define USB_PID 0x1001
|
||||||
|
|
||||||
|
/*
|
||||||
|
#define EXTERNAL_NUM_INTERRUPTS 46
|
||||||
|
#define NUM_DIGITAL_PINS 48
|
||||||
|
#define NUM_ANALOG_INPUTS 20
|
||||||
|
|
||||||
|
// Multi Function Display OBP60 V2.0
|
||||||
|
static const uint8_t LED_BUILTIN = SOC_GPIO_PIN_COUNT+48;
|
||||||
|
#define BUILTIN_LED LED_BUILTIN // backward compatibility
|
||||||
|
#define LED_BUILTIN LED_BUILTIN
|
||||||
|
#define RGB_BUILTIN LED_BUILTIN
|
||||||
|
#define RGB_BRIGHTNESS 64
|
||||||
|
|
||||||
|
#define analogInputToDigitalPin(p) (((p)<20)?(analogChannelToDigitalPin(p)):-1)
|
||||||
|
#define digitalPinToInterrupt(p) (((p)<48)?(p):-1)
|
||||||
|
#define digitalPinHasPWM(p) (p < 46)
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const uint8_t TX = 43;
|
||||||
|
static const uint8_t RX = 44;
|
||||||
|
|
||||||
|
static const uint8_t SDA = 21;
|
||||||
|
static const uint8_t SCL = 38;
|
||||||
|
|
||||||
|
static const uint8_t SS = 45;
|
||||||
|
static const uint8_t MOSI = 11;
|
||||||
|
static const uint8_t MISO = 13;
|
||||||
|
static const uint8_t SCK = 12;
|
||||||
|
|
||||||
|
static const uint8_t A0 = 1;
|
||||||
|
static const uint8_t A1 = 2;
|
||||||
|
static const uint8_t A2 = 3;
|
||||||
|
static const uint8_t A3 = 4;
|
||||||
|
static const uint8_t A4 = 5;
|
||||||
|
static const uint8_t A5 = 6;
|
||||||
|
static const uint8_t A6 = 7;
|
||||||
|
static const uint8_t A7 = 8;
|
||||||
|
static const uint8_t A8 = 9;
|
||||||
|
static const uint8_t A9 = 10;
|
||||||
|
static const uint8_t A10 = 11;
|
||||||
|
static const uint8_t A11 = 12;
|
||||||
|
static const uint8_t A12 = 13;
|
||||||
|
static const uint8_t A13 = 14;
|
||||||
|
static const uint8_t A14 = 15;
|
||||||
|
static const uint8_t A15 = 16;
|
||||||
|
static const uint8_t A16 = 17;
|
||||||
|
static const uint8_t A17 = 18;
|
||||||
|
static const uint8_t A18 = 19;
|
||||||
|
static const uint8_t A19 = 20;
|
||||||
|
|
||||||
|
static const uint8_t T1 = 1;
|
||||||
|
static const uint8_t T2 = 2;
|
||||||
|
static const uint8_t T3 = 3;
|
||||||
|
static const uint8_t T4 = 4;
|
||||||
|
static const uint8_t T5 = 5;
|
||||||
|
static const uint8_t T6 = 6;
|
||||||
|
static const uint8_t T7 = 7;
|
||||||
|
static const uint8_t T8 = 8;
|
||||||
|
static const uint8_t T9 = 9;
|
||||||
|
static const uint8_t T10 = 10;
|
||||||
|
static const uint8_t T11 = 11;
|
||||||
|
static const uint8_t T12 = 12;
|
||||||
|
static const uint8_t T13 = 13;
|
||||||
|
static const uint8_t T14 = 14;
|
||||||
|
|
||||||
|
#endif /* Pins_Arduino_h */
|
||||||
Reference in New Issue
Block a user