diff --git a/.gitignore b/.gitignore index f85bd69..339663e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ .vscode/c_cpp_properties.json .vscode/launch.json .vscode/ipch -generated/* \ No newline at end of file +generated/* +lib/generated \ No newline at end of file diff --git a/extra_script.py b/extra_script.py index 89efc93..c8d81a8 100644 --- a/extra_script.py +++ b/extra_script.py @@ -5,15 +5,17 @@ import os import sys import inspect import json +import glob from datetime import datetime Import("env") #print(env.Dump()) OWN_FILE="extra_script.py" -GEN_DIR='generated' +GEN_DIR='lib/generated' CFG_FILE='web/config.json' XDR_FILE='web/xdrconfig.json' CFG_INCLUDE='GwConfigDefinitions.h' XDR_INCLUDE='GwXdrTypeMappings.h' +TASK_INCLUDE='GwUserTasks.h' EMBEDDED_INCLUDE="GwEmbeddedFiles.h" def getEmbeddedFiles(env): @@ -155,6 +157,30 @@ def generateXdrMappings(fp,oh,inFile=''): oh.write("\n") oh.write("};\n") +def genereateUserTasks(outfile): + includes=[] + taskdirs=glob.glob(os.path.join('lib','*task*')) + for task in taskdirs: + #print("##taskdir=%s"%task) + base=os.path.basename(task) + includeNames=[base.lower()+".h",'gw'+base.lower()+'.h'] + for f in os.listdir(task): + if not f.endswith('.h'): + continue + match=False + for cmp in includeNames: + #print("##check %s<->%s"%(f.lower(),cmp)) + if f.lower() == cmp: + match=True + if not match: + continue + includes.append(f) + includeData="" + for i in includes: + print("#task include %s"%i) + includeData+="#include <%s>\n"%i + writeFileIfChanged(outfile,includeData) + def generateEmbedded(elist,outFile): content="" for entry in elist: @@ -196,6 +222,7 @@ def prebuild(env): else: print("#WARNING: infile %s for %s not found"%(inFile,ef)) generateEmbedded(filedefs,os.path.join(outPath(),EMBEDDED_INCLUDE)) + genereateUserTasks(os.path.join(outPath(), TASK_INCLUDE)) generateFile(os.path.join(basePath(),CFG_FILE),os.path.join(outPath(),CFG_INCLUDE),generateCfg) generateFile(os.path.join(basePath(),XDR_FILE),os.path.join(outPath(),XDR_INCLUDE),generateXdrMappings) version="dev"+datetime.now().strftime("%Y%m%d") diff --git a/lib/api/GwApi.h b/lib/api/GwApi.h index 4b3f095..6ecac9e 100644 --- a/lib/api/GwApi.h +++ b/lib/api/GwApi.h @@ -1,8 +1,8 @@ #ifndef _GWAPI_H #define _GWAPI_H #include "GwMessage.h" -#include "N2kMessages.h" -#include "NMEA0183Messages.h" +#include "N2kMsg.h" +#include "NMEA0183Msg.h" #include "GWConfig.h" #include "GwBoatData.h" //API to be used for additional tasks @@ -16,4 +16,7 @@ class GwApi{ virtual GwLog *getLogger()=0; virtual GwBoatData *getBoatData()=0; }; -#endif \ No newline at end of file +#ifndef DECLARE_USERTASK +#define DECLARE_USERTASK(task) +#endif +#endif diff --git a/lib/exampletask/GwExampleHardware.h b/lib/exampletask/GwExampleHardware.h new file mode 100644 index 0000000..e2d646e --- /dev/null +++ b/lib/exampletask/GwExampleHardware.h @@ -0,0 +1,25 @@ +#ifndef _GWEXAMPLEHARDWARE_H +#define _GWEXAMPLEHARDWARE_H + +#ifdef BOARD_TEST +#define ESP32_CAN_TX_PIN GPIO_NUM_22 +#define ESP32_CAN_RX_PIN GPIO_NUM_19 +//if using tail485 +#define GWSERIAL_TX 26 +#define GWSERIAL_RX 32 +#define GWSERIAL_MODE "UNI" +#define GWBUTTON_PIN GPIO_NUM_39 +#define GWBUTTON_ACTIVE LOW +//if GWBUTTON_PULLUPDOWN we enable a pulup/pulldown +#define GWBUTTON_PULLUPDOWN +//led handling +//if we define GWLED_FASTNET the arduino fastnet lib is used +#define GWLED_FASTLED +#define GWLED_TYPE SK6812 +//color schema for fastled +#define GWLED_SCHEMA GRB +#define GWLED_PIN GPIO_NUM_27 +//brightness 0...255 +#define GWLED_BRIGHTNESS 64 +#endif +#endif \ No newline at end of file diff --git a/lib/exampletask/GwExampleTask.cpp b/lib/exampletask/GwExampleTask.cpp index 7c67fd2..7acd041 100644 --- a/lib/exampletask/GwExampleTask.cpp +++ b/lib/exampletask/GwExampleTask.cpp @@ -1,4 +1,7 @@ +//we only compile for some boards +#ifdef BOARD_TEST +#include "GwExampleTask.h" #include "GwApi.h" #define INVALID_COORD -99999 @@ -79,4 +82,6 @@ void exampleTask(void *param){ } vTaskDelete(NULL); -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/lib/exampletask/GwExampleTask.h b/lib/exampletask/GwExampleTask.h index 64aff8d..048991b 100644 --- a/lib/exampletask/GwExampleTask.h +++ b/lib/exampletask/GwExampleTask.h @@ -1,5 +1,11 @@ #ifndef _GWEXAMPLETASK_H #define _GWEXAMPLETASK_H -//task function +#include "GwExampleHardware.h" +#include "GwApi.h" +//we only compile for some boards +#ifdef BOARD_TEST void exampleTask(void *param); +//make the task known to the core +DECLARE_USERTASK(exampleTask); +#endif #endif \ No newline at end of file diff --git a/lib/exampletask/Readme.md b/lib/exampletask/Readme.md new file mode 100644 index 0000000..5842405 --- /dev/null +++ b/lib/exampletask/Readme.md @@ -0,0 +1,31 @@ +Extending the Core +================== +This directory contains an example on how you can extend the base functionality of the gateway. +Basically you can define own boards here and can add one or more tasks that will be started by the core. +You can also add additional libraries that will be used for your task. +In this example we define an addtional board (environment) with the name "testboard". +When building for this board we add the -DTEST_BOARD to the compilation - see [platformio.ini](platformio.ini). +The additional task that we defined will only be compiled and started for this environment (see the #ifdef TEST_BOARD in the code). +You can add your own directory below "lib". The name of the directory must contain "task". + +Files +----- + * [platformio.ini](platformio.ini) + extend the base configuration - we add a dummy library here and define our buil environment (board) + * [GwExampleTask.h](GwExampleTask.h) the name of this include must match the name of the directory (ignoring case) with a "gw" in front. This file includes our special hardware definitions and registers our task at the core (DECLARE_USERTASK in the code). + * [GwExampleTaks.cpp](GwExampleTask.cpp) includes the implementation of our task. This tasks runs in an own thread - see the comments in the code. + * [GwExampleHardware.h](GwExampleHardware.h) includes our pin definitions for the board. + + Hints + ----- + Just be careful not to interfere with names from the core - so it is a good practice to prefix your files and class like in the example. + + Developing + ---------- + To develop I recommend forking the gateway repository and adding your own directory below lib (with the string task in it's name). + As your code goes into a separate directory it should be very easy to fetch upstream changes without the need to adapt your code. + + Future Plans + ------------ + If there will be a need we can extend this extension API by means of adding config items and specific java script code and css for the UI. + diff --git a/lib/exampletask/platformio.ini b/lib/exampletask/platformio.ini new file mode 100644 index 0000000..ff5fa54 --- /dev/null +++ b/lib/exampletask/platformio.ini @@ -0,0 +1,10 @@ +[env:testboard] +board = m5stack-atom +lib_deps = + ${env.lib_deps} + own_lib +build_flags= + -D BOARD_TEST + ${env.build_flags} +upload_port = /dev/esp32 +upload_protocol = esptool \ No newline at end of file diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index c1ba5a9..7c0f97a 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -13,6 +13,7 @@ */ #ifndef _GWHARDWARE_H #define _GWHARDWARE_H +#include "GwUserTasks.h" //SERIAL_MODE can be: UNI (RX or TX only), BI (both), RX, TX //board specific pins diff --git a/platformio.ini b/platformio.ini index f3f6732..b3ce6bb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -7,6 +7,9 @@ ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html +[platformio] +extra_configs= + lib/*task*/platformio.ini [env] platform = espressif32 @@ -19,16 +22,16 @@ lib_deps = ottowinter/ESPAsyncWebServer-esphome@^2.0.1 fastled/FastLED @ ^3.4.0 board_build.embed_files = - generated/index.html.gz - generated/index.js.gz - generated/index.css.gz - generated/config.json.gz - generated/xdrconfig.json.gz + lib/generated/index.html.gz + lib/generated/index.js.gz + lib/generated/index.css.gz + lib/generated/config.json.gz + lib/generated/xdrconfig.json.gz board_build.partitions = partitions_custom.csv extra_scripts = pre:extra_script.py post:post.py -build_flags = -Igenerated +lib_ldf_mode = chain+ monitor_speed = 115200 [env:m5stack-atom] diff --git a/src/main.cpp b/src/main.cpp index c84f2c0..8c483d8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,9 +30,25 @@ // #define GW_MESSAGE_DEBUG_ENABLED // #define FALLBACK_SERIAL const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting +#include +//user task handling +std::vector userTasks; + +void registerUserTask(TaskFunction_t task){ + //logWriter.write("register user task\n"); + userTasks.push_back(task); +} + +class GwUserTask{ + public: + GwUserTask(TaskFunction_t task){ + registerUserTask(task); + } +}; +#define DECLARE_USERTASK(task) GwUserTask __##task##__(task); +//#include "GwUserTasks.h" #include "GwHardware.h" -#include #include // This will automatically choose right CAN library and create suitable NMEA2000 object #include #include @@ -43,6 +59,7 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting #include #include #include +#include #include "esp_heap_caps.h" #include "N2kDataToNMEA0183.h" @@ -57,14 +74,14 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting #include "GwSerial.h" #include "GwWebServer.h" #include "NMEA0183DataToN2K.h" -#include "GwApi.h" #include "GwButtons.h" #include "GwLeds.h" #include "GwCounter.h" #include "GwXDRMappings.h" -#include "GwExampleTask.h" +#include "GwApi.h" + //NMEA message channels #define N2K_CHANNEL_ID 0 #define USB_CHANNEL_ID 1 @@ -74,6 +91,8 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting #define MAX_NMEA2000_MESSAGE_SEASMART_SIZE 500 #define MAX_NMEA0183_MESSAGE_SIZE 150 // For AIS + + typedef std::map StringMap; @@ -211,6 +230,8 @@ class GwSerialLog : public GwLogWriter{ }; +GwSerialLog logWriter; + class ApiImpl : public GwApi { private: @@ -260,6 +281,7 @@ bool delayedRestart(){ } + void startAddOnTask(TaskFunction_t task,int sourceId){ ApiImpl* api=new ApiImpl(sourceId); xTaskCreate(task,"user",2000,api,3,NULL); @@ -522,9 +544,8 @@ void setup() { logger.prefix="FALLBACK:"; } else{ - GwSerialLog *writer=new GwSerialLog(); logger.prefix="GWSERIAL:"; - logger.setWriter(writer); + logger.setWriter(&logWriter); logger.logDebug(GwLog::LOG,"created GwSerial for USB port"); } logger.logDebug(GwLog::LOG,"config: %s", config.toString().c_str()); @@ -695,7 +716,12 @@ void setup() { startAddOnTask(handleButtons,100); setLedMode(LED_GREEN); startAddOnTask(handleLeds,101); - startAddOnTask(exampleTask,102); + int userTaskId=200; + for (auto it=userTasks.begin();it != userTasks.end();it++){ + logger.logDebug(GwLog::LOG,"starting user task with id %d",userTaskId); + startAddOnTask(*it,userTaskId); + userTaskId++; + } logger.logDebug(GwLog::LOG,"setup done"); } //*****************************************************************************