1
0
mirror of https://github.com/thooge/esp32-nmea2000-obp60.git synced 2025-12-23 18:53:06 +01:00

1 Commits

Author SHA1 Message Date
c5e346f4eb First design experiments for barograph 2024-12-21 10:25:53 +01:00
29 changed files with 1155 additions and 3989 deletions

View File

@@ -1,56 +0,0 @@
{
"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=0",
"-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"
}

View File

@@ -4,6 +4,7 @@
#include "GwApi.h"
#include "OBP60Hardware.h"
class Color{
public:
uint8_t r;
@@ -91,4 +92,5 @@ class LedTaskData{
//task function
void createSpiLedTask(LedTaskData *param);
#endif

View File

@@ -1,6 +1,9 @@
#ifdef BOARD_OBP60S3
#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 <Wire.h> // I2C
#include <RTClib.h> // Driver for DS1388 RTC
@@ -8,7 +11,6 @@
#include "Pagedata.h"
#include "OBP60Hardware.h"
#include "OBP60Extensions.h"
#include "imglib.h"
// Character sets
#include "Ubuntu_Bold8pt7b.h"
@@ -58,10 +60,6 @@ GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay(){r
// Horter I2C moduls
PCF8574 pcf8574_Out(PCF8574_I2C_ADDR1); // First digital output modul PCF8574 from Horter
// FRAM
Adafruit_FRAM_I2C fram;
bool hasFRAM = false;
// Global vars
bool blinkingLED = false; // Enable / disable blinking flash LED
bool statusLED = false; // Actual status of flash LED on/off
@@ -71,7 +69,7 @@ int uvDuration = 0; // Under voltage duration in n x 100ms
LedTaskData *ledTaskData=nullptr;
void hardwareInit(GwApi *api)
void hardwareInit()
{
Wire.begin();
// Init PCF8574 digital outputs
@@ -79,27 +77,12 @@ void hardwareInit(GwApi *api)
if(pcf8574_Out.begin()){ // Initialize PCF8574
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){
@@ -112,11 +95,6 @@ void togglePortPin(uint pin){
digitalWrite(pin, !digitalRead(pin));
}
void startLedTask(GwApi *api){
ledTaskData=new LedTaskData(api);
createSpiLedTask(ledTaskData);
}
// Valid colors see hue
Color colorMapping(const String &colorString){
Color color = COLOR_RED;
@@ -201,30 +179,6 @@ String xdrDelete(String input){
return input;
}
Point rotatePoint(const Point& origin, const Point& p, double angle) {
// rotate poind around origin by degrees
Point rotated;
double phi = angle * M_PI / 180.0;
double dx = p.x - origin.x;
double dy = p.y - origin.y;
rotated.x = origin.x + cos(phi) * dx - sin(phi) * dy;
rotated.y = origin.y + sin(phi) * dx + cos(phi) * dy;
return rotated;
}
std::vector<Point> rotatePoints(const Point& origin, const std::vector<Point>& pts, double angle) {
std::vector<Point> rotatedPoints;
for (const auto& p : pts) {
rotatedPoints.push_back(rotatePoint(origin, p, angle));
}
return rotatedPoints;
}
void fillPoly4(const std::vector<Point>& p4, uint16_t color) {
getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[1].x, p4[1].y, p4[2].x, p4[2].y, color);
getdisplay().fillTriangle(p4[0].x, p4[0].y, p4[2].x, p4[2].y, p4[3].x, p4[3].y, color);
}
// Draw centered text
void drawTextCenter(int16_t cx, int16_t cy, String text) {
int16_t x1, y1;
@@ -468,45 +422,4 @@ void generatorGraphic(uint x, uint y, int pcolor, int bcolor){
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

View File

@@ -3,24 +3,11 @@
#include <Arduino.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 <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)
extern const GFXfont Ubuntu_Bold8pt7b;
@@ -52,15 +39,7 @@ GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> & getdisplay();
GxEPD2_BW<GxEPD2_420_SE0420NQ04, GxEPD2_420_SE0420NQ04::HEIGHT> & getdisplay();
#endif
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 hardwareInit();
void setPortPin(uint pin, bool value); // Set port pin for extension port
@@ -94,13 +73,4 @@ 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 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

View File

@@ -1,7 +1,6 @@
// General hardware definitions
// CAN and RS485 bus pin definitions see obp60task.h
#ifdef HARDWARE_V21
// Direction pin for RS485 NMEA0183
#define OBP_DIRECTION_PIN 18
// I2C
@@ -31,8 +30,6 @@
#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 39
#define OBP_SPI_DC 40
@@ -42,6 +39,11 @@
#define OBP_SPI_DIN 48
#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 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)
#define OBP_GPS_RX 2
@@ -55,7 +57,7 @@
#define TONE3 3500 // 3500Hz
#define TONE4 4000 // 4000Hz
// Analog Input
#define OBP_ANALOG0 4 // Analog input for voltage power supply
#define OBP_ANALOG0 4 // Analog input for voltage power supplay
#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)
// Touch buttons
@@ -70,92 +72,9 @@
#define NUM_FLASH_LED 1 // Number of flash LED
#define OBP_FLASH_LED 7 // GPIO port
// Backlight LEDs (6x WS2812B)
#define NUM_BACKLIGHT_LED 6 // Number of Backlight LEDs
#define NUM_BACKLIGHT_LED 6 // Numebr of Backlight LEDs
#define OBP_BACKLIGHT_LED 15 // GPIO port
// 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

View File

@@ -14,223 +14,167 @@ 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 keycodeold = 0; // Old keycode
int keycodeold2 = 0; // Old keycode for short pressed key
int keystatus = 0; // Status of key [0...11]
bool keyoff = false; // Disable all keys
int keydelay = 250; // Delay after key pressed in [ms]
bool keylock = false; // Key lock after pressed key is valid (repeat protection by conginous pressing)
long starttime = 0; // Start time point for pressed key
#ifdef HARDWARE_V21
// Keypad functions for original OBP60 hardware
int readKeypad(uint thSensitivity) {
// Touch sensor values
// 35000 - Not touched
// 50000 - Light toched with fingertip
// 70000 - Touched
// 170000 - Strong touched
uint32_t touchthreshold = (thSensitivity * -1200) + 170000; // thSensitivity 0...100%
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;
int readKeypad(uint thSensitivity) {
// Touch sensor values
// 35000 - Not touched
// 50000 - Light toched with fingertip
// 70000 - Touched
// 170000 - Strong touched
uint32_t touchthreshold = (thSensitivity * -1200) + 170000; // thSensitivity 0...100%
// Read key code
if(touchRead(14) > touchthreshold){ // Touch pad 1
keypad[1] = 1;
}
else{
keypad[1] = 0;
}
if(touchRead(13) > touchthreshold){ // Touch pad 2
keypad[2] = 1;
}
else{
keypad[2] = 0;
}
if(touchRead(12) > touchthreshold){ // Touch pad 3
keypad[3] = 1;
}
else{
keypad[3] = 0;
}
if(touchRead(11) > touchthreshold){ // Touch pad 4
keypad[4] = 1;
}
else{
keypad[4] = 0;
}
if(touchRead(10) > touchthreshold){ // Touch pad 5
keypad[5] = 1;
}
else{
keypad[5] = 0;
}
if(touchRead(9) > touchthreshold){ // Touch pad 6
keypad[6] = 1;
}
else{
keypad[6] = 0;
}
// Nothing touched
if(keypad[1] == 0 && keypad[2] == 0 && keypad[3] == 0 && keypad[4] == 0 && keypad[5] == 0 && keypad[6] == 0){
keypad[0] = 1;
}
else{
keypad[0] = 0;
}
int 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;
for (int i = 0; i < 9; i++) {
if(i > 0){
// Convert keypad to keycode
if(keypad[i] == 1){
key = 1;
}
else{
key = 0;
}
keycode += key * i;
}
}
// Read key code
if(touchRead(14) > touchthreshold){ // Touch pad 1
keypad[1] = 1;
}
else{
keypad[1] = 0;
}
if(touchRead(13) > touchthreshold){ // Touch pad 2
keypad[2] = 1;
}
else{
keypad[2] = 0;
}
if(touchRead(12) > touchthreshold){ // Touch pad 3
keypad[3] = 1;
}
else{
keypad[3] = 0;
}
if(touchRead(11) > touchthreshold){ // Touch pad 4
keypad[4] = 1;
}
else{
keypad[4] = 0;
}
if(touchRead(10) > touchthreshold){ // Touch pad 5
keypad[5] = 1;
}
else{
keypad[5] = 0;
}
if(touchRead(9) > touchthreshold){ // Touch pad 6
keypad[6] = 1;
}
else{
keypad[6] = 0;
}
// Nothing touched
if(keypad[1] == 0 && keypad[2] == 0 && keypad[3] == 0 && keypad[4] == 0 && keypad[5] == 0 && keypad[6] == 0){
keypad[0] = 1;
}
else{
keypad[0] = 0;
}
// Detect short keynumber
if (keycode > 0 ){
if(keylock == false){
starttime = millis();
keylock = true;
for (int i = 0; i < 9; i++) {
if(i > 0){
// Convert keypad to keycode
if(keypad[i] == 1){
key = 1;
}
else{
key = 0;
}
keycode += key * i;
}
if (keycode != keycodeold){
keylock = false;
}
// Detect short keynumber
if (keycode > 0 ){
if(keylock == false){
starttime = millis();
keylock = true;
}
if (keycode != keycodeold){
keylock = false;
}
// Detect a very short keynumber (10ms)
if (millis() > starttime + 10 && keycode == keycodeold && keylock == true) {
// Process only valid keys
if(keycode == 1 || keycode == 6){
keycode2 = keycode;
}
// Detect a very short keynumber (10ms)
if (millis() > starttime + 10 && keycode == keycodeold && keylock == true) {
// Process only valid keys
if(keycode == 1 || keycode == 6){
keycode2 = keycode;
}
// Clear by unvalid keys
else{
keycode2 = 0;
keycodeold2 = 0;
}
}
// Timeout for very short pressed key
if(millis() > starttime + 200){
keycode2 = 0;
}
// Detect a short keynumber (200ms)
if (keyoff == false && millis() > starttime + 200 && keycode == keycodeold && keylock == true) {
keystatus = keycode;
keycode = 0;
keycodeold = 0;
// Clear by unvalid keys
else{
keycode2 = 0;
keycodeold2 = 0;
buzzer(TONE4, 100);
keylock = false;
delay(keydelay);
}
}
// Key lock with key 1 and 6 or 6 and 1 in fast series
if((keycode2 == 1 && keycodeold2 == 6) || (keycode2 == 6 && keycodeold2 == 1)) {
// Timeout for very short pressed key
if(millis() > starttime + 200){
keycode2 = 0;
}
// Detect a short keynumber (200ms)
if (keyoff == false && millis() > starttime + 200 && keycode == keycodeold && keylock == true) {
keystatus = keycode;
keycode = 0;
keycodeold = 0;
keycode2 = 0;
keycodeold2 = 0;
buzzer(TONE4, 1000);
buzzer(TONE4, 100);
keylock = false;
delay(keydelay);
keyoff = !keyoff;
keystatus = 11;
}
// Detect swipe right
if (keyoff == false && keycode > 0 && keycodeold > 0 && keycode > keycodeold && !((keycode == 1 && keycodeold == 6) || (keycode == 6 && keycodeold == 1))){
//if (keycode > 0 && keycodeold > 0 && keycode > keycodeold){
keycode = 0;
keycodeold = 0;
keycode2 = 0;
keycodeold2 = 0;
keystatus = 9;
buzzer(TONE3, 150);
buzzer(TONE4, 150);
}
// Detect swipe left
if (keyoff == false && keycode > 0 && keycodeold > 0 && keycode < keycodeold && !((keycode == 1 && keycodeold == 6) || (keycode == 6 && keycodeold == 1))){
//if (keycode > 0 && keycodeold > 0 && keycode < keycodeold){
keycode = 0;
keycodeold = 0;
keycode2 = 0;
keycodeold2 = 0;
keystatus = 10;
buzzer(TONE4, 150);
buzzer(TONE3, 150);
}
// Reset keylock after release
if (keycode == 0){
keylock = false;
}
// Copy keycode
keycodeold = keycode;
keycodeold2 = keycode2;
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;
// Key lock with key 1 and 6 or 6 and 1 in fast series
if((keycode2 == 1 && keycodeold2 == 6) || (keycode2 == 6 && keycodeold2 == 1)) {
keycode = 0;
keycodeold = 0;
keycode2 = 0;
keycodeold2 = 0;
buzzer(TONE4, 1000);
keylock = false;
delay(keydelay);
keyoff = !keyoff;
keystatus = 11;
}
#endif
// Detect swipe right
if (keyoff == false && keycode > 0 && keycodeold > 0 && keycode > keycodeold && !((keycode == 1 && keycodeold == 6) || (keycode == 6 && keycodeold == 1))){
//if (keycode > 0 && keycodeold > 0 && keycode > keycodeold){
keycode = 0;
keycodeold = 0;
keycode2 = 0;
keycodeold2 = 0;
keystatus = 9;
buzzer(TONE3, 150);
buzzer(TONE4, 150);
}
// Detect swipe left
if (keyoff == false && keycode > 0 && keycodeold > 0 && keycode < keycodeold && !((keycode == 1 && keycodeold == 6) || (keycode == 6 && keycodeold == 1))){
//if (keycode > 0 && keycodeold > 0 && keycode < keycodeold){
keycode = 0;
keycodeold = 0;
keycode2 = 0;
keycodeold2 = 0;
keystatus = 10;
buzzer(TONE4, 150);
buzzer(TONE3, 150);
}
// Reset keylock after release
if (keycode == 0){
keylock = false;
}
// Copy keycode
keycodeold = keycode;
keycodeold2 = keycode2;
return keystatus;
}
#endif

View File

@@ -456,7 +456,7 @@ void sensorTask(void *param){
}
}
// Send supply voltage value all 1s
// Send supplay voltage value all 1s
if(millis() > starttime5 + 1000 && String(powsensor1) == "off"){
starttime5 = millis();
sensors.batteryVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // Vin = 1/20

View File

@@ -0,0 +1,192 @@
#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

View File

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

View File

@@ -22,6 +22,43 @@ 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_height 16
static unsigned char fuel_bits[] = {
@@ -59,13 +96,12 @@ static unsigned char gasoline_bits[] = {
class PageFluid : public Page{
bool keylock = false; // Keylock
bool holdvalues = false;
int fluidtype;
public:
PageFluid(CommonData &common){
common.logger->logDebug(GwLog::LOG,"Instantiate PageFluid");
holdvalues = common.config->getBool(common.config->holdvalues);
common.logger->logDebug(GwLog::LOG,"Show PageFluid");
fluidtype = common.config->getInt("page" + String(common.data.actpage) + "fluid", 0);
}
virtual int handleKey(int key){
@@ -76,18 +112,10 @@ class PageFluid : public Page{
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){
GwConfigHandler *config = commonData.config;
GwLog *logger=commonData.logger;
// Old values for hold function
static double value1old;
// Get config data
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
@@ -98,14 +126,13 @@ class PageFluid : public Page{
setFlashLED(false);
}
// Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageFluid");
GwApi::BoatValue *bvalue1 = pageData.values[0];
String name1 = bvalue1->getName();
if (holdvalues and bvalue1->valid) {
value1old = bvalue1->value;
}
// Logging boat values
LOG_DEBUG(GwLog::LOG,"Drawing at PageFluid: value=%f", bvalue1->value);
double value1 = bvalue1->value;
bool valid1 = bvalue1->valid;
// Draw page
//***********************************************************
@@ -144,7 +171,7 @@ class PageFluid : public Page{
} else {
strcpy(buffer, "---");
}
drawTextCenter(c.x, c.y + r - 20, String(buffer));
drawTextCentered(c.x, c.y + r - 20, String(buffer));
// draw symbol (as bitmap)
switch (fluidtype) {
@@ -170,18 +197,18 @@ class PageFluid : public Page{
// scale texts
getdisplay().setFont(&Ubuntu_Bold8pt7b);
p = {c.x, c.y - r + 30};
drawTextCenter(p.x, p.y, "1/2");
drawTextCentered(p.x, p.y, "1/2");
pr = rotatePoint(c, p, -60);
drawTextCenter(pr.x, pr.y, "1/4");
drawTextCentered(pr.x, pr.y, "1/4");
pr = rotatePoint(c, p, 60);
drawTextCenter(pr.x, pr.y, "3/4");
drawTextCentered(pr.x, pr.y, "3/4");
// empty and full
getdisplay().setFont(&Ubuntu_Bold12pt7b);
p = rotatePoint(c, {c.x, c.y - r + 30}, -130);
drawTextCenter(p.x, p.y, "E");
drawTextCentered(p.x, p.y, "E");
p = rotatePoint(c, {c.x, c.y - r + 30}, 130);
drawTextCenter(p.x, p.y, "F");
drawTextCentered(p.x, p.y, "F");
// lines
std::vector<Point> pts = {

View File

@@ -342,9 +342,9 @@ static Page *createPage(CommonData &common){
PageDescription registerPageRollPitch(
"RollPitch", // Page name
createPage, // Action
2, // Number of bus values depends on selection in Web configuration
0, // Number of bus values depends on selection in Web configuration
// {"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
);

View File

@@ -8,44 +8,25 @@ class PageVoltage : public Page
{
bool init = false; // Marker for init done
bool keylock = false; // Keylock
uint8_t average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
int average = 0; // Average type [0...3], 0=off, 1=10s, 2=60s, 3=300s
bool trend = true; // Trend indicator [0|1], 0=off, 1=on
double raw = 0;
char mode = 'D'; // display mode (A)nalog | (D)igital
public:
PageVoltage(CommonData &common){
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);
}
common.logger->logDebug(GwLog::LOG,"Show PageVoltage");
}
virtual int handleKey(int key){
// Change average
if(key == 1){
average ++;
average = average % 4; // Modulo 4
if (hasFRAM) fram.write(FRAM_VOLTAGE_AVG, average);
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
if(key == 5){
trend = !trend;
if (hasFRAM) fram.write(FRAM_VOLTAGE_TREND, trend);
return 0; // Commit the key
}
@@ -57,41 +38,6 @@ public:
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)
{
GwConfigHandler *config = commonData.config;
@@ -189,182 +135,92 @@ public:
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
if (mode == 'D') {
// Display mode digital
// Show name
getdisplay().setTextColor(commonData.fgcolor);
getdisplay().setFont(&Ubuntu_Bold32pt7b);
getdisplay().setCursor(20, 100);
getdisplay().print(name1); // Value name
// Show name
getdisplay().setTextColor(commonData.fgcolor);
getdisplay().setFont(&Ubuntu_Bold32pt7b);
getdisplay().setCursor(20, 100);
getdisplay().print(name1); // Value name
// Show unit
getdisplay().setFont(&Ubuntu_Bold20pt7b);
getdisplay().setCursor(270, 100);
getdisplay().print("V");
// Show unit
getdisplay().setFont(&Ubuntu_Bold20pt7b);
getdisplay().setCursor(270, 100);
getdisplay().print("V");
// Show battery type
getdisplay().setFont(&Ubuntu_Bold8pt7b);
getdisplay().setCursor(295, 100);
getdisplay().print(batType);
// Show battery type
getdisplay().setFont(&Ubuntu_Bold8pt7b);
getdisplay().setCursor(295, 100);
getdisplay().print(batType);
// Show average settings
getdisplay().setFont(&Ubuntu_Bold8pt7b);
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;
}
// Show average settings
printAvg(average, 320, 84, true);
// Reading bus data or using simulation data
getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b);
getdisplay().setCursor(20, 240);
if(simulation == true){
if(batVoltage == "12V"){
value1 = 12.0;
// Reading bus data or using simulation data
getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b);
getdisplay().setCursor(20, 240);
if(simulation == true){
if(batVoltage == "12V"){
value1 = 12.0;
}
if(batVoltage == "24V"){
value1 = 24.0;
}
value1 += float(random(0, 5)) / 10; // Simulation data
getdisplay().print(value1,1);
}
else{
// Check for valid real data, display also if hold values activated
if(valid1 == true || holdvalues == true){
// Resolution switching
if(value1 < 10){
getdisplay().print(value1,2);
}
if(batVoltage == "24V"){
value1 = 24.0;
if(value1 >= 10 && value1 < 100){
getdisplay().print(value1,1);
}
if(value1 >= 100){
getdisplay().print(value1,0);
}
value1 += float(random(0, 5)) / 10; // Simulation data
getdisplay().print(value1,1);
}
else{
// Check for valid real data, display also if hold values activated
if(valid1 == true || holdvalues == true){
// Resolution switching
if(value1 < 10){
getdisplay().print(value1,2);
}
if(value1 >= 10 && value1 < 100){
getdisplay().print(value1,1);
}
if(value1 >= 100){
getdisplay().print(value1,0);
}
}
else{
getdisplay().print("---"); // Missing bus data
}
}
// Trend indicator
// Show trend indicator
if(trend == true){
getdisplay().fillRect(310, 240, 40, 120, commonData.bgcolor); // Clear area
getdisplay().fillRect(315, 183, 35, 4, commonData.fgcolor); // Draw separator
if(int(raw * 10) > int(valueTrend * 10)){
displayTrendHigh(320, 174, 11, commonData.fgcolor); // Show high indicator
}
if(int(raw * 10) < int(valueTrend * 10)){
displayTrendLow(320, 195, 11, commonData.fgcolor); // Show low indicator
}
}
// No trend indicator
else{
getdisplay().fillRect(310, 240, 40, 120, commonData.bgcolor); // Clear area
}
getdisplay().print("---"); // Missing bus data
}
}
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);
}
// Trend indicator
// Show trend indicator
if(trend == true){
getdisplay().fillRect(310, 240, 40, 120, commonData.bgcolor); // Clear area
getdisplay().fillRect(315, 183, 35, 4, commonData.fgcolor); // Draw separator
if(int(raw * 10) > int(valueTrend * 10)){
displayTrendHigh(320, 174, 11, commonData.fgcolor); // Show high indicator
}
// Pointer rotation and limits
double angle;
if (not valid1) {
angle = -0.5;
if(int(raw * 10) < int(valueTrend * 10)){
displayTrendLow(320, 195, 11, commonData.fgcolor); // Show low indicator
}
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);
}
}
// No trend indicator
else{
getdisplay().fillRect(310, 240, 40, 120, commonData.bgcolor); // Clear area
}
// Key Layout
getdisplay().setTextColor(commonData.fgcolor);
@@ -372,8 +228,6 @@ public:
if(keylock == false){
getdisplay().setCursor(10, 290);
getdisplay().print("[AVG]");
getdisplay().setCursor(62, 290);
getdisplay().print("[MODE]");
getdisplay().setCursor(130, 290);
getdisplay().print("[ <<<< " + String(commonData.data.actpage) + "/" + String(commonData.data.maxpage) + " >>>> ]");
getdisplay().setCursor(293, 290);

View File

@@ -1,650 +0,0 @@
#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

View File

@@ -186,8 +186,7 @@ public:
getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b);
getdisplay().setCursor(295, 65);
if(valid3 == true){
// getdisplay().print(abs(value3 * 180 / PI), 0); // Value
getdisplay().print(svalue3); // Value
getdisplay().print(abs(value3 * 180 / PI), 0); // Value
}
else{
getdisplay().print("---"); // Value

View File

@@ -4,12 +4,9 @@
#include <functional>
#include <vector>
#define MAX_PAGE_NUMBER 10 // Max number of pages for show data
typedef std::vector<GwApi::BoatValue *> ValueList;
typedef struct{
String pageName;
uint8_t pageNumber; // page number in sequence of visible pages
//the values will always contain the user defined values first
ValueList values;
} PageData;
@@ -117,13 +114,6 @@ class PageDescription{
}
};
class PageStruct{
public:
Page *page=NULL;
PageData parameters;
PageDescription *description=NULL;
};
// Structure for formated boat values
typedef struct{
double value;

View File

@@ -1,35 +0,0 @@
OBP40
=====
Important information:
***************************************************
THIS BRANCH IS NOT INTENDED TO MERGE INTO "master"!
***************************************************
platformio.ini adapted to compile directly
Differences to OBP60
--------------------
- no GPS
- no RTC
- no LEDs
- no environment sensor
- no key lock
- no swipe
- only 5 keys
+ SD card interface
Keyboard
--------
Keycode Key Function
-------- ---------------- -------------------------
1 Menu key Key 1 of OBP60 (left)
2 Exit key Key 2 of OBP60
3 Jog-Dial press
9 Jog-Dial down Next page
10 Jog-Dial up Previous page

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
#!/bin/perl -w
#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.
%NoOfFieldsPerPage=qw(
ApparentWind 0
@@ -82,7 +81,7 @@ for ($PageNo=1;$PageNo<=$NoOfPages;$PageNo++){
print "\t",'"condition":[';
foreach $page (@Pages) {
if($NoOfFieldsPerPage{$page}>=$FieldNo){
print '{"page',$PageNo,'type":"',$page,'"},';
print '{"page1type":"',$page,'"},';
}
}
print "\b],\n";

View File

@@ -1,123 +0,0 @@
#!/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(",")

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

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

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

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

View File

@@ -1,347 +0,0 @@
/******************************************************************************
* 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;
}

View File

@@ -1,70 +0,0 @@
/******************************************************************************
*
* 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 */

View File

@@ -13,10 +13,6 @@
#include "OBP60Extensions.h" // Functions lib for extension board
#include "OBP60Keypad.h" // Functions for keypad
#include <FS.h> // SD-Card access
#include <SD.h>
#include <SPI.h>
// True type character sets includes
// See OBP60ExtensionPort.cpp
@@ -27,6 +23,8 @@
#include "OBP60QRWiFi.h" // Functions lib for WiFi QR code
#include "OBPSensorTask.h" // Functions lib for sensor data
#include "LedSpiTask.h"
// Global vars
bool initComplete = false; // Initialization complete
int taskRunCounter = 0; // Task couter for loop section
@@ -35,9 +33,6 @@ int taskRunCounter = 0; // Task couter for loop section
//####################################################################################
void OBP60Init(GwApi *api){
GwLog *logger = api->getLogger();
GwConfigHandler *config = api->getConfig();
// Set a new device name and hidden the original name in the main config
String devicename = api->getConfig()->getConfigItem(api->getConfig()->deviceName,true)->asString();
api->getConfig()->setValue(GwConfigDefinitions::systemName, devicename, GwConfigInterface::ConfigType::HIDDEN);
@@ -48,51 +43,16 @@ void OBP60Init(GwApi *api){
// Init hardware
hardwareInit(api);
String sdcard = config->getConfigItem(config->useSDCard, true)->asString();
if (sdcard == "on") {
setPortPin(OBP_POWER_SD, true); // Power on SD
delay(10);
SPIClass SD_SPI = SPIClass(HSPI);
SD_SPI.begin(SD_SPI_CLK, SD_SPI_MISO, SD_SPI_MOSI);
if (SD.begin(SD_SPI_CS, SD_SPI, 80000000)) {
String sdtype = "unknown";
uint8_t cardType = SD.cardType();
switch (cardType) {
case CARD_MMC:
sdtype = "MMC";
break;
case CARD_SD:
sdtype = "SDSC";
break;
case CARD_SDHC:
sdtype = "SDHC";
break;
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
LOG_DEBUG(GwLog::LOG,"SD card type %s of size %d MB detected", sdtype, cardSize);
}
}
hardwareInit();
// Init power rail 5.0V
String powermode = api->getConfig()->getConfigItem(api->getConfig()->powerMode,true)->asString();
api->getLogger()->logDebug(GwLog::DEBUG,"Power Mode is: %s", powermode.c_str());
if(powermode == "Max Power" || powermode == "Only 5.0V"){
#ifdef HARDWARE_V21
setPortPin(OBP_POWER_50, true); // Power on 5.0V rail
#endif
#ifdef HARDWARE_LIGHT
setPortPin(OBP_POWER_EPD, true);// Power on ePaper display
#endif
}
else{
#ifdef HARDWARE_V21
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
@@ -223,6 +183,13 @@ 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
* each page should have defined a registerXXXPage variable of type
@@ -245,8 +212,8 @@ void registerAllPages(PageList &list){
list.add(&registerPageFourValues);
extern PageDescription registerPageFourValues2;
list.add(&registerPageFourValues2);
extern PageDescription registerPageWind;
list.add(&registerPageWind);
extern PageDescription registerPageApparentWind;
list.add(&registerPageApparentWind);
extern PageDescription registerPageWindRose;
list.add(&registerPageWindRose);
extern PageDescription registerPageWindRoseFlex;
@@ -279,6 +246,8 @@ void registerAllPages(PageList &list){
list.add(&registerPageXTETrack);
extern PageDescription registerPageFluid;
list.add(&registerPageFluid);
extern PageDescription registerPageBarograph;
list.add(&registerPageBarograph);
}
// Undervoltage detection for shutdown display
@@ -286,7 +255,7 @@ void underVoltageDetection(GwApi *api, CommonData &common){
// Read settings
float vslope = uint(api->getConfig()->getConfigItem(api->getConfig()->vSlope,true)->asFloat());
float voffset = uint(api->getConfig()->getConfigItem(api->getConfig()->vOffset,true)->asFloat());
// Read supply voltage
// Read supplay voltage
float actVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20; // V = 1/20 * Vin
actVoltage = actVoltage * vslope + voffset;
if(actVoltage < MIN_VOLTAGE){
@@ -318,9 +287,7 @@ void OBP60Task(GwApi *api){
// return;
GwLog *logger=api->getLogger();
GwConfigHandler *config=api->getConfig();
#ifdef HARDWARE_V21
startLedTask(api);
#endif
PageList allPages;
registerAllPages(allPages);
CommonData commonData;
@@ -388,10 +355,6 @@ void OBP60Task(GwApi *api){
// Init pages
int numPages=1;
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
//commonData.distanceformat=config->getString(xxx);
//add all necessary data to common data
@@ -415,7 +378,6 @@ void OBP60Task(GwApi *api){
pages[i].description=description;
pages[i].page=description->creator(commonData);
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);
//fill in all the user defined parameters
for (int uid=0;uid<description->userParam;uid++){
@@ -435,13 +397,6 @@ void OBP60Task(GwApi *api){
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);
});
//now we have prepared the page data
//we start a separate task that will fetch our keys...
MyData allParameters;
@@ -478,6 +433,9 @@ void OBP60Task(GwApi *api){
GwApi::BoatValue *hdop = boatValues.findValueOrCreate("HDOP"); // Load GpsHDOP
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.date = boatValues.findValueOrCreate("GPSD"); // Load GpsTime

View File

@@ -3,28 +3,15 @@
//we only compile for some boards
#ifdef BOARD_OBP60S3
#define USBSerial Serial
#ifdef HARDWARE_V21
// CAN NMEA2000
#define ESP32_CAN_TX_PIN 46
#define ESP32_CAN_RX_PIN 3
// Bus load in 50mA steps
#define N2K_LOAD_LEVEL 5 // 5x50mA = 250mA max bus load with back light on
// RS485 NMEA0183
#define GWSERIAL_TX 17
#define GWSERIAL_RX 8
#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
// CAN NMEA2000
#define ESP32_CAN_TX_PIN 46
#define ESP32_CAN_RX_PIN 3
// Bus load in 50mA steps
#define N2K_LOAD_LEVEL 5 // 5x50mA = 250mA max bus load with back light on
// RS485 NMEA0183
#define GWSERIAL_TX 17
#define GWSERIAL_RX 8
#define GWSERIAL_MODE "UNI"
// Allowed to set a new password for access point
#define FORCE_AP_PWCHANGE

View File

@@ -9,25 +9,22 @@ 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
board = obp60_s3_n16r8 #ESP32-S3 N16R8, 16MB flash, 8MB PSRAM, production series
#board_build.partitions = default_8MB.csv #ESP32-S3 N8, 8MB flash
board_build.partitions = default_16MB.csv #ESP32-S3 N16, 16MB flash
framework = arduino
lib_deps =
${basedeps.lib_deps}
Wire
SPI
SD
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
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
@@ -37,26 +34,23 @@ lib_deps =
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=0 #0=JTAG, 1=CDC (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 HARDWARE_V20 #Hardware revision V2.0
-D HARDWARE_V21 #Hardware revision V2.1
# -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_port = /dev/ttyACM0
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

View File

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

View File

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

View File

@@ -1,74 +0,0 @@
#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 */