From ec4bf842ecabed462c49a00013453b89761d6138 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 24 Aug 2023 14:07:42 +0200 Subject: [PATCH 001/178] correctly handle headers --- webinstall/install.php | 58 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/webinstall/install.php b/webinstall/install.php index e429bec..344f57a 100644 --- a/webinstall/install.php +++ b/webinstall/install.php @@ -117,9 +117,33 @@ header('Access-Control-Allow-Origin:*'); return curl_exec($ch); } + function setFw($curl){ + $headers=getallheaders(); + $FWHDR = ['User-Agent']; + $outHeaders = array(); + foreach ($FWHDR as $k) { + if (isset($headers[$k])) { + array_push($outHeaders, "$k: $headers[$k]"); + } + } + curl_setopt($curl, CURLOPT_HTTPHEADER, $outHeaders); + } + function getJson($url){ + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL,$url); + curl_setopt($curl,CURLOPT_RETURNTRANSFER, true); + setFw($curl); + $response = curl_exec($curl); + if($e = curl_error($curl)) { + curl_close($curl); + return array('error'=>$e); + } else { + curl_close($curl); + return json_decode($response, true); + } + } function proxy($url) { - $headers=getallheaders(); $ch = curl_init($url); curl_setopt_array( $ch, @@ -128,14 +152,7 @@ CURLOPT_CONNECTTIMEOUT => 30, ] ); - $FWHDR = ['User-Agent']; - $outHeaders = array(); - foreach ($FWHDR as $k) { - if (isset($headers[$k])) { - array_push($outHeaders, "$k: $headers[$k]"); - } - } - curl_setopt($ch, CURLOPT_HTTPHEADER, $outHeaders); + setFw($ch); $response = curl_exec_follow($ch); curl_close($ch); } @@ -151,5 +168,26 @@ proxy(replaceVars($download,$vars)); exit(0); } + if (isset($_REQUEST['flash'])){ + $vars=fillUserAndRepo(); + $json=getJson(replaceVars($api,$vars)); + $assets=$json['assets']; + $targetUrl=null; + $targetBase=$_REQUEST['flash']; + $mode='all'; + if (isset($_REQUEST['update'])) $mode='update'; + $lb=strlen($targetBase); + foreach ($assets as &$asset){ + if (substr($asset['name'],0,$lb) == $targetBase){ + if (! preg_match("/-$mode.bin/",$asset['name'])) continue; + $targetUrl=$asset['browser_download_url']; + break; + } + } + if (! $targetUrl) die("unable to find $targetBase $mode\n"); + #echo("download for $targetBase=$targetUrl\n"); + proxy($targetUrl); + exit(0); + } die("invalid request"); - ?> \ No newline at end of file + ?> From f025fddc719022140bd2c41496c3e077dc4a552f Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 24 Aug 2023 15:22:02 +0200 Subject: [PATCH 002/178] move to newest framework 6.3.2 --- extra_script.py | 2 +- lib/boatData/GwBoatData.h | 9 +++++---- lib/socketserver/GwSocketServer.cpp | 2 +- lib/socketserver/GwTcpClient.cpp | 4 ++-- lib/webserver/GwWebServer.cpp | 1 - platformio.ini | 4 +++- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/extra_script.py b/extra_script.py index 0331e39..af10f27 100644 --- a/extra_script.py +++ b/extra_script.py @@ -131,7 +131,7 @@ def generateCfg(inFile,outFile,addDirs=[]): continue if len(n) > 15: raise Exception("%s: config names must be max 15 caracters"%n) - data+=' static constexpr const __FlashStringHelper* %s=F("%s");\n'%(n,n) + data+=' static constexpr const char* %s="%s";\n'%(n,n) data+=' protected:\n' data+=' GwConfigInterface *configs[%d]={\n'%(l) first=True diff --git a/lib/boatData/GwBoatData.h b/lib/boatData/GwBoatData.h index 71742cf..67052dc 100644 --- a/lib/boatData/GwBoatData.h +++ b/lib/boatData/GwBoatData.h @@ -4,8 +4,9 @@ #include "GwLog.h" #include #include +#include #define GW_BOAT_VALUE_LEN 32 -#define GWSC(name) static constexpr const __FlashStringHelper* name=F(#name) +#define GWSC(name) static constexpr const char* name=#name //see https://github.com/wellenvogel/esp32-nmea2000/issues/44 //factor to convert from N2k/SI rad/s to current NMEA rad/min @@ -164,10 +165,10 @@ public: virtual ~GwBoatItemNameProvider() {} }; #define GWBOATDATA(type,name,time,fmt) \ - static constexpr const __FlashStringHelper* _##name=F(#name); \ - GwBoatItem *name=new GwBoatItem(F(#name),GwBoatItemBase::fmt,time,&values) ; + static constexpr const char* _##name=#name; \ + GwBoatItem *name=new GwBoatItem(#name,GwBoatItemBase::fmt,time,&values) ; #define GWSPECBOATDATA(clazz,name,time,fmt) \ - clazz *name=new clazz(F(#name),GwBoatItemBase::fmt,time,&values) ; + clazz *name=new clazz(#name,GwBoatItemBase::fmt,time,&values) ; class GwBoatData{ private: GwLog *logger; diff --git a/lib/socketserver/GwSocketServer.cpp b/lib/socketserver/GwSocketServer.cpp index c225726..185e3a4 100644 --- a/lib/socketserver/GwSocketServer.cpp +++ b/lib/socketserver/GwSocketServer.cpp @@ -59,7 +59,7 @@ int GwSocketServer::available() int client_sock; struct sockaddr_in _client; int cs = sizeof(struct sockaddr_in); - client_sock = lwip_accept_r(listener, (struct sockaddr *)&_client, (socklen_t *)&cs); + client_sock = accept(listener, (struct sockaddr *)&_client, (socklen_t *)&cs); if (client_sock >= 0) { int val = 1; diff --git a/lib/socketserver/GwTcpClient.cpp b/lib/socketserver/GwTcpClient.cpp index c8fba27..e5f914b 100644 --- a/lib/socketserver/GwTcpClient.cpp +++ b/lib/socketserver/GwTcpClient.cpp @@ -80,7 +80,7 @@ void GwTcpClient::startConnection() return; } fcntl( sockfd, F_SETFL, fcntl( sockfd, F_GETFL, 0 ) | O_NONBLOCK ); - int res = lwip_connect_r(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)); + int res = connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)); if (res < 0 ) { if (errno != EINPROGRESS){ error=String("connect error ")+String(strerror(errno)); @@ -258,7 +258,7 @@ void GwTcpClient::resolveHost(String host) if (xTaskCreate([](void *p) { ResolveArgs *args = (ResolveArgs *)p; - struct ip4_addr addr; + esp_ip4_addr_t addr; addr.addr = 0; esp_err_t err = mdns_query_a(args->host.c_str(), args->timeout, &addr); if (err) diff --git a/lib/webserver/GwWebServer.cpp b/lib/webserver/GwWebServer.cpp index 2b3a79a..6261c42 100644 --- a/lib/webserver/GwWebServer.cpp +++ b/lib/webserver/GwWebServer.cpp @@ -64,7 +64,6 @@ void GwWebServer::begin(){ GwWebServer::~GwWebServer(){ server->end(); delete server; - vQueueDelete(queue); } void GwWebServer::handleAsyncWebRequest(AsyncWebServerRequest *request, GwRequestMessage *msg) { diff --git a/platformio.ini b/platformio.ini index d6d1109..9d5e199 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,8 +17,10 @@ extra_configs= lib/*task*/platformio.ini [env] -platform = espressif32 @ 3.4.0 +platform = espressif32 @ 6.3.2 framework = arduino +;platform_packages= +; framework-arduinoespressif32 @ 3.20011.230801 lib_deps = ttlappalainen/NMEA2000-library @ 4.17.2 ttlappalainen/NMEA0183 @ 1.7.1 From 7baa5688efadaf45bebf00c450f36942d056e98c Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 24 Aug 2023 17:32:13 +0200 Subject: [PATCH 003/178] 1st twai version with send/receive --- lib/nmea2ktwai/Nmea2kTwai.cpp | 81 +++++++++++++++++++++++++++++++++++ lib/nmea2ktwai/Nmea2kTwai.h | 27 ++++++++++++ src/main.cpp | 15 +++++-- 3 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 lib/nmea2ktwai/Nmea2kTwai.cpp create mode 100644 lib/nmea2ktwai/Nmea2kTwai.h diff --git a/lib/nmea2ktwai/Nmea2kTwai.cpp b/lib/nmea2ktwai/Nmea2kTwai.cpp new file mode 100644 index 0000000..0dc7ca1 --- /dev/null +++ b/lib/nmea2ktwai/Nmea2kTwai.cpp @@ -0,0 +1,81 @@ +#include "Nmea2kTwai.h" +#include "driver/gpio.h" +#include "driver/twai.h" +#include "GwLog.h" + +#define TWAI_DEBUG 1 +#if TWAI_DEBUG == 1 + #define TWAI_LOG(...) LOG_DEBUG(__VA_ARGS__) + #define TWAI_LDEBUG(...) +#else + #if TWAI_DEBUG == 2 + #define TWAI_LOG(...) LOG_DEBUG(__VA_ARGS__) + #define TWA_LDEBUG(...) LOG_DEBUG(__VA_ARGS__) + #else + #define TWAI_LOG(...) + #define TWAI_LDEBUG(...) + #endif +#endif + +Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin,GwLog *l): + tNMEA2000(),RxPin(_RxPin),TxPin(_TxPin),logger(l) +{ +} + +bool Nmea2kTwai::CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent) +{ + twai_message_t message; + message.identifier = id; + message.extd = 1; + message.data_length_code = len; + memcpy(message.data,buf,len); + esp_err_t rt=twai_transmit(&message,0); + if (rt != ESP_OK){ + TWAI_LOG(GwLog::DEBUG,"twai transmit for %ld failed: %d",id,(int)rt); + return false; + } + TWAI_LDEBUG(GwLog::DEBUG,"twai transmit id %ld, len %d",id,(int)len); + return true; +} +bool Nmea2kTwai::CANOpen() +{ + esp_err_t rt=twai_start(); + if (rt != ESP_OK){ + LOG_DEBUG(GwLog::ERROR,"CANOpen failed: %d",(int)rt); + return false; + } + else{ + LOG_DEBUG(GwLog::LOG,"CANOpen ok"); + } + return true; +} +bool Nmea2kTwai::CANGetFrame(unsigned long &id, unsigned char &len, unsigned char *buf) +{ + twai_message_t message; + esp_err_t rt=twai_receive(&message,0); + if (rt != ESP_OK){ + return false; + } + id=message.identifier; + len=message.data_length_code; + TWAI_LDEBUG(GwLog::DEBUG,"twai rcv id=%ld,len=%d, ext=%d",message.identifier,message.data_length_code,message.extd); + memcpy(buf,message.data,message.data_length_code); + return true; +} +// This will be called on Open() before any other initialization. Inherit this, if buffers can be set for the driver +// and you want to change size of library send frame buffer size. See e.g. NMEA2000_teensy.cpp. +void Nmea2kTwai::InitCANFrameBuffers() +{ + twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TxPin,RxPin, TWAI_MODE_NORMAL); + twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS(); + twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); + esp_err_t rt=twai_driver_install(&g_config, &t_config, &f_config); + if (rt == ESP_OK) { + LOG_DEBUG(GwLog::LOG,"twai driver initialzed, rx=%d,tx=%d",(int)RxPin,(int)TxPin); + } + else{ + LOG_DEBUG(GwLog::ERROR,"twai driver init failed: %d",(int)rt); + } + tNMEA2000::InitCANFrameBuffers(); + +} \ No newline at end of file diff --git a/lib/nmea2ktwai/Nmea2kTwai.h b/lib/nmea2ktwai/Nmea2kTwai.h new file mode 100644 index 0000000..3d91301 --- /dev/null +++ b/lib/nmea2ktwai/Nmea2kTwai.h @@ -0,0 +1,27 @@ +#ifndef _NMEA2KTWAI_H +#define _NMEA2KTWAI_H +#include "NMEA2000.h" +#include "GwLog.h" + +class Nmea2kTwai : public tNMEA2000{ + public: + Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin,GwLog *logger); + protected: + // Virtual functions for different interfaces. Currently there are own classes + // for Arduino due internal CAN (NMEA2000_due), external MCP2515 SPI CAN bus controller (NMEA2000_mcp), + // Teensy FlexCAN (NMEA2000_Teensy), NMEA2000_avr for AVR, NMEA2000_mbed for MBED and NMEA2000_socketCAN for e.g. RPi. + virtual bool CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent=true); + virtual bool CANOpen(); + virtual bool CANGetFrame(unsigned long &id, unsigned char &len, unsigned char *buf); + // This will be called on Open() before any other initialization. Inherit this, if buffers can be set for the driver + // and you want to change size of library send frame buffer size. See e.g. NMEA2000_teensy.cpp. + virtual void InitCANFrameBuffers(); + + private: + gpio_num_t TxPin; + gpio_num_t RxPin; + GwLog *logger; + +}; + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 557c35a..ddeaf37 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,6 +12,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "GwAppInfo.h" +#define USE_TWAI // #define GW_MESSAGE_DEBUG_ENABLED //#define FALLBACK_SERIAL //#define CAN_ESP_DEBUG @@ -65,17 +66,14 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting #include "GwTcpClient.h" #include "GwChannel.h" #include "GwChannelList.h" - -#include // forked from https://github.com/ttlappalainen/NMEA2000_esp32 #ifdef FALLBACK_SERIAL #ifdef CAN_ESP_DEBUG #define CDBS &Serial #else #define CDBS NULL #endif - tNMEA2000 &NMEA2000=*(new tNMEA2000_esp32(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,CDBS)); #else - tNMEA2000 &NMEA2000=*(new tNMEA2000_esp32()); + #define CDBS NULL #endif @@ -111,6 +109,15 @@ typedef std::map StringMap; GwLog logger(LOGLEVEL,NULL); GwConfigHandler config(&logger); + +#ifndef USE_TWAI +#include // forked from https://github.com/ttlappalainen/NMEA2000_esp32 +tNMEA2000 &NMEA2000=*(new tNMEA2000_esp32(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,CDBS)); +#else +#include "Nmea2kTwai.h" +tNMEA2000 &NMEA2000=*(new Nmea2kTwai(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,&logger)); +#endif + #ifdef GWBUTTON_PIN bool fixedApPass=false; #else From 80486eff75a5b77cc08aed7b4c9d2cddcce3be12 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 24 Aug 2023 18:39:51 +0200 Subject: [PATCH 004/178] get rid of esp32_nmea2000 --- lib/nmea2000esp32/ESP32_CAN_def.h | 291 -------------------- lib/nmea2000esp32/NMEA2000_esp32.cpp | 385 --------------------------- lib/nmea2000esp32/NMEA2000_esp32.h | 106 -------- lib/nmea2000esp32/readme.txt | 3 - src/main.cpp | 6 - 5 files changed, 791 deletions(-) delete mode 100644 lib/nmea2000esp32/ESP32_CAN_def.h delete mode 100644 lib/nmea2000esp32/NMEA2000_esp32.cpp delete mode 100644 lib/nmea2000esp32/NMEA2000_esp32.h delete mode 100644 lib/nmea2000esp32/readme.txt diff --git a/lib/nmea2000esp32/ESP32_CAN_def.h b/lib/nmea2000esp32/ESP32_CAN_def.h deleted file mode 100644 index 98eede4..0000000 --- a/lib/nmea2000esp32/ESP32_CAN_def.h +++ /dev/null @@ -1,291 +0,0 @@ -/** - * @section License - * - * The MIT License (MIT) - * - * Copyright (c) 2017, Thomas Barth, barth-dev.de - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef __DRIVERS_CAN_REGDEF_H_ -#define __DRIVERS_CAN_REGDEF_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - CAN_SPEED_100KBPS=100, /**< \brief CAN Node runs at 100kBit/s. */ - CAN_SPEED_125KBPS=125, /**< \brief CAN Node runs at 125kBit/s. */ - CAN_SPEED_250KBPS=250, /**< \brief CAN Node runs at 250kBit/s. */ - CAN_SPEED_500KBPS=500, /**< \brief CAN Node runs at 500kBit/s. */ - CAN_SPEED_800KBPS=800, /**< \brief CAN Node runs at 800kBit/s. */ - CAN_SPEED_1000KBPS=1000 /**< \brief CAN Node runs at 1000kBit/s. */ -}CAN_speed_t; - -/** - * \brief CAN frame type (standard/extended) - */ -typedef enum { - CAN_frame_std=0, /**< Standard frame, using 11 bit identifer. */ - CAN_frame_ext=1 /**< Extended frame, using 29 bit identifer. */ -}CAN_frame_format_t; - -/** - * \brief CAN RTR - */ -typedef enum { - CAN_no_RTR=0, /**< No RTR frame. */ - CAN_RTR=1 /**< RTR frame. */ -}CAN_RTR_t; - -/** \brief Frame information record type */ -typedef union{uint32_t U; /**< \brief Unsigned access */ - struct { - uint8_t DLC:4; /**< \brief [3:0] DLC, Data length container */ - unsigned int unknown_2:2; /**< \brief \internal unknown */ - CAN_RTR_t RTR:1; /**< \brief [6:6] RTR, Remote Transmission Request */ - CAN_frame_format_t FF:1; /**< \brief [7:7] Frame Format, see# CAN_frame_format_t*/ - unsigned int reserved_24:24; /**< \brief \internal Reserved */ - } B; -} CAN_FIR_t; - -/** \brief Start address of CAN registers */ -#define MODULE_CAN ((volatile CAN_Module_t *)0x3ff6b000) - -/** \brief Get standard message ID */ -#define _CAN_GET_STD_ID (((uint32_t)MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[0] << 3) | \ - (MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[1] >> 5)) - -/** \brief Get extended message ID */ -#define _CAN_GET_EXT_ID (((uint32_t)MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[0] << 21) | \ - (MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[1] << 13) | \ - (MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[2] << 5) | \ - (MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[3] >> 3 )) - -/** \brief Set standard message ID */ -#define _CAN_SET_STD_ID(x) MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[0] = ((x) >> 3); \ - MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[1] = ((x) << 5); - -/** \brief Set extended message ID */ -#define _CAN_SET_EXT_ID(x) MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[0] = ((x) >> 21); \ - MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[1] = ((x) >> 13); \ - MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[2] = ((x) >> 5); \ - MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[3] = ((x) << 3); \ - -/** \brief Interrupt status register */ -typedef enum { - __CAN_IRQ_RX= BIT(0), /**< \brief RX Interrupt */ - __CAN_IRQ_TX= BIT(1), /**< \brief TX Interrupt */ - __CAN_IRQ_ERR= BIT(2), /**< \brief Error Interrupt */ - __CAN_IRQ_DATA_OVERRUN= BIT(3), /**< \brief Date Overrun Interrupt */ - __CAN_IRQ_WAKEUP= BIT(4), /**< \brief Wakeup Interrupt */ - __CAN_IRQ_ERR_PASSIVE= BIT(5), /**< \brief Passive Error Interrupt */ - __CAN_IRQ_ARB_LOST= BIT(6), /**< \brief Arbitration lost interrupt */ - __CAN_IRQ_BUS_ERR= BIT(7), /**< \brief Bus error Interrupt */ -}__CAN_IRQ_t; - - -/** \brief OCMODE options. */ -typedef enum { - __CAN_OC_BOM= 0b00, /**< \brief bi-phase output mode */ - __CAN_OC_TOM= 0b01, /**< \brief test output mode */ - __CAN_OC_NOM= 0b10, /**< \brief normal output mode */ - __CAN_OC_COM= 0b11, /**< \brief clock output mode */ -}__CAN_OCMODE_t; - - -/** - * CAN controller (SJA1000). - */ -typedef struct { - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int RM:1; /**< \brief MOD.0 Reset Mode */ - unsigned int LOM:1; /**< \brief MOD.1 Listen Only Mode */ - unsigned int STM:1; /**< \brief MOD.2 Self Test Mode */ - unsigned int AFM:1; /**< \brief MOD.3 Acceptance Filter Mode */ - unsigned int SM:1; /**< \brief MOD.4 Sleep Mode */ - unsigned int reserved_27:27; /**< \brief \internal Reserved */ - } B; - } MOD; - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int TR:1; /**< \brief CMR.0 Transmission Request */ - unsigned int AT:1; /**< \brief CMR.1 Abort Transmission */ - unsigned int RRB:1; /**< \brief CMR.2 Release Receive Buffer */ - unsigned int CDO:1; /**< \brief CMR.3 Clear Data Overrun */ - unsigned int GTS:1; /**< \brief CMR.4 Go To Sleep */ - unsigned int reserved_27:27; /**< \brief \internal Reserved */ - } B; - } CMR; - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int RBS:1; /**< \brief SR.0 Receive Buffer Status */ - unsigned int DOS:1; /**< \brief SR.1 Data Overrun Status */ - unsigned int TBS:1; /**< \brief SR.2 Transmit Buffer Status */ - unsigned int TCS:1; /**< \brief SR.3 Transmission Complete Status */ - unsigned int RS:1; /**< \brief SR.4 Receive Status */ - unsigned int TS:1; /**< \brief SR.5 Transmit Status */ - unsigned int ES:1; /**< \brief SR.6 Error Status */ - unsigned int BS:1; /**< \brief SR.7 Bus Status */ - unsigned int reserved_24:24; /**< \brief \internal Reserved */ - } B; - } SR; - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int RI:1; /**< \brief IR.0 Receive Interrupt */ - unsigned int TI:1; /**< \brief IR.1 Transmit Interrupt */ - unsigned int EI:1; /**< \brief IR.2 Error Interrupt */ - unsigned int DOI:1; /**< \brief IR.3 Data Overrun Interrupt */ - unsigned int WUI:1; /**< \brief IR.4 Wake-Up Interrupt */ - unsigned int EPI:1; /**< \brief IR.5 Error Passive Interrupt */ - unsigned int ALI:1; /**< \brief IR.6 Arbitration Lost Interrupt */ - unsigned int BEI:1; /**< \brief IR.7 Bus Error Interrupt */ - unsigned int reserved_24:24; /**< \brief \internal Reserved */ - } B; - } IR; - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int RIE:1; /**< \brief IER.0 Receive Interrupt Enable */ - unsigned int TIE:1; /**< \brief IER.1 Transmit Interrupt Enable */ - unsigned int EIE:1; /**< \brief IER.2 Error Interrupt Enable */ - unsigned int DOIE:1; /**< \brief IER.3 Data Overrun Interrupt Enable */ - unsigned int WUIE:1; /**< \brief IER.4 Wake-Up Interrupt Enable */ - unsigned int EPIE:1; /**< \brief IER.5 Error Passive Interrupt Enable */ - unsigned int ALIE:1; /**< \brief IER.6 Arbitration Lost Interrupt Enable */ - unsigned int BEIE:1; /**< \brief IER.7 Bus Error Interrupt Enable */ - unsigned int reserved_24:24; /**< \brief \internal Reserved */ - } B; - } IER; - uint32_t RESERVED0; - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int BRP:6; /**< \brief BTR0[5:0] Baud Rate Prescaler */ - unsigned int SJW:2; /**< \brief BTR0[7:6] Synchronization Jump Width*/ - unsigned int reserved_24:24; /**< \brief \internal Reserved */ - } B; - } BTR0; - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int TSEG1:4; /**< \brief BTR1[3:0] Timing Segment 1 */ - unsigned int TSEG2:3; /**< \brief BTR1[6:4] Timing Segment 2*/ - unsigned int SAM:1; /**< \brief BTR1.7 Sampling*/ - unsigned int reserved_24:24; /**< \brief \internal Reserved */ - } B; - } BTR1; - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int OCMODE:2; /**< \brief OCR[1:0] Output Control Mode, see # */ - unsigned int OCPOL0:1; /**< \brief OCR.2 Output Control Polarity 0 */ - unsigned int OCTN0:1; /**< \brief OCR.3 Output Control Transistor N0 */ - unsigned int OCTP0:1; /**< \brief OCR.4 Output Control Transistor P0 */ - unsigned int OCPOL1:1; /**< \brief OCR.5 Output Control Polarity 1 */ - unsigned int OCTN1:1; /**< \brief OCR.6 Output Control Transistor N1 */ - unsigned int OCTP1:1; /**< \brief OCR.7 Output Control Transistor P1 */ - unsigned int reserved_24:24; /**< \brief \internal Reserved */ - } B; - } OCR; - uint32_t RESERVED1[2]; - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int ALC:8; /**< \brief ALC[7:0] Arbitration Lost Capture */ - unsigned int reserved_24:24; /**< \brief \internal Reserved */ - } B; - } ALC; - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int ECC:8; /**< \brief ECC[7:0] Error Code Capture */ - unsigned int reserved_24:24; /**< \brief \internal Reserved */ - } B; - } ECC; - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int EWLR:8; /**< \brief EWLR[7:0] Error Warning Limit */ - unsigned int reserved_24:24; /**< \brief \internal Reserved */ - } B; - } EWLR; - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int RXERR:8; /**< \brief RXERR[7:0] Receive Error Counter */ - unsigned int reserved_24:24; /**< \brief \internal Reserved */ - } B; - } RXERR; - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int TXERR:8; /**< \brief TXERR[7:0] Transmit Error Counter */ - unsigned int reserved_24:24; /**< \brief \internal Reserved */ - } B; - } TXERR; - - union { - struct { - uint32_t CODE[4]; /**< \brief Acceptance Message ID */ - uint32_t MASK[4]; /**< \brief Acceptance Mask */ - uint32_t RESERVED2[5]; - } ACC; /**< \brief Acceptance filtering */ - struct { - CAN_FIR_t FIR; /**< \brief Frame information record */ - union{ - struct { - uint32_t ID[2]; /**< \brief Standard frame message-ID*/ - uint32_t data[8]; /**< \brief Standard frame payload */ - uint32_t reserved[2]; - } STD; /**< \brief Standard frame format */ - struct { - uint32_t ID[4]; /**< \brief Extended frame message-ID*/ - uint32_t data[8]; /**< \brief Extended frame payload */ - } EXT; /**< \brief Extended frame format */ - }TX_RX; /**< \brief RX/TX interface */ - }FCTRL; /**< \brief Function control regs */ - } MBX_CTRL; /**< \brief Mailbox control */ - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int RMC:8; /**< \brief RMC[7:0] RX Message Counter */ - unsigned int reserved_24:24; /**< \brief \internal Reserved Enable */ - } B; - } RMC; - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int RBSA:8; /**< \brief RBSA[7:0] RX Buffer Start Address */ - unsigned int reserved_24:24; /**< \brief \internal Reserved Enable */ - } B; - } RBSA; - union{uint32_t U; /**< \brief Unsigned access */ - struct { - unsigned int COD:3; /**< \brief CDR[2:0] CLKOUT frequency selector based of fOSC*/ - unsigned int COFF:1; /**< \brief CDR.3 CLKOUT off*/ - unsigned int reserved_1:1; /**< \brief \internal Reserved */ - unsigned int RXINTEN:1; /**< \brief CDR.5 This bit allows the TX1 output to be used as a dedicated receive interrupt output*/ - unsigned int CBP:1; /**< \brief CDR.6 allows to bypass the CAN input comparator and is only possible in reset mode.*/ - unsigned int CAN_M:1; /**< \brief CDR.7 If CDR.7 is at logic 0 the CAN controller operates in BasicCAN mode. If set to logic 1 the CAN controller operates in PeliCAN mode. Write access is only possible in reset mode*/ - unsigned int reserved_24:24; /**< \brief \internal Reserved */ - } B; - } CDR; - uint32_t IRAM[2]; -}CAN_Module_t; - -#ifdef __cplusplus -} -#endif - -#endif /* __DRIVERS_CAN_REGDEF_H_ */ diff --git a/lib/nmea2000esp32/NMEA2000_esp32.cpp b/lib/nmea2000esp32/NMEA2000_esp32.cpp deleted file mode 100644 index 24377f9..0000000 --- a/lib/nmea2000esp32/NMEA2000_esp32.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/* -NMEA2000_esp32.cpp - -Copyright (c) 2015-2020 Timo Lappalainen, Kave Oy, www.kave.fi - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the -Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Inherited NMEA2000 object for ESP32 modules. See also NMEA2000 library. - -Thanks to Thomas Barth, barth-dev.de, who has written ESP32 CAN code. To avoid extra -libraries, I implemented his code directly to the NMEA2000_esp32 to avoid extra -can.h library, which may cause even naming problem. -*/ - -#include "driver/periph_ctrl.h" - -#include "soc/dport_reg.h" -#include "NMEA2000_esp32.h" - -//time to reinit CAN bus if the queue is full for that time -#define SEND_CANCEL_TIME 2000 -//reinit CAN bis if nothing send/received within this time -#define RECEIVE_REINIT_TIME 60000 - -bool tNMEA2000_esp32::CanInUse=false; -tNMEA2000_esp32 *pNMEA2000_esp32=0; - -void ESP32Can1Interrupt(void *); - -#define ECDEBUG(fmt,args...) if(debugStream){debugStream->printf(fmt, ## args);} - -//***************************************************************************** -tNMEA2000_esp32::tNMEA2000_esp32(gpio_num_t _TxPin, - gpio_num_t _RxPin, - Print *dbg) : - tNMEA2000(), IsOpen(false), - speed(CAN_SPEED_250KBPS), TxPin(_TxPin), RxPin(_RxPin), - RxQueue(NULL), TxQueue(NULL) { - debugStream=dbg; -} -//***************************************************************************** -bool tNMEA2000_esp32::CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool /*wait_sent*/) { - tCANFrame frame; - unsigned long now=millis(); - if ( uxQueueSpacesAvailable(TxQueue)==0 ) { - if (lastSend && (lastSend + SEND_CANCEL_TIME) < now){ - ECDEBUG("CanSendFrame Aborting and emptying queue\n"); - while (xQueueReceive(TxQueue,&frame,0)){} - errReinit++; - CAN_init(false); - if ( uxQueueSpacesAvailable(TxQueue)==0 ) return false; - } - else{ - ECDEBUG("CanSendFrame queue full\n"); - return false; // can not send to queue - } - } - lastSend=now; - frame.id=id; - frame.len=len>8?8:len; - memcpy(frame.buf,buf,len); - CheckBusOff(); - ECDEBUG("CanSendFrame IntrCnt %d\n",cntIntr); - ECDEBUG("CanSendFrame Error TX/RX %d/%d\n",MODULE_CAN->TXERR.U,MODULE_CAN->RXERR.U); - ECDEBUG("CanSendFrame Error Overrun %d\n",errOverrun); - ECDEBUG("CanSendFrame Error Arbitration %d\n",errArb); - ECDEBUG("CanSendFrame Error Bus %d\n",errBus); - ECDEBUG("CanSendFrame Error Recovery %d\n",errRecovery); - ECDEBUG("CanSendFrame ErrorCount %d\n",errCountTxInternal); - ECDEBUG("CanSendFrame ErrorCancel %d\n",errCancelTransmit); - ECDEBUG("CanSendFrame ErrorReinit %d\n",errReinit); - ECDEBUG("CanSendFrame busOff=%d, errPassive=%d\n",MODULE_CAN->SR.B.BS,MODULE_CAN->SR.B.ES) - xQueueSendToBack(TxQueue,&frame,0); // Add frame to queue - if ( MODULE_CAN->SR.B.TBS==0 ) { - ECDEBUG("CanSendFrame: wait for ISR to send %d\n",frame.id); - return true; // Currently sending, ISR takes care of sending - } - - if ( MODULE_CAN->SR.B.TBS==1 ) { // Check again and restart send, if is not going on - xQueueReceive(TxQueue,&frame,0); - ECDEBUG("CanSendFrame: send direct %d\n",frame.id); - CAN_send_frame(frame); - //return MODULE_CAN->TXERR.U < 127; - } - - return true; -} - -//***************************************************************************** -void tNMEA2000_esp32::InitCANFrameBuffers() { - if (MaxCANReceiveFrames<10 ) MaxCANReceiveFrames=50; // ESP32 has plenty of RAM - if (MaxCANSendFrames<10 ) MaxCANSendFrames=40; - uint16_t CANGlobalBufSize=MaxCANSendFrames-4; - MaxCANSendFrames=4; // we do not need much libary internal buffer since driver has them. - RxQueue=xQueueCreate(MaxCANReceiveFrames,sizeof(tCANFrame)); - TxQueue=xQueueCreate(CANGlobalBufSize,sizeof(tCANFrame)); - - tNMEA2000::InitCANFrameBuffers(); // call main initialization -} - -//***************************************************************************** -bool tNMEA2000_esp32::CANOpen() { - if (IsOpen) return true; - - if (CanInUse) return false; // currently prevent accidental second instance. Maybe possible in future. - - pNMEA2000_esp32=this; - IsOpen=true; - CAN_init(); - - CanInUse=IsOpen; - - return IsOpen; -} - -//***************************************************************************** -bool tNMEA2000_esp32::CANGetFrame(unsigned long &id, unsigned char &len, unsigned char *buf) { - bool HasFrame=false; - tCANFrame frame; - CheckBusOff(); - unsigned long now=millis(); - //receive next CAN frame from queue - if ( xQueueReceive(RxQueue,&frame, 0)==pdTRUE ) { - HasFrame=true; - id=frame.id; - len=frame.len; - memcpy(buf,frame.buf,frame.len); - lastReceive=now; - } - else{ - if (lastReceive != 0 && (lastReceive + RECEIVE_REINIT_TIME) < now && (lastSend + RECEIVE_REINIT_TIME) < now){ - ECDEBUG("Noting received within %d ms, reinit",RECEIVE_REINIT_TIME); - CAN_init(false); - } - } - - return HasFrame; -} - -//***************************************************************************** -void tNMEA2000_esp32::CAN_init(bool installIsr) { - - //Time quantum - double __tq; - - - // A soft reset of the ESP32 leaves it's CAN controller in an undefined state so a reset is needed. - // Reset CAN controller to same state as it would be in after a power down reset. - periph_module_reset(PERIPH_CAN_MODULE); - - - //enable module - DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_CAN_CLK_EN); - DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST); - - //configure RX pin - gpio_set_direction(RxPin,GPIO_MODE_INPUT); - gpio_matrix_in(RxPin,CAN_RX_IDX,0); - gpio_pad_select_gpio(RxPin); - - //set to PELICAN mode - MODULE_CAN->CDR.B.CAN_M=0x1; - - //synchronization jump width is the same for all baud rates - MODULE_CAN->BTR0.B.SJW =0x1; - - //TSEG2 is the same for all baud rates - MODULE_CAN->BTR1.B.TSEG2 =0x1; - - //select time quantum and set TSEG1 - switch (speed) { - case CAN_SPEED_1000KBPS: - MODULE_CAN->BTR1.B.TSEG1 =0x4; - __tq = 0.125; - break; - - case CAN_SPEED_800KBPS: - MODULE_CAN->BTR1.B.TSEG1 =0x6; - __tq = 0.125; - break; - default: - MODULE_CAN->BTR1.B.TSEG1 =0xc; - __tq = ((float)1000/speed) / 16; - } - - //set baud rate prescaler - MODULE_CAN->BTR0.B.BRP=(uint8_t)round((((APB_CLK_FREQ * __tq) / 2) - 1)/1000000)-1; - - /* Set sampling - * 1 -> triple; the bus is sampled three times; recommended for low/medium speed buses (class A and B) where filtering spikes on the bus line is beneficial - * 0 -> single; the bus is sampled once; recommended for high speed buses (SAE class C)*/ - MODULE_CAN->BTR1.B.SAM =0x1; - - //enable all interrupts - MODULE_CAN->IER.U = 0xef; // bit 0x10 contains Baud Rate Prescaler Divider (BRP_DIV) bit - - //no acceptance filtering, as we want to fetch all messages - MODULE_CAN->MBX_CTRL.ACC.CODE[0] = 0; - MODULE_CAN->MBX_CTRL.ACC.CODE[1] = 0; - MODULE_CAN->MBX_CTRL.ACC.CODE[2] = 0; - MODULE_CAN->MBX_CTRL.ACC.CODE[3] = 0; - MODULE_CAN->MBX_CTRL.ACC.MASK[0] = 0xff; - MODULE_CAN->MBX_CTRL.ACC.MASK[1] = 0xff; - MODULE_CAN->MBX_CTRL.ACC.MASK[2] = 0xff; - MODULE_CAN->MBX_CTRL.ACC.MASK[3] = 0xff; - - //set to normal mode - MODULE_CAN->OCR.B.OCMODE=__CAN_OC_NOM; - - //clear error counters - MODULE_CAN->TXERR.U = 0; - MODULE_CAN->RXERR.U = 0; - (void)MODULE_CAN->ECC; - - //clear interrupt flags - (void)MODULE_CAN->IR.U; - - //install CAN ISR - if (installIsr) esp_intr_alloc(ETS_CAN_INTR_SOURCE,0,ESP32Can1Interrupt,NULL,NULL); - - //configure TX pin - // We do late configure, since some initialization above caused CAN Tx flash - // shortly causing one error frame on startup. By setting CAN pin here - // it works right. - gpio_set_direction(TxPin,GPIO_MODE_OUTPUT); - gpio_matrix_out(TxPin,CAN_TX_IDX,0,0); - gpio_pad_select_gpio(TxPin); - - //Showtime. Release Reset Mode. - MODULE_CAN->MOD.B.RM = 0; -} - -//***************************************************************************** -void tNMEA2000_esp32::CAN_read_frame() { - tCANFrame frame; - CAN_FIR_t FIR; - - //get FIR - FIR.U=MODULE_CAN->MBX_CTRL.FCTRL.FIR.U; - frame.len=FIR.B.DLC>8?8:FIR.B.DLC; - - // Handle only extended frames - if (FIR.B.FF==CAN_frame_ext) { //extended frame - //Get Message ID - frame.id = _CAN_GET_EXT_ID; - - //deep copy data bytes - for( size_t i=0; iMBX_CTRL.FCTRL.TX_RX.EXT.data[i]; - } - - //send frame to input queue - xQueueSendToBackFromISR(RxQueue,&frame,0); - } - - //Let the hardware know the frame has been read. - MODULE_CAN->CMR.B.RRB=1; -} - -//***************************************************************************** -void tNMEA2000_esp32::CAN_send_frame(tCANFrame &frame) { - CAN_FIR_t FIR; - - FIR.U=0; - FIR.B.DLC=frame.len>8?8:frame.len; - FIR.B.FF=CAN_frame_ext; - - //copy frame information record - MODULE_CAN->MBX_CTRL.FCTRL.FIR.U=FIR.U; - - //Write message ID - _CAN_SET_EXT_ID(frame.id); - - // Copy the frame data to the hardware - for ( size_t i=0; iMBX_CTRL.FCTRL.TX_RX.EXT.data[i]=frame.buf[i]; - } - - // Transmit frame - MODULE_CAN->CMR.B.TR=1; -} -#define CAN_MAX_TX_RETRY 12 -#define RECOVERY_RETRY_MS 1000 -void tNMEA2000_esp32::CAN_bus_off_recovery(){ - unsigned long now=millis(); - if (recoveryStarted && (recoveryStarted + RECOVERY_RETRY_MS) > now ) return; - ECDEBUG("CAN_bus_off_recovery started\n"); - recoveryStarted=now; - errRecovery++; - MODULE_CAN->CMR.B.AT=1; // abort transmission - (void)MODULE_CAN->SR.U; - MODULE_CAN->TXERR.U = 127; - MODULE_CAN->RXERR.U = 0; - MODULE_CAN->MOD.B.RM = 0; -} - -void tNMEA2000_esp32::CheckBusOff(){ - //should we really recover here? - if (MODULE_CAN->SR.B.BS){ - ECDEBUG("Bus off detected, trying recovery\n"); - CAN_bus_off_recovery(); - } - -} - -//***************************************************************************** -void tNMEA2000_esp32::InterruptHandler() { - //Interrupt flag buffer - cntIntr++; - uint32_t interrupt; - - // Read interrupt status and clear flags - interrupt = (MODULE_CAN->IR.U & 0xff); - - // Handle TX complete interrupt - //see http://uglyduck.vajn.icu/PDF/wireless/Espressif/ESP32/Eco_and_Workarounds_for_Bugs_in_ESP32.pdf, 3.13.4 - if ((interrupt & __CAN_IRQ_TX) != 0 || MODULE_CAN->SR.B.TBS) { - tCANFrame frame; - if ( (xQueueReceiveFromISR(TxQueue,&frame,NULL)==pdTRUE) ) { - CAN_send_frame(frame); - } - } - - // Handle RX frame available interrupt - if ((interrupt & __CAN_IRQ_RX) != 0) { - CAN_read_frame(); - } - - // Handle error interrupts. - if ((interrupt & (__CAN_IRQ_ERR //0x4 - - | __CAN_IRQ_WAKEUP //0x10 - | __CAN_IRQ_ERR_PASSIVE //0x20 - - - )) != 0) { - /*handler*/ - - } - //https://www.esp32.com/viewtopic.php?t=5010 - // Handle error interrupts. - if (interrupt & __CAN_IRQ_DATA_OVERRUN ) { //0x08 - errOverrun++; - MODULE_CAN->CMR.B.CDO=1; - (void)MODULE_CAN->SR.U; // read SR after write to CMR to settle register changes - } - if (interrupt & __CAN_IRQ_ARB_LOST ) { //0x40 - errArb++; - (void)MODULE_CAN->ALC.U; // must be read to re-enable interrupt - errCountTxInternal++; - } - if (interrupt & __CAN_IRQ_BUS_ERR ) { //0x80 - errBus++; - (void)MODULE_CAN->ECC.U; // must be read to re-enable interrupt - errCountTxInternal+=2; - } - if (MODULE_CAN->TXERR.U == 0){ - recoveryStarted=0; - } - if (errCountTxInternal >= 2 *CAN_MAX_TX_RETRY){ - MODULE_CAN->CMR.B.AT=1; // abort transmission - (void)MODULE_CAN->SR.U; - errCountTxInternal=0; - } -} - -//***************************************************************************** -void ESP32Can1Interrupt(void *) { - pNMEA2000_esp32->InterruptHandler(); -} diff --git a/lib/nmea2000esp32/NMEA2000_esp32.h b/lib/nmea2000esp32/NMEA2000_esp32.h deleted file mode 100644 index 59d63f9..0000000 --- a/lib/nmea2000esp32/NMEA2000_esp32.h +++ /dev/null @@ -1,106 +0,0 @@ -/* -NMEA2000_esp32.h - -Copyright (c) 2015-2020 Timo Lappalainen, Kave Oy, www.kave.fi - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the -Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Inherited NMEA2000 object for ESP32 modules. See also NMEA2000 library. - -Thanks to Thomas Barth, barth-dev.de, who has written ESP32 CAN code. To avoid extra -libraries, I implemented his code directly to the NMEA2000_esp32 to avoid extra -can.h library, which may cause even naming problem. - -The library sets as default CAN Tx pin to GPIO 16 and CAN Rx pint to GPIO 4. If you -want to use other pins (I have not tested can any pins be used), add defines e.g. -#define ESP32_CAN_TX_PIN GPIO_NUM_34 -#define ESP32_CAN_RX_PIN GPIO_NUM_35 -before including NMEA2000_esp32.h or NMEA2000_CAN.h -*/ - -#ifndef _NMEA2000_ESP32_H_ -#define _NMEA2000_ESP32_H_ - -#include "freertos/FreeRTOS.h" -#include "freertos/queue.h" -#include "driver/gpio.h" -#include "NMEA2000.h" -#include "N2kMsg.h" -#include "ESP32_CAN_def.h" - -#ifndef ESP32_CAN_TX_PIN -#define ESP32_CAN_TX_PIN GPIO_NUM_16 -#endif -#ifndef ESP32_CAN_RX_PIN -#define ESP32_CAN_RX_PIN GPIO_NUM_4 -#endif - -class tNMEA2000_esp32 : public tNMEA2000 -{ -private: - bool IsOpen; - static bool CanInUse; - -protected: - struct tCANFrame { - uint32_t id; // can identifier - uint8_t len; // length of data - uint8_t buf[8]; - }; - -protected: - CAN_speed_t speed; - gpio_num_t TxPin; - gpio_num_t RxPin; - QueueHandle_t RxQueue; - QueueHandle_t TxQueue; - Print *debugStream; - int errOverrun=0; - int errArb=0; - int errBus=0; - int errRecovery=0; - int errCountTxInternal=0; - int errCancelTransmit=0; - int errReinit=0; - unsigned long recoveryStarted=0; - unsigned long lastSend=0; - unsigned long lastReceive=0; - int cntIntr=0; - -protected: - void CAN_read_frame(); // Read frame to queue within interrupt - void CAN_send_frame(tCANFrame &frame); // Send frame - void CAN_init(bool installIsr=true); - void CAN_bus_off_recovery(); //recover from bus off - void CheckBusOff(); - -protected: - bool CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent=true); - bool CANOpen(); - bool CANGetFrame(unsigned long &id, unsigned char &len, unsigned char *buf); - virtual void InitCANFrameBuffers(); - -public: - tNMEA2000_esp32(gpio_num_t _TxPin=ESP32_CAN_TX_PIN, - gpio_num_t _RxPin=ESP32_CAN_RX_PIN, - Print *debugStream=NULL); - - void InterruptHandler(); -}; - -#endif diff --git a/lib/nmea2000esp32/readme.txt b/lib/nmea2000esp32/readme.txt deleted file mode 100644 index 1ff5aa0..0000000 --- a/lib/nmea2000esp32/readme.txt +++ /dev/null @@ -1,3 +0,0 @@ -forked from https://github.com/ttlappalainen/NMEA2000_esp32 -with some error handling additions -based on https://www.esp32.com/viewtopic.php?t=5010 \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index ddeaf37..9b3e004 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,7 +12,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "GwAppInfo.h" -#define USE_TWAI // #define GW_MESSAGE_DEBUG_ENABLED //#define FALLBACK_SERIAL //#define CAN_ESP_DEBUG @@ -110,13 +109,8 @@ typedef std::map StringMap; GwLog logger(LOGLEVEL,NULL); GwConfigHandler config(&logger); -#ifndef USE_TWAI -#include // forked from https://github.com/ttlappalainen/NMEA2000_esp32 -tNMEA2000 &NMEA2000=*(new tNMEA2000_esp32(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,CDBS)); -#else #include "Nmea2kTwai.h" tNMEA2000 &NMEA2000=*(new Nmea2kTwai(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,&logger)); -#endif #ifdef GWBUTTON_PIN bool fixedApPass=false; From 3be08a0c26f124913658fa2930683b2048704563 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 24 Aug 2023 19:30:20 +0200 Subject: [PATCH 005/178] correctly initialize send messages, increase queue --- lib/nmea2ktwai/Nmea2kTwai.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/nmea2ktwai/Nmea2kTwai.cpp b/lib/nmea2ktwai/Nmea2kTwai.cpp index 0dc7ca1..62fd270 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.cpp +++ b/lib/nmea2ktwai/Nmea2kTwai.cpp @@ -25,23 +25,24 @@ Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin,GwLog *l): bool Nmea2kTwai::CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent) { twai_message_t message; + memset(&message,0,sizeof(message)); message.identifier = id; message.extd = 1; message.data_length_code = len; memcpy(message.data,buf,len); esp_err_t rt=twai_transmit(&message,0); if (rt != ESP_OK){ - TWAI_LOG(GwLog::DEBUG,"twai transmit for %ld failed: %d",id,(int)rt); + TWAI_LOG(GwLog::DEBUG,"twai transmit for %ld failed: %x",(id & 0x1ffff),(int)rt); return false; } - TWAI_LDEBUG(GwLog::DEBUG,"twai transmit id %ld, len %d",id,(int)len); + TWAI_LDEBUG(GwLog::DEBUG,"twai transmit id %ld, len %d",(id & 0x1ffff),(int)len); return true; } bool Nmea2kTwai::CANOpen() { esp_err_t rt=twai_start(); if (rt != ESP_OK){ - LOG_DEBUG(GwLog::ERROR,"CANOpen failed: %d",(int)rt); + LOG_DEBUG(GwLog::ERROR,"CANOpen failed: %x",(int)rt); return false; } else{ @@ -56,10 +57,19 @@ bool Nmea2kTwai::CANGetFrame(unsigned long &id, unsigned char &len, unsigned cha if (rt != ESP_OK){ return false; } + if (! message.extd){ + return false; + } id=message.identifier; len=message.data_length_code; + if (len > 8){ + TWAI_LOG(GwLog::ERROR,"twai: received invalid message %lld, len %d",id,len); + len=8; + } TWAI_LDEBUG(GwLog::DEBUG,"twai rcv id=%ld,len=%d, ext=%d",message.identifier,message.data_length_code,message.extd); - memcpy(buf,message.data,message.data_length_code); + if (! message.rtr){ + memcpy(buf,message.data,message.data_length_code); + } return true; } // This will be called on Open() before any other initialization. Inherit this, if buffers can be set for the driver @@ -67,6 +77,7 @@ bool Nmea2kTwai::CANGetFrame(unsigned long &id, unsigned char &len, unsigned cha void Nmea2kTwai::InitCANFrameBuffers() { twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TxPin,RxPin, TWAI_MODE_NORMAL); + g_config.tx_queue_len=20; twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS(); twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); esp_err_t rt=twai_driver_install(&g_config, &t_config, &f_config); @@ -74,7 +85,7 @@ void Nmea2kTwai::InitCANFrameBuffers() LOG_DEBUG(GwLog::LOG,"twai driver initialzed, rx=%d,tx=%d",(int)RxPin,(int)TxPin); } else{ - LOG_DEBUG(GwLog::ERROR,"twai driver init failed: %d",(int)rt); + LOG_DEBUG(GwLog::ERROR,"twai driver init failed: %x",(int)rt); } tNMEA2000::InitCANFrameBuffers(); From 41d85786813b3489a2fcd73e6e9fccd1bad61a4f Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 24 Aug 2023 20:00:19 +0200 Subject: [PATCH 006/178] add bus off recovery --- lib/nmea2ktwai/Nmea2kTwai.cpp | 28 +++++++++++++++++++++++++++- lib/nmea2ktwai/Nmea2kTwai.h | 11 +++++++++++ src/main.cpp | 15 ++++++++++++++- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/lib/nmea2ktwai/Nmea2kTwai.cpp b/lib/nmea2ktwai/Nmea2kTwai.cpp index 62fd270..de562db 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.cpp +++ b/lib/nmea2ktwai/Nmea2kTwai.cpp @@ -3,7 +3,7 @@ #include "driver/twai.h" #include "GwLog.h" -#define TWAI_DEBUG 1 +#define TWAI_DEBUG 0 #if TWAI_DEBUG == 1 #define TWAI_LOG(...) LOG_DEBUG(__VA_ARGS__) #define TWAI_LDEBUG(...) @@ -89,4 +89,30 @@ void Nmea2kTwai::InitCANFrameBuffers() } tNMEA2000::InitCANFrameBuffers(); +} +Nmea2kTwai::STATE Nmea2kTwai::getState(){ + twai_status_info_t state; + if (twai_get_status_info(&state) != ESP_OK){ + return ST_ERROR; + } + switch(state.state){ + case TWAI_STATE_STOPPED: + return ST_STOPPED; + case TWAI_STATE_RUNNING: + return ST_RUNNING; + case TWAI_STATE_BUS_OFF: + return ST_BUS_OFF; + case TWAI_STATE_RECOVERING: + return ST_RECOVERING; + } + return ST_ERROR; +} +bool Nmea2kTwai::startRecovery(){ + esp_err_t rt=twai_initiate_recovery(); + if (rt != ESP_OK){ + LOG_DEBUG(GwLog::ERROR,"twai: initiate recovery failed with error %x",(int)rt); + return false; + } + LOG_DEBUG(GwLog::LOG,"twai: bus recovery started"); + return true; } \ No newline at end of file diff --git a/lib/nmea2ktwai/Nmea2kTwai.h b/lib/nmea2ktwai/Nmea2kTwai.h index 3d91301..7ce7958 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.h +++ b/lib/nmea2ktwai/Nmea2kTwai.h @@ -6,6 +6,15 @@ class Nmea2kTwai : public tNMEA2000{ public: Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin,GwLog *logger); + typedef enum{ + ST_STOPPED, + ST_RUNNING, + ST_BUS_OFF, + ST_RECOVERING, + ST_ERROR + } STATE; + STATE getState(); + bool startRecovery(); protected: // Virtual functions for different interfaces. Currently there are own classes // for Arduino due internal CAN (NMEA2000_due), external MCP2515 SPI CAN bus controller (NMEA2000_mcp), @@ -17,6 +26,8 @@ class Nmea2kTwai : public tNMEA2000{ // and you want to change size of library send frame buffer size. See e.g. NMEA2000_teensy.cpp. virtual void InitCANFrameBuffers(); + + private: gpio_num_t TxPin; gpio_num_t RxPin; diff --git a/src/main.cpp b/src/main.cpp index 9b3e004..84a55c8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -110,7 +110,7 @@ GwLog logger(LOGLEVEL,NULL); GwConfigHandler config(&logger); #include "Nmea2kTwai.h" -tNMEA2000 &NMEA2000=*(new Nmea2kTwai(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,&logger)); +Nmea2kTwai &NMEA2000=*(new Nmea2kTwai(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,&logger)); #ifdef GWBUTTON_PIN bool fixedApPass=false; @@ -821,6 +821,8 @@ void handleSendAndRead(bool handleRead){ TimeMonitor monitor(20,0.2); unsigned long lastHeapReport=0; +unsigned long lastCanRecovery=0; +static const unsigned long CAN_RECOVERY_PERIOD=3000; //ms void loop() { monitor.reset(); GWSYNCHRONIZED(&mainLock); @@ -839,6 +841,17 @@ void loop() { logger.logDebug(GwLog::DEBUG,"Main loop %s",monitor.getLog().c_str()); } } + if (now > (lastCanRecovery + CAN_RECOVERY_PERIOD)){ + lastCanRecovery=now; + Nmea2kTwai::STATE canState=NMEA2000.getState(); + if (canState != Nmea2kTwai::ST_RUNNING){ + logger.logDebug(GwLog::DEBUG,"can state: %d",canState); + if (canState == Nmea2kTwai::ST_BUS_OFF){ + bool rt=NMEA2000.startRecovery(); + logger.logDebug(GwLog::LOG,"start can recovery - result %d",(int)rt); + } + } + } monitor.setTime(3); channels.allChannels([](GwChannel *c){ c->loop(true,false); From 475652ff6efa7d266b0f49c472ca5699653b839e Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 24 Aug 2023 21:04:28 +0200 Subject: [PATCH 007/178] intermediate: recovery --- lib/nmea2ktwai/Nmea2kTwai.cpp | 42 +++++++++++++++++++++++++++++++++++ lib/nmea2ktwai/Nmea2kTwai.h | 10 ++++++++- src/main.cpp | 7 +++++- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/lib/nmea2ktwai/Nmea2kTwai.cpp b/lib/nmea2ktwai/Nmea2kTwai.cpp index de562db..01bdcfb 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.cpp +++ b/lib/nmea2ktwai/Nmea2kTwai.cpp @@ -24,6 +24,14 @@ Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin,GwLog *l): bool Nmea2kTwai::CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent) { + if (recoveryStarted){ + if (getState() == ST_RUNNING){ + recoveryStarted=false; + } + else{ + return false; + } + } twai_message_t message; memset(&message,0,sizeof(message)); message.identifier = id; @@ -52,6 +60,14 @@ bool Nmea2kTwai::CANOpen() } bool Nmea2kTwai::CANGetFrame(unsigned long &id, unsigned char &len, unsigned char *buf) { + if (recoveryStarted){ + if (getState() == ST_RUNNING){ + recoveryStarted=false; + } + else{ + return false; + } + } twai_message_t message; esp_err_t rt=twai_receive(&message,0); if (rt != ESP_OK){ @@ -107,12 +123,38 @@ Nmea2kTwai::STATE Nmea2kTwai::getState(){ } return ST_ERROR; } +Nmea2kTwai::ERRORS Nmea2kTwai::getErrors(){ + ERRORS rt; + twai_status_info_t state; + if (twai_get_status_info(&state) != ESP_OK){ + return rt; + } + rt.rx_errors=state.rx_error_counter; + rt.tx_errors=state.tx_error_counter; + rt.tx_failed=state.tx_failed_count; + return rt; +} bool Nmea2kTwai::startRecovery(){ + if (! recoveryStarted){ + recoveryStarted=true; //prevent any further sends + return true; + } esp_err_t rt=twai_initiate_recovery(); if (rt != ESP_OK){ LOG_DEBUG(GwLog::ERROR,"twai: initiate recovery failed with error %x",(int)rt); return false; } + recoveryStarted=true; LOG_DEBUG(GwLog::LOG,"twai: bus recovery started"); return true; +} +const char * Nmea2kTwai::stateStr(const Nmea2kTwai::STATE &st){ + switch (st) + { + case ST_BUS_OFF: return "BUS_OFF"; + case ST_RECOVERING: return "RECOVERING"; + case ST_RUNNING: return "RUNNING"; + case ST_STOPPED: return "STOPPED"; + } + return "ERROR"; } \ No newline at end of file diff --git a/lib/nmea2ktwai/Nmea2kTwai.h b/lib/nmea2ktwai/Nmea2kTwai.h index 7ce7958..9f57d04 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.h +++ b/lib/nmea2ktwai/Nmea2kTwai.h @@ -13,14 +13,21 @@ class Nmea2kTwai : public tNMEA2000{ ST_RECOVERING, ST_ERROR } STATE; + typedef struct{ + uint32_t rx_errors=0; + uint32_t tx_errors=0; + uint32_t tx_failed=0; + } ERRORS; STATE getState(); + ERRORS getErrors(); bool startRecovery(); + static const char * stateStr(const STATE &st); + virtual bool CANOpen(); protected: // Virtual functions for different interfaces. Currently there are own classes // for Arduino due internal CAN (NMEA2000_due), external MCP2515 SPI CAN bus controller (NMEA2000_mcp), // Teensy FlexCAN (NMEA2000_Teensy), NMEA2000_avr for AVR, NMEA2000_mbed for MBED and NMEA2000_socketCAN for e.g. RPi. virtual bool CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent=true); - virtual bool CANOpen(); virtual bool CANGetFrame(unsigned long &id, unsigned char &len, unsigned char *buf); // This will be called on Open() before any other initialization. Inherit this, if buffers can be set for the driver // and you want to change size of library send frame buffer size. See e.g. NMEA2000_teensy.cpp. @@ -32,6 +39,7 @@ class Nmea2kTwai : public tNMEA2000{ gpio_num_t TxPin; gpio_num_t RxPin; GwLog *logger; + bool recoveryStarted=false; }; diff --git a/src/main.cpp b/src/main.cpp index 84a55c8..fa48026 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -844,12 +844,17 @@ void loop() { if (now > (lastCanRecovery + CAN_RECOVERY_PERIOD)){ lastCanRecovery=now; Nmea2kTwai::STATE canState=NMEA2000.getState(); + Nmea2kTwai::ERRORS canErrors=NMEA2000.getErrors(); + logger.logDebug(GwLog::DEBUG,"can state %s, rxerr %d, txerr %d, txfail %d",NMEA2000.stateStr(canState),canErrors.rx_errors,canErrors.tx_errors,canErrors.tx_failed); if (canState != Nmea2kTwai::ST_RUNNING){ - logger.logDebug(GwLog::DEBUG,"can state: %d",canState); if (canState == Nmea2kTwai::ST_BUS_OFF){ bool rt=NMEA2000.startRecovery(); logger.logDebug(GwLog::LOG,"start can recovery - result %d",(int)rt); } + if (canState == Nmea2kTwai::ST_STOPPED){ + bool rt=NMEA2000.CANOpen(); + logger.logDebug(GwLog::LOG,"restart can driver - result %d",(int)rt); + } } } monitor.setTime(3); From ec95007d9d99d55eda48fa813a4df90158cf73d8 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 24 Aug 2023 21:15:37 +0200 Subject: [PATCH 008/178] use driver_uninstall for bus recovery to avoid crashes --- lib/nmea2ktwai/Nmea2kTwai.cpp | 42 ++++++++++------------------------- lib/nmea2ktwai/Nmea2kTwai.h | 3 +-- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/lib/nmea2ktwai/Nmea2kTwai.cpp b/lib/nmea2ktwai/Nmea2kTwai.cpp index 01bdcfb..e345a8a 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.cpp +++ b/lib/nmea2ktwai/Nmea2kTwai.cpp @@ -24,14 +24,6 @@ Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin,GwLog *l): bool Nmea2kTwai::CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent) { - if (recoveryStarted){ - if (getState() == ST_RUNNING){ - recoveryStarted=false; - } - else{ - return false; - } - } twai_message_t message; memset(&message,0,sizeof(message)); message.identifier = id; @@ -60,14 +52,6 @@ bool Nmea2kTwai::CANOpen() } bool Nmea2kTwai::CANGetFrame(unsigned long &id, unsigned char &len, unsigned char *buf) { - if (recoveryStarted){ - if (getState() == ST_RUNNING){ - recoveryStarted=false; - } - else{ - return false; - } - } twai_message_t message; esp_err_t rt=twai_receive(&message,0); if (rt != ESP_OK){ @@ -88,10 +72,7 @@ bool Nmea2kTwai::CANGetFrame(unsigned long &id, unsigned char &len, unsigned cha } return true; } -// This will be called on Open() before any other initialization. Inherit this, if buffers can be set for the driver -// and you want to change size of library send frame buffer size. See e.g. NMEA2000_teensy.cpp. -void Nmea2kTwai::InitCANFrameBuffers() -{ +void Nmea2kTwai::initDriver(){ twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TxPin,RxPin, TWAI_MODE_NORMAL); g_config.tx_queue_len=20; twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS(); @@ -103,6 +84,12 @@ void Nmea2kTwai::InitCANFrameBuffers() else{ LOG_DEBUG(GwLog::ERROR,"twai driver init failed: %x",(int)rt); } +} +// This will be called on Open() before any other initialization. Inherit this, if buffers can be set for the driver +// and you want to change size of library send frame buffer size. See e.g. NMEA2000_teensy.cpp. +void Nmea2kTwai::InitCANFrameBuffers() +{ + initDriver(); tNMEA2000::InitCANFrameBuffers(); } @@ -135,18 +122,13 @@ Nmea2kTwai::ERRORS Nmea2kTwai::getErrors(){ return rt; } bool Nmea2kTwai::startRecovery(){ - if (! recoveryStarted){ - recoveryStarted=true; //prevent any further sends - return true; - } - esp_err_t rt=twai_initiate_recovery(); + esp_err_t rt=twai_driver_uninstall(); if (rt != ESP_OK){ - LOG_DEBUG(GwLog::ERROR,"twai: initiate recovery failed with error %x",(int)rt); - return false; + LOG_DEBUG(GwLog::ERROR,"twai: deinit for recovery failed with %x",(int)rt); } - recoveryStarted=true; - LOG_DEBUG(GwLog::LOG,"twai: bus recovery started"); - return true; + initDriver(); + bool frt=CANOpen(); + return frt; } const char * Nmea2kTwai::stateStr(const Nmea2kTwai::STATE &st){ switch (st) diff --git a/lib/nmea2ktwai/Nmea2kTwai.h b/lib/nmea2ktwai/Nmea2kTwai.h index 9f57d04..069e247 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.h +++ b/lib/nmea2ktwai/Nmea2kTwai.h @@ -36,11 +36,10 @@ class Nmea2kTwai : public tNMEA2000{ private: + void initDriver(); gpio_num_t TxPin; gpio_num_t RxPin; GwLog *logger; - bool recoveryStarted=false; - }; #endif \ No newline at end of file From 9fd25ab6a84982435dffd311f4d6fb97243de770 Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 25 Aug 2023 19:35:38 +0200 Subject: [PATCH 009/178] improve status handling for twai, add n2k state to UI --- lib/log/GWLog.cpp | 5 ++- lib/log/GwLog.h | 1 + lib/nmea2ktwai/Nmea2kTwai.cpp | 64 ++++++++++++++--------------------- lib/nmea2ktwai/Nmea2kTwai.h | 19 +++++++---- src/main.cpp | 38 ++++++++++++++++----- web/index.html | 6 +++- 6 files changed, 79 insertions(+), 54 deletions(-) diff --git a/lib/log/GWLog.cpp b/lib/log/GWLog.cpp index 6601f2a..936e876 100644 --- a/lib/log/GWLog.cpp +++ b/lib/log/GWLog.cpp @@ -37,9 +37,12 @@ void GwLog::logString(const char *fmt,...){ xSemaphoreGive(locker); } void GwLog::logDebug(int level,const char *fmt,...){ - if (level > logLevel) return; va_list args; va_start(args,fmt); + logDebug(level,fmt,args); +} +void GwLog::logDebug(int level,const char *fmt,va_list args){ + if (level > logLevel) return; xSemaphoreTake(locker, portMAX_DELAY); recordCounter++; vsnprintf(buffer,bufferSize-1,fmt,args); diff --git a/lib/log/GwLog.h b/lib/log/GwLog.h index e04c7b5..d4ea76d 100644 --- a/lib/log/GwLog.h +++ b/lib/log/GwLog.h @@ -27,6 +27,7 @@ class GwLog{ void setWriter(GwLogWriter *writer); void logString(const char *fmt,...); void logDebug(int level, const char *fmt,...); + void logDebug(int level, const char *fmt,va_list ap); int isActive(int level){return level <= logLevel;}; void flush(); void setLevel(int level){this->logLevel=level;} diff --git a/lib/nmea2ktwai/Nmea2kTwai.cpp b/lib/nmea2ktwai/Nmea2kTwai.cpp index e345a8a..73fb256 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.cpp +++ b/lib/nmea2ktwai/Nmea2kTwai.cpp @@ -3,22 +3,10 @@ #include "driver/twai.h" #include "GwLog.h" -#define TWAI_DEBUG 0 -#if TWAI_DEBUG == 1 - #define TWAI_LOG(...) LOG_DEBUG(__VA_ARGS__) - #define TWAI_LDEBUG(...) -#else - #if TWAI_DEBUG == 2 - #define TWAI_LOG(...) LOG_DEBUG(__VA_ARGS__) - #define TWA_LDEBUG(...) LOG_DEBUG(__VA_ARGS__) - #else - #define TWAI_LOG(...) - #define TWAI_LDEBUG(...) - #endif -#endif +#define LOGID(id) ((id >> 8) & 0x1ffff) -Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin,GwLog *l): - tNMEA2000(),RxPin(_RxPin),TxPin(_TxPin),logger(l) +Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin): + tNMEA2000(),RxPin(_RxPin),TxPin(_TxPin) { } @@ -32,21 +20,21 @@ bool Nmea2kTwai::CANSendFrame(unsigned long id, unsigned char len, const unsigne memcpy(message.data,buf,len); esp_err_t rt=twai_transmit(&message,0); if (rt != ESP_OK){ - TWAI_LOG(GwLog::DEBUG,"twai transmit for %ld failed: %x",(id & 0x1ffff),(int)rt); + logDebug(LOG_DEBUG,"twai transmit for %ld failed: %x",LOGID(id),(int)rt); return false; } - TWAI_LDEBUG(GwLog::DEBUG,"twai transmit id %ld, len %d",(id & 0x1ffff),(int)len); + logDebug(LOG_MSG,"twai transmit id %ld, len %d",LOGID(id),(int)len); return true; } bool Nmea2kTwai::CANOpen() { esp_err_t rt=twai_start(); if (rt != ESP_OK){ - LOG_DEBUG(GwLog::ERROR,"CANOpen failed: %x",(int)rt); + logDebug(LOG_ERR,"CANOpen failed: %x",(int)rt); return false; } else{ - LOG_DEBUG(GwLog::LOG,"CANOpen ok"); + logDebug(LOG_INFO,"CANOpen ok"); } return true; } @@ -63,10 +51,10 @@ bool Nmea2kTwai::CANGetFrame(unsigned long &id, unsigned char &len, unsigned cha id=message.identifier; len=message.data_length_code; if (len > 8){ - TWAI_LOG(GwLog::ERROR,"twai: received invalid message %lld, len %d",id,len); + logDebug(LOG_DEBUG,"twai: received invalid message %lld, len %d",LOGID(id),len); len=8; } - TWAI_LDEBUG(GwLog::DEBUG,"twai rcv id=%ld,len=%d, ext=%d",message.identifier,message.data_length_code,message.extd); + logDebug(LOG_MSG,"twai rcv id=%ld,len=%d, ext=%d",LOGID(message.identifier),message.data_length_code,message.extd); if (! message.rtr){ memcpy(buf,message.data,message.data_length_code); } @@ -79,10 +67,10 @@ void Nmea2kTwai::initDriver(){ twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); esp_err_t rt=twai_driver_install(&g_config, &t_config, &f_config); if (rt == ESP_OK) { - LOG_DEBUG(GwLog::LOG,"twai driver initialzed, rx=%d,tx=%d",(int)RxPin,(int)TxPin); + logDebug(LOG_INFO,"twai driver initialzed, rx=%d,tx=%d",(int)RxPin,(int)TxPin); } else{ - LOG_DEBUG(GwLog::ERROR,"twai driver init failed: %x",(int)rt); + logDebug(LOG_ERR,"twai driver init failed: %x",(int)rt); } } // This will be called on Open() before any other initialization. Inherit this, if buffers can be set for the driver @@ -93,38 +81,38 @@ void Nmea2kTwai::InitCANFrameBuffers() tNMEA2000::InitCANFrameBuffers(); } -Nmea2kTwai::STATE Nmea2kTwai::getState(){ +Nmea2kTwai::Status Nmea2kTwai::getStatus(){ twai_status_info_t state; + Status rt; if (twai_get_status_info(&state) != ESP_OK){ - return ST_ERROR; + return rt; } switch(state.state){ case TWAI_STATE_STOPPED: - return ST_STOPPED; + rt.state=ST_STOPPED; + break; case TWAI_STATE_RUNNING: - return ST_RUNNING; + rt.state=ST_RUNNING; + break; case TWAI_STATE_BUS_OFF: - return ST_BUS_OFF; + rt.state=ST_BUS_OFF; + break; case TWAI_STATE_RECOVERING: - return ST_RECOVERING; - } - return ST_ERROR; -} -Nmea2kTwai::ERRORS Nmea2kTwai::getErrors(){ - ERRORS rt; - twai_status_info_t state; - if (twai_get_status_info(&state) != ESP_OK){ - return rt; + rt.state=ST_RECOVERING; + break; } rt.rx_errors=state.rx_error_counter; rt.tx_errors=state.tx_error_counter; rt.tx_failed=state.tx_failed_count; + rt.rx_missed=state.rx_missed_count; + rt.rx_overrun=state.rx_overrun_count; return rt; } + bool Nmea2kTwai::startRecovery(){ esp_err_t rt=twai_driver_uninstall(); if (rt != ESP_OK){ - LOG_DEBUG(GwLog::ERROR,"twai: deinit for recovery failed with %x",(int)rt); + logDebug(LOG_ERR,"twai: deinit for recovery failed with %x",(int)rt); } initDriver(); bool frt=CANOpen(); diff --git a/lib/nmea2ktwai/Nmea2kTwai.h b/lib/nmea2ktwai/Nmea2kTwai.h index 069e247..0fa9a92 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.h +++ b/lib/nmea2ktwai/Nmea2kTwai.h @@ -5,7 +5,7 @@ class Nmea2kTwai : public tNMEA2000{ public: - Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin,GwLog *logger); + Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin); typedef enum{ ST_STOPPED, ST_RUNNING, @@ -14,15 +14,23 @@ class Nmea2kTwai : public tNMEA2000{ ST_ERROR } STATE; typedef struct{ + //see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/twai.html#_CPPv418twai_status_info_t uint32_t rx_errors=0; uint32_t tx_errors=0; uint32_t tx_failed=0; - } ERRORS; - STATE getState(); - ERRORS getErrors(); + uint32_t rx_missed=0; + uint32_t rx_overrun=0; + STATE state=ST_ERROR; + } Status; + Status getStatus(); bool startRecovery(); static const char * stateStr(const STATE &st); virtual bool CANOpen(); + virtual ~Nmea2kTwai(){}; + static const int LOG_ERR=0; + static const int LOG_INFO=1; + static const int LOG_DEBUG=2; + static const int LOG_MSG=3; protected: // Virtual functions for different interfaces. Currently there are own classes // for Arduino due internal CAN (NMEA2000_due), external MCP2515 SPI CAN bus controller (NMEA2000_mcp), @@ -32,14 +40,13 @@ class Nmea2kTwai : public tNMEA2000{ // This will be called on Open() before any other initialization. Inherit this, if buffers can be set for the driver // and you want to change size of library send frame buffer size. See e.g. NMEA2000_teensy.cpp. virtual void InitCANFrameBuffers(); - + virtual void logDebug(int level,const char *fmt,...){} private: void initDriver(); gpio_num_t TxPin; gpio_num_t RxPin; - GwLog *logger; }; #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index fa48026..8830e4f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -110,7 +110,22 @@ GwLog logger(LOGLEVEL,NULL); GwConfigHandler config(&logger); #include "Nmea2kTwai.h" -Nmea2kTwai &NMEA2000=*(new Nmea2kTwai(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,&logger)); +class Nmea2kTwaiLog : public Nmea2kTwai{ + private: + GwLog* logger; + public: + Nmea2kTwaiLog(gpio_num_t _TxPin, gpio_num_t _RxPin, GwLog *l): + Nmea2kTwai(_TxPin,_RxPin),logger(l){} + virtual void logDebug(int level, const char *fmt,...){ + va_list args; + va_start(args,fmt); + if (level > 2) level++; //error+info+debug are similar, map msg to 4 + logger->logDebug(level,fmt,args); + } +}; + + +Nmea2kTwai &NMEA2000=*(new Nmea2kTwaiLog(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,&logger)); #ifdef GWBUTTON_PIN bool fixedApPass=false; @@ -354,7 +369,7 @@ public: protected: virtual void processRequest() { - GwJsonDocument status(256 + + GwJsonDocument status(290 + countNMEA2KIn.getJsonSize()+ countNMEA2KOut.getJsonSize() + channels.getJsonSize() @@ -370,6 +385,8 @@ protected: status["salt"] = buffer; status["fwtype"]= firmwareType; status["heap"]=(long)xPortGetFreeHeapSize(); + Nmea2kTwai::Status n2kState=NMEA2000.getStatus(); + status["n2kstate"]=NMEA2000.stateStr(n2kState.state); //nmea0183Converter->toJson(status); countNMEA2KIn.toJson(status); countNMEA2KOut.toJson(status); @@ -843,15 +860,20 @@ void loop() { } if (now > (lastCanRecovery + CAN_RECOVERY_PERIOD)){ lastCanRecovery=now; - Nmea2kTwai::STATE canState=NMEA2000.getState(); - Nmea2kTwai::ERRORS canErrors=NMEA2000.getErrors(); - logger.logDebug(GwLog::DEBUG,"can state %s, rxerr %d, txerr %d, txfail %d",NMEA2000.stateStr(canState),canErrors.rx_errors,canErrors.tx_errors,canErrors.tx_failed); - if (canState != Nmea2kTwai::ST_RUNNING){ - if (canState == Nmea2kTwai::ST_BUS_OFF){ + Nmea2kTwai::Status canState=NMEA2000.getStatus(); + logger.logDebug(GwLog::DEBUG,"can state %s, rxerr %d, txerr %d, txfail %d, rxmiss %d, rxoverrun %d", + NMEA2000.stateStr(canState.state), + canState.rx_errors, + canState.tx_errors, + canState.tx_failed, + canState.rx_missed, + canState.rx_overrun); + if (canState.state != Nmea2kTwai::ST_RUNNING){ + if (canState.state == Nmea2kTwai::ST_BUS_OFF){ bool rt=NMEA2000.startRecovery(); logger.logDebug(GwLog::LOG,"start can recovery - result %d",(int)rt); } - if (canState == Nmea2kTwai::ST_STOPPED){ + if (canState.state == Nmea2kTwai::ST_STOPPED){ bool rt=NMEA2000.CANOpen(); logger.logDebug(GwLog::LOG,"restart can driver - result %d",(int)rt); } diff --git a/web/index.html b/web/index.html index 2ecf096..e50eabc 100644 --- a/web/index.html +++ b/web/index.html @@ -58,7 +58,11 @@
Free heap --- -
+ +
+ N2K State + UNKNOWN +
From f5e48fcab35088183d782b684d2b1f6f777e6b46 Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 25 Aug 2023 19:48:53 +0200 Subject: [PATCH 010/178] count timeouts to detect offline state --- lib/nmea2ktwai/Nmea2kTwai.cpp | 11 ++++++++++- lib/nmea2ktwai/Nmea2kTwai.h | 3 +++ src/main.cpp | 4 +++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/nmea2ktwai/Nmea2kTwai.cpp b/lib/nmea2ktwai/Nmea2kTwai.cpp index 73fb256..084f428 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.cpp +++ b/lib/nmea2ktwai/Nmea2kTwai.cpp @@ -20,9 +20,13 @@ bool Nmea2kTwai::CANSendFrame(unsigned long id, unsigned char len, const unsigne memcpy(message.data,buf,len); esp_err_t rt=twai_transmit(&message,0); if (rt != ESP_OK){ - logDebug(LOG_DEBUG,"twai transmit for %ld failed: %x",LOGID(id),(int)rt); + if (rt == ESP_ERR_TIMEOUT){ + txTimeouts++; + } + logDebug(LOG_MSG,"twai transmit for %ld failed: %x",LOGID(id),(int)rt); return false; } + txTimeouts=0; logDebug(LOG_MSG,"twai transmit id %ld, len %d",LOGID(id),(int)len); return true; } @@ -106,6 +110,10 @@ Nmea2kTwai::Status Nmea2kTwai::getStatus(){ rt.tx_failed=state.tx_failed_count; rt.rx_missed=state.rx_missed_count; rt.rx_overrun=state.rx_overrun_count; + rt.tx_timeouts=txTimeouts; + if (rt.tx_timeouts > 256 && rt.state == ST_RUNNING){ + rt.state=ST_OFFLINE; + } return rt; } @@ -125,6 +133,7 @@ const char * Nmea2kTwai::stateStr(const Nmea2kTwai::STATE &st){ case ST_RECOVERING: return "RECOVERING"; case ST_RUNNING: return "RUNNING"; case ST_STOPPED: return "STOPPED"; + case ST_OFFLINE: return "OFFLINE"; } return "ERROR"; } \ No newline at end of file diff --git a/lib/nmea2ktwai/Nmea2kTwai.h b/lib/nmea2ktwai/Nmea2kTwai.h index 0fa9a92..8f614ac 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.h +++ b/lib/nmea2ktwai/Nmea2kTwai.h @@ -11,6 +11,7 @@ class Nmea2kTwai : public tNMEA2000{ ST_RUNNING, ST_BUS_OFF, ST_RECOVERING, + ST_OFFLINE, ST_ERROR } STATE; typedef struct{ @@ -20,6 +21,7 @@ class Nmea2kTwai : public tNMEA2000{ uint32_t tx_failed=0; uint32_t rx_missed=0; uint32_t rx_overrun=0; + uint32_t tx_timeouts=0; STATE state=ST_ERROR; } Status; Status getStatus(); @@ -47,6 +49,7 @@ class Nmea2kTwai : public tNMEA2000{ void initDriver(); gpio_num_t TxPin; gpio_num_t RxPin; + uint32_t txTimeouts=0; }; #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 8830e4f..2f44706 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -861,11 +861,13 @@ void loop() { if (now > (lastCanRecovery + CAN_RECOVERY_PERIOD)){ lastCanRecovery=now; Nmea2kTwai::Status canState=NMEA2000.getStatus(); - logger.logDebug(GwLog::DEBUG,"can state %s, rxerr %d, txerr %d, txfail %d, rxmiss %d, rxoverrun %d", + logger.logDebug(GwLog::DEBUG, + "can state %s, rxerr %d, txerr %d, txfail %d, txtimeout %d, rxmiss %d, rxoverrun %d", NMEA2000.stateStr(canState.state), canState.rx_errors, canState.tx_errors, canState.tx_failed, + canState.tx_timeouts, canState.rx_missed, canState.rx_overrun); if (canState.state != Nmea2kTwai::ST_RUNNING){ From 0d5215e00f5246693969afc3f34039a372d7c68d Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 25 Aug 2023 20:14:26 +0200 Subject: [PATCH 011/178] handle bus recovery in driver class --- lib/nmea2ktwai/Nmea2kTwai.cpp | 31 +++++++++++++++++++++++++++---- lib/nmea2ktwai/Nmea2kTwai.h | 7 +++++-- src/main.cpp | 25 ++++++++----------------- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/lib/nmea2ktwai/Nmea2kTwai.cpp b/lib/nmea2ktwai/Nmea2kTwai.cpp index 084f428..5053f7e 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.cpp +++ b/lib/nmea2ktwai/Nmea2kTwai.cpp @@ -5,9 +5,12 @@ #define LOGID(id) ((id >> 8) & 0x1ffff) -Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin): - tNMEA2000(),RxPin(_RxPin),TxPin(_TxPin) +static const int TIMEOUT_OFFLINE=256; //# of timeouts to consider offline + +Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin, unsigned long recP): + tNMEA2000(),RxPin(_RxPin),TxPin(_TxPin),recoveryPeriod(recP) { + lastRecoveryCheck=millis(); } bool Nmea2kTwai::CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent) @@ -21,7 +24,7 @@ bool Nmea2kTwai::CANSendFrame(unsigned long id, unsigned char len, const unsigne esp_err_t rt=twai_transmit(&message,0); if (rt != ESP_OK){ if (rt == ESP_ERR_TIMEOUT){ - txTimeouts++; + if (txTimeouts < TIMEOUT_OFFLINE) txTimeouts++; } logDebug(LOG_MSG,"twai transmit for %ld failed: %x",LOGID(id),(int)rt); return false; @@ -111,11 +114,31 @@ Nmea2kTwai::Status Nmea2kTwai::getStatus(){ rt.rx_missed=state.rx_missed_count; rt.rx_overrun=state.rx_overrun_count; rt.tx_timeouts=txTimeouts; - if (rt.tx_timeouts > 256 && rt.state == ST_RUNNING){ + if (rt.tx_timeouts >= TIMEOUT_OFFLINE && rt.state == ST_RUNNING){ rt.state=ST_OFFLINE; } return rt; } +bool Nmea2kTwai::checkRecovery(){ + if (recoveryPeriod == 0) return false; //no check + unsigned long now=millis(); + if ((lastRecoveryCheck+recoveryPeriod) > now) return false; + lastRecoveryCheck=now; + Status canState=getStatus(); + bool strt=false; + if (canState.state != Nmea2kTwai::ST_RUNNING){ + if (canState.state == Nmea2kTwai::ST_BUS_OFF){ + strt=true; + bool rt=startRecovery(); + logDebug(LOG_INFO,"start can recovery - result %d",(int)rt); + } + if (canState.state == Nmea2kTwai::ST_STOPPED){ + bool rt=CANOpen(); + logDebug(LOG_INFO,"restart can driver - result %d",(int)rt); + } + } + return strt; +} bool Nmea2kTwai::startRecovery(){ esp_err_t rt=twai_driver_uninstall(); diff --git a/lib/nmea2ktwai/Nmea2kTwai.h b/lib/nmea2ktwai/Nmea2kTwai.h index 8f614ac..4a89c2c 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.h +++ b/lib/nmea2ktwai/Nmea2kTwai.h @@ -5,7 +5,7 @@ class Nmea2kTwai : public tNMEA2000{ public: - Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin); + Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin, unsigned long recP=0); typedef enum{ ST_STOPPED, ST_RUNNING, @@ -25,7 +25,7 @@ class Nmea2kTwai : public tNMEA2000{ STATE state=ST_ERROR; } Status; Status getStatus(); - bool startRecovery(); + bool checkRecovery(); static const char * stateStr(const STATE &st); virtual bool CANOpen(); virtual ~Nmea2kTwai(){}; @@ -47,9 +47,12 @@ class Nmea2kTwai : public tNMEA2000{ private: void initDriver(); + bool startRecovery(); gpio_num_t TxPin; gpio_num_t RxPin; uint32_t txTimeouts=0; + unsigned long recoveryPeriod=0; + unsigned long lastRecoveryCheck=0; }; #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 2f44706..c0bfad5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -110,12 +110,13 @@ GwLog logger(LOGLEVEL,NULL); GwConfigHandler config(&logger); #include "Nmea2kTwai.h" +static const unsigned long CAN_RECOVERY_PERIOD=3000; //ms class Nmea2kTwaiLog : public Nmea2kTwai{ private: GwLog* logger; public: - Nmea2kTwaiLog(gpio_num_t _TxPin, gpio_num_t _RxPin, GwLog *l): - Nmea2kTwai(_TxPin,_RxPin),logger(l){} + Nmea2kTwaiLog(gpio_num_t _TxPin, gpio_num_t _RxPin, unsigned long recoveryPeriod,GwLog *l): + Nmea2kTwai(_TxPin,_RxPin,recoveryPeriod),logger(l){} virtual void logDebug(int level, const char *fmt,...){ va_list args; va_start(args,fmt); @@ -125,7 +126,7 @@ class Nmea2kTwaiLog : public Nmea2kTwai{ }; -Nmea2kTwai &NMEA2000=*(new Nmea2kTwaiLog(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,&logger)); +Nmea2kTwai &NMEA2000=*(new Nmea2kTwaiLog(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,CAN_RECOVERY_PERIOD,&logger)); #ifdef GWBUTTON_PIN bool fixedApPass=false; @@ -838,8 +839,7 @@ void handleSendAndRead(bool handleRead){ TimeMonitor monitor(20,0.2); unsigned long lastHeapReport=0; -unsigned long lastCanRecovery=0; -static const unsigned long CAN_RECOVERY_PERIOD=3000; //ms +unsigned long lastCanStatus=0; void loop() { monitor.reset(); GWSYNCHRONIZED(&mainLock); @@ -848,6 +848,7 @@ void loop() { gwWifi.loop(); unsigned long now=millis(); monitor.setTime(2); + NMEA2000.checkRecovery(); if (HEAP_REPORT_TIME > 0 && now > (lastHeapReport+HEAP_REPORT_TIME)){ lastHeapReport=now; if (logger.isActive(GwLog::DEBUG)){ @@ -858,8 +859,8 @@ void loop() { logger.logDebug(GwLog::DEBUG,"Main loop %s",monitor.getLog().c_str()); } } - if (now > (lastCanRecovery + CAN_RECOVERY_PERIOD)){ - lastCanRecovery=now; + if (now > (lastCanStatus + CAN_RECOVERY_PERIOD)){ + lastCanStatus=now; Nmea2kTwai::Status canState=NMEA2000.getStatus(); logger.logDebug(GwLog::DEBUG, "can state %s, rxerr %d, txerr %d, txfail %d, txtimeout %d, rxmiss %d, rxoverrun %d", @@ -870,16 +871,6 @@ void loop() { canState.tx_timeouts, canState.rx_missed, canState.rx_overrun); - if (canState.state != Nmea2kTwai::ST_RUNNING){ - if (canState.state == Nmea2kTwai::ST_BUS_OFF){ - bool rt=NMEA2000.startRecovery(); - logger.logDebug(GwLog::LOG,"start can recovery - result %d",(int)rt); - } - if (canState.state == Nmea2kTwai::ST_STOPPED){ - bool rt=NMEA2000.CANOpen(); - logger.logDebug(GwLog::LOG,"restart can driver - result %d",(int)rt); - } - } } monitor.setTime(3); channels.allChannels([](GwChannel *c){ From e61cfac15ee76598097a7f8e9865658445100b31 Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 25 Aug 2023 21:13:12 +0200 Subject: [PATCH 012/178] example to lock idf version --- platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio.ini b/platformio.ini index 9d5e199..57045ed 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,6 +21,7 @@ platform = espressif32 @ 6.3.2 framework = arduino ;platform_packages= ; framework-arduinoespressif32 @ 3.20011.230801 +; framework-espidf @ 3.50101.0 lib_deps = ttlappalainen/NMEA2000-library @ 4.17.2 ttlappalainen/NMEA0183 @ 1.7.1 From 28431bfdcf89018a44c79c42d9f5d2e902ca5ba4 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 26 Aug 2023 10:53:46 +0200 Subject: [PATCH 013/178] add timer class for periodic runs --- lib/nmea2ktwai/Nmea2kTwai.cpp | 52 +++++++++++++++++++++++------------ lib/nmea2ktwai/Nmea2kTwai.h | 11 ++++---- lib/timer/GwTimer.h | 48 ++++++++++++++++++++++++++++++++ src/main.cpp | 46 ++++++++++++------------------- 4 files changed, 105 insertions(+), 52 deletions(-) create mode 100644 lib/timer/GwTimer.h diff --git a/lib/nmea2ktwai/Nmea2kTwai.cpp b/lib/nmea2ktwai/Nmea2kTwai.cpp index 5053f7e..15dceed 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.cpp +++ b/lib/nmea2ktwai/Nmea2kTwai.cpp @@ -1,16 +1,16 @@ #include "Nmea2kTwai.h" #include "driver/gpio.h" #include "driver/twai.h" -#include "GwLog.h" #define LOGID(id) ((id >> 8) & 0x1ffff) static const int TIMEOUT_OFFLINE=256; //# of timeouts to consider offline -Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin, unsigned long recP): - tNMEA2000(),RxPin(_RxPin),TxPin(_TxPin),recoveryPeriod(recP) +Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin, unsigned long recP, unsigned long logP): + tNMEA2000(),RxPin(_RxPin),TxPin(_TxPin) { - lastRecoveryCheck=millis(); + timers.addAction(logP,[this](){logStatus();}); + timers.addAction(recP,[this](){checkRecovery();}); } bool Nmea2kTwai::CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent) @@ -120,26 +120,42 @@ Nmea2kTwai::Status Nmea2kTwai::getStatus(){ return rt; } bool Nmea2kTwai::checkRecovery(){ - if (recoveryPeriod == 0) return false; //no check - unsigned long now=millis(); - if ((lastRecoveryCheck+recoveryPeriod) > now) return false; - lastRecoveryCheck=now; Status canState=getStatus(); bool strt=false; - if (canState.state != Nmea2kTwai::ST_RUNNING){ - if (canState.state == Nmea2kTwai::ST_BUS_OFF){ - strt=true; - bool rt=startRecovery(); - logDebug(LOG_INFO,"start can recovery - result %d",(int)rt); - } - if (canState.state == Nmea2kTwai::ST_STOPPED){ - bool rt=CANOpen(); - logDebug(LOG_INFO,"restart can driver - result %d",(int)rt); - } + if (canState.state != Nmea2kTwai::ST_RUNNING) + { + if (canState.state == Nmea2kTwai::ST_BUS_OFF) + { + strt = true; + bool rt = startRecovery(); + logDebug(LOG_INFO, "twai BUS_OFF: start can recovery - result %d", (int)rt); + } + if (canState.state == Nmea2kTwai::ST_STOPPED) + { + bool rt = CANOpen(); + logDebug(LOG_INFO, "twai STOPPED: restart can driver - result %d", (int)rt); + } } return strt; } +void Nmea2kTwai::loop(){ + timers.loop(); +} + +Nmea2kTwai::Status Nmea2kTwai::logStatus(){ + Status canState=getStatus(); + logDebug(LOG_INFO, "twai state %s, rxerr %d, txerr %d, txfail %d, txtimeout %d, rxmiss %d, rxoverrun %d", + stateStr(canState.state), + canState.rx_errors, + canState.tx_errors, + canState.tx_failed, + canState.tx_timeouts, + canState.rx_missed, + canState.rx_overrun); + return canState; +} + bool Nmea2kTwai::startRecovery(){ esp_err_t rt=twai_driver_uninstall(); if (rt != ESP_OK){ diff --git a/lib/nmea2ktwai/Nmea2kTwai.h b/lib/nmea2ktwai/Nmea2kTwai.h index 4a89c2c..f44e4ea 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.h +++ b/lib/nmea2ktwai/Nmea2kTwai.h @@ -1,11 +1,11 @@ #ifndef _NMEA2KTWAI_H #define _NMEA2KTWAI_H #include "NMEA2000.h" -#include "GwLog.h" +#include "GwTimer.h" class Nmea2kTwai : public tNMEA2000{ public: - Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin, unsigned long recP=0); + Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin, unsigned long recP=0, unsigned long logPeriod=0); typedef enum{ ST_STOPPED, ST_RUNNING, @@ -25,7 +25,7 @@ class Nmea2kTwai : public tNMEA2000{ STATE state=ST_ERROR; } Status; Status getStatus(); - bool checkRecovery(); + void loop(); static const char * stateStr(const STATE &st); virtual bool CANOpen(); virtual ~Nmea2kTwai(){}; @@ -48,11 +48,12 @@ class Nmea2kTwai : public tNMEA2000{ private: void initDriver(); bool startRecovery(); + bool checkRecovery(); + Status logStatus(); gpio_num_t TxPin; gpio_num_t RxPin; uint32_t txTimeouts=0; - unsigned long recoveryPeriod=0; - unsigned long lastRecoveryCheck=0; + GwIntervalRunner timers; }; #endif \ No newline at end of file diff --git a/lib/timer/GwTimer.h b/lib/timer/GwTimer.h new file mode 100644 index 0000000..e1fecdd --- /dev/null +++ b/lib/timer/GwTimer.h @@ -0,0 +1,48 @@ +#ifndef _GWTIMER_H +#define _GWTIMER_H +#include +#include +#include +class GwIntervalRunner{ + public: + using RunFunction=std::function; + private: + class Run{ + public: + unsigned long interval=0; + RunFunction runner; + unsigned long last=0; + Run(RunFunction r,unsigned long iv,unsigned long l=0): + runner(r),interval(iv),last(l){} + bool shouldRun(unsigned long now){ + if ((last+interval) > now) return false; + last=now; + return true; + } + bool runIf(unsigned long now){ + if (shouldRun(now)) { + runner(); + return true; + } + return false; + } + }; + std::vector runners; + unsigned long startTime=0; + public: + GwIntervalRunner(unsigned long now=millis()){ + startTime=now; + } + void addAction(unsigned long interval,RunFunction run,unsigned long start=0){ + if (start=0) start=startTime; + runners.push_back(Run(run,interval,start)); + } + bool loop(unsigned long now=millis()){ + bool rt=false; + for (auto it=runners.begin();it!=runners.end();it++){ + if (it->runIf(now)) rt=true; + } + return rt; + } +}; +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index c0bfad5..07e9042 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -65,6 +65,7 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting #include "GwTcpClient.h" #include "GwChannel.h" #include "GwChannelList.h" +#include "GwTimer.h" #ifdef FALLBACK_SERIAL #ifdef CAN_ESP_DEBUG #define CDBS &Serial @@ -116,7 +117,7 @@ class Nmea2kTwaiLog : public Nmea2kTwai{ GwLog* logger; public: Nmea2kTwaiLog(gpio_num_t _TxPin, gpio_num_t _RxPin, unsigned long recoveryPeriod,GwLog *l): - Nmea2kTwai(_TxPin,_RxPin,recoveryPeriod),logger(l){} + Nmea2kTwai(_TxPin,_RxPin,recoveryPeriod,recoveryPeriod),logger(l){} virtual void logDebug(int level, const char *fmt,...){ va_list args; va_start(args,fmt); @@ -157,7 +158,7 @@ GwWebServer webserver(&logger,&mainQueue,80); GwCounter countNMEA2KIn("count2Kin"); GwCounter countNMEA2KOut("count2Kout"); - +GwIntervalRunner timers; bool checkPass(String hash){ return config.checkPass(hash); @@ -388,6 +389,7 @@ protected: status["heap"]=(long)xPortGetFreeHeapSize(); Nmea2kTwai::Status n2kState=NMEA2000.getStatus(); status["n2kstate"]=NMEA2000.stateStr(n2kState.state); + status["n2knode"]=NodeAddress; //nmea0183Converter->toJson(status); countNMEA2KIn.toJson(status); countNMEA2KOut.toJson(status); @@ -675,6 +677,7 @@ void handleConfigRequestData(AsyncWebServerRequest *request, uint8_t *data, size } } +TimeMonitor monitor(20,0.2); void setup() { mainLock=xSemaphoreCreateMutex(); @@ -799,6 +802,7 @@ void setup() { logger.flush(); NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode, NodeAddress); NMEA2000.SetForwardOwnMessages(false); + NMEA2000.SetHeartbeatInterval(5000); if (sendOutN2k){ // Set the information for other bus devices, which messages we support unsigned long *pgns=toN2KConverter->handledPgns(); @@ -826,6 +830,15 @@ void setup() { GWSYNCHRONIZED(&mainLock); userCodeHandler.startUserTasks(MIN_USER_TASK); } + timers.addAction(HEAP_REPORT_TIME,[](){ + if (logger.isActive(GwLog::DEBUG)){ + logger.logDebug(GwLog::DEBUG,"Heap free=%ld, minFree=%ld", + (long)xPortGetFreeHeapSize(), + (long)xPortGetMinimumEverFreeHeapSize() + ); + logger.logDebug(GwLog::DEBUG,"Main loop %s",monitor.getLog().c_str()); + } + }); logger.logString("wifi AP pass: %s",fixedApPass? gwWifi.AP_password:config.getString(config.apPassword).c_str()); logger.logString("admin pass: %s",config.getString(config.adminPassword).c_str()); logger.logDebug(GwLog::LOG,"setup done"); @@ -837,9 +850,6 @@ void handleSendAndRead(bool handleRead){ }); } -TimeMonitor monitor(20,0.2); -unsigned long lastHeapReport=0; -unsigned long lastCanStatus=0; void loop() { monitor.reset(); GWSYNCHRONIZED(&mainLock); @@ -848,30 +858,8 @@ void loop() { gwWifi.loop(); unsigned long now=millis(); monitor.setTime(2); - NMEA2000.checkRecovery(); - if (HEAP_REPORT_TIME > 0 && now > (lastHeapReport+HEAP_REPORT_TIME)){ - lastHeapReport=now; - if (logger.isActive(GwLog::DEBUG)){ - logger.logDebug(GwLog::DEBUG,"Heap free=%ld, minFree=%ld", - (long)xPortGetFreeHeapSize(), - (long)xPortGetMinimumEverFreeHeapSize() - ); - logger.logDebug(GwLog::DEBUG,"Main loop %s",monitor.getLog().c_str()); - } - } - if (now > (lastCanStatus + CAN_RECOVERY_PERIOD)){ - lastCanStatus=now; - Nmea2kTwai::Status canState=NMEA2000.getStatus(); - logger.logDebug(GwLog::DEBUG, - "can state %s, rxerr %d, txerr %d, txfail %d, txtimeout %d, rxmiss %d, rxoverrun %d", - NMEA2000.stateStr(canState.state), - canState.rx_errors, - canState.tx_errors, - canState.tx_failed, - canState.tx_timeouts, - canState.rx_missed, - canState.rx_overrun); - } + timers.loop(); + NMEA2000.loop(); monitor.setTime(3); channels.allChannels([](GwChannel *c){ c->loop(true,false); From b7a7dad290848d79ec7d6b896435204394de549f Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 26 Aug 2023 11:06:11 +0200 Subject: [PATCH 014/178] better display for bus off condition --- lib/nmea2ktwai/Nmea2kTwai.cpp | 1 + lib/nmea2ktwai/Nmea2kTwai.h | 2 ++ src/main.cpp | 15 +++++++++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/nmea2ktwai/Nmea2kTwai.cpp b/lib/nmea2ktwai/Nmea2kTwai.cpp index 15dceed..a5f0312 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.cpp +++ b/lib/nmea2ktwai/Nmea2kTwai.cpp @@ -157,6 +157,7 @@ Nmea2kTwai::Status Nmea2kTwai::logStatus(){ } bool Nmea2kTwai::startRecovery(){ + lastRecoveryStart=millis(); esp_err_t rt=twai_driver_uninstall(); if (rt != ESP_OK){ logDebug(LOG_ERR,"twai: deinit for recovery failed with %x",(int)rt); diff --git a/lib/nmea2ktwai/Nmea2kTwai.h b/lib/nmea2ktwai/Nmea2kTwai.h index f44e4ea..46e9b0c 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.h +++ b/lib/nmea2ktwai/Nmea2kTwai.h @@ -25,6 +25,7 @@ class Nmea2kTwai : public tNMEA2000{ STATE state=ST_ERROR; } Status; Status getStatus(); + unsigned long getLastRecoveryStart(){return lastRecoveryStart;} void loop(); static const char * stateStr(const STATE &st); virtual bool CANOpen(); @@ -54,6 +55,7 @@ class Nmea2kTwai : public tNMEA2000{ gpio_num_t RxPin; uint32_t txTimeouts=0; GwIntervalRunner timers; + unsigned long lastRecoveryStart=0; }; #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 07e9042..eba541d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -112,6 +112,7 @@ GwConfigHandler config(&logger); #include "Nmea2kTwai.h" static const unsigned long CAN_RECOVERY_PERIOD=3000; //ms +static const unsigned long NMEA2000_HEARTBEAT_INTERVAL=5000; class Nmea2kTwaiLog : public Nmea2kTwai{ private: GwLog* logger; @@ -388,7 +389,17 @@ protected: status["fwtype"]= firmwareType; status["heap"]=(long)xPortGetFreeHeapSize(); Nmea2kTwai::Status n2kState=NMEA2000.getStatus(); - status["n2kstate"]=NMEA2000.stateStr(n2kState.state); + Nmea2kTwai::STATE driverState=n2kState.state; + if (driverState == Nmea2kTwai::ST_RUNNING){ + unsigned long lastRec=NMEA2000.getLastRecoveryStart(); + if (lastRec > 0 && (lastRec+NMEA2000_HEARTBEAT_INTERVAL*2) > millis()){ + //we still report bus off at least for 2 heartbeat intervals + //this avoids always reporting BUS_OFF-RUNNING-BUS_OFF if the bus off condition + //remains + driverState=Nmea2kTwai::ST_BUS_OFF; + } + } + status["n2kstate"]=NMEA2000.stateStr(driverState); status["n2knode"]=NodeAddress; //nmea0183Converter->toJson(status); countNMEA2KIn.toJson(status); @@ -802,7 +813,7 @@ void setup() { logger.flush(); NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode, NodeAddress); NMEA2000.SetForwardOwnMessages(false); - NMEA2000.SetHeartbeatInterval(5000); + NMEA2000.SetHeartbeatInterval(NMEA2000_HEARTBEAT_INTERVAL); if (sendOutN2k){ // Set the information for other bus devices, which messages we support unsigned long *pgns=toN2KConverter->handledPgns(); From 3c9670b8d0189a03d24a8196002b587081ca1540 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 26 Aug 2023 11:41:40 +0200 Subject: [PATCH 015/178] better display of n2k nodeid/status --- web/index.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/web/index.html b/web/index.html index e50eabc..f6c15fa 100644 --- a/web/index.html +++ b/web/index.html @@ -47,7 +47,7 @@ # clients --- -
+
TCP client connected ---
@@ -55,14 +55,15 @@ TCP client error ---
-
+
Free heap ---
- N2K State + NMEA2000 State + [---UNKNOWN -
+
From e17ab3be585ae5eb92df7e814b995cbf05620b04 Mon Sep 17 00:00:00 2001 From: free-x Date: Sat, 26 Aug 2023 15:51:53 +0200 Subject: [PATCH 016/178] add atoms3 with canunit env --- lib/hardware/GwHardware.h | 23 +++++++++++++++++++++-- platformio.ini | 13 ++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 8b6dd9f..11e16ba 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -50,14 +50,33 @@ //led handling //if we define GWLED_FASTNET the arduino fastnet lib is used #define GWLED_FASTLED -#define GWLED_TYPE SK6812 +#define GWLED_TYPE WS2812 //color schema for fastled #define GWLED_SCHEMA GRB -#define GWLED_PIN GPIO_NUM_27 +#define GWLED_PIN GPIO_NUM_35 //brightness 0...255 #define GWLED_BRIGHTNESS 64 #endif +#ifdef BOARD_M5ATOMS3_CANUNIT +#define ESP32_CAN_TX_PIN GPIO_NUM_26 +#define ESP32_CAN_RX_PIN GPIO_NUM_32 +#define GWBUTTON_PIN GPIO_NUM_41 +#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_35 +//brightness 0...255 +//#define GWLED_BRIGHTNESS 64 +#endif + + #ifdef BOARD_M5ATOM_RS232_CANUNIT #define ESP32_CAN_TX_PIN GPIO_NUM_26 #define ESP32_CAN_RX_PIN GPIO_NUM_32 diff --git a/platformio.ini b/platformio.ini index 57045ed..e01cad6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -27,7 +27,8 @@ lib_deps = ttlappalainen/NMEA0183 @ 1.7.1 ArduinoJson @ 6.18.5 ottowinter/ESPAsyncWebServer-esphome@2.0.1 - fastled/FastLED @ 3.4.0 + #fastled/FastLED @ 3.4.0 + fastled/FastLED @ 3.6.0 board_build.embed_files = lib/generated/index.html.gz lib/generated/index.js.gz @@ -62,6 +63,16 @@ build_flags = upload_port = /dev/esp32 upload_protocol = esptool +[env:m5stack-atoms3-canunit] +board = m5stack-atoms3 +lib_deps = ${env.lib_deps} +build_flags = + -D BOARD_M5ATOMS3_CANUNIT + ${env.build_flags} +upload_port = /dev/esp32 +upload_protocol = esptool + + [env:m5stack-atom-rs232-canunit] board = m5stack-atom lib_deps = ${env.lib_deps} From 5a8b30235c51bd37063dd0a5c218ee65d851f272 Mon Sep 17 00:00:00 2001 From: free-x Date: Sat, 26 Aug 2023 16:05:04 +0200 Subject: [PATCH 017/178] adjust Grove GPIO for atoms3 --- lib/hardware/GwHardware.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 11e16ba..ed774c8 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -59,8 +59,8 @@ #endif #ifdef BOARD_M5ATOMS3_CANUNIT -#define ESP32_CAN_TX_PIN GPIO_NUM_26 -#define ESP32_CAN_RX_PIN GPIO_NUM_32 +#define ESP32_CAN_TX_PIN GPIO_NUM_2 +#define ESP32_CAN_RX_PIN GPIO_NUM_1 #define GWBUTTON_PIN GPIO_NUM_41 #define GWBUTTON_ACTIVE LOW //if GWBUTTON_PULLUPDOWN we enable a pulup/pulldown From 74f4937844ce9223edadde5f6eb204256ea3a8be Mon Sep 17 00:00:00 2001 From: free-x Date: Sat, 26 Aug 2023 16:17:21 +0200 Subject: [PATCH 018/178] clean up atom/atoms3 --- lib/hardware/GwHardware.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index ed774c8..df9929d 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -40,6 +40,7 @@ //brightness 0...255 #define GWLED_BRIGHTNESS 64 #endif + #ifdef BOARD_M5ATOM_CANUNIT #define ESP32_CAN_TX_PIN GPIO_NUM_26 #define ESP32_CAN_RX_PIN GPIO_NUM_32 @@ -50,10 +51,10 @@ //led handling //if we define GWLED_FASTNET the arduino fastnet lib is used #define GWLED_FASTLED -#define GWLED_TYPE WS2812 +#define GWLED_TYPE SK6812 //color schema for fastled #define GWLED_SCHEMA GRB -#define GWLED_PIN GPIO_NUM_35 +#define GWLED_PIN GPIO_NUM_27 //brightness 0...255 #define GWLED_BRIGHTNESS 64 #endif @@ -67,13 +68,13 @@ #define GWBUTTON_PULLUPDOWN //led handling //if we define GWLED_FASTNET the arduino fastnet lib is used -//#define GWLED_FASTLED -//#define GWLED_TYPE SK6812 +#define GWLED_FASTLED +#define GWLED_TYPE WS2812 //color schema for fastled #define GWLED_SCHEMA GRB #define GWLED_PIN GPIO_NUM_35 //brightness 0...255 -//#define GWLED_BRIGHTNESS 64 +#define GWLED_BRIGHTNESS 64 #endif From 5bdcd0f8a9ae024a7d790f539cd0532f7789b4ff Mon Sep 17 00:00:00 2001 From: free-x Date: Sun, 27 Aug 2023 10:18:56 +0200 Subject: [PATCH 019/178] Add dokumentation about AtomS3 --- doc/Hardware.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/Hardware.md b/doc/Hardware.md index 988c8d6..07d7694 100644 --- a/doc/Hardware.md +++ b/doc/Hardware.md @@ -78,6 +78,13 @@ Can be used e.g. as an NMEA2000 Adapter for a laptop running e.g. OpenCPN with t ![OpenCPN on Laptop via USB and MFD on Android via WiFi](in_action1.jpg) ![OpenCPN on Laptop via USB and AvNav on Android via WiFi](in_action2.jpg) +M5 Stack AtomS3Lite Canunit (experimental since dev20230826) +--------------------- +* Hardware: [M5_ATOMS3 Lite](http://docs.m5stack.com/en/core/AtomS3%20Lite) + [CAN Unit](http://docs.m5stack.com/en/unit/can) +* Prebuild Binary: m5stack-atoms3-canunit-all.bin +* Build Define: BOARD_M5ATOMS3_CANUNIT +* Power: Via USB + M5 Stick C Canunit ------------------ * Hardware: [M5_StickC+](http://docs.m5stack.com/en/core/m5stickc_plus) + [CAN Unit](http://docs.m5stack.com/en/unit/can) From 9fa55c1edfc85b49379c98cc9f92d95ff2365299 Mon Sep 17 00:00:00 2001 From: free-x Date: Tue, 29 Aug 2023 16:04:06 +0200 Subject: [PATCH 020/178] fixes for atom s3 lite --- platformio.ini | 2 +- tools/99-usb-serial.rules | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index e01cad6..c9d3290 100644 --- a/platformio.ini +++ b/platformio.ini @@ -69,7 +69,7 @@ lib_deps = ${env.lib_deps} build_flags = -D BOARD_M5ATOMS3_CANUNIT ${env.build_flags} -upload_port = /dev/esp32 +upload_port = /dev/esp32s3 upload_protocol = esptool diff --git a/tools/99-usb-serial.rules b/tools/99-usb-serial.rules index f1f7573..f7fe7fb 100644 --- a/tools/99-usb-serial.rules +++ b/tools/99-usb-serial.rules @@ -1 +1,3 @@ SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="esp32" +SUBSYSTEM=="tty", ATTRS{idVendor}=="303a", ATTRS{idProduct}=="1001", SYMLINK+="esp32s3" + From 9178499e48f9ca46d7bc4825fd6a495e93f3fc40 Mon Sep 17 00:00:00 2001 From: free-x Date: Tue, 29 Aug 2023 16:07:19 +0200 Subject: [PATCH 021/178] sync build.os ci with release --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf2c408..2562875 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-20.04] + os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: From 5dc2c98642f44d207dd351f8a2715ab0a4e5d870 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 30 Aug 2023 10:15:25 +0200 Subject: [PATCH 022/178] intermediate: reorganize hardware definitions --- extra_script.py | 5 +- lib/hardware/GwHardware.h | 145 +++++++++++++++++--------------------- platformio.ini | 9 +++ 3 files changed, 77 insertions(+), 82 deletions(-) diff --git a/extra_script.py b/extra_script.py index af10f27..8a50062 100644 --- a/extra_script.py +++ b/extra_script.py @@ -282,8 +282,11 @@ def cleangenerated(source, target, env): print("#prescript...") prebuild(env) +board="PLATFORM_BOARD_%s"%env["BOARD"].replace("-","_").upper() +print("Board=#%s#"%board) env.Append( - LINKFLAGS=[ "-u", "custom_app_desc" ] + LINKFLAGS=[ "-u", "custom_app_desc" ], + CPPDEFINES=[(board,"1")] ) #script does not run on clean yet - maybe in the future env.AddPostAction("clean",cleangenerated) diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index df9929d..596a571 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -15,110 +15,93 @@ #define _GWHARDWARE_H #include "GwUserTasks.h" +//general definitions for M5AtomLite +#ifdef PLATFORM_BOARD_M5STACK_ATOM + #define GROOVE_PIN_1 GPIO_NUM_26 + #define GROOVE_PIN_2 GPIO_NUM_32 + #define GWBUTTON_PIN GPIO_NUM_39 + #define GWLED_FASTLED + #define GWLED_TYPE SK6812 + //color schema for fastled + #define GWLED_SCHEMA GRB + #define GWLED_PIN GPIO_NUM_27 + #define GWBUTTON_ACTIVE LOW + //if GWBUTTON_PULLUPDOWN we enable a pulup/pulldown + #define GWBUTTON_PULLUPDOWN + #define BOARD_LEFT1 GPIO_NUM_22 + #define BOARD_LEFT2 GPIO_NUM_19 +#endif +//general definitiones for M5AtomS3 +#ifdef PLATFORM_BOARD_M5STACK_ATOMS3 + #define GROOVE_PIN_1 GPIO_NUM_2 + #define GROOVE_PIN_2 GPIO_NUM_1 + #define GWBUTTON_PIN GPIO_NUM_41 + #define GWLED_FASTLED + #define GWLED_TYPE WS2812 + //color schema for fastled + #define GWLED_SCHEMA GRB + #define GWLED_PIN GPIO_NUM_35 + #define GWBUTTON_ACTIVE LOW + //if GWBUTTON_PULLUPDOWN we enable a pulup/pulldown + #define GWBUTTON_PULLUPDOWN + #define BOARD_LEFT1 GPIO_NUM_5 + #define BOARD_LEFT2 GPIO_NUM_6 +#endif + +//serial adapter at the M5 groove pins +#define SERIAL_GROOVE(SMODE) \ + static const gpio_num_t GWSERIAL_TX=GROOVE_PIN_1; \ + static const gpio_num_t GWSERIAL_RX=GROOVE_PIN_2; \ + static const char* GWSERIAL_MODE=SMODE; + +//M5 Serial (Atomic RS232 Base) +#define SERIAL_KIT(SMODE) \ + static const gpio_num_t GWSERIAL_TX=BOARD_LEFT2; \ + static const gpio_num_t GWSERIAL_RX=BOARD_LEFT1; \ + static const char* GWSERIAL_MODE=SMODE; + + +#define CANKIT \ + static const gpio_num_t ESP32_CAN_TX_PIN=BOARD_LEFT1; \ + static const gpio_num_t ESP32_CAN_RX_PIN=BOARD_LEFT2; + +#define CANUNIT \ + static const gpio_num_t ESP32_CAN_TX_PIN=GROOVE_PIN_1; \ + static const gpio_num_t ESP32_CAN_RX_PIN=GROOVE_PIN_2; //SERIAL_MODE can be: UNI (RX or TX only), BI (both), RX, TX //board specific pins #ifdef BOARD_M5ATOM -#define ESP32_CAN_TX_PIN GPIO_NUM_22 -#define ESP32_CAN_RX_PIN GPIO_NUM_19 +CANKIT //150mA if we power from the bus #define N2K_LOAD_LEVEL 3 //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 +SERIAL_GROOVE("UNI") //brightness 0...255 #define GWLED_BRIGHTNESS 64 #endif #ifdef BOARD_M5ATOM_CANUNIT -#define ESP32_CAN_TX_PIN GPIO_NUM_26 -#define ESP32_CAN_RX_PIN GPIO_NUM_32 -#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 +CANUNIT #define GWLED_BRIGHTNESS 64 +//150mA if we power from the bus +#define N2K_LOAD_LEVEL 3 #endif #ifdef BOARD_M5ATOMS3_CANUNIT -#define ESP32_CAN_TX_PIN GPIO_NUM_2 -#define ESP32_CAN_RX_PIN GPIO_NUM_1 -#define GWBUTTON_PIN GPIO_NUM_41 -#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 WS2812 -//color schema for fastled -#define GWLED_SCHEMA GRB -#define GWLED_PIN GPIO_NUM_35 -//brightness 0...255 +CANUNIT #define GWLED_BRIGHTNESS 64 #endif #ifdef BOARD_M5ATOM_RS232_CANUNIT -#define ESP32_CAN_TX_PIN GPIO_NUM_26 -#define ESP32_CAN_RX_PIN GPIO_NUM_32 -//if using rs232 -#define GWSERIAL_TX 19 -#define GWSERIAL_RX 22 -#define GWSERIAL_MODE "BI" -#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 +CANUNIT +SERIALKIT("BI") #define GWLED_BRIGHTNESS 64 #endif #ifdef BOARD_M5ATOM_RS485_CANUNIT -#define ESP32_CAN_TX_PIN GPIO_NUM_26 -#define ESP32_CAN_RX_PIN GPIO_NUM_32 -//if using rs232 -#define GWSERIAL_TX 19 -#define GWSERIAL_RX 22 -#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 +SERIALKIT("UNI") +CANUNIT #define GWLED_BRIGHTNESS 64 #endif @@ -131,7 +114,7 @@ #define ESP32_CAN_TX_PIN GPIO_NUM_5 #define ESP32_CAN_RX_PIN GPIO_NUM_4 //serial input only -#define GWSERIAL_RX 16 +#define GWSERIAL_RX GPIO_NUM_16 #define GWSERIAL_MODE "RX" #define GWBUTTON_PIN GPIO_NUM_0 diff --git a/platformio.ini b/platformio.ini index c9d3290..817b7bb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -54,6 +54,15 @@ build_flags = upload_port = /dev/esp32 upload_protocol = esptool +[env:m5stack-atoms3] +board = m5stack-atoms3 +lib_deps = ${env.lib_deps} +build_flags = + -D BOARD_M5ATOMS3 + ${env.build_flags} +upload_port = /dev/esp32s3 +upload_protocol = esptool + [env:m5stack-atom-canunit] board = m5stack-atom lib_deps = ${env.lib_deps} From 55207473a7a2b4584a26b44343effea6e7272337 Mon Sep 17 00:00:00 2001 From: free-x Date: Wed, 30 Aug 2023 11:08:49 +0200 Subject: [PATCH 023/178] bump nmea2000 library --- lib/nmea0183ton2k/NMEA0183AIStoNMEA2000.h | 2 +- lib/nmea2kto0183/N2kDataToNMEA0183.cpp | 7 ++++--- platformio.ini | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/nmea0183ton2k/NMEA0183AIStoNMEA2000.h b/lib/nmea0183ton2k/NMEA0183AIStoNMEA2000.h index bd22fd6..169f181 100644 --- a/lib/nmea0183ton2k/NMEA0183AIStoNMEA2000.h +++ b/lib/nmea0183ton2k/NMEA0183AIStoNMEA2000.h @@ -160,7 +160,7 @@ class MyAisDecoder : public AIS::AisDecoder _uToPort + _uToStarboard, _uToStarboard, _uToBow, eta_days, (_uEtaHour * 3600) + (_uEtaMinute * 60), _uDraught / 10.0, Dest, (tN2kAISVersion) _ais_version, (tN2kGNSStype) _uFixType, - (tN2kAISDTE) _dte, (tN2kAISTranceiverInfo) _ais_version); + (tN2kAISDTE) _dte, (tN2kAISTransceiverInformation) _ais_version); send(N2kMsg); } diff --git a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp index 1e61d8e..1f490df 100644 --- a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp +++ b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp @@ -738,7 +738,7 @@ private: char _Destination[21]; tN2kAISVersion _AISversion; tN2kGNSStype _GNSStype; - tN2kAISTranceiverInfo _AISinfo; + tN2kAISTransceiverInformation _AISinfo; tN2kAISDTE _DTE; tNMEA0183AISMsg NMEA0183AISMsg; @@ -848,15 +848,16 @@ private: tN2kAISUnit _Unit; bool _Display, _DSC, _Band, _Msg22, _State; tN2kAISMode _Mode; + tN2kAISTransceiverInformation _AISTranceiverInformation; if (ParseN2kPGN129039(N2kMsg, _MessageID, _Repeat, _UserID, _Latitude, _Longitude, _Accuracy, _RAIM, - _Seconds, _COG, _SOG, _Heading, _Unit, _Display, _DSC, _Band, _Msg22, _Mode, _State)) + _Seconds, _COG, _SOG, _AISTranceiverInformation, _Heading, _Unit, _Display, _DSC, _Band, _Msg22, _Mode, _State)) { tNMEA0183AISMsg NMEA0183AISMsg; if (SetAISClassBMessage18(NMEA0183AISMsg, _MessageID, _Repeat, _UserID, _Latitude, _Longitude, _Accuracy, _RAIM, - _Seconds, _COG, _SOG, _Heading, _Unit, _Display, _DSC, _Band, _Msg22, _Mode, _State)) + _Seconds, _COG, _SOG, _Heading, _Unit, _Display, _DSC, _Band, _Msg22, _Mode, _State)) { SendMessage(NMEA0183AISMsg); diff --git a/platformio.ini b/platformio.ini index c9d3290..646b569 100644 --- a/platformio.ini +++ b/platformio.ini @@ -23,7 +23,7 @@ framework = arduino ; framework-arduinoespressif32 @ 3.20011.230801 ; framework-espidf @ 3.50101.0 lib_deps = - ttlappalainen/NMEA2000-library @ 4.17.2 + ttlappalainen/NMEA2000-library @ 4.18.7 ttlappalainen/NMEA0183 @ 1.7.1 ArduinoJson @ 6.18.5 ottowinter/ESPAsyncWebServer-esphome@2.0.1 From 0e71baaa9e71ba118b170fd192589733eb8fcdc3 Mon Sep 17 00:00:00 2001 From: free-x Date: Wed, 30 Aug 2023 11:21:44 +0200 Subject: [PATCH 024/178] bump nmea0183 library --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 646b569..ecf3001 100644 --- a/platformio.ini +++ b/platformio.ini @@ -24,7 +24,7 @@ framework = arduino ; framework-espidf @ 3.50101.0 lib_deps = ttlappalainen/NMEA2000-library @ 4.18.7 - ttlappalainen/NMEA0183 @ 1.7.1 + ttlappalainen/NMEA0183 @ 1.9.1 ArduinoJson @ 6.18.5 ottowinter/ESPAsyncWebServer-esphome@2.0.1 #fastled/FastLED @ 3.4.0 From ac91aeb491aee271552b056b3420971638e4eb22 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 30 Aug 2023 16:42:08 +0200 Subject: [PATCH 025/178] reorganize HW defs again, add generic boards, allow undefined CAN rx/tx --- lib/hardware/GwHardware.h | 96 ++++++++++++++++++++++++++------------- platformio.ini | 24 ++++++++++ src/main.cpp | 8 ++++ 3 files changed, 97 insertions(+), 31 deletions(-) diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 596a571..84df350 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -48,68 +48,62 @@ #define BOARD_LEFT2 GPIO_NUM_6 #endif -//serial adapter at the M5 groove pins -#define SERIAL_GROOVE(SMODE) \ - static const gpio_num_t GWSERIAL_TX=GROOVE_PIN_1; \ - static const gpio_num_t GWSERIAL_RX=GROOVE_PIN_2; \ - static const char* GWSERIAL_MODE=SMODE; +//M5Stick C +#ifdef PLATFORM_BOARD_M5STICK_C + #define GROOVE_PIN_1 GPIO_NUM_32 + #define GROOVE_PIN_2 GPIO_NUM_31 +#endif -//M5 Serial (Atomic RS232 Base) -#define SERIAL_KIT(SMODE) \ - static const gpio_num_t GWSERIAL_TX=BOARD_LEFT2; \ - static const gpio_num_t GWSERIAL_RX=BOARD_LEFT1; \ - static const char* GWSERIAL_MODE=SMODE; - - -#define CANKIT \ - static const gpio_num_t ESP32_CAN_TX_PIN=BOARD_LEFT1; \ - static const gpio_num_t ESP32_CAN_RX_PIN=BOARD_LEFT2; - -#define CANUNIT \ - static const gpio_num_t ESP32_CAN_TX_PIN=GROOVE_PIN_1; \ - static const gpio_num_t ESP32_CAN_RX_PIN=GROOVE_PIN_2; -//SERIAL_MODE can be: UNI (RX or TX only), BI (both), RX, TX -//board specific pins #ifdef BOARD_M5ATOM -CANKIT +#define M5_CAN_KIT //150mA if we power from the bus #define N2K_LOAD_LEVEL 3 //if using tail485 -SERIAL_GROOVE("UNI") +#define SERIAL_GROOVE_485 +//brightness 0...255 +#define GWLED_BRIGHTNESS 64 +#endif + +#ifdef BOARD_M5ATOMS3 +#define M5_CAN_KIT +//150mA if we power from the bus +#define N2K_LOAD_LEVEL 3 +//if using tail485 +#define SERIAL_GROOVE_485 //brightness 0...255 #define GWLED_BRIGHTNESS 64 #endif #ifdef BOARD_M5ATOM_CANUNIT -CANUNIT +#define M5_CANUNIT #define GWLED_BRIGHTNESS 64 //150mA if we power from the bus #define N2K_LOAD_LEVEL 3 #endif #ifdef BOARD_M5ATOMS3_CANUNIT -CANUNIT +#define M5_CANUNIT #define GWLED_BRIGHTNESS 64 #endif #ifdef BOARD_M5ATOM_RS232_CANUNIT -CANUNIT -SERIALKIT("BI") +#define M5_CANUNIT +#define M5_SERIAL_KIT_232 #define GWLED_BRIGHTNESS 64 #endif #ifdef BOARD_M5ATOM_RS485_CANUNIT -SERIALKIT("UNI") -CANUNIT +#define M5_SERIAL_KIT_485 +#define M5_CANUNIT #define GWLED_BRIGHTNESS 64 #endif #ifdef BOARD_M5STICK_CANUNIT -#define ESP32_CAN_TX_PIN GPIO_NUM_32 -#define ESP32_CAN_RX_PIN GPIO_NUM_33 +#define M5_CANUNIT #endif + #ifdef BOARD_HOMBERGER #define ESP32_CAN_TX_PIN GPIO_NUM_5 #define ESP32_CAN_RX_PIN GPIO_NUM_4 @@ -123,4 +117,44 @@ CANUNIT #define GWBUTTON_PULLUPDOWN #endif +//below we define the final device config based on the above +//boards and peripherals +//this allows us toe easily also set the from outside +//serial adapter at the M5 groove pins +#ifdef SERIAL_GROOVE_485 + #define GWSERIAL_TX GROOVE_PIN_1 + #define GWSERIAL_RX GROOVE_PIN_2 + #define GWSERIAL_MODE "UNI" +#endif +#ifdef SERIAL_GROOVE_232 + #define GWSERIAL_TX GROOVE_PIN_1 + #define GWSERIAL_RX GROOVE_PIN_2 + #define GWSERIAL_MODE "BI" +#endif + +//M5 Serial (Atomic RS232 Base) +#ifdef M5_SERIAL_KIT_232 + #define GWSERIAL_TX BOARD_LEFT2 + #define GWSERIAL_RX BOARD_LEFT1 + #define GWSERIAL_MODE "BI" +#endif + +//M5 Serial (Atomic RS485 Base) +#ifdef M5_SERIAL_KIT_485 + #define GWSERIAL_TX BOARD_LEFT2 + #define GWSERIAL_RX BOARD_LEFT1 + #define GWSERIAL_MODE "UNI" +#endif + +//can kit for M5 Atom +#ifdef M5_CAN_KIT + #define ESP32_CAN_TX_PIN BOARD_LEFT1 + #define ESP32_CAN_RX_PIN BOARD_LEFT2 +#endif +//CAN via groove +#ifdef M5_CANUNIT + #define ESP32_CAN_TX_PIN GROOVE_PIN_1 + #define ESP32_CAN_RX_PIN GROOVE_PIN_2 +#endif + #endif diff --git a/platformio.ini b/platformio.ini index 817b7bb..0ca5fbf 100644 --- a/platformio.ini +++ b/platformio.ini @@ -54,6 +54,14 @@ build_flags = upload_port = /dev/esp32 upload_protocol = esptool +[env:m5stack-atom-generic] +board = m5stack-atom +lib_deps = ${env.lib_deps} +build_flags = + ${env.build_flags} +upload_port = /dev/esp32 +upload_protocol = esptool + [env:m5stack-atoms3] board = m5stack-atoms3 lib_deps = ${env.lib_deps} @@ -63,6 +71,14 @@ build_flags = upload_port = /dev/esp32s3 upload_protocol = esptool +[env:m5stack-atoms3-generic] +board = m5stack-atoms3 +lib_deps = ${env.lib_deps} +build_flags = + ${env.build_flags} +upload_port = /dev/esp32s3 +upload_protocol = esptool + [env:m5stack-atom-canunit] board = m5stack-atom lib_deps = ${env.lib_deps} @@ -118,3 +134,11 @@ build_flags = ${env.build_flags} upload_port = /dev/esp32 upload_protocol = esptool + +[env:nodemcu-generic] +board = nodemcu-32s +lib_deps = ${env.lib_deps} +build_flags = + ${env.build_flags} +upload_port = /dev/esp32 +upload_protocol = esptool diff --git a/src/main.cpp b/src/main.cpp index eba541d..c884a62 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -127,6 +127,14 @@ class Nmea2kTwaiLog : public Nmea2kTwai{ } }; +#ifndef ESP32_CAN_TX_PIN + #pragma message "WARNING: ESP32_CAN_TX_PIN not defined" + #define ESP32_CAN_TX_PIN GPIO_NUM_NC +#endif +#ifndef ESP32_CAN_RX_PIN + #pragma message "WARNING: ESP32_CAN_RX_PIN not defined" + #define ESP32_CAN_RX_PIN GPIO_NUM_NC +#endif Nmea2kTwai &NMEA2000=*(new Nmea2kTwaiLog(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,CAN_RECOVERY_PERIOD,&logger)); From dc2f353893805d2c4ef0bdda468e0be593b312d7 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 30 Aug 2023 19:55:26 +0200 Subject: [PATCH 026/178] using the externals for USBSerial and Serial1 --- lib/channel/GwChannelList.cpp | 10 +++++----- lib/hardware/GwHardware.h | 9 +++++++++ lib/serial/GwSerial.cpp | 17 +++++------------ lib/serial/GwSerial.h | 6 ++---- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/channel/GwChannelList.cpp b/lib/channel/GwChannelList.cpp index 635799a..5465ea9 100644 --- a/lib/channel/GwChannelList.cpp +++ b/lib/channel/GwChannelList.cpp @@ -63,8 +63,8 @@ void GwChannelList::begin(bool fallbackSerial){ GwChannel *channel=NULL; //usb if (! fallbackSerial){ - GwSerial *usb=new GwSerial(NULL,0,USB_CHANNEL_ID); - usb->setup(config->getInt(config->usbBaud),3,1); + GwSerial *usb=new GwSerial(NULL,&USBSerial,USB_CHANNEL_ID); + USBSerial.begin(config->getInt(config->usbBaud)); logger->setWriter(new GwSerialLog(usb,config->getBool(config->usbActisense))); logger->prefix="GWSERIAL:"; channel=new GwChannel(logger,"USB",USB_CHANNEL_ID); @@ -133,9 +133,9 @@ void GwChannelList::begin(bool fallbackSerial){ ); if (serialtx != -1 || serialrx != -1 ){ LOG_DEBUG(GwLog::LOG,"creating serial interface rx=%d, tx=%d",serialrx,serialtx); - GwSerial *serial=new GwSerial(logger,1,SERIAL1_CHANNEL_ID,serCanRead); - int rt=serial->setup(config->getInt(config->serialBaud,115200),serialrx,serialtx); - LOG_DEBUG(GwLog::LOG,"starting serial returns %d",rt); + Serial1.begin(config->getInt(config->serialBaud,115200),SERIAL_8N1,serialrx,serialtx); + GwSerial *serial=new GwSerial(logger,&Serial1,SERIAL1_CHANNEL_ID,serCanRead); + LOG_DEBUG(GwLog::LOG,"starting serial1 "); channel=new GwChannel(logger,"SER",SERIAL1_CHANNEL_ID); channel->setImpl(serial); channel->begin( diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 84df350..fa133ce 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -13,6 +13,7 @@ */ #ifndef _GWHARDWARE_H #define _GWHARDWARE_H +#include #include "GwUserTasks.h" //general definitions for M5AtomLite @@ -30,6 +31,7 @@ #define GWBUTTON_PULLUPDOWN #define BOARD_LEFT1 GPIO_NUM_22 #define BOARD_LEFT2 GPIO_NUM_19 + #define USBSerial Serial #endif //general definitiones for M5AtomS3 #ifdef PLATFORM_BOARD_M5STACK_ATOMS3 @@ -52,6 +54,12 @@ #ifdef PLATFORM_BOARD_M5STICK_C #define GROOVE_PIN_1 GPIO_NUM_32 #define GROOVE_PIN_2 GPIO_NUM_31 + #define USBSerial Serial +#endif + +//NodeMCU 32 S +#ifdef PLATFORM_BOARD_NODEMCU_32S + #define USBSerial Serial #endif #ifdef BOARD_M5ATOM @@ -157,4 +165,5 @@ #define ESP32_CAN_RX_PIN GROOVE_PIN_2 #endif + #endif diff --git a/lib/serial/GwSerial.cpp b/lib/serial/GwSerial.cpp index 6f35856..9327881 100644 --- a/lib/serial/GwSerial.cpp +++ b/lib/serial/GwSerial.cpp @@ -40,12 +40,11 @@ class GwSerialStream: public Stream{ -GwSerial::GwSerial(GwLog *logger, int num, int id,bool allowRead) +GwSerial::GwSerial(GwLog *logger, Stream *s, int id,bool allowRead):serial(s) { - LOG_DEBUG(GwLog::DEBUG,"creating GwSerial %p port %d for %d",this,(int)num,id); + LOG_DEBUG(GwLog::DEBUG,"creating GwSerial %p id %d",this,id); this->id=id; this->logger = logger; - this->num = num; String bufName="Ser("; bufName+=String(id); bufName+=")"; @@ -54,21 +53,15 @@ GwSerial::GwSerial(GwLog *logger, int num, int id,bool allowRead) if (allowRead){ this->readBuffer=new GwBuffer(logger, GwBuffer::RX_BUFFER_SIZE,bufName+"rd"); } - this->serial=new HardwareSerial(num); + buffer->reset("init"); + initialized=true; } GwSerial::~GwSerial() { delete buffer; if (readBuffer) delete readBuffer; - delete serial; -} -int GwSerial::setup(int baud, int rxpin, int txpin) -{ - serial->begin(baud,SERIAL_8N1,rxpin,txpin); - buffer->reset(F("init")); - initialized = true; - return 0; } + bool GwSerial::isInitialized() { return initialized; } size_t GwSerial::enqueue(const uint8_t *data, size_t len, bool partial) { diff --git a/lib/serial/GwSerial.h b/lib/serial/GwSerial.h index b3880fc..d7b01ed 100644 --- a/lib/serial/GwSerial.h +++ b/lib/serial/GwSerial.h @@ -10,19 +10,17 @@ class GwSerial : public GwChannelInterface{ GwBuffer *buffer; GwBuffer *readBuffer=NULL; GwLog *logger; - int num; bool initialized=false; bool allowRead=true; GwBuffer::WriteStatus write(); int id=-1; int overflows=0; size_t enqueue(const uint8_t *data, size_t len,bool partial=false); - HardwareSerial *serial; + Stream *serial; public: static const int bufferSize=200; - GwSerial(GwLog *logger,int num,int id,bool allowRead=true); + GwSerial(GwLog *logger,Stream *stream,int id,bool allowRead=true); ~GwSerial(); - int setup(int baud,int rxpin,int txpin); bool isInitialized(); virtual size_t sendToClients(const char *buf,int sourceId,bool partial=false); virtual void loop(bool handleRead=true,bool handleWrite=true); From 17615adca5cac1b50cb49dffac5f47573547248c Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 30 Aug 2023 20:20:28 +0200 Subject: [PATCH 027/178] cleanup unused code, make fallback_serial working again --- lib/channel/GwChannelList.h | 5 +++-- lib/exampletask/GwExampleTask.h | 1 + lib/exampletask/platformio.ini | 1 - lib/log/GWLog.cpp | 3 ++- src/main.cpp | 14 ++------------ 5 files changed, 8 insertions(+), 16 deletions(-) diff --git a/lib/channel/GwChannelList.h b/lib/channel/GwChannelList.h index cbef1d4..1cca126 100644 --- a/lib/channel/GwChannelList.h +++ b/lib/channel/GwChannelList.h @@ -12,8 +12,9 @@ #define N2K_CHANNEL_ID 0 #define USB_CHANNEL_ID 1 #define SERIAL1_CHANNEL_ID 2 -#define TCP_CLIENT_CHANNEL_ID 3 -#define MIN_TCP_CHANNEL_ID 4 +#define SERIAL2_CHANNEL_ID 3 +#define TCP_CLIENT_CHANNEL_ID 4 +#define MIN_TCP_CHANNEL_ID 5 #define MIN_USER_TASK 200 class GwSocketServer; diff --git a/lib/exampletask/GwExampleTask.h b/lib/exampletask/GwExampleTask.h index 5dbb5b6..1ff2219 100644 --- a/lib/exampletask/GwExampleTask.h +++ b/lib/exampletask/GwExampleTask.h @@ -22,6 +22,7 @@ #define GWLED_PIN GPIO_NUM_27 //brightness 0...255 #define GWLED_BRIGHTNESS 64 +#define USBSerial Serial void exampleTask(GwApi *param); void exampleInit(GwApi *param); diff --git a/lib/exampletask/platformio.ini b/lib/exampletask/platformio.ini index b2cd394..096a7cd 100644 --- a/lib/exampletask/platformio.ini +++ b/lib/exampletask/platformio.ini @@ -7,7 +7,6 @@ board = m5stack-atom lib_deps = ${env.lib_deps} - own_lib build_flags= -D BOARD_TEST ${env.build_flags} diff --git a/lib/log/GWLog.cpp b/lib/log/GWLog.cpp index 936e876..65d8236 100644 --- a/lib/log/GWLog.cpp +++ b/lib/log/GWLog.cpp @@ -1,10 +1,11 @@ #include "GwLog.h" +#include "GwHardware.h" class DefaultLogWriter: public GwLogWriter{ public: virtual ~DefaultLogWriter(){}; virtual void write(const char *data){ - Serial.print(data); + USBSerial.print(data); } }; diff --git a/src/main.cpp b/src/main.cpp index c884a62..5521501 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,7 +14,6 @@ #include "GwAppInfo.h" // #define GW_MESSAGE_DEBUG_ENABLED //#define FALLBACK_SERIAL -//#define CAN_ESP_DEBUG const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting #include #include "GwApi.h" @@ -66,15 +65,6 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting #include "GwChannel.h" #include "GwChannelList.h" #include "GwTimer.h" -#ifdef FALLBACK_SERIAL - #ifdef CAN_ESP_DEBUG - #define CDBS &Serial - #else - #define CDBS NULL - #endif -#else - #define CDBS NULL -#endif #define MAX_NMEA2000_MESSAGE_SEASMART_SIZE 500 @@ -709,8 +699,8 @@ void setup() { #ifdef FALLBACK_SERIAL fallbackSerial=true; //falling back to old style serial for logging - Serial.begin(115200); - Serial.printf("fallback serial enabled\n"); + USBSerial.begin(115200); + USBSerial.printf("fallback serial enabled\n"); logger.prefix="FALLBACK:"; #endif userCodeHandler.startInitTasks(MIN_USER_TASK); From 019fb8ff6aab182c42613298124334d4e3a97be5 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 30 Aug 2023 21:10:00 +0200 Subject: [PATCH 028/178] intermediate, untested: second serial channel --- lib/channel/GwChannelList.cpp | 126 ++++++++++++++++++++-------------- lib/channel/GwChannelList.h | 1 + src/main.cpp | 8 ++- web/config.json | 122 ++++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+), 51 deletions(-) diff --git a/lib/channel/GwChannelList.cpp b/lib/channel/GwChannelList.cpp index 5465ea9..2acbdd5 100644 --- a/lib/channel/GwChannelList.cpp +++ b/lib/channel/GwChannelList.cpp @@ -58,6 +58,64 @@ void GwChannelList::allChannels(ChannelAction action){ action(*it); } } + +void GwChannelList::addSerial(int id,const String &mode,int rx,int tx){ + if (id != SERIAL1_CHANNEL_ID && id != SERIAL2_CHANNEL_ID){ + logger->logDebug(GwLog::ERROR,"trying to set up an unknown serial channel: %d",id); + return; + } + if (rx < 0 && tx < 0){ + logger->logDebug(GwLog::ERROR,"useless config for serial %d: both rx/tx undefined"); + return; + } + String cfgName; + bool canRead=false; + bool canWrite=false; + if (mode == "BI"){ + cfgName=(id==SERIAL1_CHANNEL_ID)?config->receiveSerial:config->receiveSerial2; + canRead=config->getBool(cfgName); + cfgName=(id==SERIAL2_CHANNEL_ID)?config->sendSerial:config->sendSerial2; + canWrite=config->getBool(cfgName); + } + if (mode == "TX"){ + canWrite=true; + } + if (mode == "RX"){ + canRead=true; + } + if (mode == "UNI"){ + cfgName=(id == SERIAL1_CHANNEL_ID)?config->serialDirection:config->serial2Dir; + String cfgMode=config->getString(cfgName); + if (cfgMode == "receive"){ + canRead=true; + } + if (cfgMode == "send"){ + canWrite=true; + } + } + if (rx < 0) canRead=false; + if (tx < 0) canWrite=false; + HardwareSerial *serialStream=(id == SERIAL1_CHANNEL_ID)?&Serial1:&Serial2; + LOG_DEBUG(GwLog::DEBUG,"serial set up: mode=%s,rx=%d,canRead=%d,tx=%d,canWrite=%d", + mode.c_str(),rx,(int)canRead,tx,(int)canWrite); + serialStream->begin(config->getInt(config->serialBaud,115200),SERIAL_8N1,rx,tx); + GwSerial *serial = new GwSerial(logger, serialStream, id, canRead); + LOG_DEBUG(GwLog::LOG, "starting serial %d ", id); + GwChannel *channel = new GwChannel(logger, (id == SERIAL1_CHANNEL_ID) ? "SER" : "SER2", id); + channel->setImpl(serial); + channel->begin( + canRead || canWrite, + canWrite, + canRead, + config->getString((id == SERIAL1_CHANNEL_ID) ? config->serialReadF : config->serial2ReadF), + config->getString((id == SERIAL1_CHANNEL_ID) ? config->serialWriteF : config->serial2WriteF), + false, + config->getBool((id == SERIAL1_CHANNEL_ID) ? config->serialToN2k : config->serial2ToN2k), + false, + false); + LOG_DEBUG(GwLog::LOG, "%s", channel->toString().c_str()); + theChannels.push_back(channel); +} void GwChannelList::begin(bool fallbackSerial){ LOG_DEBUG(GwLog::DEBUG,"GwChannelList::begin"); GwChannel *channel=NULL; @@ -102,57 +160,25 @@ void GwChannelList::begin(bool fallbackSerial){ theChannels.push_back(channel); //serial 1 - bool serCanRead=true; - bool serCanWrite=true; - int serialrx=-1; - int serialtx=-1; - #ifdef GWSERIAL_MODE - #ifdef GWSERIAL_TX - serialtx=GWSERIAL_TX; - #endif - #ifdef GWSERIAL_RX - serialrx=GWSERIAL_RX; - #endif - if (serialrx != -1 && serialtx != -1){ - serialMode=GWSERIAL_MODE; - } + #ifndef GWSERIAL_TX + #define GWSERIAL_TX -1 + #endif + #ifndef GWSERIAL_RX + #define GWSERIAL_RX -1 + #endif + #ifdef GWSERIAL_MODE + addSerial(SERIAL1_CHANNEL_ID,GWSERIAL_MODE,GWSERIAL_RX,GWSERIAL_TX); + #endif + //serial 2 + #ifndef GWSERIAL2_TX + #define GWSERIAL2_TX -1 + #endif + #ifndef GWSERIAL2_RX + #define GWSERIAL2_RX -1 + #endif + #ifdef GWSERIAL2_MODE + addSerial(SERIAL2_CHANNEL_ID,GWSERIAL2_MODE,GWSERIAL2_RX,GWSERIAL2_TX); #endif - //the serial direction is from the config (only valid for mode UNI) - String serialDirection=config->getString(config->serialDirection); - //we only consider the direction if mode is UNI - if (serialMode != String("UNI")){ - serialDirection=String(""); - //if mode is UNI it depends on the selection - serCanRead=config->getBool(config->receiveSerial); - serCanWrite=config->getBool(config->sendSerial); - } - if (serialDirection == "receive" || serialDirection == "off" || serialMode == "RX") serCanWrite=false; - if (serialDirection == "send" || serialDirection == "off" || serialMode == "TX") serCanRead=false; - LOG_DEBUG(GwLog::DEBUG,"serial set up: mode=%s,direction=%s,rx=%d,tx=%d", - serialMode.c_str(),serialDirection.c_str(),serialrx,serialtx - ); - if (serialtx != -1 || serialrx != -1 ){ - LOG_DEBUG(GwLog::LOG,"creating serial interface rx=%d, tx=%d",serialrx,serialtx); - Serial1.begin(config->getInt(config->serialBaud,115200),SERIAL_8N1,serialrx,serialtx); - GwSerial *serial=new GwSerial(logger,&Serial1,SERIAL1_CHANNEL_ID,serCanRead); - LOG_DEBUG(GwLog::LOG,"starting serial1 "); - channel=new GwChannel(logger,"SER",SERIAL1_CHANNEL_ID); - channel->setImpl(serial); - channel->begin( - serCanRead || serCanWrite, - serCanWrite, - serCanRead, - config->getString(config->serialReadF), - config->getString(config->serialWriteF), - false, - config->getBool(config->serialToN2k), - false, - false - ); - LOG_DEBUG(GwLog::LOG,"%s",channel->toString().c_str()); - theChannels.push_back(channel); - } - //tcp client bool tclEnabled=config->getBool(config->tclEnabled); channel=new GwChannel(logger,"TCPClient",TCP_CLIENT_CHANNEL_ID); diff --git a/lib/channel/GwChannelList.h b/lib/channel/GwChannelList.h index 1cca126..c4072e2 100644 --- a/lib/channel/GwChannelList.h +++ b/lib/channel/GwChannelList.h @@ -29,6 +29,7 @@ class GwChannelList{ GwSocketServer *sockets; GwTcpClient *client; String serialMode=F("NONE"); + void addSerial(int id,const String &mode,int rx,int tx); public: GwChannelList(GwLog *logger, GwConfigHandler *config); typedef std::function ChannelAction; diff --git a/src/main.cpp b/src/main.cpp index 5521501..877ed98 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -427,7 +427,7 @@ class CapabilitiesRequest : public GwRequestMessage{ protected: virtual void processRequest(){ int numCapabilities=userCodeHandler.getCapabilities()->size(); - GwJsonDocument json(JSON_OBJECT_SIZE(numCapabilities*3+6)); + GwJsonDocument json(JSON_OBJECT_SIZE(numCapabilities*3+8)); for (auto it=userCodeHandler.getCapabilities()->begin(); it != userCodeHandler.getCapabilities()->end();it++){ json[it->first]=it->second; @@ -438,6 +438,12 @@ class CapabilitiesRequest : public GwRequestMessage{ String serial(F("NONE")); #endif json["serialmode"]=serial; + #ifdef GWSERIAL2_MODE + String serial2(F(GWSERIAL2_MODE)); + #else + String serial2(F("NONE")); + #endif + json["serial2mode"]=serial2; #ifdef GWBUTTON_PIN json["hardwareReset"]="true"; #endif diff --git a/web/config.json b/web/config.json index cee92ae..78e3196 100644 --- a/web/config.json +++ b/web/config.json @@ -392,6 +392,128 @@ ] }, "category": "serial port" + } + , + { + "name": "serial2Dir", + "label": "serial2 direction", + "type": "list", + "default": "receive", + "list": [ + "send", + "receive", + "off" + ], + "description": "use the serial2 port to send or receive data", + "capabilities": { + "serial2mode": [ + "UNI" + ] + }, + "category": "serial2 port" + }, + { + "name": "serial2Baud", + "label": "serial2 baud rate", + "type": "list", + "default": "115200", + "description": "baud rate for the serial port 2", + "list": [ + 1200, + 2400, + 4800, + 9600, + 14400, + 19200, + 28800, + 38400, + 57600, + 115200, + 230400, + 460800 + ], + "capabilities": { + "serial2mode": [ + "RX", + "TX", + "UNI", + "BI" + ] + }, + "category": "serial2 port" + }, + { + "name": "sendSerial2", + "label": "NMEA to Serial2", + "type": "boolean", + "default": "true", + "description": "send out NMEA data on the serial port 2", + "capabilities": { + "serial2mode": [ + "TX", + "BI" + ] + }, + "category": "serial2 port" + }, + { + "name": "receiveSerial2", + "label": "NMEA from Serial2", + "type": "boolean", + "default": "true", + "description": "receive NMEA data on the serial port 2", + "capabilities": { + "serial2mode": [ + "RX", + "BI" + ] + }, + "category": "serial2 port" + }, + { + "name": "serial2ToN2k", + "label": "serial2 to NMEA2000", + "type": "boolean", + "default": "true", + "description": "convert NMEA0183 from the serial port 2 to NMEA2000", + "capabilities": { + "serial2mode": [ + "RX", + "BI", + "UNI" + ] + }, + "category": "serial2 port" + }, + { + "name": "serial2ReadF", + "label": "serial2 read Filter", + "type": "filter", + "default": "", + "description": "filter for NMEA0183 data when reading from serial2\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB", + "capabilities": { + "serial2mode": [ + "RX", + "BI", + "UNI" + ] + }, + "category": "serial2 port" + }, + { + "name": "serial2WriteF", + "label": "serial2 write Filter", + "type": "filter", + "default": "", + "description": "filter for NMEA0183 data when writing to serial2\nselect aison|aisoff, set a whitelist or a blacklist with NMEA sentences like RMC,RMB", + "capabilities": { + "serial2mode": [ + "TX", + "BI", + "UNI" + ] + }, + "category": "serial2 port" }, { "name": "serverPort", From cb983246042b29fd0cdf210d388c9db4ce631fc8 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 31 Aug 2023 12:24:21 +0200 Subject: [PATCH 029/178] make the counter display and the channel names dynamic in the UI --- lib/channel/GwChannel.cpp | 7 +++- lib/channel/GwChannelList.cpp | 75 ++++++++++++++++++++++++++--------- lib/channel/GwChannelList.h | 4 +- src/main.cpp | 5 ++- web/index.js | 72 +++++++++++++++++++-------------- 5 files changed, 109 insertions(+), 54 deletions(-) diff --git a/lib/channel/GwChannel.cpp b/lib/channel/GwChannel.cpp index d25841d..ef9d64e 100644 --- a/lib/channel/GwChannel.cpp +++ b/lib/channel/GwChannel.cpp @@ -57,7 +57,7 @@ GwChannel::GwChannel(GwLog *logger, this->logger = logger; this->name=name; this->sourceId=sourceId; - this->maxSourceId=sourceId; + this->maxSourceId=maxSourceId; this->countIn=new GwCounter(String("count")+name+String("in")); this->countOut=new GwCounter(String("count")+name+String("out")); this->impl=NULL; @@ -146,12 +146,15 @@ bool GwChannel::canReceive(const char *buffer){ } int GwChannel::getJsonSize(){ - int rt=2; + int rt=JSON_OBJECT_SIZE(6); if (countIn) rt+=countIn->getJsonSize(); if (countOut) rt+=countOut->getJsonSize(); return rt; } void GwChannel::toJson(GwJsonDocument &doc){ + JsonObject jo=doc.createNestedObject("ch"+name); + jo["id"]=sourceId; + jo["max"]=maxSourceId; if (countOut) countOut->toJson(doc); if (countIn) countIn->toJson(doc); } diff --git a/lib/channel/GwChannelList.cpp b/lib/channel/GwChannelList.cpp index 2acbdd5..c077768 100644 --- a/lib/channel/GwChannelList.cpp +++ b/lib/channel/GwChannelList.cpp @@ -58,9 +58,53 @@ void GwChannelList::allChannels(ChannelAction action){ action(*it); } } +typedef struct { + int id; + const char *baud; + const char *receive; + const char *send; + const char *direction; + const char *toN2K; + const char *readF; + const char *writeF; + const char *name; +} SerialParam; -void GwChannelList::addSerial(int id,const String &mode,int rx,int tx){ - if (id != SERIAL1_CHANNEL_ID && id != SERIAL2_CHANNEL_ID){ +static SerialParam serialParameters[]={ + { + .id=SERIAL1_CHANNEL_ID, + .baud=GwConfigDefinitions::serialBaud, + .receive=GwConfigDefinitions::receiveSerial, + .send=GwConfigDefinitions::sendSerial, + .direction=GwConfigDefinitions::serialDirection, + .toN2K=GwConfigDefinitions::serialToN2k, + .readF=GwConfigDefinitions::serialReadF, + .writeF=GwConfigDefinitions::serialWriteF, + .name="Serial" + }, + { + .id=SERIAL2_CHANNEL_ID, + .baud=GwConfigDefinitions::serial2Baud, + .receive=GwConfigDefinitions::receiveSerial2, + .send=GwConfigDefinitions::sendSerial2, + .direction=GwConfigDefinitions::serial2Dir, + .toN2K=GwConfigDefinitions::serial2ToN2k, + .readF=GwConfigDefinitions::serial2ReadF, + .writeF=GwConfigDefinitions::serial2WriteF, + .name="Serial2" + } +}; + +static SerialParam *getSerialParam(int id){ + for (size_t idx=0;idxlogDebug(GwLog::ERROR,"trying to set up an unknown serial channel: %d",id); return; } @@ -68,14 +112,11 @@ void GwChannelList::addSerial(int id,const String &mode,int rx,int tx){ logger->logDebug(GwLog::ERROR,"useless config for serial %d: both rx/tx undefined"); return; } - String cfgName; bool canRead=false; bool canWrite=false; if (mode == "BI"){ - cfgName=(id==SERIAL1_CHANNEL_ID)?config->receiveSerial:config->receiveSerial2; - canRead=config->getBool(cfgName); - cfgName=(id==SERIAL2_CHANNEL_ID)?config->sendSerial:config->sendSerial2; - canWrite=config->getBool(cfgName); + canRead=config->getBool(param->receive); + canWrite=config->getBool(param->send); } if (mode == "TX"){ canWrite=true; @@ -84,8 +125,7 @@ void GwChannelList::addSerial(int id,const String &mode,int rx,int tx){ canRead=true; } if (mode == "UNI"){ - cfgName=(id == SERIAL1_CHANNEL_ID)?config->serialDirection:config->serial2Dir; - String cfgMode=config->getString(cfgName); + String cfgMode=config->getString(param->direction); if (cfgMode == "receive"){ canRead=true; } @@ -95,22 +135,21 @@ void GwChannelList::addSerial(int id,const String &mode,int rx,int tx){ } if (rx < 0) canRead=false; if (tx < 0) canWrite=false; - HardwareSerial *serialStream=(id == SERIAL1_CHANNEL_ID)?&Serial1:&Serial2; LOG_DEBUG(GwLog::DEBUG,"serial set up: mode=%s,rx=%d,canRead=%d,tx=%d,canWrite=%d", mode.c_str(),rx,(int)canRead,tx,(int)canWrite); - serialStream->begin(config->getInt(config->serialBaud,115200),SERIAL_8N1,rx,tx); + serialStream->begin(config->getInt(param->baud,115200),SERIAL_8N1,rx,tx); GwSerial *serial = new GwSerial(logger, serialStream, id, canRead); LOG_DEBUG(GwLog::LOG, "starting serial %d ", id); - GwChannel *channel = new GwChannel(logger, (id == SERIAL1_CHANNEL_ID) ? "SER" : "SER2", id); + GwChannel *channel = new GwChannel(logger, param->name, id); channel->setImpl(serial); channel->begin( canRead || canWrite, canWrite, canRead, - config->getString((id == SERIAL1_CHANNEL_ID) ? config->serialReadF : config->serial2ReadF), - config->getString((id == SERIAL1_CHANNEL_ID) ? config->serialWriteF : config->serial2WriteF), + config->getString(param->readF), + config->getString(param->writeF), false, - config->getBool((id == SERIAL1_CHANNEL_ID) ? config->serialToN2k : config->serial2ToN2k), + config->getBool(param->toN2K), false, false); LOG_DEBUG(GwLog::LOG, "%s", channel->toString().c_str()); @@ -143,7 +182,7 @@ void GwChannelList::begin(bool fallbackSerial){ //TCP server sockets=new GwSocketServer(config,logger,MIN_TCP_CHANNEL_ID); sockets->begin(); - channel=new GwChannel(logger,"TCP",MIN_TCP_CHANNEL_ID,MIN_TCP_CHANNEL_ID+10); + channel=new GwChannel(logger,"TCPserver",MIN_TCP_CHANNEL_ID,MIN_TCP_CHANNEL_ID+10); channel->setImpl(sockets); channel->begin( true, @@ -167,7 +206,7 @@ void GwChannelList::begin(bool fallbackSerial){ #define GWSERIAL_RX -1 #endif #ifdef GWSERIAL_MODE - addSerial(SERIAL1_CHANNEL_ID,GWSERIAL_MODE,GWSERIAL_RX,GWSERIAL_TX); + addSerial(&Serial1,SERIAL1_CHANNEL_ID,GWSERIAL_MODE,GWSERIAL_RX,GWSERIAL_TX); #endif //serial 2 #ifndef GWSERIAL2_TX @@ -177,7 +216,7 @@ void GwChannelList::begin(bool fallbackSerial){ #define GWSERIAL2_RX -1 #endif #ifdef GWSERIAL2_MODE - addSerial(SERIAL2_CHANNEL_ID,GWSERIAL2_MODE,GWSERIAL2_RX,GWSERIAL2_TX); + addSerial(&Serial2,SERIAL2_CHANNEL_ID,GWSERIAL2_MODE,GWSERIAL2_RX,GWSERIAL2_TX); #endif //tcp client bool tclEnabled=config->getBool(config->tclEnabled); diff --git a/lib/channel/GwChannelList.h b/lib/channel/GwChannelList.h index c4072e2..6787146 100644 --- a/lib/channel/GwChannelList.h +++ b/lib/channel/GwChannelList.h @@ -7,6 +7,7 @@ #include "GWConfig.h" #include "GwJsonDocument.h" #include "GwApi.h" +#include //NMEA message channels #define N2K_CHANNEL_ID 0 @@ -28,8 +29,7 @@ class GwChannelList{ GwSocketServer *sockets; GwTcpClient *client; - String serialMode=F("NONE"); - void addSerial(int id,const String &mode,int rx,int tx); + void addSerial(HardwareSerial *stream,int id,const String &mode,int rx,int tx); public: GwChannelList(GwLog *logger, GwConfigHandler *config); typedef std::function ChannelAction; diff --git a/src/main.cpp b/src/main.cpp index 877ed98..6e18a8c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -155,8 +155,8 @@ SemaphoreHandle_t mainLock; GwRequestQueue mainQueue(&logger,20); GwWebServer webserver(&logger,&mainQueue,80); -GwCounter countNMEA2KIn("count2Kin"); -GwCounter countNMEA2KOut("count2Kout"); +GwCounter countNMEA2KIn("countNMEA2000in"); +GwCounter countNMEA2KOut("countNMEA2000out"); GwIntervalRunner timers; bool checkPass(String hash){ @@ -399,6 +399,7 @@ protected: } status["n2kstate"]=NMEA2000.stateStr(driverState); status["n2knode"]=NodeAddress; + status["minUser"]=MIN_USER_TASK; //nmea0183Converter->toJson(status); countNMEA2KIn.toJson(status); countNMEA2KOut.toJson(status); diff --git a/web/index.js b/web/index.js index 28ea82f..10eae20 100644 --- a/web/index.js +++ b/web/index.js @@ -3,6 +3,8 @@ let lastUpdate = (new Date()).getTime(); let reloadConfig = false; let needAdminPass=true; let lastSalt=""; +let channelList={}; +let minUser=200; function addEl(type, clazz, parent, text) { let el = document.createElement(type); if (clazz) { @@ -65,22 +67,39 @@ function update() { } getJson('/api/status') .then(function (jsonData) { + let statusPage=document.getElementById('statusPageContent'); + let even=true; //first counter for (let k in jsonData) { if (k == "salt"){ lastSalt=jsonData[k]; + continue; } + if (k == "minUser"){ + minUser=parseInt(jsonData[k]); + continue; + } + if (! statusPage) continue; if (typeof (jsonData[k]) === 'object') { - for (let sk in jsonData[k]) { - let key = k + "." + sk; - if (typeof (jsonData[k][sk]) === 'object') { - //msg details - updateMsgDetails(key, jsonData[k][sk]); - } - else { - let el = document.getElementById(key); - if (el) el.textContent = jsonData[k][sk]; + if (k.indexOf('count') == 0) { + createCounterDisplay(statusPage, k.replace("count", "").replace(/in$/," in").replace(/out$/," out"), k, even); + even = !even; + for (let sk in jsonData[k]) { + let key = k + "." + sk; + if (typeof (jsonData[k][sk]) === 'object') { + //msg details + updateMsgDetails(key, jsonData[k][sk]); + } + else { + let el = document.getElementById(key); + if (el) el.textContent = jsonData[k][sk]; + } } } + if (k.indexOf("ch")==0){ + //channel def + let name=k.substring(2); + channelList[name]=jsonData[k]; + } } else { let el = document.getElementById(k); @@ -286,9 +305,13 @@ function factoryReset() { .catch(function (e) { }); } function createCounterDisplay(parent,label,key,isEven){ + if (parent.querySelector("#"+key)){ + return; + } let clazz="row icon-row counter-row"; if (isEven) clazz+=" even"; let row=addEl('div',clazz,parent); + row.setAttribute("id",key); let icon=addEl('span','icon icon-more',row); addEl('span','label',row,label); let value=addEl('span','value',row,'---'); @@ -331,18 +354,7 @@ function updateMsgDetails(key, details) { },frame); }); } -let counters={ - count2Kin: 'NMEA2000 in', - count2Kout: 'NMEA2000 out', - countTCPin: 'TCPserver in', - countTCPout: 'TCPserver out', - countTCPClientin: 'TCPclient in', - countTCPClientout: 'TCPclient out', - countUSBin: 'USB in', - countUSBout: 'USB out', - countSERin: 'Serial in', - countSERout: 'Serial out' -} + function showOverlay(text, isHtml) { let el = document.getElementById('overlayContent'); if (isHtml) { @@ -1448,13 +1460,13 @@ function createDashboard() { frame.innerHTML = ''; } function sourceName(v){ - if (v == 0) return "N2K"; - if (v == 1) return "USB"; - if (v == 2) return "SER"; - if (v == 3) return "TCPcl" - if (v >= 4 && v <= 20) return "TCPser"; - if (v >= 200) return "USER"; - return "---"; + for (let n in channelList){ + if (v >= channelList[n].id && v <= channelList[n].max){ + return n; + } + } + if (v < minUser) return "---"; + return "USER["+v+"]"; } let lastSelectList=[]; function updateDashboard(data) { @@ -1716,13 +1728,13 @@ window.addEventListener('load', function () { } }catch(e){} let statusPage=document.getElementById('statusPageContent'); - if (statusPage){ + /*if (statusPage){ let even=true; for (let c in counters){ createCounterDisplay(statusPage,counters[c],c,even); even=!even; } - } + }*/ forEl('#uploadFile',function(el){ el.addEventListener('change',function(ev){ if (ev.target.files.length < 1) return; From 1b3840f207dddfef3daeefd8ed95db26ce57c408 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 31 Aug 2023 12:32:48 +0200 Subject: [PATCH 030/178] #48: add a second serial config to the example task --- lib/exampletask/GwExampleTask.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/exampletask/GwExampleTask.h b/lib/exampletask/GwExampleTask.h index 1ff2219..643c478 100644 --- a/lib/exampletask/GwExampleTask.h +++ b/lib/exampletask/GwExampleTask.h @@ -9,6 +9,11 @@ #define GWSERIAL_TX 26 #define GWSERIAL_RX 32 #define GWSERIAL_MODE "UNI" + +#define GWSERIAL2_TX 14 +#define GWSERIAL2_RX 15 +#define GWSERIAL2_MODE "BI" + #define GWBUTTON_PIN GPIO_NUM_39 #define GWBUTTON_ACTIVE LOW //if GWBUTTON_PULLUPDOWN we enable a pulup/pulldown From f36dd37b8b038febc459c21e50ed8219619a4835 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 31 Aug 2023 19:12:10 +0200 Subject: [PATCH 031/178] better handling if no CAN Pins are configured --- lib/nmea2ktwai/Nmea2kTwai.cpp | 31 ++++++++++++++++++++++++++++--- lib/nmea2ktwai/Nmea2kTwai.h | 2 ++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/nmea2ktwai/Nmea2kTwai.cpp b/lib/nmea2ktwai/Nmea2kTwai.cpp index a5f0312..77c51d9 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.cpp +++ b/lib/nmea2ktwai/Nmea2kTwai.cpp @@ -9,12 +9,18 @@ static const int TIMEOUT_OFFLINE=256; //# of timeouts to consider offline Nmea2kTwai::Nmea2kTwai(gpio_num_t _TxPin, gpio_num_t _RxPin, unsigned long recP, unsigned long logP): tNMEA2000(),RxPin(_RxPin),TxPin(_TxPin) { - timers.addAction(logP,[this](){logStatus();}); - timers.addAction(recP,[this](){checkRecovery();}); + if (RxPin < 0 || TxPin < 0){ + disabled=true; + } + else{ + timers.addAction(logP,[this](){logStatus();}); + timers.addAction(recP,[this](){checkRecovery();}); + } } bool Nmea2kTwai::CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent) { + if (disabled) return true; twai_message_t message; memset(&message,0,sizeof(message)); message.identifier = id; @@ -35,6 +41,10 @@ bool Nmea2kTwai::CANSendFrame(unsigned long id, unsigned char len, const unsigne } bool Nmea2kTwai::CANOpen() { + if (disabled){ + logDebug(LOG_INFO,"CAN disabled"); + return true; + } esp_err_t rt=twai_start(); if (rt != ESP_OK){ logDebug(LOG_ERR,"CANOpen failed: %x",(int)rt); @@ -47,6 +57,7 @@ bool Nmea2kTwai::CANOpen() } bool Nmea2kTwai::CANGetFrame(unsigned long &id, unsigned char &len, unsigned char *buf) { + if (disabled) return false; twai_message_t message; esp_err_t rt=twai_receive(&message,0); if (rt != ESP_OK){ @@ -68,6 +79,7 @@ bool Nmea2kTwai::CANGetFrame(unsigned long &id, unsigned char &len, unsigned cha return true; } void Nmea2kTwai::initDriver(){ + if (disabled) return; twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TxPin,RxPin, TWAI_MODE_NORMAL); g_config.tx_queue_len=20; twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS(); @@ -84,13 +96,22 @@ void Nmea2kTwai::initDriver(){ // and you want to change size of library send frame buffer size. See e.g. NMEA2000_teensy.cpp. void Nmea2kTwai::InitCANFrameBuffers() { - initDriver(); + if (disabled){ + logDebug(LOG_INFO,"twai init - disabled"); + } + else{ + initDriver(); + } tNMEA2000::InitCANFrameBuffers(); } Nmea2kTwai::Status Nmea2kTwai::getStatus(){ twai_status_info_t state; Status rt; + if (disabled){ + rt.state=ST_DISABLED; + return rt; + } if (twai_get_status_info(&state) != ESP_OK){ return rt; } @@ -120,6 +141,7 @@ Nmea2kTwai::Status Nmea2kTwai::getStatus(){ return rt; } bool Nmea2kTwai::checkRecovery(){ + if (disabled) return false; Status canState=getStatus(); bool strt=false; if (canState.state != Nmea2kTwai::ST_RUNNING) @@ -140,6 +162,7 @@ bool Nmea2kTwai::checkRecovery(){ } void Nmea2kTwai::loop(){ + if (disabled) return; timers.loop(); } @@ -157,6 +180,7 @@ Nmea2kTwai::Status Nmea2kTwai::logStatus(){ } bool Nmea2kTwai::startRecovery(){ + if (disabled) return false; lastRecoveryStart=millis(); esp_err_t rt=twai_driver_uninstall(); if (rt != ESP_OK){ @@ -174,6 +198,7 @@ const char * Nmea2kTwai::stateStr(const Nmea2kTwai::STATE &st){ case ST_RUNNING: return "RUNNING"; case ST_STOPPED: return "STOPPED"; case ST_OFFLINE: return "OFFLINE"; + case ST_DISABLED: return "DISABLED"; } return "ERROR"; } \ No newline at end of file diff --git a/lib/nmea2ktwai/Nmea2kTwai.h b/lib/nmea2ktwai/Nmea2kTwai.h index 46e9b0c..456e633 100644 --- a/lib/nmea2ktwai/Nmea2kTwai.h +++ b/lib/nmea2ktwai/Nmea2kTwai.h @@ -12,6 +12,7 @@ class Nmea2kTwai : public tNMEA2000{ ST_BUS_OFF, ST_RECOVERING, ST_OFFLINE, + ST_DISABLED, ST_ERROR } STATE; typedef struct{ @@ -55,6 +56,7 @@ class Nmea2kTwai : public tNMEA2000{ gpio_num_t RxPin; uint32_t txTimeouts=0; GwIntervalRunner timers; + bool disabled=false; unsigned long lastRecoveryStart=0; }; From 8c7540d956f1d4c01040b5e70ffc087fd576bd27 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 31 Aug 2023 22:03:23 +0200 Subject: [PATCH 032/178] move serial channel mode to serial channel type (integer) --- lib/channel/GwChannelList.cpp | 46 ++++++++++++++++++++++++++++++++--- lib/channel/GwChannelList.h | 6 +++-- lib/hardware/GwHardware.h | 16 +++++++----- src/main.cpp | 16 +++--------- 4 files changed, 59 insertions(+), 25 deletions(-) diff --git a/lib/channel/GwChannelList.cpp b/lib/channel/GwChannelList.cpp index c077768..6d803c4 100644 --- a/lib/channel/GwChannelList.cpp +++ b/lib/channel/GwChannelList.cpp @@ -102,6 +102,29 @@ static SerialParam *getSerialParam(int id){ return nullptr; } +void GwChannelList:: addSerial(HardwareSerial *stream,int id,int type,int rx,int tx){ + const char *mode=nullptr; + switch (type) + { + case GWSERIAL_TYPE_UNI: + mode="UNI"; + break; + case GWSERIAL_TYPE_BI: + mode="BI"; + break; + case GWSERIAL_TYPE_RX: + mode="RX"; + break; + case GWSERIAL_TYPE_TX: + mode="TX"; + break; + } + if (mode == nullptr) { + LOG_DEBUG(GwLog::ERROR,"unknown serial type %d",type); + return; + } + addSerial(stream,id,mode,rx,tx); +} void GwChannelList::addSerial(HardwareSerial *serialStream,int id,const String &mode,int rx,int tx){ SerialParam *param=getSerialParam(id); if (param == nullptr){ @@ -112,6 +135,7 @@ void GwChannelList::addSerial(HardwareSerial *serialStream,int id,const String & logger->logDebug(GwLog::ERROR,"useless config for serial %d: both rx/tx undefined"); return; } + modes[id]=String(mode); bool canRead=false; bool canWrite=false; if (mode == "BI"){ @@ -155,6 +179,7 @@ void GwChannelList::addSerial(HardwareSerial *serialStream,int id,const String & LOG_DEBUG(GwLog::LOG, "%s", channel->toString().c_str()); theChannels.push_back(channel); } + void GwChannelList::begin(bool fallbackSerial){ LOG_DEBUG(GwLog::DEBUG,"GwChannelList::begin"); GwChannel *channel=NULL; @@ -205,8 +230,12 @@ void GwChannelList::begin(bool fallbackSerial){ #ifndef GWSERIAL_RX #define GWSERIAL_RX -1 #endif - #ifdef GWSERIAL_MODE - addSerial(&Serial1,SERIAL1_CHANNEL_ID,GWSERIAL_MODE,GWSERIAL_RX,GWSERIAL_TX); + #ifdef GWSERIAL_TYPE + addSerial(&Serial1,SERIAL1_CHANNEL_ID,GWSERIAL_TYPE,GWSERIAL_RX,GWSERIAL_TX); + #else + #ifdef GWSERIAL_MODE + addSerial(&Serial1,SERIAL1_CHANNEL_ID,GWSERIAL_MODE,GWSERIAL_RX,GWSERIAL_TX); + #endif #endif //serial 2 #ifndef GWSERIAL2_TX @@ -215,8 +244,12 @@ void GwChannelList::begin(bool fallbackSerial){ #ifndef GWSERIAL2_RX #define GWSERIAL2_RX -1 #endif - #ifdef GWSERIAL2_MODE - addSerial(&Serial2,SERIAL2_CHANNEL_ID,GWSERIAL2_MODE,GWSERIAL2_RX,GWSERIAL2_TX); + #ifdef GWSERIAL2_TYPE + addSerial(&Serial2,SERIAL2_CHANNEL_ID,GWSERIAL2_TYPE,GWSERIAL2_RX,GWSERIAL2_TX); + #else + #ifdef GWSERIAL2_MODE + addSerial(&Serial2,SERIAL2_CHANNEL_ID,GWSERIAL2_MODE,GWSERIAL2_RX,GWSERIAL2_TX); + #endif #endif //tcp client bool tclEnabled=config->getBool(config->tclEnabled); @@ -245,6 +278,11 @@ void GwChannelList::begin(bool fallbackSerial){ LOG_DEBUG(GwLog::LOG,"%s",channel->toString().c_str()); logger->flush(); } +String GwChannelList::getMode(int id){ + auto it=modes.find(id); + if (it != modes.end()) return it->second; + return "UNKNOWN"; +} int GwChannelList::getJsonSize(){ int rt=0; allChannels([&](GwChannel *c){ diff --git a/lib/channel/GwChannelList.h b/lib/channel/GwChannelList.h index 6787146..1bddfca 100644 --- a/lib/channel/GwChannelList.h +++ b/lib/channel/GwChannelList.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include "GwChannel.h" #include "GwLog.h" @@ -26,10 +27,11 @@ class GwChannelList{ GwConfigHandler *config; typedef std::vector ChannelList; ChannelList theChannels; - + std::map modes; GwSocketServer *sockets; GwTcpClient *client; void addSerial(HardwareSerial *stream,int id,const String &mode,int rx,int tx); + void addSerial(HardwareSerial *stream,int id,int type,int rx,int tx); public: GwChannelList(GwLog *logger, GwConfigHandler *config); typedef std::function ChannelAction; @@ -42,6 +44,6 @@ class GwChannelList{ //single channel GwChannel *getChannelById(int sourceId); void fillStatus(GwApi::Status &status); - + String getMode(int id); }; diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index fa133ce..2dc67e6 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -13,6 +13,11 @@ */ #ifndef _GWHARDWARE_H #define _GWHARDWARE_H +#define GWSERIAL_TYPE_UNI 1 +#define GWSERIAL_TYPE_BI 2 +#define GWSERIAL_TYPE_RX 3 +#define GWSERIAL_TYPE_TX 4 + #include #include "GwUserTasks.h" @@ -117,7 +122,7 @@ #define ESP32_CAN_RX_PIN GPIO_NUM_4 //serial input only #define GWSERIAL_RX GPIO_NUM_16 -#define GWSERIAL_MODE "RX" +#define GWSERIAL_TYPE GWSERIAL_TYPE_RX #define GWBUTTON_PIN GPIO_NUM_0 #define GWBUTTON_ACTIVE LOW @@ -132,26 +137,26 @@ #ifdef SERIAL_GROOVE_485 #define GWSERIAL_TX GROOVE_PIN_1 #define GWSERIAL_RX GROOVE_PIN_2 - #define GWSERIAL_MODE "UNI" + #define GWSERIAL_TYPE GWSERIAL_TYPE_UNI #endif #ifdef SERIAL_GROOVE_232 #define GWSERIAL_TX GROOVE_PIN_1 #define GWSERIAL_RX GROOVE_PIN_2 - #define GWSERIAL_MODE "BI" + #define GWSERIAL_TYPE GWSERIAL_TYPE_BI #endif //M5 Serial (Atomic RS232 Base) #ifdef M5_SERIAL_KIT_232 #define GWSERIAL_TX BOARD_LEFT2 #define GWSERIAL_RX BOARD_LEFT1 - #define GWSERIAL_MODE "BI" + #define GWSERIAL_TYPE GWSERIAL_TYPE_BI #endif //M5 Serial (Atomic RS485 Base) #ifdef M5_SERIAL_KIT_485 #define GWSERIAL_TX BOARD_LEFT2 #define GWSERIAL_RX BOARD_LEFT1 - #define GWSERIAL_MODE "UNI" + #define GWSERIAL_TYPE GWSERIAL_TYPE_UNI #endif //can kit for M5 Atom @@ -165,5 +170,4 @@ #define ESP32_CAN_RX_PIN GROOVE_PIN_2 #endif - #endif diff --git a/src/main.cpp b/src/main.cpp index 6e18a8c..b3b29b7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -126,7 +126,7 @@ class Nmea2kTwaiLog : public Nmea2kTwai{ #define ESP32_CAN_RX_PIN GPIO_NUM_NC #endif -Nmea2kTwai &NMEA2000=*(new Nmea2kTwaiLog(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,CAN_RECOVERY_PERIOD,&logger)); +Nmea2kTwai &NMEA2000=*(new Nmea2kTwaiLog((gpio_num_t)ESP32_CAN_TX_PIN,(gpio_num_t)ESP32_CAN_RX_PIN,CAN_RECOVERY_PERIOD,&logger)); #ifdef GWBUTTON_PIN bool fixedApPass=false; @@ -433,18 +433,8 @@ class CapabilitiesRequest : public GwRequestMessage{ it != userCodeHandler.getCapabilities()->end();it++){ json[it->first]=it->second; } - #ifdef GWSERIAL_MODE - String serial(F(GWSERIAL_MODE)); - #else - String serial(F("NONE")); - #endif - json["serialmode"]=serial; - #ifdef GWSERIAL2_MODE - String serial2(F(GWSERIAL2_MODE)); - #else - String serial2(F("NONE")); - #endif - json["serial2mode"]=serial2; + json["serialmode"]=channels.getMode(SERIAL1_CHANNEL_ID); + json["serial2mode"]=channels.getMode(SERIAL2_CHANNEL_ID); #ifdef GWBUTTON_PIN json["hardwareReset"]="true"; #endif From d5cae7d9d320b90c17182cbd3a0ce2dee646e032 Mon Sep 17 00:00:00 2001 From: Andreas Vogel Date: Sat, 2 Sep 2023 11:25:42 +0200 Subject: [PATCH 033/178] Add .circleci/config.yml --- .circleci/config.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..4175da6 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/configuration-reference/#executor-job + docker: + - image: cimg/base:stable + # Add steps to the job + # See: https://circleci.com/docs/configuration-reference/#steps + steps: + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/configuration-reference/#workflows +workflows: + say-hello-workflow: + jobs: + - say-hello From efaf69e66c77fd33474a203131d0f6e00a7bdb89 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 2 Sep 2023 11:50:13 +0200 Subject: [PATCH 034/178] 1st circle ci build --- .circleci/config.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4175da6..65bbd2c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ version: 2.1 # Define a job to be invoked later in a workflow. # See: https://circleci.com/docs/configuration-reference/#jobs jobs: - say-hello: + pio-build: # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. # See: https://circleci.com/docs/configuration-reference/#executor-job docker: @@ -15,12 +15,15 @@ jobs: steps: - checkout - run: - name: "Say hello" - command: "echo Hello, World!" + name: "platformio install" + command: "python3 -m pip install platformio" + - run: + name: "build" + command: "pip run" # Orchestrate jobs using workflows # See: https://circleci.com/docs/configuration-reference/#workflows workflows: - say-hello-workflow: + build-workflow: jobs: - - say-hello + - pio-build From 2a830778448fdfd6ac312979c813aa1c29e1b29c Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 2 Sep 2023 11:52:48 +0200 Subject: [PATCH 035/178] circle ci build --- .circleci/config.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 65bbd2c..23b37cf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,21 +2,21 @@ # See: https://circleci.com/docs/configuration-reference version: 2.1 +orbs: + python: circleci/python@1.4.0 + # Define a job to be invoked later in a workflow. # See: https://circleci.com/docs/configuration-reference/#jobs jobs: pio-build: - # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/configuration-reference/#executor-job - docker: - - image: cimg/base:stable + executor: python/default # Add steps to the job # See: https://circleci.com/docs/configuration-reference/#steps steps: - checkout - run: name: "platformio install" - command: "python3 -m pip install platformio" + command: "pip install --upgrade platformio" - run: name: "build" command: "pip run" From ee07bd785109356c22394691de2c388a697167dc Mon Sep 17 00:00:00 2001 From: Andreas Vogel Date: Sat, 2 Sep 2023 11:54:18 +0200 Subject: [PATCH 036/178] Updated config.yml --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 23b37cf..ea98b11 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,7 +19,7 @@ jobs: command: "pip install --upgrade platformio" - run: name: "build" - command: "pip run" + command: "pio run" # Orchestrate jobs using workflows # See: https://circleci.com/docs/configuration-reference/#workflows From 36a5844148941ebb36800a7e38a320b5b10d9a97 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 2 Sep 2023 12:08:18 +0200 Subject: [PATCH 037/178] circle ci build --- .circleci/config.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 23b37cf..51ee025 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,13 @@ # Use the latest 2.1 version of CircleCI pipeline process engine. # See: https://circleci.com/docs/configuration-reference version: 2.1 - +parameters: + run_build: + type: boolean + default: false + environment: + type: string + default: "m5-atom" orbs: python: circleci/python@1.4.0 @@ -12,6 +18,8 @@ jobs: executor: python/default # Add steps to the job # See: https://circleci.com/docs/configuration-reference/#steps + environment: + steps: - checkout - run: @@ -19,11 +27,12 @@ jobs: command: "pip install --upgrade platformio" - run: name: "build" - command: "pip run" + command: "pip run -e << pipeline.parameters.environment >>" # Orchestrate jobs using workflows # See: https://circleci.com/docs/configuration-reference/#workflows workflows: build-workflow: + when: << pipeline.parameters.run_build >> jobs: - pio-build From 63d877553a04d866dc0f9b038c55dd62e522a5fc Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 2 Sep 2023 12:20:52 +0200 Subject: [PATCH 038/178] circle ci build --- .circleci/config.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b383d4d..3f2d884 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,10 @@ parameters: default: false environment: type: string - default: "m5-atom" + default: "m5stack-atom" + build_flags: + type: string + default: "" orbs: python: circleci/python@1.4.0 @@ -19,7 +22,7 @@ jobs: # Add steps to the job # See: https://circleci.com/docs/configuration-reference/#steps environment: - + PLATFORMIO_BUILD_FLAGS: << pipeline.parameters.build_flags >> steps: - checkout - run: @@ -28,7 +31,12 @@ jobs: - run: name: "build" command: "pio run -e << pipeline.parameters.environment >>" - + - store_artifacts: + path: .pio/build/<< pipeline.parameters.environment >>/firmware.bin + destination: << pipeline.parameters.environment >>-update.bin + - store_artifacts: + path: .pio/build/<< pipeline.parameters.environment >>/<< pipeline.parameters.environment >>-all.bin + destination: << pipeline.parameters.environment >>-all.bin # Orchestrate jobs using workflows # See: https://circleci.com/docs/configuration-reference/#workflows workflows: From 9091bef390ead80b5e275344727553bc944c1033 Mon Sep 17 00:00:00 2001 From: Andreas Vogel Date: Sun, 3 Sep 2023 11:30:54 +0200 Subject: [PATCH 039/178] Updated config.yml --- .circleci/config.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3f2d884..f47b57a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -31,9 +31,16 @@ jobs: - run: name: "build" command: "pio run -e << pipeline.parameters.environment >>" + - run: + name: "rename" + command: "mv .pio/build/<< pipeline.parameters.environment >>/firmware.bin .pio/build/<< pipeline.parameters.environment >>/<< pipeline.parameters.environment >>-update.bin" + - run: + name: "compress" + working_directory: ".pio/build/<< pipeline.parameters.environment >>" + command: "zip << pipeline.parameters.environment >>.zip << pipeline.parameters.environment >>-*.bin" - store_artifacts: - path: .pio/build/<< pipeline.parameters.environment >>/firmware.bin - destination: << pipeline.parameters.environment >>-update.bin + path: .pio/build/<< pipeline.parameters.environment >>/<< pipeline.parameters.environment >>.zip + destination: << pipeline.parameters.environment >>.zip - store_artifacts: path: .pio/build/<< pipeline.parameters.environment >>/<< pipeline.parameters.environment >>-all.bin destination: << pipeline.parameters.environment >>-all.bin From 776d8105c0f7351d710633c49cb00c0392919a67 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 3 Sep 2023 12:01:18 +0200 Subject: [PATCH 040/178] update ci script --- .circleci/config.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f47b57a..de4f895 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -11,6 +11,9 @@ parameters: build_flags: type: string default: "" + config: + type: string + default: "{}" orbs: python: circleci/python@1.4.0 @@ -31,19 +34,24 @@ jobs: - run: name: "build" command: "pio run -e << pipeline.parameters.environment >>" + - run: + name: "save config" + working_directory: ".pio/build/<< pipeline.parameters.environment >>" + command: "echo '<< pipeline.parameters.config >>' > config.json" + - run: + name: "save build config" + working_directory: ".pio/build/<< pipeline.parameters.environment >>" + command: "echo 'PLATFORMIO_BUILD_FLAGS=\"<< pipeline.parameters.config >>\" pio run -e << pipeline.parameters.environment >> ' > buildconfig.txt" - run: name: "rename" command: "mv .pio/build/<< pipeline.parameters.environment >>/firmware.bin .pio/build/<< pipeline.parameters.environment >>/<< pipeline.parameters.environment >>-update.bin" - run: name: "compress" working_directory: ".pio/build/<< pipeline.parameters.environment >>" - command: "zip << pipeline.parameters.environment >>.zip << pipeline.parameters.environment >>-*.bin" + command: "zip << pipeline.parameters.environment >>.zip << pipeline.parameters.environment >>-*.bin config.json buildconfig.txt" - store_artifacts: path: .pio/build/<< pipeline.parameters.environment >>/<< pipeline.parameters.environment >>.zip destination: << pipeline.parameters.environment >>.zip - - store_artifacts: - path: .pio/build/<< pipeline.parameters.environment >>/<< pipeline.parameters.environment >>-all.bin - destination: << pipeline.parameters.environment >>-all.bin # Orchestrate jobs using workflows # See: https://circleci.com/docs/configuration-reference/#workflows workflows: From 87bd0a305b79323131a9b3669866f648751918f2 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 3 Sep 2023 12:06:48 +0200 Subject: [PATCH 041/178] correct saving of build config --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index de4f895..fa3a181 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -41,7 +41,7 @@ jobs: - run: name: "save build config" working_directory: ".pio/build/<< pipeline.parameters.environment >>" - command: "echo 'PLATFORMIO_BUILD_FLAGS=\"<< pipeline.parameters.config >>\" pio run -e << pipeline.parameters.environment >> ' > buildconfig.txt" + command: "echo 'PLATFORMIO_BUILD_FLAGS=\"<< pipeline.parameters.environment >>\" pio run -e << pipeline.parameters.environment >> ' > buildconfig.txt" - run: name: "rename" command: "mv .pio/build/<< pipeline.parameters.environment >>/firmware.bin .pio/build/<< pipeline.parameters.environment >>/<< pipeline.parameters.environment >>-update.bin" From a80c0f2d72d2d83d630c766327aad81419dfabfd Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 3 Sep 2023 12:13:41 +0200 Subject: [PATCH 042/178] add name suffix to output files in ci --- .circleci/config.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fa3a181..8cb8c44 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,6 +14,9 @@ parameters: config: type: string default: "{}" + suffix: + type: string + default: "" orbs: python: circleci/python@1.4.0 @@ -44,14 +47,19 @@ jobs: command: "echo 'PLATFORMIO_BUILD_FLAGS=\"<< pipeline.parameters.environment >>\" pio run -e << pipeline.parameters.environment >> ' > buildconfig.txt" - run: name: "rename" - command: "mv .pio/build/<< pipeline.parameters.environment >>/firmware.bin .pio/build/<< pipeline.parameters.environment >>/<< pipeline.parameters.environment >>-update.bin" + working_directory: ".pio/build/<< pipeline.parameters.environment >>" + command: "mv firmware.bin << pipeline.parameters.environment >><< pipeline.parameters.suffix >>-update.bin" + - run: + name: "rename2" + working_directory: ".pio/build/<< pipeline.parameters.environment >>" + command: "mv << pipeline.parameters.environment >>-all.bin << pipeline.parameters.environment >><< pipeline.parameters.suffix >>-all.bin" - run: name: "compress" working_directory: ".pio/build/<< pipeline.parameters.environment >>" - command: "zip << pipeline.parameters.environment >>.zip << pipeline.parameters.environment >>-*.bin config.json buildconfig.txt" + command: "zip << pipeline.parameters.environment >><< pipeline.parameters.suffix >>.zip << pipeline.parameters.environment >>*.bin config.json buildconfig.txt" - store_artifacts: - path: .pio/build/<< pipeline.parameters.environment >>/<< pipeline.parameters.environment >>.zip - destination: << pipeline.parameters.environment >>.zip + path: .pio/build/<< pipeline.parameters.environment >>/<< pipeline.parameters.environment >><< pipeline.parameters.suffix >>.zip + destination: << pipeline.parameters.environment >><< pipeline.parameters.suffix >>.zip # Orchestrate jobs using workflows # See: https://circleci.com/docs/configuration-reference/#workflows workflows: From 8e70ce311259e40f7c6860d0ba0eb0094cc8c5ce Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 3 Sep 2023 12:23:20 +0200 Subject: [PATCH 043/178] correct saving of build config --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8cb8c44..f68f0c5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -44,7 +44,7 @@ jobs: - run: name: "save build config" working_directory: ".pio/build/<< pipeline.parameters.environment >>" - command: "echo 'PLATFORMIO_BUILD_FLAGS=\"<< pipeline.parameters.environment >>\" pio run -e << pipeline.parameters.environment >> ' > buildconfig.txt" + command: "echo 'PLATFORMIO_BUILD_FLAGS=\"<< pipeline.parameters.build_flags >>\" pio run -e << pipeline.parameters.environment >> ' > buildconfig.txt" - run: name: "rename" working_directory: ".pio/build/<< pipeline.parameters.environment >>" From abf87f19553ad227f7de39e7b1a41a05a03739f1 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 3 Sep 2023 16:24:10 +0200 Subject: [PATCH 044/178] step 1 for cibuild php --- .gitignore | 3 +- webinstall/cibuild.php | 122 ++++++++++++++++++++++++++++++ webinstall/config.php | 26 +++++++ webinstall/functions.php | 152 +++++++++++++++++++++++++++++++++++++ webinstall/install.php | 158 +-------------------------------------- webinstall/watch.sh | 12 +++ 6 files changed, 317 insertions(+), 156 deletions(-) create mode 100644 webinstall/cibuild.php create mode 100644 webinstall/config.php create mode 100644 webinstall/functions.php create mode 100755 webinstall/watch.sh diff --git a/.gitignore b/.gitignore index 339663e..b5ca1aa 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ .vscode/launch.json .vscode/ipch generated/* -lib/generated \ No newline at end of file +lib/generated +webinstall/token.php diff --git a/webinstall/cibuild.php b/webinstall/cibuild.php new file mode 100644 index 0000000..e2fb9c2 --- /dev/null +++ b/webinstall/cibuild.php @@ -0,0 +1,122 @@ +$CI_TOKEN); +} +function getPipeline($pipeline){ + $url=apiBase."/pipeline/$pipeline"; + $token=getTokenHeaders(); + return getJson($url,$token,true); +} +function getWorkflow($pipeline,$workflowName){ + $url=apiBase."/pipeline/$pipeline/workflow"; + $token=getTokenHeaders(); + $pstate=getJson($url,$token,true); + if (! isset($pstate['items'])){ + throw new Exception("no workflows in pipeline"); + } + foreach ($pstate['items'] as $workflow){ + if (isset($workflow['name']) && $workflow['name'] == $workflowName){ + if (!isset($workflow['id'])){ + throw new Exception("no workflow id found"); + } + return $workflow; + } + } + throw new Exception("workflow $workflowName not found"); +} +function getJob($pipeline,$workflow,$jobName){ + $url=apiBase."/workflow/".$workflow."/job"; + $token=getTokenHeaders(); + $wstate=getJson($url,$token,true); + if (! isset($wstate['items'])){ + throw new Exception("no jobs in workflow"); + } + foreach ($wstate['items'] as $job){ + if (isset($job['name']) && $job['name'] == $jobName){ + if (! isset($job['id'])){ + throw new Exception("no job id found"); + } + return $job; + } + } + throw new Exception("job $jobName not found"); +} +function getJobStatus($pipeline,$wf=workflowName,$job=jobName){ + $pstat=getPipeline($pipeline); + if (isset($pstat['error'])){ + throw new Exception($pstat["error"]); + } + if (! isset($pstat['state'])){ + throw new Exception("state not set"); + } + if ($pstat['state'] != 'created'){ + return $pstat; + } + $pstat=getWorkflow($pipeline,$wf); + $pstat=getJob($pipeline,$pstat['id'],$job); + return $pstat; +} + +function getArtifacts($job,$slug){ + $url=apiBase."/project/$slug/$job/artifacts"; + return getJson($url,getTokenHeaders(),true); +} + +if (isset($_REQUEST['api'])){ + $action=$_REQUEST['api']; + header("Content-Type: application/json"); + $par=array(); + if ($action == 'status') { + addVars( + $par, + ['pipeline', 'workflow', 'job'], + array('workflow' => workflowName, 'job' => jobName) + ); + try { + $pstat = getJobStatus($par['pipeline'], $par['workflow'], $par['job']); + echo(json_encode($pstat)); + } catch (Exception $e) { + $rt=array('status'=>'error','error'=>$e->getMessage()); + echo(json_encode($rt)); + } + exit(0); + } + if ($action == 'artifacts'){ + addVars( + $par, + ['pipeline', 'workflow', 'job'], + array('workflow' => workflowName, 'job' => jobName) + ); + try{ + $jstat=getJobStatus($par['pipeline'], $par['workflow'], $par['job']); + if (! isset($jstat['project_slug'])){ + throw new Exception("no project_slug in job"); + } + if (! isset($jstat['status'])){ + throw new Exception("no job status"); + } + if ($jstat['status'] != 'success'){ + throw new Exception("invalid job status ".$jstat['status']); + } + $astat=getArtifacts($jstat['job_number'],$jstat['project_slug']); + echo (json_encode($astat)); + }catch (Exception $e){ + echo(json_encode(array('status'=>'error','error'=>$e->getMessage()))); + } + exit(0); + } + die("invalid api $action"); +} +die("no action"); +?> \ No newline at end of file diff --git a/webinstall/config.php b/webinstall/config.php new file mode 100644 index 0000000..afc3741 --- /dev/null +++ b/webinstall/config.php @@ -0,0 +1,26 @@ + array('wellenvogel'), + 'repo'=> array('esp32-nmea2000') +); + + +function fillUserAndRepo($vars=null){ + global $allowed; + if ($vars == null) { + $vars=array(); + } + foreach (array('user','repo') as $n){ + if (! isset($_REQUEST[$n])){ + die("missing parameter $n"); + } + $v=$_REQUEST[$n]; + $av=$allowed[$n]; + if (! in_array($v,$av)){ + die("value $v for $n not allowed"); + } + $vars[$n]=$v; + } + return $vars; +} +?> \ No newline at end of file diff --git a/webinstall/functions.php b/webinstall/functions.php new file mode 100644 index 0000000..1be14d9 --- /dev/null +++ b/webinstall/functions.php @@ -0,0 +1,152 @@ + &$v) { + $str = str_replace("#" . $n . "#", $v, $str); + } + return $str; +} +if (!function_exists('getallheaders')) { + function getallheaders() + { + $headers = []; + foreach ($_SERVER as $name => $value) { + if (substr($name, 0, 5) == 'HTTP_') { + $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; + } + } + return $headers; + } +} +function addVars(&$vars,$names,$defaults=null){ + foreach ($names as $n){ + $v=null; + if (! isset($_REQUEST[$n])){ + if ($defaults == null || ! isset($defaults[$n])) die("missing parameter $n"); + $v=$defaults[$n]; + } + else{ + $v=safeName($_REQUEST[$n]); + } + $vars[$n]=$v; + } + return $vars; +} + +function curl_exec_follow(/*resource*/ $ch, /*int*/ &$maxredirect = null) { + $mr = $maxredirect === null ? 5 : intval($maxredirect); + if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off') && false) { + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $mr > 0); + curl_setopt($ch, CURLOPT_MAXREDIRS, $mr); + } else { + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); + if ($mr > 0) { + $newurl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); + $rch = curl_copy_handle($ch); + curl_setopt($rch, CURLOPT_HEADER, true); + curl_setopt($rch, CURLOPT_NOBODY, true); + curl_setopt($rch, CURLOPT_FORBID_REUSE, false); + curl_setopt($rch, CURLOPT_RETURNTRANSFER, true); + do { + curl_setopt($rch, CURLOPT_URL, $newurl); + $header = curl_exec($rch); + if (curl_errno($rch)) { + $code = 0; + } else { + $code = curl_getinfo($rch, CURLINFO_HTTP_CODE); + if ($code == 301 || $code == 302) { + preg_match('/Location:(.*?)\n/', $header, $matches); + $newurl = trim(array_pop($matches)); + } else { + $code = 0; + } + } + } while ($code && --$mr); + curl_close($rch); + if (!$mr) { + if ($maxredirect === null) { + trigger_error('Too many redirects. When following redirects, libcurl hit the maximum amount.', E_USER_WARNING); + } else { + $maxredirect = 0; + } + return false; + } + curl_setopt($ch, CURLOPT_URL, $newurl); + } + } + curl_setopt( + $ch, + CURLOPT_HEADERFUNCTION, + function ($curl, $header) { + header($header); + return strlen($header); + } + ); + curl_setopt( + $ch, + CURLOPT_WRITEFUNCTION, + function ($curl, $body) { + echo $body; + return strlen($body); + } + ); + header('Access-Control-Allow-Origin:*'); + return curl_exec($ch); +} + +function setFw($curl,$aheaders=null){ + $headers=getallheaders(); + $FWHDR = ['User-Agent']; + $outHeaders = array(); + foreach ($FWHDR as $k) { + if (isset($headers[$k])) { + array_push($outHeaders, "$k: $headers[$k]"); + } + } + if ($aheaders != null){ + foreach ($aheaders as $hk => $hv){ + array_push($outHeaders,"$hk: $hv"); + } + } + curl_setopt($curl, CURLOPT_HTTPHEADER, $outHeaders); +} +function getJson($url,$headers=null){ + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL,$url); + curl_setopt($curl,CURLOPT_RETURNTRANSFER, true); + setFw($curl,$headers); + $response = curl_exec($curl); + $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + #echo("curl exec for $url:$response:$httpcode\n"); + if($e = curl_error($curl)) { + curl_close($curl); + return array('error'=>$e); + } else { + if ($httpcode >= 300){ + curl_close($curl); + return array('error'=>"HTTP code ".$httpcode); + } + curl_close($curl); + return json_decode($response, true); + } +} +function proxy($url) +{ + $ch = curl_init($url); + curl_setopt_array( + $ch, + [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CONNECTTIMEOUT => 30, + ] + ); + setFw($ch); + $response = curl_exec_follow($ch); + curl_close($ch); +} + +?> \ No newline at end of file diff --git a/webinstall/install.php b/webinstall/install.php index 344f57a..2cd046b 100644 --- a/webinstall/install.php +++ b/webinstall/install.php @@ -1,162 +1,10 @@ array('wellenvogel'), - 'repo'=> array('esp32-nmea2000') - ); - if (!function_exists('getallheaders')) { - function getallheaders() - { - $headers = []; - foreach ($_SERVER as $name => $value) { - if (substr($name, 0, 5) == 'HTTP_') { - $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; - } - } - return $headers; - } - } - function safeName($name){ - return preg_replace('[^0-9_a-zA-Z.-]','',$name); - } - function replaceVars($str,$vars){ - foreach ($vars as $n => &$v){ - $str=str_replace("#".$n."#",$v,$str); - } - return $str; - } - - function fillUserAndRepo($vars=null){ - global $allowed; - if ($vars == null) { - $vars=array(); - } - foreach (array('user','repo') as $n){ - if (! isset($_REQUEST[$n])){ - die("missing parameter $n"); - } - $v=$_REQUEST[$n]; - $av=$allowed[$n]; - if (! in_array($v,$av)){ - die("value $v for $n not allowed"); - } - $vars[$n]=$v; - } - return $vars; - } - function addVars($vars,$names){ - foreach ($names as $n){ - if (! isset($_REQUEST[$n])){ - die("missing parameter $n"); - } - $safe=safeName($_REQUEST[$n]); - $vars[$n]=$safe; - } - return $vars; - } - - function curl_exec_follow(/*resource*/ $ch, /*int*/ &$maxredirect = null) { - $mr = $maxredirect === null ? 5 : intval($maxredirect); - if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off') && false) { - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $mr > 0); - curl_setopt($ch, CURLOPT_MAXREDIRS, $mr); - } else { - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); - if ($mr > 0) { - $newurl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); - $rch = curl_copy_handle($ch); - curl_setopt($rch, CURLOPT_HEADER, true); - curl_setopt($rch, CURLOPT_NOBODY, true); - curl_setopt($rch, CURLOPT_FORBID_REUSE, false); - curl_setopt($rch, CURLOPT_RETURNTRANSFER, true); - do { - curl_setopt($rch, CURLOPT_URL, $newurl); - $header = curl_exec($rch); - if (curl_errno($rch)) { - $code = 0; - } else { - $code = curl_getinfo($rch, CURLINFO_HTTP_CODE); - if ($code == 301 || $code == 302) { - preg_match('/Location:(.*?)\n/', $header, $matches); - $newurl = trim(array_pop($matches)); - } else { - $code = 0; - } - } - } while ($code && --$mr); - curl_close($rch); - if (!$mr) { - if ($maxredirect === null) { - trigger_error('Too many redirects. When following redirects, libcurl hit the maximum amount.', E_USER_WARNING); - } else { - $maxredirect = 0; - } - return false; - } - curl_setopt($ch, CURLOPT_URL, $newurl); - } - } - curl_setopt( - $ch, - CURLOPT_HEADERFUNCTION, - function ($curl, $header) { - header($header); - return strlen($header); - } - ); - curl_setopt( - $ch, - CURLOPT_WRITEFUNCTION, - function ($curl, $body) { - echo $body; - return strlen($body); - } - ); - header('Access-Control-Allow-Origin:*'); - return curl_exec($ch); - } - function setFw($curl){ - $headers=getallheaders(); - $FWHDR = ['User-Agent']; - $outHeaders = array(); - foreach ($FWHDR as $k) { - if (isset($headers[$k])) { - array_push($outHeaders, "$k: $headers[$k]"); - } - } - curl_setopt($curl, CURLOPT_HTTPHEADER, $outHeaders); - } - function getJson($url){ - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL,$url); - curl_setopt($curl,CURLOPT_RETURNTRANSFER, true); - setFw($curl); - $response = curl_exec($curl); - if($e = curl_error($curl)) { - curl_close($curl); - return array('error'=>$e); - } else { - curl_close($curl); - return json_decode($response, true); - } - } - function proxy($url) - { - $ch = curl_init($url); - curl_setopt_array( - $ch, - [ - CURLOPT_RETURNTRANSFER => true, - CURLOPT_CONNECTTIMEOUT => 30, - ] - ); - setFw($ch); - $response = curl_exec_follow($ch); - curl_close($ch); - } - + if (isset($_REQUEST['api'])) { $vars=fillUserAndRepo(); proxy(replaceVars($api,$vars)); diff --git a/webinstall/watch.sh b/webinstall/watch.sh new file mode 100755 index 0000000..99de9ef --- /dev/null +++ b/webinstall/watch.sh @@ -0,0 +1,12 @@ +#! /bin/sh +if [ "$1" = "" ] ; then + echo "usage: $0 targetDir" + exit 1 +fi +while true +do + inotifywait -e modify -e create -e delete -r `dirname $0` + echo sync + rsync -rav --exclude=\*.swp --exclude=\*~ `dirname $0`/ $1 + +done From 0709e92b6f2170f6ca5068db8ba9e4f18ab0bfe7 Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 4 Sep 2023 19:45:12 +0200 Subject: [PATCH 045/178] handle artifact download correctly --- webinstall/cibuild.php | 115 ++++++++++++++++++++++++--------------- webinstall/functions.php | 96 ++++++++++++++++++++++++++------ webinstall/install.php | 70 ++++++++++++++---------- 3 files changed, 192 insertions(+), 89 deletions(-) diff --git a/webinstall/cibuild.php b/webinstall/cibuild.php index e2fb9c2..77e4979 100644 --- a/webinstall/cibuild.php +++ b/webinstall/cibuild.php @@ -73,50 +73,79 @@ function getArtifacts($job,$slug){ return getJson($url,getTokenHeaders(),true); } -if (isset($_REQUEST['api'])){ - $action=$_REQUEST['api']; - header("Content-Type: application/json"); - $par=array(); - if ($action == 'status') { - addVars( - $par, - ['pipeline', 'workflow', 'job'], - array('workflow' => workflowName, 'job' => jobName) - ); - try { - $pstat = getJobStatus($par['pipeline'], $par['workflow'], $par['job']); - echo(json_encode($pstat)); - } catch (Exception $e) { - $rt=array('status'=>'error','error'=>$e->getMessage()); - echo(json_encode($rt)); - } - exit(0); +function getArtifactsForPipeline($pipeline,$wf=workflowName,$job=jobName){ + $jstat=getJobStatus($pipeline,$wf,$job); + if (! isset($jstat['job_number'])){ + throw new Exception("no job number"); } - if ($action == 'artifacts'){ - addVars( - $par, - ['pipeline', 'workflow', 'job'], - array('workflow' => workflowName, 'job' => jobName) - ); - try{ - $jstat=getJobStatus($par['pipeline'], $par['workflow'], $par['job']); - if (! isset($jstat['project_slug'])){ - throw new Exception("no project_slug in job"); - } - if (! isset($jstat['status'])){ - throw new Exception("no job status"); - } - if ($jstat['status'] != 'success'){ - throw new Exception("invalid job status ".$jstat['status']); - } - $astat=getArtifacts($jstat['job_number'],$jstat['project_slug']); - echo (json_encode($astat)); - }catch (Exception $e){ - echo(json_encode(array('status'=>'error','error'=>$e->getMessage()))); - } - exit(0); + if (! isset($jstat['status'])){ + throw new Exception("no job status"); } - die("invalid api $action"); + if ($jstat['status'] != 'success'){ + throw new Exception("invalid job status ".$jstat['status']); + } + $astat=getArtifacts($jstat['job_number'],$jstat['project_slug']); + return $astat; +} +try { + if (isset($_REQUEST['api'])) { + $action = $_REQUEST['api']; + header("Content-Type: application/json"); + $par = array(); + if ($action == 'status') { + addVars( + $par, + ['pipeline', 'workflow', 'job'], + array('workflow' => workflowName, 'job' => jobName) + ); + try { + $pstat = getJobStatus($par['pipeline'], $par['workflow'], $par['job']); + echo (json_encode($pstat)); + } catch (Exception $e) { + $rt = array('status' => 'error', 'error' => $e->getMessage()); + echo (json_encode($rt)); + } + exit(0); + } + if ($action == 'artifacts') { + addVars( + $par, + ['pipeline', 'workflow', 'job'], + array('workflow' => workflowName, 'job' => jobName) + ); + try { + $astat = getArtifactsForPipeline($par['pipeline'], $par['workflow'], $par['job']); + echo (json_encode($astat)); + } catch (Exception $e) { + echo (json_encode(array('status' => 'error', 'error' => $e->getMessage()))); + } + exit(0); + } + die("invalid api $action"); + } + if (isset($_REQUEST['download'])) { + $pipeline = $_REQUEST['download']; + $par = array('pipeline' => $pipeline); + addVars( + $par, + ['workflow', 'job'], + array('workflow' => workflowName, 'job' => jobName) + ); + $astat = getArtifactsForPipeline($par['pipeline'], $par['workflow'], $par['job']); + if (!isset($astat['items']) || count($astat['items']) < 1) { + die("no artifacts for job"); + } + $dlurl = $astat['items'][0]['url']; + #echo("DL: $dlurl\n"); + proxy($dlurl); + exit(0); + } + die("no action"); +} catch (HTTPErrorException $h) { + header($_SERVER['SERVER_PROTOCOL'] . " " . $h->code . " " . $h->getMessage()); + die($h->getMessage()); +} catch (Exception $e) { + header($_SERVER['SERVER_PROTOCOL'] . ' 500 ' . $e->getMessage()); + die($e->getMessage()); } -die("no action"); ?> \ No newline at end of file diff --git a/webinstall/functions.php b/webinstall/functions.php index 1be14d9..96557a6 100644 --- a/webinstall/functions.php +++ b/webinstall/functions.php @@ -39,6 +39,7 @@ function addVars(&$vars,$names,$defaults=null){ function curl_exec_follow(/*resource*/ $ch, /*int*/ &$maxredirect = null) { $mr = $maxredirect === null ? 5 : intval($maxredirect); + #echo("###handling redirects $mr\n"); if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off') && false) { curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $mr > 0); curl_setopt($ch, CURLOPT_MAXREDIRS, $mr); @@ -48,20 +49,26 @@ function curl_exec_follow(/*resource*/ $ch, /*int*/ &$maxredirect = null) { $newurl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); $rch = curl_copy_handle($ch); curl_setopt($rch, CURLOPT_HEADER, true); - curl_setopt($rch, CURLOPT_NOBODY, true); + #curl_setopt($rch, CURLOPT_NOBODY, true); curl_setopt($rch, CURLOPT_FORBID_REUSE, false); curl_setopt($rch, CURLOPT_RETURNTRANSFER, true); do { + #echo("###trying $newurl\n"); curl_setopt($rch, CURLOPT_URL, $newurl); + curl_setopt($ch, CURLOPT_URL, $newurl); $header = curl_exec($rch); if (curl_errno($rch)) { $code = 0; } else { $code = curl_getinfo($rch, CURLINFO_HTTP_CODE); + #echo("###code=$code\n"); if ($code == 301 || $code == 302) { preg_match('/Location:(.*?)\n/', $header, $matches); $newurl = trim(array_pop($matches)); } else { + if ($code >= 300){ + trigger_error("HTTP error $code"); + } $code = 0; } } @@ -98,7 +105,7 @@ function curl_exec_follow(/*resource*/ $ch, /*int*/ &$maxredirect = null) { return curl_exec($ch); } -function setFw($curl,$aheaders=null){ +function getFwHeaders($aheaders=null){ $headers=getallheaders(); $FWHDR = ['User-Agent']; $outHeaders = array(); @@ -112,41 +119,98 @@ function setFw($curl,$aheaders=null){ array_push($outHeaders,"$hk: $hv"); } } - curl_setopt($curl, CURLOPT_HTTPHEADER, $outHeaders); + return $outHeaders; } -function getJson($url,$headers=null){ +function getJson($url,$headers=null,$doThrow=false){ $curl = curl_init(); curl_setopt($curl, CURLOPT_URL,$url); curl_setopt($curl,CURLOPT_RETURNTRANSFER, true); - setFw($curl,$headers); + curl_setopt($curl, CURLOPT_HTTPHEADER, getFwHeaders($headers)); $response = curl_exec($curl); $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); #echo("curl exec for $url:$response:$httpcode\n"); if($e = curl_error($curl)) { curl_close($curl); + if ($doThrow) throw new Exception($e); return array('error'=>$e); } else { if ($httpcode >= 300){ curl_close($curl); + if ($doThrow) throw new Exception("HTTP error $httpcode"); return array('error'=>"HTTP code ".$httpcode); } curl_close($curl); return json_decode($response, true); } } +class HTTPErrorException extends Exception{ + public $code=0; + public function __construct($c,$text){ + parent::__construct($text); + $this->code=$c; + } +}; +function proxy_impl($url, $timeout=30,$headers=null,$num = 5) +{ + $nexturl=$url; + while ($num > 0 && $nexturl != null) { + $num--; + $code=0; + $ch = curl_init($nexturl); + $nexturl=null; + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); + curl_setopt($ch, CURLOPT_TIMEOUT,$timeout); + if ($headers != null){ + curl_setopt($ch,CURLOPT_HTTPHEADER,$headers); + } + curl_setopt( + $ch, + CURLOPT_HEADERFUNCTION, + function ($curl, $header) use(&$nexturl,&$code){ + #echo ("###header:$header\n"); + if ($code == 0){ + $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); + } + #echo ("???code=$code\n"); + if ($code == 301 || $code == 302) { + if(preg_match('/Location:(.*?)\n/', $header, $matches)){ + $nexturl = trim(array_pop($matches)); + #echo("???nexturl=$nexturl\n"); + } + } + if ($code != 0 && $code < 300){ + header($header); + } + return strlen($header); + } + ); + curl_setopt( + $ch, + CURLOPT_WRITEFUNCTION, + function ($curl, $body) use(&$code) { + if ($code != 0 && $code < 300){ + #echo ("### body part " . strlen($body)."\n"); + echo $body; + return strlen($body); + } + return false; + } + ); + $rs = curl_exec($ch); + #echo ("###code=$code\n"); + curl_close($ch); + if ($nexturl == null){ + if ($code != 200) throw new HTTPErrorException($code,"HTTP status $code"); + return true; + } + } + throw new HTTPErrorException(500,"too many redirects"); +} + function proxy($url) { - $ch = curl_init($url); - curl_setopt_array( - $ch, - [ - CURLOPT_RETURNTRANSFER => true, - CURLOPT_CONNECTTIMEOUT => 30, - ] - ); - setFw($ch); - $response = curl_exec_follow($ch); - curl_close($ch); + header('Access-Control-Allow-Origin:*'); + return proxy_impl($url,30,getFwHeaders()); } ?> \ No newline at end of file diff --git a/webinstall/install.php b/webinstall/install.php index 2cd046b..ed5281e 100644 --- a/webinstall/install.php +++ b/webinstall/install.php @@ -1,41 +1,51 @@ +} catch (HTTPErrorException $h) { + header($_SERVER['SERVER_PROTOCOL'] . " " . $h->code . " " . $h->getMessage()); + die($h->getMessage()); +} catch (Exception $e) { + header($_SERVER['SERVER_PROTOCOL'] . ' 500 ' . $e->getMessage()); + die($e->getMessage()); +} +die("invalid request"); +?> \ No newline at end of file From f6cbcecb7809e2b8b9bc774243b0d0578d08bf9f Mon Sep 17 00:00:00 2001 From: andreas Date: Tue, 5 Sep 2023 19:21:53 +0200 Subject: [PATCH 046/178] handle empty suffix correctly --- .circleci/config.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f68f0c5..91dfe07 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -49,10 +49,15 @@ jobs: name: "rename" working_directory: ".pio/build/<< pipeline.parameters.environment >>" command: "mv firmware.bin << pipeline.parameters.environment >><< pipeline.parameters.suffix >>-update.bin" - - run: - name: "rename2" - working_directory: ".pio/build/<< pipeline.parameters.environment >>" - command: "mv << pipeline.parameters.environment >>-all.bin << pipeline.parameters.environment >><< pipeline.parameters.suffix >>-all.bin" + - when: + condition: + not: + equal: [ << pipeline.parameters.suffix >> ,""] + steps: + - run: + name: "rename2" + working_directory: ".pio/build/<< pipeline.parameters.environment >>" + command: "mv << pipeline.parameters.environment >>-all.bin << pipeline.parameters.environment >><< pipeline.parameters.suffix >>-all.bin" - run: name: "compress" working_directory: ".pio/build/<< pipeline.parameters.environment >>" From 56b5598c15c023bfe2d70dfb7ea4eda3c265ebe1 Mon Sep 17 00:00:00 2001 From: andreas Date: Tue, 5 Sep 2023 19:44:25 +0200 Subject: [PATCH 047/178] api functions for ci --- webinstall/cibuild.php | 59 ++++++++++++++++++++++++++++++++++++++-- webinstall/config.php | 9 ++++-- webinstall/functions.php | 11 ++++++-- 3 files changed, 72 insertions(+), 7 deletions(-) diff --git a/webinstall/cibuild.php b/webinstall/cibuild.php index 77e4979..77cfefb 100644 --- a/webinstall/cibuild.php +++ b/webinstall/cibuild.php @@ -4,10 +4,13 @@ include("functions.php"); include("config.php"); if (! isset($CI_TOKEN)) die("no token"); const apiBase="https://circleci.com/api/v2/"; +const webApp="https://app.circleci.com/"; const apiRepo="project/gh/#user#/#repo#"; const workflowName="build-workflow"; const jobName="pio-build"; - +const defaultBranch='circleci-project-setup'; +const defaultUser='wellenvogel'; +const defaultRepo='esp32-nmea2000'; function getTokenHeaders(){ global $CI_TOKEN; @@ -63,8 +66,21 @@ function getJobStatus($pipeline,$wf=workflowName,$job=jobName){ if ($pstat['state'] != 'created'){ return $pstat; } + $pipeline_id=$pstat['id']; + $pipeline_number=$pstat['number']; $pstat=getWorkflow($pipeline,$wf); + $workflow_id=$pstat['id']; + $workflow_number=$pstat['workflow_number']; $pstat=getJob($pipeline,$pstat['id'],$job); + $pstat['pipeline_id']=$pipeline_id; + $pstat['pipeline_number']=$pipeline_number; + $pstat['workflow_id']=$workflow_id; + $pstat['workflow_number']=$workflow_number; + if (isset($pstat['project_slug'])){ + $pstat['status_url']=webApp."/pipelines/". + preg_replace('/^gh/','github',$pstat['project_slug'])."/". + $pipeline_number."/workflows/".$workflow_id."/jobs/".$pstat['job_number']; + } return $pstat; } @@ -121,7 +137,46 @@ try { } exit(0); } - die("invalid api $action"); + if ($action == 'pipeline'){ + addVars( + $par, + ['number','user','repo'], + array('user'=>defaultUser,'repo'=>defaultRepo) + ); + $url=apiBase."/".replaceVars(apiRepo,fillUserAndRepo(null,$par))."/pipeline/".$par['number']; + $rt=getJson($url,getTokenHeaders(),true); + echo(json_encode($rt)); + exit(0); + } + if ($action == 'start'){ + addVars( + $par, + ['environment','buildflags','config','suffix','branch','user','repo'], + array('suffix'=>'', + 'branch'=>defaultBranch, + 'config'=>'{}', + 'user'=>defaultUser, + 'repo'=>defaultRepo, + 'buildflags'=>'' + ) + ); + $requestParam=array( + 'branch'=>$par['branch'], + 'parameters'=> array( + 'run_build'=>true, + 'environment'=>$par['environment'], + 'suffix'=>$par['suffix'], + 'config'=>$par['config'], + 'build_flags'=>$par['buildflags'] + ) + ); + $userRepo=fillUserAndRepo(null,$par); + $url=apiBase."/".replaceVars(apiRepo,$userRepo)."/pipeline"; + $rt=getJson($url,getTokenHeaders(),true,$requestParam); + echo (json_encode($rt)); + exit(0); + } + throw new Exception("invalid api $action"); } if (isset($_REQUEST['download'])) { $pipeline = $_REQUEST['download']; diff --git a/webinstall/config.php b/webinstall/config.php index afc3741..e63f800 100644 --- a/webinstall/config.php +++ b/webinstall/config.php @@ -5,16 +5,19 @@ $allowed=array( ); -function fillUserAndRepo($vars=null){ +function fillUserAndRepo($vars=null,$source=null){ global $allowed; if ($vars == null) { $vars=array(); } + if ($source == null){ + $source=$_REQUEST; + } foreach (array('user','repo') as $n){ - if (! isset($_REQUEST[$n])){ + if (! isset($source[$n])){ die("missing parameter $n"); } - $v=$_REQUEST[$n]; + $v=$source[$n]; $av=$allowed[$n]; if (! in_array($v,$av)){ die("value $v for $n not allowed"); diff --git a/webinstall/functions.php b/webinstall/functions.php index 96557a6..d431058 100644 --- a/webinstall/functions.php +++ b/webinstall/functions.php @@ -121,11 +121,18 @@ function getFwHeaders($aheaders=null){ } return $outHeaders; } -function getJson($url,$headers=null,$doThrow=false){ +function getJson($url,$headers=null,$doThrow=false,$jsonData=null){ $curl = curl_init(); curl_setopt($curl, CURLOPT_URL,$url); curl_setopt($curl,CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_HTTPHEADER, getFwHeaders($headers)); + $outHeaders=getFwHeaders($headers); + if ($jsonData != null){ + $json=json_encode($jsonData); + array_push($outHeaders,"Content-Type: application/json"); + array_push($outHeaders,"Content-length: ".strlen($json)); + curl_setopt($curl, CURLOPT_POSTFIELDS,$json); + } + curl_setopt($curl, CURLOPT_HTTPHEADER, $outHeaders); $response = curl_exec($curl); $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); #echo("curl exec for $url:$response:$httpcode\n"); From 0e0be144155123b9a43c6331ef328e9a7bc8ef90 Mon Sep 17 00:00:00 2001 From: andreas Date: Tue, 5 Sep 2023 21:15:07 +0200 Subject: [PATCH 048/178] first trigger of builds via GUI --- webinstall/cibuild.css | 3 ++ webinstall/cibuild.html | 38 +++++++++++++++ webinstall/cibuild.js | 98 +++++++++++++++++++++++++++++++++++++++ webinstall/helper.js | 96 ++++++++++++++++++++++++++++++++++++++ webinstall/install.html | 2 +- webinstall/install.js | 6 +-- webinstall/installUtil.js | 33 ------------- 7 files changed, 239 insertions(+), 37 deletions(-) create mode 100644 webinstall/cibuild.css create mode 100644 webinstall/cibuild.html create mode 100644 webinstall/cibuild.js create mode 100644 webinstall/helper.js diff --git a/webinstall/cibuild.css b/webinstall/cibuild.css new file mode 100644 index 0000000..b5164fc --- /dev/null +++ b/webinstall/cibuild.css @@ -0,0 +1,3 @@ +.hidden{ + display: none; +} \ No newline at end of file diff --git a/webinstall/cibuild.html b/webinstall/cibuild.html new file mode 100644 index 0000000..d1de53a --- /dev/null +++ b/webinstall/cibuild.html @@ -0,0 +1,38 @@ + + + + + + + + + +

Build your own ESP32-NMEA2000

+
+ + +
+
+ + +
+
+ +
+
+ +
---
+
+
+ +
---
+
+ + + + + \ No newline at end of file diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js new file mode 100644 index 0000000..5627f32 --- /dev/null +++ b/webinstall/cibuild.js @@ -0,0 +1,98 @@ +import { setButtons,fillValues, setValue, buildUrl, fetchJson, setVisible, enableEl } from "./helper"; +(function(){ + const STATUS_INTERVAL=2000; + const CURRENT_PIPELINE='pipeline'; + let API="cibuild.php"; + let currentPipeline=undefined; + let downloadUrl=undefined; + let timer=undefined; + const fetchStatus=()=>{ + if (currentPipeline === undefined) return; + fetchJson(API,{api:'status',pipeline:currentPipeline}) + .then((st)=>{ + setValue('status',st.status); + let l=document.getElementById('link'); + if (l){ + if (st.status_url){ + l.setAttribute('href',st.status_url); + setVisible(l.parentElement,true); + } + else{ + setVisible(l.parentElement,false); + } + } + if (st.status === 'success'){ + enableEl('start',true); + fetchJson(API,{api:'artifacts',pipeline:currentPipeline}) + .then((ar)=>{ + if (! ar.items || ar.items.length < 1){ + throw new Error("no download link"); + } + downloadUrl=ar.items[0].url; + setVisible(document.getElementById('download'),true,true); + + }) + .catch((err)=>alert("Unable to get build result: "+err)); + return; + } + else{ + setVisible(document.getElementById('download'),false,true); + } + timer=window.setTimeout(fetchStatus,STATUS_INTERVAL) + }) + .catch((e)=>{ + timer=window.setTimeout(fetchStatus,STATUS_INTERVAL); + }) + } + const setCurrentPipeline=(pipeline)=>{ + currentPipeline=pipeline; + window.localStorage.setItem(CURRENT_PIPELINE,pipeline); + }; + const startBuild=()=>{ + let param={}; + currentPipeline=undefined; + if (timer) window.clearTimeout(timer); + timer=undefined; + fillValues(param,['environment','buildflags']); + setValue('status','requested'); + fetchJson(API,Object.assign({ + api:'start'},param)) + .then((json)=>{ + if (json.status === 'error'){ + throw new Error("unable to create job "+(json.error||'')); + } + if (!json.id) throw new Error("unable to create job, no id"); + setCurrentPipeline(json.id); + setValue('pipeline',currentPipeline); + setValue('status',json.status); + enableEl('start',false); + timer=window.setTimeout(fetchStatus,STATUS_INTERVAL); + }) + .catch((err)=>{ + setValue('status','error'); + enableEl('start',true); + alert(err); + }); + } + const runDownload=()=>{ + if (! downloadUrl) return; + let df=document.getElementById('dlframe'); + if (df){ + df.setAttribute('src',null); + df.setAttribute('src',downloadUrl); + } + } + const btConfig={ + start:startBuild, + download:runDownload + }; + window.onload=()=>{ + setButtons(btConfig); + currentPipeline=window.localStorage.getItem(CURRENT_PIPELINE); + if (currentPipeline){ + setValue('pipeline',currentPipeline); + enableEl('start',false); + fetchStatus(); + } + } +})(); \ No newline at end of file diff --git a/webinstall/helper.js b/webinstall/helper.js new file mode 100644 index 0000000..82c3085 --- /dev/null +++ b/webinstall/helper.js @@ -0,0 +1,96 @@ +const getParam = (key) => { + let value = RegExp("" + key + "[^&]+").exec(window.location.search); + // Return the unescaped value minus everything starting from the equals sign or an empty string + return decodeURIComponent(!!value ? value.toString().replace(/^[^=]+./, "") : ""); +}; +/** + * add an HTML element + * @param {*} type + * @param {*} clazz + * @param {*} parent + * @param {*} text + * @returns + */ +const addEl = (type, clazz, parent, text) => { + let el = document.createElement(type); + if (clazz) { + if (!(clazz instanceof Array)) { + clazz = clazz.split(/ */); + } + clazz.forEach(function (ce) { + el.classList.add(ce); + }); + } + if (text) el.textContent = text; + if (parent) parent.appendChild(el); + return el; +} +/** + * call a function for each matching element + * @param {*} selector + * @param {*} cb + */ +const forEachEl = (selector, cb) => { + let arr = document.querySelectorAll(selector); + for (let i = 0; i < arr.length; i++) { + cb(arr[i]); + } +} + +const setButtons=(config)=>{ + for (let k in config){ + let bt=document.getElementById(k); + if (bt){ + bt.addEventListener('click',config[k]); + } + } +} +const fillValues=(values,items)=>{ + items.forEach((it)=>{ + let e=document.getElementById(it); + if (e){ + values[it]=e.value; //TODO: type of el + } + }) +}; +const setValue=(id,value)=>{ + let el=document.getElementById(id); + if (! el) return; + if (el.tagName == 'DIV'){ + el.textContent=value; + return; + } + if (el.tagName == 'INPUT'){ + el.value=value; + } +} +const buildUrl=(url,pars)=>{ + let delim=(url.match("[?]"))?"&":"?"; + for (let k in pars){ + url+=delim; + delim="&"; + url+=encodeURIComponent(k); + url+="="; + url+=encodeURIComponent(pars[k]); + } + return url; +} +const fetchJson=(url,pars)=>{ + let furl=buildUrl(url,pars); + return fetch(furl).then((rs)=>rs.json()); +} +const setVisible=(el,vis,useParent)=>{ + if (! el) return; + if (useParent) el=el.parentElement; + if (! el) return; + if (vis) el.classList.remove('hidden'); + else el.classList.add('hidden'); +} +const enableEl=(id,en)=>{ + let el=document.getElementById(id); + if (!el) return; + if (en) el.disabled=false; + else el.disabled=true; +} + +export { getParam, addEl, forEachEl,setButtons,fillValues, setValue,buildUrl,fetchJson,setVisible, enableEl } \ No newline at end of file diff --git a/webinstall/install.html b/webinstall/install.html index e578a82..414d431 100644 --- a/webinstall/install.html +++ b/webinstall/install.html @@ -9,9 +9,9 @@ -
+ \ No newline at end of file diff --git a/webinstall/install.js b/webinstall/install.js index 0281ff4..ff7d958 100644 --- a/webinstall/install.js +++ b/webinstall/install.js @@ -1,10 +1,10 @@ import {XtermOutputHandler} from "./installUtil.js"; import ESPInstaller from "./installUtil.js"; +import { addEl, getParam } from "./helper.js"; (function(){ let espLoaderTerminal; let espInstaller; let releaseData={}; - const addEl=ESPInstaller.addEl; //shorter typing let showConsole; let hideConsole; const enableConsole=(enable,disableBoth)=>{ @@ -134,8 +134,8 @@ import ESPInstaller from "./installUtil.js"; showError("your browser does not support the ESP flashing (no serial)"); return; } - let user = window.gitHubUser||ESPInstaller.getParam('user'); - let repo = window.gitHubRepo || ESPInstaller.getParam('repo'); + let user = window.gitHubUser||getParam('user'); + let repo = window.gitHubRepo || getParam('repo'); if (!user || !repo) { alert("missing parameter user or repo"); } diff --git a/webinstall/installUtil.js b/webinstall/installUtil.js index 87f91c3..a40a691 100644 --- a/webinstall/installUtil.js +++ b/webinstall/installUtil.js @@ -72,39 +72,6 @@ class ESPInstaller{ // Return the unescaped value minus everything starting from the equals sign or an empty string return decodeURIComponent(!!value ? value.toString().replace(/^[^=]+./,"") : ""); }; - /** - * add an HTML element - * @param {*} type - * @param {*} clazz - * @param {*} parent - * @param {*} text - * @returns - */ - static addEl(type, clazz, parent, text) { - let el = document.createElement(type); - if (clazz) { - if (!(clazz instanceof Array)) { - clazz = clazz.split(/ */); - } - clazz.forEach(function (ce) { - el.classList.add(ce); - }); - } - if (text) el.textContent = text; - if (parent) parent.appendChild(el); - return el; - } - /** - * call a function for each matching element - * @param {*} selector - * @param {*} cb - */ - static forEachEl(selector,cb){ - let arr=document.querySelectorAll(selector); - for (let i=0;i Date: Wed, 6 Sep 2023 12:22:33 +0200 Subject: [PATCH 049/178] intermediate: prepare custom install in webinstaller --- webinstall/cibuild.css | 93 +++++++++++++++++++++++++++++++++++++++ webinstall/cibuild.html | 32 +++++++++----- webinstall/cibuild.js | 71 ++++++++++++++++++++---------- webinstall/cibuild.php | 1 + webinstall/helper.js | 18 +++++++- webinstall/install.js | 85 ++++++++++++++++++++++++++++++----- webinstall/installUtil.js | 30 +++++++++++++ 7 files changed, 283 insertions(+), 47 deletions(-) diff --git a/webinstall/cibuild.css b/webinstall/cibuild.css index b5164fc..564271f 100644 --- a/webinstall/cibuild.css +++ b/webinstall/cibuild.css @@ -1,3 +1,96 @@ .hidden{ display: none; +} + +/* reused stuff from configui */ +.configui.container{ + max-width: 35em; + margin-left: auto; + margin-right: auto; + position: relative; +} +.configui .info{ + margin-bottom: 1em; + opacity: 0.6; + white-space: pre-line; +} +.configui .parameters { + border-bottom: 1px solid grey; + margin-bottom: 1em; +} +.configui .row input[type="checkbox"] { + flex-grow: 1; + appearance: auto; +} + +.configui .row { + display: flex; + flex-direction: row; + margin: 0.5em; + flex-wrap: unset; + padding: 0; +} + +.configui .row .label { + width: 10em; + opacity: 0.6; + padding: 0; +} +.configui .since { + display: block; + font-size: 0.8em; +} +.configui input[type=checkbox] { + width: 1.5em; + height: 1.5em; + opacity: 1; + z-index: unset; +} + +.configui .buttons { + display: flex; + flex-direction: row; + justify-content: flex-end; +} +.configui button { + padding: 0.5em; +} +.configui .footer { + text-align: right; + margin-top: 1em; + font-size: 0.8em; +} +.configui .visually-hidden { + position: absolute !important; + height: 1px; + width: 1px; + overflow: hidden; + clip: rect(1px, 1px, 1px, 1px); + } + +.configui .dialogBack { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + z-index: 10; + background-color: #8a8c8ec7; + display: flex; +} +.configui .hidden{ + display: none; +} +.configui .dialog{ + max-width: 35em; + margin: auto; + background-color: white; + padding: 2em; +} +.configui #warn{ + display: none; + color: red; +} +.configui #warn.warn{ + display: block; } \ No newline at end of file diff --git a/webinstall/cibuild.html b/webinstall/cibuild.html index d1de53a..363b0b4 100644 --- a/webinstall/cibuild.html +++ b/webinstall/cibuild.html @@ -7,32 +7,40 @@ +

Build your own ESP32-NMEA2000

-
- +
+ Board type
-
- +
+ Build Flags
-
+
-
- +
+ Job Id
---
-
- +
+ Status
---
- +
+ chip type + --- +
currentVersion --- diff --git a/web/index.js b/web/index.js index 10eae20..699a82e 100644 --- a/web/index.js +++ b/web/index.js @@ -1564,9 +1564,15 @@ function uploadBin(ev){ .then(function (result) { let currentType; let currentVersion; + let chiptype; forEl('.status-version', function (el) { currentVersion = el.textContent }); forEl('.status-fwtype', function (el) { currentType = el.textContent }); + forEl('.status-chiptype', function (el) { chiptype = el.textContent }); let confirmText = 'Ready to update firmware?\n'; + if (result.chip.match(/^@@/) && (result.chip.substr(2) != chiptype)){ + confirmText += "WARNING: the chiptype in the image ("+result.chip.substr(2); + confirmText +=") does not match the current chip type ("+chiptype+").\n"; + } if (currentType != result.fwtype) { confirmText += "WARNING: image has different type: " + result.fwtype + "\n"; confirmText += "** Really update anyway? - device can become unusable **"; @@ -1643,7 +1649,8 @@ function uploadBin(ev){ let HDROFFSET=288; let VERSIONOFFSET=16; let NAMEOFFSET=48; -let MINSIZE=HDROFFSET+NAMEOFFSET+32; +let CHIPOFFSET=NAMEOFFSET+64; +let MINSIZE = HDROFFSET + CHIPOFFSET + 32; let imageCheckBytes={ 0: 0xe9, //image magic 288: 0x32, //app header magic @@ -1678,9 +1685,11 @@ function checkImageFile(file){ } let version=decodeFromBuffer(content,HDROFFSET+VERSIONOFFSET,32); let fwtype=decodeFromBuffer(content,HDROFFSET+NAMEOFFSET,32); + let chip=decodeFromBuffer(content,HDROFFSET+CHIPOFFSET,32); let rt={ fwtype:fwtype, version: version, + chip:chip }; resolve(rt); }); @@ -1742,7 +1751,12 @@ window.addEventListener('load', function () { checkImageFile(file) .then(function(res){ forEl('#imageProperties',function(iel){ - iel.textContent=res.fwtype+", "+res.version; + let txt=""; + if (res.chip.match(/^@@/)){ + txt=res.chip.substr(2)+", " + } + txt+=res.fwtype+", "+res.version; + iel.textContent=txt; iel.classList.remove("error"); }) }) From e8c6a72a43f64dec86b473cb902c28822ea8821d Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 7 Sep 2023 20:43:55 +0200 Subject: [PATCH 054/178] better handling of result visibility --- webinstall/cibuild.html | 5 ++++- webinstall/cibuild.js | 32 ++++++++++++++++++++++++++------ webinstall/helper.js | 13 ++++++++++++- webinstall/install.js | 6 ++++-- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/webinstall/cibuild.html b/webinstall/cibuild.html index 363b0b4..bec08ea 100644 --- a/webinstall/cibuild.html +++ b/webinstall/cibuild.html @@ -9,9 +9,11 @@

Build your own ESP32-NMEA2000

+

New Build

Board type - +
Build Flags @@ -20,6 +22,7 @@
+

Last Build

Job Id
---
diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index ce28750..3b9c3d4 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -1,4 +1,4 @@ -import { setButtons,fillValues, setValue, buildUrl, fetchJson, setVisible, enableEl, setValues } from "./helper"; +import { setButtons,fillValues, setValue, buildUrl, fetchJson, setVisible, enableEl, setValues, getParam, fillSelect, forEachEl } from "./helper.js"; (function(){ const STATUS_INTERVAL=2000; const CURRENT_PIPELINE='pipeline'; @@ -6,6 +6,8 @@ import { setButtons,fillValues, setValue, buildUrl, fetchJson, setVisible, enabl let currentPipeline=undefined; let downloadUrl=undefined; let timer=undefined; + let branch=getParam('branch'); + if (! branch) branch='master'; const showError=(text)=>{ if (text === undefined){ setVisible('buildError',false,true); @@ -14,12 +16,21 @@ import { setButtons,fillValues, setValue, buildUrl, fetchJson, setVisible, enabl setValue('buildError',text); setVisible('buildError',true,true); } + const hideResults = () => { + downloadUrl = undefined; + currentPipeline = undefined; + setValue('pipeline', currentPipeline); + setValue('status',''); + showError(); + setVisible('download', false, true); + setVisible('status_url', false, true); + } const setRunning=(active)=>{ if (active){ - downloadUrl=undefined; showError(); - setVisible('download',false,true); - setVisible('status_url',false,true); + downloadUrl=undefined; + setVisible('download', false, true); + setVisible('status_url', false, true); } enableEl('start',!active); } @@ -68,12 +79,13 @@ import { setButtons,fillValues, setValue, buildUrl, fetchJson, setVisible, enabl window.localStorage.setItem(CURRENT_PIPELINE,pipeline); }; const startBuild=()=>{ - let param={}; + let param={'branch':branch}; currentPipeline=undefined; if (timer) window.clearTimeout(timer); timer=undefined; fillValues(param,['environment','buildflags']); setValue('status','requested'); + setValue('pipeline',''); setRunning(true); fetchJson(API,Object.assign({ api:'start'},param)) @@ -111,13 +123,21 @@ import { setButtons,fillValues, setValue, buildUrl, fetchJson, setVisible, enabl download:runDownload, webinstall:webInstall }; + const environments=[ + 'm5stack-atom-generic', + 'm5stack-atoms3-generic', + 'nodemcu-generic' + ]; window.onload=()=>{ setButtons(btConfig); + forEachEl('#environment',(el)=>el.addEventListener('change',hideResults)); + forEachEl('#buildflags',(el)=>el.addEventListener('change',hideResults)); + fillSelect('environment',environments); currentPipeline=window.localStorage.getItem(CURRENT_PIPELINE); if (currentPipeline){ setValue('pipeline',currentPipeline); - setRunning(true); fetchStatus(true); + setRunning(true); } } })(); \ No newline at end of file diff --git a/webinstall/helper.js b/webinstall/helper.js index d3c070e..3e51220 100644 --- a/webinstall/helper.js +++ b/webinstall/helper.js @@ -108,5 +108,16 @@ const enableEl=(id,en)=>{ if (en) el.disabled=false; else el.disabled=true; } +const fillSelect=(el,values)=>{ + if (typeof(el) !== 'object') el=document.getElementById(el); + if (! el) return; + el.textContent=''; + let kf=(values instanceof Array)?(k)=>values[k]:(k)=>k; + for (let k in values){ + let o=addEl('option','',el); + o.setAttribute('value',kf(k)); + o.textContent=values[k]; + } +} -export { getParam, addEl, forEachEl,setButtons,fillValues, setValue,setValues,buildUrl,fetchJson,setVisible, enableEl } \ No newline at end of file +export { getParam, addEl, forEachEl,setButtons,fillValues, setValue,setValues,buildUrl,fetchJson,setVisible, enableEl,fillSelect } \ No newline at end of file diff --git a/webinstall/install.js b/webinstall/install.js index 059e431..3c7de3d 100644 --- a/webinstall/install.js +++ b/webinstall/install.js @@ -13,8 +13,8 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; const NAMEOFFSET = 48; const CHIPOFFSET=NAMEOFFSET+64; const MINSIZE = HDROFFSET + CHIPOFFSET + 32; + const imageMagic=0xe9; //at byte 0 const imageCheckBytes = { - 0: 0xe9, //image magic 288: 0x32, //app header magic 289: 0x54, 290: 0xcd, @@ -25,7 +25,6 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; length--; } if (length <= 0) return ""; - let decoder = new TextDecoder(); return buffer.substr(start,length); } /** @@ -38,6 +37,9 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; if (content.length < (MINSIZE+startOffset)) { throw new Error(prfx+"image to small, only " + content.length + " expected " + (MINSIZE+startOffset)); } + if (content.charCodeAt(0) != imageMagic){ + throw new Error("no image magic "+imageMagic+" at start of "+prfx+"image"); + } for (let idx in imageCheckBytes) { let cb=content.charCodeAt(parseInt(idx)+startOffset); if (cb != imageCheckBytes[idx]) { From 4fcf6b753f8dd1977c6769de52f01f1e28e2658c Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 9 Sep 2023 13:01:00 +0200 Subject: [PATCH 055/178] intermediate: handle flusing of s3 images in webinstaller --- webinstall/install.js | 41 ++++++++++++++++++++++++++++++--------- webinstall/installUtil.js | 11 +++++------ 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/webinstall/install.js b/webinstall/install.js index 3c7de3d..6db2cb1 100644 --- a/webinstall/install.js +++ b/webinstall/install.js @@ -4,7 +4,6 @@ import { addEl, getParam, setValue, setVisible } from "./helper.js"; import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; (function(){ - const FULL_START=4096; const UPDATE_START=65536; //taken from index.js @@ -13,6 +12,7 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; const NAMEOFFSET = 48; const CHIPOFFSET=NAMEOFFSET+64; const MINSIZE = HDROFFSET + CHIPOFFSET + 32; + const CHIPIDOFFSET=12; //2 bytes chip id here const imageMagic=0xe9; //at byte 0 const imageCheckBytes = { 288: 0x32, //app header magic @@ -20,6 +20,14 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; 290: 0xcd, 291: 0xab }; + /** + * map of our known chip ids to flash starts for full images + * 9 - esp32s3 - starts at 0 + */ + const FLASHSTART={ + 0:0x1000, + 9:0 + }; const decodeFromBuffer=(buffer, start, length)=>{ while (length > 0 && buffer.charCodeAt(start + length - 1) == 0) { length--; @@ -27,13 +35,25 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; if (length <= 0) return ""; return buffer.substr(start,length); } + const getChipId=(buffer)=>{ + if (buffer.length < CHIPIDOFFSET+2) return -1; + return buffer.charCodeAt(CHIPIDOFFSET)+256*buffer.charCodeAt(CHIPIDOFFSET+1); + } /** * * @param {string} content the content to be checked */ const checkImage = (content,isFull) => { let prfx=isFull?"full":"update"; - let startOffset=isFull?(UPDATE_START-FULL_START):0; + let startOffset=0; + let flashStart=UPDATE_START; + if (isFull){ + let chipId=getChipId(content); + if (chipId < 0) throw new Error(prfx+"image: no valid chip id found"); + let flashStart=FLASHSTART[chipId]; + if (flashStart === undefined) throw new Error(prfx+"image: unknown chip id "+chipId); + startOffset=UPDATE_START-flashStart; + } if (content.length < (MINSIZE+startOffset)) { throw new Error(prfx+"image to small, only " + content.length + " expected " + (MINSIZE+startOffset)); } @@ -53,7 +73,8 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; let rt = { fwtype: fwtype, version: version, - chip:chip + chip:chip, + flashStart: flashStart }; return rt; } @@ -260,7 +281,7 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; user, repo, version, - 4096, + item.basic, checkChip ) enableConsole(true); @@ -273,7 +294,7 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; user, repo, version, - 65536, + item.update, checkChip ) enableConsole(true); @@ -286,12 +307,14 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; if (!bFrame) return; if (fullData === undefined && updateData === undefined) return; let version; + let vinfo; + let uinfo; if (fullData !== undefined){ - let vinfo=checkImage(fullData,true); + vinfo=checkImage(fullData,true); version=vinfo.version; } if (updateData !== undefined){ - let uinfo=checkImage(updateData); + uinfo=checkImage(updateData); if (version !== undefined){ if (uinfo.version != version){ throw new Error("different versions in full("+version+") and update("+uinfo.version+") image"); @@ -316,7 +339,7 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; await espInstaller.runFlash( true, fullData, - FULL_START, + vinfo.flashStart, version, checkChip ) @@ -330,7 +353,7 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; await espInstaller.runFlash( false, updateData, - UPDATE_START, + uinfo.flashStart, version, checkChip ) diff --git a/webinstall/installUtil.js b/webinstall/installUtil.js index e2f1f27..bb0eedd 100644 --- a/webinstall/installUtil.js +++ b/webinstall/installUtil.js @@ -235,22 +235,21 @@ class ESPInstaller{ * @param {*} user * @param {*} repo * @param {*} version - * @param {*} address + * @param {*} assetName * @param {*} checkChip will be called with the found chip and the data and the isFull flag + * must return an info with the flashStart being set * @returns */ - async installClicked(isFull, user, repo, version, address, assetName) { + async installClicked(isFull, user, repo, version, assetName,checkChip) { try { await this.connect(); let imageData = await this.getReleaseAsset(user, repo, version, assetName); if (!imageData || imageData.length == 0) { throw new Error(`no image data fetched`); } - if (checkChip) { - await checkChip(this.getChipFamily(),imageData,isFull); - } + let info=await checkChip(this.getChipFamily(),imageData,isFull); let fileList = [ - { data: imageData, address: address } + { data: imageData, address: info.flashAddress } ]; let txt = isFull ? "baseImage (all data will be erased)" : "update"; if (!confirm(`ready to install ${version}\n${txt}`)) { From c87c38fca4825af46fdd54539d403862bb2f25e2 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 9 Sep 2023 17:11:32 +0200 Subject: [PATCH 056/178] revert back for chip id in app info --- lib/appinfo/GwAppInfo.h | 6 +----- post.py | 7 ++++++- src/main.cpp | 7 +++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/appinfo/GwAppInfo.h b/lib/appinfo/GwAppInfo.h index 79e4622..f7eb143 100644 --- a/lib/appinfo/GwAppInfo.h +++ b/lib/appinfo/GwAppInfo.h @@ -16,8 +16,4 @@ #endif #define FIRMWARE_TYPE GWSTRINGIFY(PIO_ENV_BUILD) -#ifdef PIO_ENV_BOARD -#define BOARD_INFO "@@" GWSTRINGIFY(PIO_ENV_BOARD) -#else -#define BOARD_INFO "" -#endif \ No newline at end of file +#define IDF_VERSION GWSTRINGIFY(ESP_IDF_VERSION_MAJOR) "." GWSTRINGIFY(ESP_IDF_VERSION_MINOR) "." GWSTRINGIFY(ESP_IDF_VERSION_PATCH) \ No newline at end of file diff --git a/post.py b/post.py index c85a740..8b04ffa 100644 --- a/post.py +++ b/post.py @@ -42,11 +42,16 @@ def post(source,target,env): print("found fwname=%s, fwversion=%s"%(fwname,version)) python=env.subst("$PYTHONEXE") print("base=%s,esptool=%s,appoffset=%s,uploaderflags=%s"%(base,esptool,appoffset,uploaderflags)) + chip="esp32" uploadparts=uploaderflags.split(" ") #currently hardcoded last 8 parameters... if len(uploadparts) < 6: print("uploaderflags does not have enough parameter") return + for i in range(0,len(uploadparts)): + if uploadparts[i]=="--chip": + if i < (len(uploadparts) -1): + chip=uploadparts[i+1] uploadfiles=uploadparts[-6:] for i in range(1,len(uploadfiles),2): if not os.path.isfile(uploadfiles[i]): @@ -64,7 +69,7 @@ def post(source,target,env): versionedFile=os.path.join(outdir,"%s%s-update.bin"%(base,ofversion)) shutil.copyfile(firmware,versionedFile) outfile=os.path.join(outdir,"%s%s-all.bin"%(base,ofversion)) - cmd=[python,esptool,"--chip","esp32","merge_bin","--target-offset",offset,"-o",outfile] + cmd=[python,esptool,"--chip",chip,"merge_bin","--target-offset",offset,"-o",outfile] cmd+=uploadfiles cmd+=[appoffset,firmware] print("running %s"%" ".join(cmd)) diff --git a/src/main.cpp b/src/main.cpp index 96a5903..09b1b03 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -76,7 +76,7 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting //assert length of firmware name and version CASSERT(strlen(FIRMWARE_TYPE) <= 32, "environment name (FIRMWARE_TYPE) must not exceed 32 chars"); CASSERT(strlen(VERSION) <= 32, "VERSION must not exceed 32 chars"); -CASSERT(strlen(BOARD_INFO) <= 32,"BOARD_INFO must not exceed 32 chars"); +CASSERT(strlen(IDF_VERSION) <= 32,"IDF_VERSION must not exceed 32 chars"); //https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/app_image_format.html //and removed the bugs in the doc... __attribute__((section(".rodata_custom_desc"))) esp_app_desc_t custom_app_desc = { @@ -87,14 +87,13 @@ __attribute__((section(".rodata_custom_desc"))) esp_app_desc_t custom_app_desc = FIRMWARE_TYPE, "00:00:00", "2021/12/13", - BOARD_INFO, + IDF_VERSION, {}, {} }; String firmwareType(FIRMWARE_TYPE); -String chipType(BOARD_INFO); typedef std::map StringMap; @@ -387,7 +386,7 @@ protected: GwConfigHandler::toHex(base,buffer,bsize); status["salt"] = buffer; status["fwtype"]= firmwareType; - status["chiptype"]=chipType.substring(2); + status["chipid"]=CONFIG_IDF_FIRMWARE_CHIP_ID; status["heap"]=(long)xPortGetFreeHeapSize(); Nmea2kTwai::Status n2kState=NMEA2000.getStatus(); Nmea2kTwai::STATE driverState=n2kState.state; From f800893ac8feec67afdcdaecd0bc3a9bd02292fc Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 9 Sep 2023 17:11:47 +0200 Subject: [PATCH 057/178] use chip id for all image checks --- extra_script.py | 6 +-- tools/flashtool/flashtool.py | 96 +++++++++++++++++++++++------------- web/index.js | 27 +++++----- webinstall/install.js | 30 ++++------- webinstall/installUtil.js | 22 +++++---- 5 files changed, 99 insertions(+), 82 deletions(-) diff --git a/extra_script.py b/extra_script.py index a91c147..8a50062 100644 --- a/extra_script.py +++ b/extra_script.py @@ -268,10 +268,7 @@ def prebuild(env): genereateUserTasks(os.path.join(outPath(), TASK_INCLUDE)) generateFile(os.path.join(basePath(),XDR_FILE),os.path.join(outPath(),XDR_INCLUDE),generateXdrMappings) version="dev"+datetime.now().strftime("%Y%m%d") - env.Append(CPPDEFINES=[ - ('GWDEVVERSION',version), - ('PIO_ENV_BOARD',env.get('BOARD_MCU')) - ]) + env.Append(CPPDEFINES=[('GWDEVVERSION',version)]) def cleangenerated(source, target, env): od=outPath() @@ -293,4 +290,3 @@ env.Append( ) #script does not run on clean yet - maybe in the future env.AddPostAction("clean",cleangenerated) -#print(env.Dump()) diff --git a/tools/flashtool/flashtool.py b/tools/flashtool/flashtool.py index 3a30162..92fef05 100755 --- a/tools/flashtool/flashtool.py +++ b/tools/flashtool/flashtool.py @@ -72,7 +72,7 @@ def main(): tk.Label(frame,textvariable=self.fileInfo).grid(row=row,column=0,columnspan=2,sticky="ew") row+=1 self.flashInfo=tk.StringVar() - self.flashInfo.set("Address 0x1000") + self.flashInfo.set("Full Flash") tk.Label(frame,textvariable=self.flashInfo).grid(row=row,column=0,columnspan=2,sticky='ew',pady=10) row+=1 btFrame=tk.Frame(frame) @@ -96,7 +96,7 @@ def main(): def updateFlashInfo(self): if self.mode.get() == 1: #full - self.flashInfo.set("Address 0x1000") + self.flashInfo.set("Full Flash") else: self.flashInfo.set("Erase(otadata): 0xe000...0xffff, Address 0x10000") def changeMode(self): @@ -141,50 +141,67 @@ def main(): self.interrupt=False raise Exception("User cancel") - FULLOFFSET=61440 + UPDATE_ADDR = 0x10000 HDROFFSET = 288 VERSIONOFFSET = 16 NAMEOFFSET = 48 + IDOFFSET=12 #2 byte chipid MINSIZE = HDROFFSET + NAMEOFFSET + 32 CHECKBYTES = { - 0: 0xe9, # image magic 288: 0x32, # app header magic 289: 0x54, 290: 0xcd, 291: 0xab } + #flash addresses for full images based on chip id + FLASH_ADDR={ + 0: 0x1000, + 9: 0 + } def getString(self,buffer, offset, len): return buffer[offset:offset + len].rstrip(b'\0').decode('utf-8') - def getFirmwareInfo(self,ih,imageFile,offset): - buffer = ih.read(self.MINSIZE) - if len(buffer) != self.MINSIZE: - return self.setErr("invalid image file %s, to short"%imageFile) - for k, v in self.CHECKBYTES.items(): - if buffer[k] != v: - return self.setErr("invalid magic at %d, expected %d got %d" + def getFirmwareInfo(self,filename,isFull): + with open(filename,"rb") as ih: + buffer = ih.read(self.MINSIZE) + if len(buffer) != self.MINSIZE: + return self.setErr("invalid image file %s, to short"%filename) + if buffer[0] != 0xe9: + return self.setErr("invalid magic in file, expected 0xe9 got 0x%02x"%buffer[0]) + chipid= buffer[self.IDOFFSET]+256*buffer[self.IDOFFSET+1] + flashoffset=self.FLASH_ADDR.get(chipid) + if flashoffset is None: + return self.setErr("unknown chip id in image %d",chipid); + if isFull: + offset=self.UPDATE_ADDR-flashoffset; + offset-=self.MINSIZE + ih.seek(offset,os.SEEK_CUR) + buffer=ih.read(self.MINSIZE) + if len(buffer) != self.MINSIZE: + return self.setErr("invalid image file %s, to short"%filename) + if buffer[0] != 0xe9: + return self.setErr("invalid magic in file, expected 0xe9 got 0x%02x"%buffer[0]) + for k, v in self.CHECKBYTES.items(): + if buffer[k] != v: + return self.setErr("invalid magic at %d, expected %d got %d" % (k+offset, v, buffer[k])) - name = self.getString(buffer, self.HDROFFSET + self.NAMEOFFSET, 32) - version = self.getString(buffer, self.HDROFFSET + self.VERSIONOFFSET, 32) - return {'error':False,'info':"%s:%s"%(name,version)} + name = self.getString(buffer, self.HDROFFSET + self.NAMEOFFSET, 32) + version = self.getString(buffer, self.HDROFFSET + self.VERSIONOFFSET, 32) + chipid= buffer[self.IDOFFSET]+256*buffer[self.IDOFFSET+1] + return { + 'error':False, + 'info':"%s:%s"%(name,version), + 'chipid':chipid, + 'flashbase':flashoffset if isFull else self.UPDATE_ADDR + } def setErr(self,err): return {'error':True,'info':err} def checkImageFile(self,filename,isFull): if not os.path.exists(filename): return self.setErr("file %s not found"%filename) - with open(filename,"rb") as fh: - offset=0 - if isFull: - b=fh.read(1) - if len(b) != 1: - return self.setErr("unable to read header") - if b[0] != 0xe9: - return self.setErr("invalid magic in file, expected 0xe9 got 0x%02x"%b[0]) - st=fh.seek(self.FULLOFFSET) - offset=self.FULLOFFSET - return self.getFirmwareInfo(fh,filename,offset) + return self.getFirmwareInfo(filename,isFull) def runCheck(self): self.text_widget.delete("1.0", "end") @@ -202,7 +219,7 @@ def main(): if info['error']: print("ERROR: %s" % info['info']) return - return {'port':port,'isFull':isFull} + return {'port':port,'isFull':isFull,'info':info} def runEspTool(self,command): for b in self.actionButtons: @@ -219,25 +236,36 @@ def main(): for b in self.actionButtons: b.configure(state=tk.NORMAL) self.cancelButton.configure(state=tk.DISABLED) - def buttonCheck(self): + def verifyChip(self): param = self.runCheck() if not param: + print("check failed") return - print("Settings OK") - command = ['--chip', 'ESP32', '--port', param['port'], 'chip_id'] - self.runEspTool(command) + imageChipId=param['info']['chipid'] + chip=esptool.ESPLoader.detect_chip(param['port']) + print("Detected chip %s, id=%d"%(chip.CHIP_NAME,chip.IMAGE_CHIP_ID)) + if (chip.IMAGE_CHIP_ID != imageChipId): + print("##Error: chip id in image %d does not match detected chip"%imageChipId) + return + print("Checks OK") + param['chipname']=chip.CHIP_NAME + return param + def buttonCheck(self): + param = self.verifyChip() + + def buttonFlash(self): - param=self.runCheck() + param=self.verifyChip() if not param: return if param['isFull']: - command=['--chip','ESP32','--port',param['port'],'write_flash','0x1000',self.filename.get()] + command=['--chip',param['chipname'],'--port',param['port'],'write_flash',str(param['info']['flashbase']),self.filename.get()] self.runEspTool(command) else: - command=['--chip','ESP32','--port',param['port'],'erase_region','0xe000','0x2000'] + command=['--chip',param['chipname'],'--port',param['port'],'erase_region','0xe000','0x2000'] self.runEspTool(command) - command = ['--chip', 'ESP32', '--port', param['port'], 'write_flash', '0x10000', self.filename.get()] + command = ['--chip', param['chipname'], '--port', param['port'], 'write_flash', str(param['info']['flashbase']), self.filename.get()] self.runEspTool(command) diff --git a/web/index.js b/web/index.js index 699a82e..4c49f9c 100644 --- a/web/index.js +++ b/web/index.js @@ -1564,14 +1564,14 @@ function uploadBin(ev){ .then(function (result) { let currentType; let currentVersion; - let chiptype; + let chipid; forEl('.status-version', function (el) { currentVersion = el.textContent }); forEl('.status-fwtype', function (el) { currentType = el.textContent }); - forEl('.status-chiptype', function (el) { chiptype = el.textContent }); + forEl('.status-chipid', function (el) { chipid = el.textContent }); let confirmText = 'Ready to update firmware?\n'; - if (result.chip.match(/^@@/) && (result.chip.substr(2) != chiptype)){ - confirmText += "WARNING: the chiptype in the image ("+result.chip.substr(2); - confirmText +=") does not match the current chip type ("+chiptype+").\n"; + if (result.chipId != chipid){ + confirmText += "WARNING: the chipid in the image ("+result.chipId; + confirmText +=") does not match the current chip id ("+chipid+").\n"; } if (currentType != result.fwtype) { confirmText += "WARNING: image has different type: " + result.fwtype + "\n"; @@ -1649,8 +1649,8 @@ function uploadBin(ev){ let HDROFFSET=288; let VERSIONOFFSET=16; let NAMEOFFSET=48; -let CHIPOFFSET=NAMEOFFSET+64; -let MINSIZE = HDROFFSET + CHIPOFFSET + 32; +let MINSIZE = HDROFFSET + NAMEOFFSET + 32; +let CHIPIDOFFSET=12; //2 bytes chip id here let imageCheckBytes={ 0: 0xe9, //image magic 288: 0x32, //app header magic @@ -1669,6 +1669,10 @@ function decodeFromBuffer(buffer,start,length){ start+length)); return rt; } +function getChipId(buffer){ + if (buffer.length < CHIPIDOFFSET+2) return -1; + return buffer[CHIPIDOFFSET]+256*buffer[CHIPIDOFFSET+1]; +} function checkImageFile(file){ return new Promise(function(resolve,reject){ if (! file) reject("no file"); @@ -1685,11 +1689,11 @@ function checkImageFile(file){ } let version=decodeFromBuffer(content,HDROFFSET+VERSIONOFFSET,32); let fwtype=decodeFromBuffer(content,HDROFFSET+NAMEOFFSET,32); - let chip=decodeFromBuffer(content,HDROFFSET+CHIPOFFSET,32); + let chipId=getChipId(content); let rt={ fwtype:fwtype, version: version, - chip:chip + chipId:chipId }; resolve(rt); }); @@ -1751,10 +1755,7 @@ window.addEventListener('load', function () { checkImageFile(file) .then(function(res){ forEl('#imageProperties',function(iel){ - let txt=""; - if (res.chip.match(/^@@/)){ - txt=res.chip.substr(2)+", " - } + let txt="["+res.chipId+"] "; txt+=res.fwtype+", "+res.version; iel.textContent=txt; iel.classList.remove("error"); diff --git a/webinstall/install.js b/webinstall/install.js index 6db2cb1..fb84eeb 100644 --- a/webinstall/install.js +++ b/webinstall/install.js @@ -10,8 +10,7 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; const HDROFFSET = 288; const VERSIONOFFSET = 16; const NAMEOFFSET = 48; - const CHIPOFFSET=NAMEOFFSET+64; - const MINSIZE = HDROFFSET + CHIPOFFSET + 32; + const MINSIZE = HDROFFSET + NAMEOFFSET + 32; const CHIPIDOFFSET=12; //2 bytes chip id here const imageMagic=0xe9; //at byte 0 const imageCheckBytes = { @@ -22,6 +21,7 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; }; /** * map of our known chip ids to flash starts for full images + * 0 - esp32 - starts at 0x1000 * 9 - esp32s3 - starts at 0 */ const FLASHSTART={ @@ -47,8 +47,8 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; let prfx=isFull?"full":"update"; let startOffset=0; let flashStart=UPDATE_START; + let chipId=getChipId(content); if (isFull){ - let chipId=getChipId(content); if (chipId < 0) throw new Error(prfx+"image: no valid chip id found"); let flashStart=FLASHSTART[chipId]; if (flashStart === undefined) throw new Error(prfx+"image: unknown chip id "+chipId); @@ -69,11 +69,10 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; } let version = decodeFromBuffer(content, startOffset+ HDROFFSET + VERSIONOFFSET, 32); let fwtype = decodeFromBuffer(content, startOffset+ HDROFFSET + NAMEOFFSET, 32); - let chip=decodeFromBuffer(content,startOffset+HDROFFSET+CHIPOFFSET,32); let rt = { fwtype: fwtype, version: version, - chip:chip, + chipId:chipId, flashStart: flashStart }; return rt; @@ -133,22 +132,13 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; hFrame.textContent=''; let h=addEl('h2',undefined,hFrame,`ESP32 Install ${info}`) } - const checkChip= async (chipFamily,data,isFull)=>{ + const checkChip= async (chipId,data,isFull)=>{ let info=checkImage(data,isFull); - if (info.chip && info.chip.match(/^@@/)){ - let chip=info.chip.substr(2); - let compare=chipFamily.toLowerCase().replace(/[^a-z0-9]*/g,''); - if (compare !== chip){ - let res=confirm("different chip signatures - image("+chip+"), chip ("+compare+")\nUse this image any way?"); - if (! res) throw new Error("user abort"); - } - return; + if (info.chipId != chipId){ + let res=confirm("different chip signatures - image("+chipId+"), chip ("+info.chipId+")\nUse this image any way?"); + if (! res) throw new Error("user abort"); } - //for now only ESP32/ESP32-S3 - if (chipFamily != "ESP32" && chipFamily != "ESP32-S3"){ - throw new Error(`unexpected chip family ${chipFamily}, expected ESP32/ESP32-S3`); - } - return; + return info; } const baudRates=[1200, 2400, @@ -339,7 +329,6 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; await espInstaller.runFlash( true, fullData, - vinfo.flashStart, version, checkChip ) @@ -353,7 +342,6 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; await espInstaller.runFlash( false, updateData, - uinfo.flashStart, version, checkChip ) diff --git a/webinstall/installUtil.js b/webinstall/installUtil.js index bb0eedd..c46f2af 100644 --- a/webinstall/installUtil.js +++ b/webinstall/installUtil.js @@ -61,6 +61,7 @@ class ESPInstaller{ this.base=import.meta.url.replace(/[^/]*$/,"install.php"); this.consoleDevice=undefined; this.consoleReader=undefined; + this.imageChipId=undefined; } /** * get an URL query parameter @@ -133,6 +134,7 @@ class ESPInstaller{ this.espLoaderTerminal.writeLine(`chip: ${foundChip}`); await this.esploader.flash_id(); this.chipFamily = this.esploader.chip.CHIP_NAME; + this.imageChipId = this.esploader.chip.IMAGE_CHIP_ID; this.espLoaderTerminal.writeLine(`chipFamily: ${this.chipFamily}`); } catch (e) { this.disconnect(); @@ -185,6 +187,10 @@ class ESPInstaller{ this.checkConnected(); return this.chipFamily; } + getChipId(){ + this.checkConnected(); + return this.imageChipId; + } /** * flass the device * @param {*} fileList : an array of entries {data:blob,address:number} @@ -236,7 +242,7 @@ class ESPInstaller{ * @param {*} repo * @param {*} version * @param {*} assetName - * @param {*} checkChip will be called with the found chip and the data and the isFull flag + * @param {*} checkChip will be called with the found chipId and the data and the isFull flag * must return an info with the flashStart being set * @returns */ @@ -247,7 +253,7 @@ class ESPInstaller{ if (!imageData || imageData.length == 0) { throw new Error(`no image data fetched`); } - let info=await checkChip(this.getChipFamily(),imageData,isFull); + let info=await checkChip(this.getChipId(),imageData,isFull); let fileList = [ { data: imageData, address: info.flashAddress } ]; @@ -267,20 +273,18 @@ class ESPInstaller{ /** * directly run the flash * @param {*} isFull - * @param {*} address * @param {*} imageData the data to be flashed * @param {*} version the info shown in the dialog - * @param {*} checkChip will be called with the found chip and the data + * @param {*} checkChip will be called with the found chipId and the data + * must return an info with flashAddress * @returns */ - async runFlash(isFull,imageData,address,version,checkChip){ + async runFlash(isFull,imageData,version,checkChip){ try { await this.connect(); - if (checkChip) { - await checkChip(this.getChipFamily(),imageData,isFull); //just check - } + let info= await checkChip(this.getChipId(),imageData,isFull); //just check let fileList = [ - { data: imageData, address: address } + { data: imageData, address: info.flashAddress } ]; let txt = isFull ? "baseImage (all data will be erased)" : "update"; if (!confirm(`ready to install ${version}\n${txt}`)) { From bb9e7b4de262a67377cf100d45f9bb2e541e42fe Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Fri, 22 Sep 2023 21:14:01 +0200 Subject: [PATCH 058/178] add repo defaults, add more chips --- webinstall/helper.js | 5 +++-- webinstall/install.js | 12 ++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/webinstall/helper.js b/webinstall/helper.js index 3e51220..b2427b2 100644 --- a/webinstall/helper.js +++ b/webinstall/helper.js @@ -1,7 +1,8 @@ -const getParam = (key) => { +const getParam = (key,opt_default) => { + if (opt_default === undefined) opt_default=""; let value = RegExp("" + key + "[^&]+").exec(window.location.search); // Return the unescaped value minus everything starting from the equals sign or an empty string - return decodeURIComponent(!!value ? value.toString().replace(/^[^=]+./, "") : ""); + return decodeURIComponent(!!value ? value.toString().replace(/^[^=]+./, "") : opt_default); }; /** * add an HTML element diff --git a/webinstall/install.js b/webinstall/install.js index fb84eeb..10053ed 100644 --- a/webinstall/install.js +++ b/webinstall/install.js @@ -21,12 +21,16 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; }; /** * map of our known chip ids to flash starts for full images + * see https://github.com/espressif/esptool-js/tree/main/src/targets + * IMAGE_CHIP_ID, BOOTLOADER_FLASH_OFFSET * 0 - esp32 - starts at 0x1000 * 9 - esp32s3 - starts at 0 */ const FLASHSTART={ - 0:0x1000, - 9:0 + 0:0x1000, //ESP32 + 9:0, //ESP32S3 + 2:0x1000, //ESP32S2 + 5:0 //ESP32C3 }; const decodeFromBuffer=(buffer, start, length)=>{ while (length > 0 && buffer.charCodeAt(start + length - 1) == 0) { @@ -427,8 +431,8 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; let repo; let errorText=`unable to query release info for user ${user}, repo ${repo}: `; if (! custom){ - user = window.gitHubUser||getParam('user'); - repo = window.gitHubRepo || getParam('repo'); + user = window.gitHubUser||getParam('user','wellenvogel'); + repo = window.gitHubRepo || getParam('repo','esp32-nmea2000'); if (!user || !repo) { alert("missing parameter user or repo"); } From a555719d498f13e4d3559565089b06f4409242f8 Mon Sep 17 00:00:00 2001 From: wellenvogel Date: Fri, 22 Sep 2023 21:23:25 +0200 Subject: [PATCH 059/178] updated flashtool --- tools/flashtool.pyz | Bin 233491 -> 234899 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tools/flashtool.pyz b/tools/flashtool.pyz index 2f50de56696f5cbb6f98fcab1954598ef62fcca4..8cc0a22383024d68411caa35d62692cc81f8265a 100644 GIT binary patch delta 1834 zcmbVN-A~(A6el?;ut2jSLImsDU4p0+nq~nFL_^&*B1wrTAL3(3MG+WcHy%kG>`;bU zk%SL16;`CV)0x;q*D-0*rcGU?ikCf9leV{R+McGp?CTF0yl$#CUpqiTktXd*vE%b~ z&+m86xmW$rrhaC#4fcY?UPjluJTsEFt=dehHuGZ}G{kLNANj`K{N>WOE>BsR%d^uS zhdpamcrKA3;RL6|oj56jZ--aFWjR?Sc3W}pbCYxD$vk{gZXX>C21Y^?fnacmu*COP ztIy}dn~N~|QY1JK4iAS$*w!}E)JCQjGCY}%i)k^2UoXNCc4i@&C56=_&@BlUi^;sA z@Cp$mnFur&G0EM~zNa{aj}a-Qhah6iy6+jj78vf)_h)Y%2&vNNFQnrs5nt5A2LjyA z!Zvm-!&&_4JyUI!Q=W3V38xS-jc~7-x@tgeILJ0oFoVepJDYdhlF@<@EKQu^89V6t17Zpz1JL=UN>2e;;C$& zOUFNi{cURscu1NzneH3!z_p_L-XU>=yG6r94fJS1^94i;D1EpWxFa_)8^=)wsP1H8wH9r?@2VF=Fg|vkLdk)gDGn z2@>O{&z;!Yuxz&8I|6Yd%tXc;acel(H*6BDuJwpUeOkj2m+?VK_QoBfu zO>;7r95;eR$M;c{_AZ6U@=7`*NeQp|pvGYyd!$jR`qrW!m4DuO%I-^ZKH7CuA zM;m=s>%(Tfuf5$pk->@nKwn7jOK+qva5Xfc3nM|dyMzOcFTX=k&3f)(PcgO}@ZU>#~ZyD5DN z8R&JE=2>8Y;-v**qAdM;h!lSqGucn*R`~NBxOnccLax$CrBHrfhtBF^TimNQ+p%*4 zCTpJC6Yp=pxmT2o>?)}AzR*f}*o;v(KHUK2c;5z`d%6ezyUaF>)6kTK@R7V+^0&1qDOa delta 795 zcmZ8fOH30{6s6qSrmaqG)It@=qhg1eNu6qe*rW;zY7`8_3W+hOQ~JtGEN#=NK}-Y+ z44;r7^igkE7+GRmo34l?8dq%G>BbnhF3`k{E{rq$)ZXOX#XaYq`*L#FOOCB^TyGbc zk5?IWbg~pHa2jXQxawCNcx3L{)76;xd-cbqhMj1Z_PM90`~IO2r!E?+SlFP6OG0?Z$?^!p%AKL(1%~g;A)K+ z)+OmhfsoTg8J319(VH@e{5U*9#~d8Na}%)QR%BrZjVdWBWRgjh=KWzi9x6Z^+qi7S zz%=;r^EA}qQUN@U1FqkWAq_fkR0H0?z4%UpAq>tyFKRQ;&t6}#Vq^|%mZ+3csesBX z)T3<DU=>F7EXcNhL`+Y$p}9`;Ul-p(zj@@kmk*pO7a~$ckkV=#NIDL4a)>9=$r?aOBRy4XK878-?wsL9-dd1KQ6#jHNIH_0cRJXg=JeT z_-sM<`_2+rxfspomGqEGdb+&2ha7`OE8iLGy>On#b_R8}L0QS(RTBIF+uk^VKa1c( z_cF9qXyG!n9_)Qu2A_@J4G=Hj0)tCvXK)zPMQHw)<-yG&WDX|!y|ep!B9HS7zL{U> z8O6sdAeoK);+GW&Rpb_%N+1w}SKH9v5!RD#Jckkay_zs>FJiU?^_Ck?K%e~qzP Date: Fri, 22 Sep 2023 21:33:44 +0200 Subject: [PATCH 060/178] update version for flash tool --- tools/flashtool.pyz | Bin 234899 -> 234899 bytes tools/flashtool/flashtool.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/flashtool.pyz b/tools/flashtool.pyz index 8cc0a22383024d68411caa35d62692cc81f8265a..0f442de730fad96489098a945eaa47bf9e8d5bac 100644 GIT binary patch delta 70 zcmbQdns4%IzJ?aY7N#xC>KPmntIfjGp7%;`*Uw<9pmKpB1j delta 70 zcmbQdns4%IzJ?aY7N#xC>KPof78!(R-M2ZmT|a|)7bCNwp5gX4nasUfOpjMhub#p@ S8?Iow+*D>GkZ90UW=8 Date: Fri, 29 Sep 2023 19:54:03 +0200 Subject: [PATCH 061/178] #57: make the conversion of AIS ship dimensions more robust --- lib/nmea2ktoais/NMEA0183AISMessages.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/nmea2ktoais/NMEA0183AISMessages.cpp b/lib/nmea2ktoais/NMEA0183AISMessages.cpp index 8e4cd66..081a1b6 100644 --- a/lib/nmea2ktoais/NMEA0183AISMessages.cpp +++ b/lib/nmea2ktoais/NMEA0183AISMessages.cpp @@ -347,26 +347,29 @@ bool AddDimensions(tNMEA0183AISMsg &NMEA0183AISMsg, double Length, double Beam, uint16_t _PosRefStbd = 0; uint16_t _PosRefPort = 0; - if ( PosRefBow >= 0.0 && PosRefBow <= 511.0 ) { - _PosRefBow = ceil(PosRefBow); + if (PosRefBow < 0) PosRefBow=0; //could be N2kIsNA + if ( PosRefBow <= 511.0 ) { + _PosRefBow = round(PosRefBow); } else { _PosRefBow = 511; } - - if ( PosRefStbd >= 0.0 && PosRefStbd <= 63.0 ) { - _PosRefStbd = ceil(PosRefStbd); + if (PosRefStbd < 0 ) PosRefStbd=0; //could be N2kIsNA + if (PosRefStbd <= 63.0 ) { + _PosRefStbd = round(PosRefStbd); } else { _PosRefStbd = 63; } if ( !N2kIsNA(Length) ) { - _PosRefStern = ceil( Length ) - _PosRefBow; - if ( _PosRefStern < 0 ) _PosRefStern = 0; + if (Length >= PosRefBow){ + _PosRefStern=round(Length - PosRefBow); + } if ( _PosRefStern > 511 ) _PosRefStern = 511; } if ( !N2kIsNA(Beam) ) { - _PosRefPort = ceil( Beam ) - _PosRefStbd; - if ( _PosRefPort < 0 ) _PosRefPort = 0; + if (Beam >= PosRefStbd){ + _PosRefPort = round( Beam - PosRefStbd); + } if ( _PosRefPort > 63 ) _PosRefPort = 63; } From 1882afc09a4edfee3050d54a1db1e9090525db07 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 30 Sep 2023 18:08:57 +0200 Subject: [PATCH 062/178] 1st step loading config --- webinstall/cibuild.js | 8 ++++- webinstall/testconfig.yaml | 63 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 webinstall/testconfig.yaml diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index 3b9c3d4..fefe2b3 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -1,4 +1,5 @@ import { setButtons,fillValues, setValue, buildUrl, fetchJson, setVisible, enableEl, setValues, getParam, fillSelect, forEachEl } from "./helper.js"; +import {load as yamlLoad} from "https://cdn.skypack.dev/js-yaml@4.1.0"; (function(){ const STATUS_INTERVAL=2000; const CURRENT_PIPELINE='pipeline'; @@ -128,7 +129,11 @@ import { setButtons,fillValues, setValue, buildUrl, fetchJson, setVisible, enabl 'm5stack-atoms3-generic', 'nodemcu-generic' ]; - window.onload=()=>{ + const loadConfig=async (url)=>{ + let config=await fetch(url).then((r)=>r.text()); + let parsed=yamlLoad(config); + } + window.onload=async ()=>{ setButtons(btConfig); forEachEl('#environment',(el)=>el.addEventListener('change',hideResults)); forEachEl('#buildflags',(el)=>el.addEventListener('change',hideResults)); @@ -139,5 +144,6 @@ import { setButtons,fillValues, setValue, buildUrl, fetchJson, setVisible, enabl fetchStatus(true); setRunning(true); } + await loadConfig("testconfig.yaml"); } })(); \ No newline at end of file diff --git a/webinstall/testconfig.yaml b/webinstall/testconfig.yaml new file mode 100644 index 0000000..42d655d --- /dev/null +++ b/webinstall/testconfig.yaml @@ -0,0 +1,63 @@ +types: + - &m5base + type: select + target: define + label: 'M5 Atom light Base' + values: + - label: "CAN KIT" + value: M5_CAN_KIT + description: "M5 Stack CAN Kit" + url: "http://docs.m5stack.com/en/atom/atom_can" + - value: M5_SERIAL_KIT_232 + description: "M5 Stack RS232 Base" + label: "Atomic RS232 Base" + url: "http://docs.m5stack.com/en/atom/Atomic%20RS232%20Base" + - value: M5_SERIAL_KIT_485 + description: "M5 Stack RS484 Base" + label: "Atomic RS485 Base" + url: "http://docs.m5stack.com/en/atom/Atomic%20RS485%20Base" + - &m5groove + type: select + label: 'M5 groove type' + values: + - label: 'CAN' + children: + - label: 'I2C' + children: + - *m5groovei2c + - label: 'Serial' + children: + - *m5grooveserial + + - &m5groovei2c + type: multi + label: "M5 I2C groove units" + values: + + - &m5grooveserial + type: select + label: "M5 Serial Unit" + target: define + values: + - label: "RS485" + value: SERIAL_GROOVE_485 + description: "M5 RS485 unit" + url: "http://docs.m5stack.com/en/unit/rs485" + - label: "Tail485" + value: SERIAL_GROOVE_485 + description: "M5 Tail 485" + url: "http://docs.m5stack.com/en/atom/tail485" + +config: + board: + type: select + target: environment + label: 'Board' + values: + - value: m5stack-atom-generic + label: m5stack-atom + description: "M5 Stack Atom light" + url: "http://docs.m5stack.com/en/core/atom_lite" + children: + - *m5base + - *m5groove \ No newline at end of file From a2b8c7d8fa4e4ed5fdae74d1ba1036f11c2d6ca2 Mon Sep 17 00:00:00 2001 From: andreas Date: Sat, 30 Sep 2023 19:47:27 +0200 Subject: [PATCH 063/178] intermediate: building build ui --- webinstall/cibuild.css | 19 ++++++++++++++ webinstall/cibuild.html | 3 +++ webinstall/cibuild.js | 52 ++++++++++++++++++++++++++++++++++++-- webinstall/testconfig.yaml | 44 ++++++++++++++++++-------------- 4 files changed, 97 insertions(+), 21 deletions(-) diff --git a/webinstall/cibuild.css b/webinstall/cibuild.css index 564271f..65c3215 100644 --- a/webinstall/cibuild.css +++ b/webinstall/cibuild.css @@ -93,4 +93,23 @@ } .configui #warn.warn{ display: block; +} +.configui .radioFrame { + display: flex; + flex-direction: row; +} +.configui input.radioCi { + appearance: auto; + float: none; + opacity: 1; + margin-left: 0.5em; + margin-right: 0.5em; + z-index: unset; +} +.configui .selector { + padding-bottom: 0.5em; + border-bottom: 1px solid grey; +} +.configui .selector .title { + font-weight: bold; } \ No newline at end of file diff --git a/webinstall/cibuild.html b/webinstall/cibuild.html index bec08ea..3a4b0df 100644 --- a/webinstall/cibuild.html +++ b/webinstall/cibuild.html @@ -10,6 +10,9 @@

Build your own ESP32-NMEA2000

New Build

+
+ +
Board type +
\ No newline at end of file diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index a26dc8a..618f719 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -1,5 +1,6 @@ -import { addEl, setButtons,fillValues, setValue, buildUrl, fetchJson, setVisible, enableEl, setValues, getParam, fillSelect, forEachEl } from "./helper.js"; +import { addEl, setButtons,fillValues, setValue, buildUrl, fetchJson, setVisible, enableEl, setValues, getParam, fillSelect, forEachEl, readFile } from "./helper.js"; import {load as yamlLoad} from "https://cdn.skypack.dev/js-yaml@4.1.0"; +import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" (function(){ const STATUS_INTERVAL=2000; const CURRENT_PIPELINE='pipeline'; @@ -122,10 +123,38 @@ import {load as yamlLoad} from "https://cdn.skypack.dev/js-yaml@4.1.0"; let url=buildUrl("install.html",{custom:downloadUrl}); window.location.href=url; } + const uploadConfig=()=>{ + let form=document.getElementById("upload"); + form.reset(); + let fsel=document.getElementById("fileSelect"); + fsel.onchange=async ()=>{ + if (fsel.files.length < 1) return; + let file=fsel.files[0]; + if (! file.name.match(/json$/)){ + alert("only json files"); + return; + } + try{ + let content=await readFile(file,true); + config=JSON.parse(content); + buildSelectors(ROOT_PATH,structure.config.children,true); + } catch (e){ + alert("upload "+fsel.files[0].name+" failed: "+e); + } + + } + fsel.click(); + } + const downloadConfig=()=>{ + let name="buildconfig.json"; + fileDownload(JSON.stringify(config),name); + } const btConfig={ start:startBuild, download:runDownload, - webinstall:webInstall + webinstall:webInstall, + uploadConfig: uploadConfig, + downloadConfig: downloadConfig }; const environments=[ 'm5stack-atom-generic', @@ -170,6 +199,7 @@ import {load as yamlLoad} from "https://cdn.skypack.dev/js-yaml@4.1.0"; let re = addEl('input', 'radioCi', ef); let val = v.value; let key=getVal(v,KEY_NAMES); + if (val === undefined) val=key; re.setAttribute('type', 'radio'); re.setAttribute('name', name); re.addEventListener('change', (ev) => callback(v.children,key,val,false)); @@ -179,6 +209,7 @@ import {load as yamlLoad} from "https://cdn.skypack.dev/js-yaml@4.1.0"; lnk.setAttribute('target', '_'); } if (key == current) { + re.setAttribute('checked','checked'); window.setTimeout(() => { callback(v.children,key,val,true); }, 0); @@ -222,15 +253,17 @@ import {load as yamlLoad} from "https://cdn.skypack.dev/js-yaml@4.1.0"; buildSelector(frame,cfg,name,current,(children,key,value,initial)=>{ buildSelectors(name,children,initial); configStruct[name]={cfg:cfg, key: key, value:value}; - buildValues(); + buildValues(initial); }) }) } const ROOT_PATH='root'; - const buildValues=()=>{ + const buildValues=(initial)=>{ let environment; let flags=""; - config={}; + if (! initial){ + config={}; + } for (let k in configStruct){ let struct=configStruct[k]; if (! struct || ! struct.cfg || struct.value === undefined) continue; @@ -265,6 +298,6 @@ import {load as yamlLoad} from "https://cdn.skypack.dev/js-yaml@4.1.0"; } structure=await loadConfig("testconfig.yaml"); buildSelectors(ROOT_PATH,structure.config.children,true); - buildValues(); + //buildValues(); } })(); \ No newline at end of file diff --git a/webinstall/helper.js b/webinstall/helper.js index b2427b2..fb37aca 100644 --- a/webinstall/helper.js +++ b/webinstall/helper.js @@ -120,5 +120,16 @@ const fillSelect=(el,values)=>{ o.textContent=values[k]; } } - -export { getParam, addEl, forEachEl,setButtons,fillValues, setValue,setValues,buildUrl,fetchJson,setVisible, enableEl,fillSelect } \ No newline at end of file +const readFile=(file,optAsText)=>{ + return new Promise((resolve,reject)=>{ + let reader = new FileReader(); + reader.addEventListener('load', function (e) { + resolve(e.target.result); + + }); + reader.addEventListener('error',(e)=>reject(e)); + if (optAsText) reader.readAsText(file); + else reader.readAsBinaryString(file); + }); +} +export { readFile, getParam, addEl, forEachEl,setButtons,fillValues, setValue,setValues,buildUrl,fetchJson,setVisible, enableEl,fillSelect } \ No newline at end of file diff --git a/webinstall/install.js b/webinstall/install.js index 10053ed..7af4928 100644 --- a/webinstall/install.js +++ b/webinstall/install.js @@ -1,6 +1,6 @@ import {XtermOutputHandler} from "./installUtil.js"; import ESPInstaller from "./installUtil.js"; -import { addEl, getParam, setValue, setVisible } from "./helper.js"; +import { readFile, addEl, getParam, setValue, setVisible } from "./helper.js"; import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; (function(){ @@ -81,16 +81,7 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm"; }; return rt; } - const readFile=(file)=>{ - return new Promise((resolve,reject)=>{ - let reader = new FileReader(); - reader.addEventListener('load', function (e) { - resolve(e.target.result); - - }); - reader.readAsBinaryString(file); - }); - } + const checkImageFile=(file,isFull)=>{ let minSize=MINSIZE+(isFull?(UPDATE_START-FULL_START):0); return new Promise(function (resolve, reject) { From 47f9d490baa519eaa8b487dc320d1872b3d4b08a Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 1 Oct 2023 12:41:57 +0200 Subject: [PATCH 067/178] rename build.yaml --- webinstall/{testconfig.yaml => build.yaml} | 0 webinstall/cibuild.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename webinstall/{testconfig.yaml => build.yaml} (100%) diff --git a/webinstall/testconfig.yaml b/webinstall/build.yaml similarity index 100% rename from webinstall/testconfig.yaml rename to webinstall/build.yaml diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index 618f719..b27d308 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -296,7 +296,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" fetchStatus(true); setRunning(true); } - structure=await loadConfig("testconfig.yaml"); + structure=await loadConfig("build.yaml"); buildSelectors(ROOT_PATH,structure.config.children,true); //buildValues(); } From 984892939608e02f2ff36dfd2e9e6b52f58313dc Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 1 Oct 2023 12:49:19 +0200 Subject: [PATCH 068/178] add S3 and can unit --- webinstall/build.yaml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/webinstall/build.yaml b/webinstall/build.yaml index 69a5d85..670046d 100644 --- a/webinstall/build.yaml +++ b/webinstall/build.yaml @@ -22,7 +22,16 @@ types: key: m5groovei2c label: "M5 I2C groove units" values: - + - &m5groovecan + type: select + key: m5groovecan + target: define + label: "M5 CAN Units" + values: + - label: "CAN Unit" + url: "http://docs.m5stack.com/en/unit/can" + description: "M5 Can unit" + value: M5_CANUNIT - &m5grooveserial type: select label: "M5 Serial Unit" @@ -46,7 +55,8 @@ types: label: 'M5 groove type' values: - label: 'CAN' - children: + children: + - *m5groovecan - label: 'I2C' children: - *m5groovei2c @@ -68,4 +78,11 @@ config: url: "http://docs.m5stack.com/en/core/atom_lite" children: - *m5base + - *m5groove + - value: m5stack-atoms3-generic + label: m5stack-atoms3 + description: "M5 Stack AtomS3 light" + url: "http://docs.m5stack.com/en/core/AtomS3%20Lite" + children: + - *m5base - *m5groove \ No newline at end of file From 1d7f62f6376e4b790ef1d5b367f0f5d93136ff10 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 1 Oct 2023 12:58:45 +0200 Subject: [PATCH 069/178] some css and text tuning, cleanup old modes --- webinstall/build.yaml | 6 +++--- webinstall/cibuild.css | 3 +++ webinstall/cibuild.html | 5 ++--- webinstall/cibuild.js | 6 ------ 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/webinstall/build.yaml b/webinstall/build.yaml index 670046d..8c66ff8 100644 --- a/webinstall/build.yaml +++ b/webinstall/build.yaml @@ -20,13 +20,13 @@ types: - &m5groovei2c type: multi key: m5groovei2c - label: "M5 I2C groove units" + label: "M5 I2C Groove Units" values: - &m5groovecan type: select key: m5groovecan target: define - label: "M5 CAN Units" + label: "M5 Groove CAN Units" values: - label: "CAN Unit" url: "http://docs.m5stack.com/en/unit/can" @@ -34,7 +34,7 @@ types: value: M5_CANUNIT - &m5grooveserial type: select - label: "M5 Serial Unit" + label: "M5 Groove Serial Unit" target: define key: m5grooveserial values: diff --git a/webinstall/cibuild.css b/webinstall/cibuild.css index e1f0660..938946b 100644 --- a/webinstall/cibuild.css +++ b/webinstall/cibuild.css @@ -127,4 +127,7 @@ height: 0; /* display: flex; */ overflow: hidden; +} +.configui .label { + width: 10em; } \ No newline at end of file diff --git a/webinstall/cibuild.html b/webinstall/cibuild.html index 01475a7..7ec4565 100644 --- a/webinstall/cibuild.html +++ b/webinstall/cibuild.html @@ -19,12 +19,11 @@
Board type - +
Build Flags - +
diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index b27d308..067e294 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -156,11 +156,6 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" uploadConfig: uploadConfig, downloadConfig: downloadConfig }; - const environments=[ - 'm5stack-atom-generic', - 'm5stack-atoms3-generic', - 'nodemcu-generic' - ]; const loadConfig=async (url)=>{ let config=await fetch(url).then((r)=>r.text()); let parsed=yamlLoad(config); @@ -289,7 +284,6 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" setButtons(btConfig); forEachEl('#environment',(el)=>el.addEventListener('change',hideResults)); forEachEl('#buildflags',(el)=>el.addEventListener('change',hideResults)); - fillSelect('environment',environments); currentPipeline=window.localStorage.getItem(CURRENT_PIPELINE); if (currentPipeline){ setValue('pipeline',currentPipeline); From 38dae47fa518be90f3d98ae17ffb5997babb2094 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 1 Oct 2023 17:53:57 +0200 Subject: [PATCH 070/178] correctly handle flashStart --- webinstall/installUtil.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webinstall/installUtil.js b/webinstall/installUtil.js index c46f2af..14910d8 100644 --- a/webinstall/installUtil.js +++ b/webinstall/installUtil.js @@ -255,7 +255,7 @@ class ESPInstaller{ } let info=await checkChip(this.getChipId(),imageData,isFull); let fileList = [ - { data: imageData, address: info.flashAddress } + { data: imageData, address: info.flashStart } ]; let txt = isFull ? "baseImage (all data will be erased)" : "update"; if (!confirm(`ready to install ${version}\n${txt}`)) { @@ -276,7 +276,7 @@ class ESPInstaller{ * @param {*} imageData the data to be flashed * @param {*} version the info shown in the dialog * @param {*} checkChip will be called with the found chipId and the data - * must return an info with flashAddress + * must return an info with flashStart * @returns */ async runFlash(isFull,imageData,version,checkChip){ @@ -284,7 +284,7 @@ class ESPInstaller{ await this.connect(); let info= await checkChip(this.getChipId(),imageData,isFull); //just check let fileList = [ - { data: imageData, address: info.flashAddress } + { data: imageData, address: info.flashStart } ]; let txt = isFull ? "baseImage (all data will be erased)" : "update"; if (!confirm(`ready to install ${version}\n${txt}`)) { From 7a5da47e17f32d3c222ac3bd7cac4bbb2d321b32 Mon Sep 17 00:00:00 2001 From: andreas Date: Sun, 1 Oct 2023 20:48:45 +0200 Subject: [PATCH 071/178] intermediate: find pipelines in DB and use existing --- webinstall/cibuild.html | 2 +- webinstall/cibuild.js | 77 ++++++++++++++++++++++++---- webinstall/cibuild.php | 110 +++++++++++++++++++++++++++++++++++++++- webinstall/helper.js | 4 ++ 4 files changed, 180 insertions(+), 13 deletions(-) diff --git a/webinstall/cibuild.html b/webinstall/cibuild.html index 7ec4565..deea2fb 100644 --- a/webinstall/cibuild.html +++ b/webinstall/cibuild.html @@ -28,7 +28,7 @@
-

Last Build

+

Last Build

Job Id
---
diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index 067e294..0a034e9 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -12,7 +12,25 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" let config={}; //values as read and stored let configStruct={}; //complete struct merged of config and struct let branch=getParam('branch'); + let displayMode='last'; + let delayedSearch=undefined; + let running=false; if (! branch) branch='master'; + const modeStrings={ + last: 'Last Build', + existing: 'Existing Build', + current: 'Current Build' + }; + const setDisplayMode=(mode)=>{ + let old=displayMode; + let ms=modeStrings[mode]; + if (ms === undefined){ + return false; + } + displayMode=mode; + setValue('resultTitle',ms); + return mode !== old; + } const showError=(text)=>{ if (text === undefined){ setVisible('buildError',false,true); @@ -30,19 +48,31 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" setVisible('download', false, true); setVisible('status_url', false, true); } - const setRunning=(active)=>{ + const setRunning=(active,opt_noActivate)=>{ + running=active; if (active){ showError(); downloadUrl=undefined; setVisible('download', false, true); setVisible('status_url', false, true); } - enableEl('start',!active); + if (active || ! opt_noActivate) enableEl('start',!active); + } + const isRunning=()=>{ + return running; } const fetchStatus=(initial)=>{ - if (currentPipeline === undefined) return; + if (currentPipeline === undefined) { + setVisible('status_url',false,true); + setVisible('error',false); + setVisible('download',false,true); + setValue('status','---'); + return; + }; + let queryPipeline=currentPipeline; fetchJson(API,{api:'status',pipeline:currentPipeline}) .then((st)=>{ + if (queryPipeline !== currentPipeline) return; setValues(st); setVisible('status_url',st.status_url !== undefined,true); setVisible('error',st.error !== undefined,true); @@ -52,7 +82,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" return; } if (st.status === 'success'){ - setRunning(false); + setRunning(false, displayMode == 'existing'); fetchJson(API,{api:'artifacts',pipeline:currentPipeline}) .then((ar)=>{ if (! ar.items || ar.items.length < 1){ @@ -79,9 +109,10 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" timer=window.setTimeout(fetchStatus,STATUS_INTERVAL); }) } - const setCurrentPipeline=(pipeline)=>{ + const setCurrentPipeline=(pipeline,doStore)=>{ currentPipeline=pipeline; - window.localStorage.setItem(CURRENT_PIPELINE,pipeline); + setValue('pipeline',currentPipeline); + if (doStore) window.localStorage.setItem(CURRENT_PIPELINE,pipeline); }; const startBuild=()=>{ let param={'branch':branch}; @@ -92,6 +123,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" setValue('status','requested'); setValue('pipeline',''); setRunning(true); + param.config=JSON.stringify(config); fetchJson(API,Object.assign({ api:'start'},param)) .then((json)=>{ @@ -99,9 +131,9 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" throw new Error("unable to create job "+(json.error||'')); } if (!json.id) throw new Error("unable to create job, no id"); - setCurrentPipeline(json.id); - setValue('pipeline',currentPipeline); + setCurrentPipeline(json.id,true); setValue('status',json.status); + setDisplayMode('current'); timer=window.setTimeout(fetchStatus,STATUS_INTERVAL); }) .catch((err)=>{ @@ -279,14 +311,37 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" } document.getElementById('environment').value=environment; document.getElementById('buildflags').value=flags; + findPipeline(); + } + const findPipeline=()=>{ + if (delayedSearch !== undefined){ + window.clearTimeout(delayedSearch); + delayedSearch=undefined; + } + if (isRunning()) { + delayedSearch=window.setTimeout(findPipeline,500); + return; + } + let param={find:1}; + fillValues(param,['environment','buildflags']); + fetchJson(API,param) + .then((res)=>{ + setCurrentPipeline(res.pipeline); + fetchStatus(true); + setDisplayMode('existing'); + enableEl('start',res.pipeline === undefined); + }) + .catch((e)=>console.log("findPipeline error ",e)); + } window.onload=async ()=>{ setButtons(btConfig); forEachEl('#environment',(el)=>el.addEventListener('change',hideResults)); forEachEl('#buildflags',(el)=>el.addEventListener('change',hideResults)); - currentPipeline=window.localStorage.getItem(CURRENT_PIPELINE); - if (currentPipeline){ - setValue('pipeline',currentPipeline); + let pipeline=window.localStorage.getItem(CURRENT_PIPELINE); + setDisplayMode('last'); + if (pipeline){ + setCurrentPipeline(pipeline); fetchStatus(true); setRunning(true); } diff --git a/webinstall/cibuild.php b/webinstall/cibuild.php index 20be940..414a400 100644 --- a/webinstall/cibuild.php +++ b/webinstall/cibuild.php @@ -2,15 +2,18 @@ include("token.php"); include("functions.php"); include("config.php"); +mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); +include("cibuild_connection.php"); if (! isset($CI_TOKEN)) die("no token"); const apiBase="https://circleci.com/api/v2/"; const webApp="https://app.circleci.com/"; const apiRepo="project/gh/#user#/#repo#"; const workflowName="build-workflow"; const jobName="pio-build"; -const defaultBranch='circleci-project-setup'; +const defaultBranch='master'; const defaultUser='wellenvogel'; const defaultRepo='esp32-nmea2000'; +const TABLENAME="CIBUILDS"; function getTokenHeaders(){ global $CI_TOKEN; @@ -68,6 +71,7 @@ function getJobStatus($pipeline,$wf=workflowName,$job=jobName){ } $pipeline_id=$pstat['id']; $pipeline_number=$pstat['number']; + $vcs=$pstat['vcs']; $pstat=getWorkflow($pipeline,$wf); $workflow_id=$pstat['id']; $workflow_number=$pstat['workflow_number']; @@ -81,6 +85,7 @@ function getJobStatus($pipeline,$wf=workflowName,$job=jobName){ preg_replace('/^gh/','github',$pstat['project_slug'])."/". $pipeline_number."/workflows/".$workflow_id."/jobs/".$pstat['job_number']; } + $pstat['vcs']=$vcs; return $pstat; } @@ -89,6 +94,79 @@ function getArtifacts($job,$slug){ return getJson($url,getTokenHeaders(),true); } +function insertPipeline($id,$param){ + global $database; + if (! isset($database)) return false; + try { + $status='created'; + $stmt = $database->prepare("INSERT into " . TABLENAME . + "(id,status,config,environment,buildflags) VALUES (?,?,?,?,?)"); + $stmt->bind_param("sssss", + $id, + $status, + $param['config'], + $param['environment'], + $param['build_flags']); + $stmt->execute(); + return true; + } catch (Exception $e) { + error_log("insert pipeline $id failed: $e"); + return false; + } +} +function updatePipeline($id,$status,$tag=null){ + global $database; + if (! isset($database)) return false; + try{ + $stmt=null; + if ($tag != null){ + $stmt=$database->prepare("UPDATE ".TABLENAME." SET status=?,tag=? where id=? and ( status <> ? or tag <> ?)"); + $stmt->bind_param("sssss",$status,$tag,$id,$status,$tag); + $stmt->execute(); + } + else{ + $stmt=$database->prepare("UPDATE ".TABLENAME." SET status=? where id=? AND status <> ?"); + $stmt->bind_param("sss",$status,$id,$status); + $stmt->execute(); + } + + }catch (Exception $e){ + error_log("update pipeline $id failed: $e"); + return false; + } + return true; +} + +function findPipeline($param) +{ + global $database; + if (!isset($database)) + return false; + try { + $stmt = null; + if (isset($param['tag'])) { + $stmt = $database->prepare("SELECT * from " . TABLENAME . + " where status='success' and environment=? and buildflags=? and tag=? order by timestamp desc"); + $stmt->bind_param("sss", $param['environment'], $param['buildflags'], $param['tag']); + } else { + $stmt = $database->prepare("SELECT id from " . TABLENAME . + " where status='success' and environment=? and buildflags=? order by timestamp desc"); + $stmt->bind_param("ss", $param['environment'], $param['buildflags']); + } + $stmt->execute(); + $id=null; + $stmt->bind_result($id); + if ($stmt->fetch()){ + return $id; + } + return false; + } catch (Exception $e) { + error_log("find pipeline failed: $e"); + return false; + } + +} + function getArtifactsForPipeline($pipeline,$wf=workflowName,$job=jobName){ $jstat=getJobStatus($pipeline,$wf,$job); if (! isset($jstat['job_number'])){ @@ -116,6 +194,12 @@ try { ); try { $pstat = getJobStatus($par['pipeline'], $par['workflow'], $par['job']); + if (isset($pstat['vcs'])){ + updatePipeline($par['pipeline'],$pstat['status'],$pstat['vcs']['revision']); + } + else{ + updatePipeline($par['pipeline'],$pstat['status']); + } echo (json_encode($pstat)); } catch (Exception $e) { $rt = array('status' => 'error', 'error' => $e->getMessage()); @@ -148,6 +232,16 @@ try { echo(json_encode($rt)); exit(0); } + if ($action == 'pipelineuuid'){ + addVars( + $par, + ['pipeline'] + ); + $url=apiBase."/pipeline/".$par['pipeline']; + $rt=getJson($url,getTokenHeaders(),true); + echo(json_encode($rt)); + exit(0); + } if ($action == 'start'){ addVars( $par, @@ -173,6 +267,7 @@ try { $userRepo=fillUserAndRepo(null,$par); $url=apiBase."/".replaceVars(apiRepo,$userRepo)."/pipeline"; $rt=getJson($url,getTokenHeaders(),true,$requestParam); + insertPipeline($rt['id'],$requestParam['parameters']); echo (json_encode($rt)); exit(0); } @@ -196,6 +291,19 @@ try { proxy($dlurl); exit(0); } + if (isset($_REQUEST['find'])){ + $par=array(); + addVars($par,['environment','buildflags']); + if (isset($_REQUEST['tag'])) $par['tag']=$_REQUEST['tag']; + $id=findPipeline($par); + header("Content-Type: application/json"); + $rt=array('status'=>'OK'); + if ($id){ + $rt['pipeline']=$id; + } + echo(json_encode($rt)); + exit(0); + } die("no action"); } catch (HTTPErrorException $h) { header($_SERVER['SERVER_PROTOCOL'] . " " . $h->code . " " . $h->getMessage()); diff --git a/webinstall/helper.js b/webinstall/helper.js index fb37aca..857427c 100644 --- a/webinstall/helper.js +++ b/webinstall/helper.js @@ -65,6 +65,10 @@ const setValue=(id,value)=>{ el.value=value; return; } + if (el.tagName.match(/^H[0-9]/)){ + el.textContent=value; + return; + } if (el.tagName == 'A'){ el.setAttribute('href',value); return; From 707c00b850fe7734757a658964d5532707b5e8c8 Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 2 Oct 2023 18:43:43 +0200 Subject: [PATCH 072/178] better handling of db connections, return timestamp for found builds --- webinstall/cibuild.php | 25 ++++++++++++------------- webinstall/create_db.php | 28 ++++++++++++++++++++++++++++ webinstall/functions.php | 13 +++++++++++++ 3 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 webinstall/create_db.php diff --git a/webinstall/cibuild.php b/webinstall/cibuild.php index 414a400..c17cd00 100644 --- a/webinstall/cibuild.php +++ b/webinstall/cibuild.php @@ -2,8 +2,6 @@ include("token.php"); include("functions.php"); include("config.php"); -mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); -include("cibuild_connection.php"); if (! isset($CI_TOKEN)) die("no token"); const apiBase="https://circleci.com/api/v2/"; const webApp="https://app.circleci.com/"; @@ -95,7 +93,7 @@ function getArtifacts($job,$slug){ } function insertPipeline($id,$param){ - global $database; + $database=openDb(); if (! isset($database)) return false; try { $status='created'; @@ -115,7 +113,7 @@ function insertPipeline($id,$param){ } } function updatePipeline($id,$status,$tag=null){ - global $database; + $database=openDb(); if (! isset($database)) return false; try{ $stmt=null; @@ -139,25 +137,26 @@ function updatePipeline($id,$status,$tag=null){ function findPipeline($param) { - global $database; + $database=openDb(); if (!isset($database)) return false; try { $stmt = null; if (isset($param['tag'])) { - $stmt = $database->prepare("SELECT * from " . TABLENAME . + $stmt = $database->prepare("SELECT id,UNIX_TIMESTAMP(timestamp) from " . TABLENAME . " where status='success' and environment=? and buildflags=? and tag=? order by timestamp desc"); $stmt->bind_param("sss", $param['environment'], $param['buildflags'], $param['tag']); } else { - $stmt = $database->prepare("SELECT id from " . TABLENAME . + $stmt = $database->prepare("SELECT id,UNIX_TIMESTAMP(timestamp) from " . TABLENAME . " where status='success' and environment=? and buildflags=? order by timestamp desc"); $stmt->bind_param("ss", $param['environment'], $param['buildflags']); } $stmt->execute(); $id=null; - $stmt->bind_result($id); + $timestamp=null; + $stmt->bind_result($id,$timestamp); if ($stmt->fetch()){ - return $id; + return array('pipeline'=>$id,'timestamp'=>$timestamp); } return false; } catch (Exception $e) { @@ -295,12 +294,12 @@ try { $par=array(); addVars($par,['environment','buildflags']); if (isset($_REQUEST['tag'])) $par['tag']=$_REQUEST['tag']; - $id=findPipeline($par); + $rt=findPipeline($par); header("Content-Type: application/json"); - $rt=array('status'=>'OK'); - if ($id){ - $rt['pipeline']=$id; + if (!$rt){ + $rt=array(); } + $rt['status']='OK'; echo(json_encode($rt)); exit(0); } diff --git a/webinstall/create_db.php b/webinstall/create_db.php new file mode 100644 index 0000000..a37139d --- /dev/null +++ b/webinstall/create_db.php @@ -0,0 +1,28 @@ +"; +$rt=$database->query($sql); +echo "execute OK
"; +} catch (Exception $e){ + echo "ERROR: ".$e; +} + + +?> \ No newline at end of file diff --git a/webinstall/functions.php b/webinstall/functions.php index d431058..bb27108 100644 --- a/webinstall/functions.php +++ b/webinstall/functions.php @@ -1,4 +1,17 @@ query("SET CHARACTER SET 'utf8'"); + return $db; + }catch (Exception $e){ + error_log("openDB error $e"); + } + return null; +} function safeName($name) { return preg_replace('[^0-9_a-zA-Z.-]', '', $name); From 9ddcbdc34a23ef47385bd01d3f242dc2f4795fba Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 2 Oct 2023 19:09:35 +0200 Subject: [PATCH 073/178] allow to set a tag for building in ci --- .circleci/config.yml | 4 ++++ webinstall/cibuild.php | 31 +++++++++++++++++++++---------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6e3f953..41f64aa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,10 @@ # Use the latest 2.1 version of CircleCI pipeline process engine. # See: https://circleci.com/docs/configuration-reference version: 2.1 +# set the filter to allow a build for any sha tag +filters: + tags: + only: /.*/ parameters: run_build: type: boolean diff --git a/webinstall/cibuild.php b/webinstall/cibuild.php index c17cd00..89c4f29 100644 --- a/webinstall/cibuild.php +++ b/webinstall/cibuild.php @@ -12,6 +12,7 @@ const defaultBranch='master'; const defaultUser='wellenvogel'; const defaultRepo='esp32-nmea2000'; const TABLENAME="CIBUILDS"; +const KEEPINTERVAL="30"; //days function getTokenHeaders(){ global $CI_TOKEN; @@ -92,20 +93,25 @@ function getArtifacts($job,$slug){ return getJson($url,getTokenHeaders(),true); } -function insertPipeline($id,$param){ +function insertPipeline($id,$requestParam){ $database=openDb(); if (! isset($database)) return false; + $param=$requestParam['parameters']; try { $status='created'; + $tag=null; + if (isset($requestParam['tag'])) $tag=$requestParam['tag']; $stmt = $database->prepare("INSERT into " . TABLENAME . - "(id,status,config,environment,buildflags) VALUES (?,?,?,?,?)"); - $stmt->bind_param("sssss", + "(id,status,config,environment,buildflags,tag) VALUES (?,?,?,?,?,?)"); + $stmt->bind_param("ssssss", $id, $status, $param['config'], $param['environment'], - $param['build_flags']); + $param['build_flags'], + $tag); $stmt->execute(); + $database->query("DELETE from ". TABLENAME. " where timestamp < NOW() - interval ". KEEPINTERVAL. " DAY"); return true; } catch (Exception $e) { error_log("insert pipeline $id failed: $e"); @@ -142,13 +148,14 @@ function findPipeline($param) return false; try { $stmt = null; + $database->query("DELETE from ". TABLENAME. " where timestamp < NOW() - interval ". KEEPINTERVAL. " DAY"); if (isset($param['tag'])) { $stmt = $database->prepare("SELECT id,UNIX_TIMESTAMP(timestamp) from " . TABLENAME . - " where status='success' and environment=? and buildflags=? and tag=? order by timestamp desc"); + " where status IN('success','running','created') and environment=? and buildflags=? and tag=? order by timestamp desc"); $stmt->bind_param("sss", $param['environment'], $param['buildflags'], $param['tag']); } else { $stmt = $database->prepare("SELECT id,UNIX_TIMESTAMP(timestamp) from " . TABLENAME . - " where status='success' and environment=? and buildflags=? order by timestamp desc"); + " where status IN('success','running','created') and environment=? and buildflags=? order by timestamp desc"); $stmt->bind_param("ss", $param['environment'], $param['buildflags']); } $stmt->execute(); @@ -244,9 +251,8 @@ try { if ($action == 'start'){ addVars( $par, - ['environment','buildflags','config','suffix','branch','user','repo'], + ['environment','buildflags','config','suffix','user','repo'], array('suffix'=>'', - 'branch'=>defaultBranch, 'config'=>'{}', 'user'=>defaultUser, 'repo'=>defaultRepo, @@ -254,7 +260,6 @@ try { ) ); $requestParam=array( - 'branch'=>$par['branch'], 'parameters'=> array( 'run_build'=>true, 'environment'=>$par['environment'], @@ -263,10 +268,16 @@ try { 'build_flags'=>$par['buildflags'] ) ); + if (isset($_REQUEST['tag'])){ + $requestParam['tag']=safeName($_REQUEST['tag']); + } + else{ + $requestParam['branch']=defaultBranch; + } $userRepo=fillUserAndRepo(null,$par); $url=apiBase."/".replaceVars(apiRepo,$userRepo)."/pipeline"; $rt=getJson($url,getTokenHeaders(),true,$requestParam); - insertPipeline($rt['id'],$requestParam['parameters']); + insertPipeline($rt['id'],$requestParam); echo (json_encode($rt)); exit(0); } From faa946c8dca5eac72461a94de48212d20e34fbe2 Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 2 Oct 2023 20:31:29 +0200 Subject: [PATCH 074/178] correctly set tag filter for jobs, compute git sha before starting a job --- .circleci/config.yml | 8 +++--- webinstall/cibuild.html | 8 ++++++ webinstall/cibuild.js | 60 +++++++++++++++++++++++++++++++++++++++-- webinstall/helper.js | 2 +- webinstall/install.php | 17 +++++++++++- 5 files changed, 87 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 41f64aa..3fb5c52 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,10 +1,6 @@ # Use the latest 2.1 version of CircleCI pipeline process engine. # See: https://circleci.com/docs/configuration-reference version: 2.1 -# set the filter to allow a build for any sha tag -filters: - tags: - only: /.*/ parameters: run_build: type: boolean @@ -76,3 +72,7 @@ workflows: when: << pipeline.parameters.run_build >> jobs: - pio-build + # set the filter to allow a build for any sha tag + filters: + tags: + only: /.*/ diff --git a/webinstall/cibuild.html b/webinstall/cibuild.html index deea2fb..98530d6 100644 --- a/webinstall/cibuild.html +++ b/webinstall/cibuild.html @@ -10,6 +10,14 @@

Build your own ESP32-NMEA2000

New Build

+
+ + --- +
+
+ GitSha + --- +
diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index 0a034e9..5143c7a 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -4,7 +4,10 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" (function(){ const STATUS_INTERVAL=2000; const CURRENT_PIPELINE='pipeline'; - let API="cibuild.php"; + const API="cibuild.php"; + const GITAPI="install.php"; + const GITUSER="wellenvogel"; + const GITREPO="esp32-nmea2000"; let currentPipeline=undefined; let downloadUrl=undefined; let timer=undefined; @@ -15,6 +18,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" let displayMode='last'; let delayedSearch=undefined; let running=false; + let gitSha=undefined; if (! branch) branch='master'; const modeStrings={ last: 'Last Build', @@ -123,6 +127,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" setValue('status','requested'); setValue('pipeline',''); setRunning(true); + if (gitSha !== undefined) param.tag=gitSha; param.config=JSON.stringify(config); fetchJson(API,Object.assign({ api:'start'},param)) @@ -324,6 +329,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" } let param={find:1}; fillValues(param,['environment','buildflags']); + if (gitSha !== undefined) param.tag=gitSha; fetchJson(API,param) .then((res)=>{ setCurrentPipeline(res.pipeline); @@ -347,6 +353,56 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" } structure=await loadConfig("build.yaml"); buildSelectors(ROOT_PATH,structure.config.children,true); - //buildValues(); + let gitParam={user:GITUSER,repo:GITREPO}; + let branch=getParam('branch'); + if (branch){ + try{ + let info=await fetchJson(GITAPI,Object.assign({},gitParam,{branch:branch})); + if (info.object){ + gitSha=info.object.sha; + setValue('branchOrTag','branch'); + setValue('branchOrTagValue',branch); + } + }catch (e){ + console.log("branch query error",e); + } + } + if (gitSha === undefined) { + let tag = getParam('tag'); + if (!tag) { + try { + let relinfo = await fetchJson(GITAPI, Object.assign({}, gitParam, { api: 1 })); + if (relinfo.tag_name) { + tag = relinfo.tag_name; + } + else { + alert("unable to query latest release"); + } + } catch (e) { + alert("unable to query release info " + e); + } + } + if (tag){ + try{ + let info=await fetchJson(GITAPI,Object.assign({},gitParam,{tag:tag})); + if (info.object){ + gitSha=info.object.sha; + setValue('branchOrTag','tag'); + setValue('branchOrTagValue',tag); + } + }catch(e){ + alert("cannot get sha for tag "+tag+": "+e); + } + } + } + if (gitSha === undefined){ + //last resort: no sha, let the CI pick up latest + setValue('gitSha','unknown'); + setValue('branchOrTag','branch'); + setValue('branchOrTagValue','master'); + } + else{ + setValue('gitSha',gitSha); + } } })(); \ No newline at end of file diff --git a/webinstall/helper.js b/webinstall/helper.js index 857427c..68dce53 100644 --- a/webinstall/helper.js +++ b/webinstall/helper.js @@ -57,7 +57,7 @@ const fillValues=(values,items)=>{ const setValue=(id,value)=>{ let el=document.getElementById(id); if (! el) return; - if (el.tagName == 'DIV'){ + if (el.tagName == 'DIV' || el.tagName == 'SPAN' || el.tagName == 'P'){ el.textContent=value; return; } diff --git a/webinstall/install.php b/webinstall/install.php index ed5281e..c3fd6a8 100644 --- a/webinstall/install.php +++ b/webinstall/install.php @@ -1,7 +1,10 @@ Date: Mon, 2 Oct 2023 20:38:15 +0200 Subject: [PATCH 075/178] correct yaml error, better error handling --- .circleci/config.yml | 8 ++++---- webinstall/cibuild.js | 2 +- webinstall/cibuild.php | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3fb5c52..7a14259 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -72,7 +72,7 @@ workflows: when: << pipeline.parameters.run_build >> jobs: - pio-build - # set the filter to allow a build for any sha tag - filters: - tags: - only: /.*/ + # set the filter to allow a build for any sha tag + filters: + tags: + only: /.*/ diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index 5143c7a..3ed336a 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -80,7 +80,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" setValues(st); setVisible('status_url',st.status_url !== undefined,true); setVisible('error',st.error !== undefined,true); - if (st.status === 'error'){ + if (st.status === 'error' || st.state === 'errored'){ setRunning(false); setVisible('download',false,true); return; diff --git a/webinstall/cibuild.php b/webinstall/cibuild.php index 89c4f29..2e0280a 100644 --- a/webinstall/cibuild.php +++ b/webinstall/cibuild.php @@ -65,7 +65,7 @@ function getJobStatus($pipeline,$wf=workflowName,$job=jobName){ if (! isset($pstat['state'])){ throw new Exception("state not set"); } - if ($pstat['state'] != 'created'){ + if ($pstat['state'] == 'created'){ return $pstat; } $pipeline_id=$pstat['id']; From abf79dd022f9bbbb6998c17977bea80f005ec747 Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 2 Oct 2023 20:41:03 +0200 Subject: [PATCH 076/178] correct yaml errors --- .circleci/config.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7a14259..955589d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -71,8 +71,7 @@ workflows: build-workflow: when: << pipeline.parameters.run_build >> jobs: - - pio-build - # set the filter to allow a build for any sha tag - filters: - tags: - only: /.*/ + - pio-build: + filters: + tags: + only: /.*/ From 43dd2059b725ba1a3a7381198bc5a92521132a67 Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 2 Oct 2023 20:45:55 +0200 Subject: [PATCH 077/178] run and find job with sha --- webinstall/cibuild.js | 3 ++- webinstall/cibuild.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index 3ed336a..e593a33 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -77,10 +77,11 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" fetchJson(API,{api:'status',pipeline:currentPipeline}) .then((st)=>{ if (queryPipeline !== currentPipeline) return; + if (st.status === undefined) st.status=st.state; setValues(st); setVisible('status_url',st.status_url !== undefined,true); setVisible('error',st.error !== undefined,true); - if (st.status === 'error' || st.state === 'errored'){ + if (st.status === 'error' || st.status === 'errored'){ setRunning(false); setVisible('download',false,true); return; diff --git a/webinstall/cibuild.php b/webinstall/cibuild.php index 2e0280a..89c4f29 100644 --- a/webinstall/cibuild.php +++ b/webinstall/cibuild.php @@ -65,7 +65,7 @@ function getJobStatus($pipeline,$wf=workflowName,$job=jobName){ if (! isset($pstat['state'])){ throw new Exception("state not set"); } - if ($pstat['state'] == 'created'){ + if ($pstat['state'] != 'created'){ return $pstat; } $pipeline_id=$pstat['id']; From 0561223327d1c5c8eaced68fb5c6a6bddc5bfb2b Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 2 Oct 2023 20:48:29 +0200 Subject: [PATCH 078/178] show release or tag --- webinstall/cibuild.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index e593a33..c3cd6d1 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -370,11 +370,13 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" } if (gitSha === undefined) { let tag = getParam('tag'); + let type="tag"; if (!tag) { try { let relinfo = await fetchJson(GITAPI, Object.assign({}, gitParam, { api: 1 })); if (relinfo.tag_name) { tag = relinfo.tag_name; + type="release"; } else { alert("unable to query latest release"); @@ -388,7 +390,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" let info=await fetchJson(GITAPI,Object.assign({},gitParam,{tag:tag})); if (info.object){ gitSha=info.object.sha; - setValue('branchOrTag','tag'); + setValue('branchOrTag',type); setValue('branchOrTagValue',tag); } }catch(e){ From a22a40b4c12a3369bc0be718d0b28968265e1007 Mon Sep 17 00:00:00 2001 From: free-x Date: Wed, 4 Oct 2023 09:58:08 +0200 Subject: [PATCH 079/178] bump nmea2000 library to 4.18.9 --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index ecf3001..63e6488 100644 --- a/platformio.ini +++ b/platformio.ini @@ -23,7 +23,7 @@ framework = arduino ; framework-arduinoespressif32 @ 3.20011.230801 ; framework-espidf @ 3.50101.0 lib_deps = - ttlappalainen/NMEA2000-library @ 4.18.7 + ttlappalainen/NMEA2000-library @ 4.18.9 ttlappalainen/NMEA0183 @ 1.9.1 ArduinoJson @ 6.18.5 ottowinter/ESPAsyncWebServer-esphome@2.0.1 From 0574e8b47bf01c0c343c7d7eade1ed9a5e60bf0d Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 4 Oct 2023 16:59:52 +0200 Subject: [PATCH 080/178] correctly handle start button enable/disable for cibuild --- webinstall/cibuild.js | 54 +++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index c3cd6d1..35d07b2 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -14,12 +14,11 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" let structure=undefined; let config={}; //values as read and stored let configStruct={}; //complete struct merged of config and struct - let branch=getParam('branch'); let displayMode='last'; let delayedSearch=undefined; let running=false; + let pipelineState=undefined; let gitSha=undefined; - if (! branch) branch='master'; const modeStrings={ last: 'Last Build', existing: 'Existing Build', @@ -52,7 +51,27 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" setVisible('download', false, true); setVisible('status_url', false, true); } - const setRunning=(active,opt_noActivate)=>{ + const updateStart=()=>{ + if (running) { + enableEl('start',false); + return; + } + if (displayMode != 'existing'){ + if (currentPipeline !== undefined){ + //check pipeline state + if (['error','success'].indexOf(pipelineState) >= 0){ + enableEl('start',true); + return; + } + enableEl('start',false); + return; + } + enableEl('start',true); + } + //display mnode existing + enableEl('start',currentPipeline === undefined); + } + const setRunning=(active)=>{ running=active; if (active){ showError(); @@ -60,7 +79,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" setVisible('download', false, true); setVisible('status_url', false, true); } - if (active || ! opt_noActivate) enableEl('start',!active); + updateStart(); } const isRunning=()=>{ return running; @@ -71,6 +90,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" setVisible('error',false); setVisible('download',false,true); setValue('status','---'); + updateStart(); return; }; let queryPipeline=currentPipeline; @@ -78,16 +98,19 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" .then((st)=>{ if (queryPipeline !== currentPipeline) return; if (st.status === undefined) st.status=st.state; + pipelineState=st.status; setValues(st); setVisible('status_url',st.status_url !== undefined,true); setVisible('error',st.error !== undefined,true); if (st.status === 'error' || st.status === 'errored'){ setRunning(false); setVisible('download',false,true); + updateStart(); return; } if (st.status === 'success'){ - setRunning(false, displayMode == 'existing'); + setRunning(false); + updateStart(); fetchJson(API,{api:'artifacts',pipeline:currentPipeline}) .then((ar)=>{ if (! ar.items || ar.items.length < 1){ @@ -108,6 +131,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" else{ setVisible('download',false,true); } + updateStart(); timer=window.setTimeout(fetchStatus,STATUS_INTERVAL) }) .catch((e)=>{ @@ -120,7 +144,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" if (doStore) window.localStorage.setItem(CURRENT_PIPELINE,pipeline); }; const startBuild=()=>{ - let param={'branch':branch}; + let param={}; currentPipeline=undefined; if (timer) window.clearTimeout(timer); timer=undefined; @@ -128,6 +152,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" setValue('status','requested'); setValue('pipeline',''); setRunning(true); + updateStart(); if (gitSha !== undefined) param.tag=gitSha; param.config=JSON.stringify(config); fetchJson(API,Object.assign({ @@ -176,6 +201,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" let content=await readFile(file,true); config=JSON.parse(content); buildSelectors(ROOT_PATH,structure.config.children,true); + findPipeline(); } catch (e){ alert("upload "+fsel.files[0].name+" failed: "+e); } @@ -243,9 +269,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" } if (key == current) { re.setAttribute('checked','checked'); - window.setTimeout(() => { - callback(v.children,key,val,true); - }, 0); + callback(v.children,key,val,true); } }); } @@ -317,8 +341,9 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" } document.getElementById('environment').value=environment; document.getElementById('buildflags').value=flags; - findPipeline(); + if (! initial) findPipeline(); } + let findIdx=0; const findPipeline=()=>{ if (delayedSearch !== undefined){ window.clearTimeout(delayedSearch); @@ -328,15 +353,18 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" delayedSearch=window.setTimeout(findPipeline,500); return; } + findIdx++; + let queryIdx=findIdx; let param={find:1}; fillValues(param,['environment','buildflags']); if (gitSha !== undefined) param.tag=gitSha; fetchJson(API,param) .then((res)=>{ + if (queryIdx != findIdx) return; setCurrentPipeline(res.pipeline); - fetchStatus(true); + updateStart(); setDisplayMode('existing'); - enableEl('start',res.pipeline === undefined); + fetchStatus(true); }) .catch((e)=>console.log("findPipeline error ",e)); @@ -354,6 +382,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" } structure=await loadConfig("build.yaml"); buildSelectors(ROOT_PATH,structure.config.children,true); + if (! running) findPipeline(); let gitParam={user:GITUSER,repo:GITREPO}; let branch=getParam('branch'); if (branch){ @@ -407,5 +436,6 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" else{ setValue('gitSha',gitSha); } + updateStart(); } })(); \ No newline at end of file From 4a388561abe81b98bcd6dc1fddb19999cb67a036 Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 4 Oct 2023 17:37:03 +0200 Subject: [PATCH 081/178] limit resources that can be specified in build config --- webinstall/build.yaml | 16 +++++++- webinstall/cibuild.css | 7 ++++ webinstall/cibuild.html | 4 ++ webinstall/cibuild.js | 81 +++++++++++++++++++++++++++++++---------- 4 files changed, 87 insertions(+), 21 deletions(-) diff --git a/webinstall/build.yaml b/webinstall/build.yaml index 8c66ff8..fb61984 100644 --- a/webinstall/build.yaml +++ b/webinstall/build.yaml @@ -9,14 +9,17 @@ types: value: M5_CAN_KIT description: "M5 Stack CAN Kit" url: "http://docs.m5stack.com/en/atom/atom_can" + resource: can - value: M5_SERIAL_KIT_232 description: "M5 Stack RS232 Base" label: "Atomic RS232 Base" url: "http://docs.m5stack.com/en/atom/Atomic%20RS232%20Base" + resource: serial - value: M5_SERIAL_KIT_485 description: "M5 Stack RS485 Base" label: "Atomic RS485 Base" url: "http://docs.m5stack.com/en/atom/Atomic%20RS485%20Base" + resource: serial - &m5groovei2c type: multi key: m5groovei2c @@ -31,7 +34,8 @@ types: - label: "CAN Unit" url: "http://docs.m5stack.com/en/unit/can" description: "M5 Can unit" - value: M5_CANUNIT + value: M5_CANUNIT + resource: can - &m5grooveserial type: select label: "M5 Groove Serial Unit" @@ -43,11 +47,13 @@ types: value: SERIAL_GROOVE_485 description: "M5 RS485 unit" url: "http://docs.m5stack.com/en/unit/rs485" + resource: serial - label: "Tail485" value: SERIAL_GROOVE_485 key: tail485 description: "M5 Tail 485" url: "http://docs.m5stack.com/en/atom/tail485" + resource: serial - &m5groove type: select @@ -63,7 +69,11 @@ types: - label: 'Serial' children: - *m5grooveserial - +resources: + default: &esp32default + serial: 2 + can: 1 + i2c: 1 config: children: @@ -76,6 +86,7 @@ config: label: m5stack-atom description: "M5 Stack Atom light" url: "http://docs.m5stack.com/en/core/atom_lite" + resource: *esp32default children: - *m5base - *m5groove @@ -83,6 +94,7 @@ config: label: m5stack-atoms3 description: "M5 Stack AtomS3 light" url: "http://docs.m5stack.com/en/core/AtomS3%20Lite" + resource: *esp32default children: - *m5base - *m5groove \ No newline at end of file diff --git a/webinstall/cibuild.css b/webinstall/cibuild.css index 938946b..fcf5312 100644 --- a/webinstall/cibuild.css +++ b/webinstall/cibuild.css @@ -91,6 +91,9 @@ display: none; color: red; } +.configui .error .value{ + color: red; +} .configui #warn.warn{ display: block; } @@ -130,4 +133,8 @@ } .configui .label { width: 10em; +} +.configui .row input{ + flex-grow: 1; + width: initial; } \ No newline at end of file diff --git a/webinstall/cibuild.html b/webinstall/cibuild.html index 98530d6..112a8a6 100644 --- a/webinstall/cibuild.html +++ b/webinstall/cibuild.html @@ -33,6 +33,10 @@ Build Flags
+
diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index 35d07b2..c62b852 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -56,6 +56,11 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" enableEl('start',false); return; } + let e=document.getElementById('configError'); + if (e.textContent) { + enableEl('start',false); + return; + } if (displayMode != 'existing'){ if (currentPipeline !== undefined){ //check pipeline state @@ -261,7 +266,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" if (val === undefined) val=key; re.setAttribute('type', 'radio'); re.setAttribute('name', name); - re.addEventListener('change', (ev) => callback(v.children,key,val,false)); + re.addEventListener('change', (ev) => callback(v.children,key,val,v.resource,false)); if (v.description && v.url) { let lnk = addEl('a', 'radioDescription', ef, v.description); lnk.setAttribute('href', v.url); @@ -269,7 +274,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" } if (key == current) { re.setAttribute('checked','checked'); - callback(v.children,key,val,true); + callback(v.children,key,val,v.resource,true); } }); } @@ -307,9 +312,9 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" configList.forEach((cfg)=>{ let name=prefix?(prefix+SEPARATOR+cfg.key):cfg.key; let current=config[name]; - buildSelector(frame,cfg,name,current,(children,key,value,initial)=>{ + buildSelector(frame,cfg,name,current,(children,key,value,resource,initial)=>{ buildSelectors(name,children,initial); - configStruct[name]={cfg:cfg, key: key, value:value}; + configStruct[name]={cfg:cfg, key: key, value:value,resource:resource}; buildValues(initial); }) }) @@ -321,27 +326,65 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" if (! initial){ config={}; } - for (let k in configStruct){ - let struct=configStruct[k]; - if (! struct || ! struct.cfg || struct.value === undefined) continue; - config[k]=struct.key; - if (struct.cfg.target !== undefined) { - if (struct.cfg.target === 'environment') { - environment = struct.value; - } - if (struct.cfg.target === 'define') { - flags += " -D" + struct.value; - } - const DEFPRFX = "define:"; - if (struct.cfg.target.indexOf(DEFPRFX) == 0) { - let def = struct.cfg.target.substring(DEFPRFX.length); - flags += " -D" + def + "=" + struct.value; + let allowedResources={}; + let currentResources={}; + let errors=""; + for (let round = 0; round <= 1; round++) { + //round1: find allowed resources + //round2: really collect values + for (let k in configStruct) { + let struct = configStruct[k]; + if (!struct || !struct.cfg || struct.value === undefined) continue; + if (round > 0) config[k] = struct.key; + if (struct.cfg.target !== undefined) { + if (struct.cfg.target === 'environment') { + if (round > 0) environment = struct.value; + else allowedResources=struct.resource; + continue; + } + if (round < 1) continue; + if (struct.resource){ + let resList=currentResources[struct.resource]; + if (! resList){ + resList=[]; + currentResources[struct.resource]=resList; + } + resList.push(struct); + } + if (struct.cfg.target === 'define') { + flags += " -D" + struct.value; + continue; + } + const DEFPRFX = "define:"; + if (struct.cfg.target.indexOf(DEFPRFX) == 0) { + let def = struct.cfg.target.substring(DEFPRFX.length); + flags += " -D" + def + "=" + struct.value; + continue; + } } } } document.getElementById('environment').value=environment; document.getElementById('buildflags').value=flags; + //check resources + for (let k in currentResources){ + let resList=currentResources[k]; + if (allowedResources[k] !== undefined){ + if (resList.length > allowedResources[k]){ + errors+=" more than "+allowedResources[k]+" "+k+" device(s) used"; + } + } + } + if (errors){ + setValue('configError',errors); + setVisible('configError',true,true); + } + else{ + setValue('configError',''); + setVisible('configError',false,true); + } if (! initial) findPipeline(); + updateStart(); } let findIdx=0; const findPipeline=()=>{ From 27de94b1ae238d99012f844e79ad57c0e5b8955e Mon Sep 17 00:00:00 2001 From: andreas Date: Wed, 4 Oct 2023 18:11:47 +0200 Subject: [PATCH 082/178] move groove serial to serial2 if also serial base is used --- lib/hardware/GwHardware.h | 44 ++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 2dc67e6..9e00ce8 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -130,21 +130,6 @@ #define GWBUTTON_PULLUPDOWN #endif -//below we define the final device config based on the above -//boards and peripherals -//this allows us toe easily also set the from outside -//serial adapter at the M5 groove pins -#ifdef SERIAL_GROOVE_485 - #define GWSERIAL_TX GROOVE_PIN_1 - #define GWSERIAL_RX GROOVE_PIN_2 - #define GWSERIAL_TYPE GWSERIAL_TYPE_UNI -#endif -#ifdef SERIAL_GROOVE_232 - #define GWSERIAL_TX GROOVE_PIN_1 - #define GWSERIAL_RX GROOVE_PIN_2 - #define GWSERIAL_TYPE GWSERIAL_TYPE_BI -#endif - //M5 Serial (Atomic RS232 Base) #ifdef M5_SERIAL_KIT_232 #define GWSERIAL_TX BOARD_LEFT2 @@ -159,6 +144,35 @@ #define GWSERIAL_TYPE GWSERIAL_TYPE_UNI #endif +//below we define the final device config based on the above +//boards and peripherals +//this allows us to easily also set them from outside +//serial adapter at the M5 groove pins +//we use serial2 for groove serial if serial1 is already defined +//before (e.g. by serial kit) +#ifdef SERIAL_GROOVE_485 + #ifdef GWSERIAL_TYPE + #define GWSERIAL2_TX GROOVE_PIN_1 + #define GWSERIAL2_RX GROOVE_PIN_2 + #define GWSERIAL2_TYPE GWSERIAL_TYPE_UNI + #else + #define GWSERIAL_TX GROOVE_PIN_1 + #define GWSERIAL_RX GROOVE_PIN_2 + #define GWSERIAL_TYPE GWSERIAL_TYPE_UNI + #endif +#endif +#ifdef SERIAL_GROOVE_232 + #ifdef GWSERIAL_TYPE + #define GWSERIAL2_TX GROOVE_PIN_1 + #define GWSERIAL2_RX GROOVE_PIN_2 + #define GWSERIAL2_TYPE GWSERIAL_TYPE_BI + #else + #define GWSERIAL_TX GROOVE_PIN_1 + #define GWSERIAL_RX GROOVE_PIN_2 + #define GWSERIAL_TYPE GWSERIAL_TYPE_BI + #endif +#endif + //can kit for M5 Atom #ifdef M5_CAN_KIT #define ESP32_CAN_TX_PIN BOARD_LEFT1 From 9572b1e95ef3fda751979d0b7344342ef678cc9c Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 5 Oct 2023 16:57:05 +0200 Subject: [PATCH 083/178] allow to set default and hide for cfg values in hardware defs --- extra_script.py | 18 ++++++++++++++++-- lib/config/GWConfig.cpp | 18 ++++++++++++++++++ lib/config/GWConfig.h | 4 ++++ lib/config/GwConfigItem.h | 7 ++++++- lib/hardware/GwHardware.h | 8 ++++++++ src/main.cpp | 7 ++++++- 6 files changed, 58 insertions(+), 4 deletions(-) diff --git a/extra_script.py b/extra_script.py index 8a50062..03ec16b 100644 --- a/extra_script.py +++ b/extra_script.py @@ -136,13 +136,27 @@ def generateCfg(inFile,outFile,addDirs=[]): data+=' GwConfigInterface *configs[%d]={\n'%(l) first=True for item in config: + name=item.get('name') + if name is None: + continue if not first: data+=',\n' first=False secret="false"; if item.get('type') == 'password': - secret="true" - data+=" new GwConfigInterface(%s,\"%s\",%s)"%(item.get('name'),item.get('default'),secret) + secret="true" + data+=" #undef __CFGHIDDEN\n" + data+=" #ifdef CFGHIDE_%s\n"%(name) + data+=" #define __CFGHIDDEN true\n" + data+=" #else\n" + data+=" #define __CFGHIDDEN false\n" + data+=" #endif\n" + data+=" #ifdef CFGDEFAULT_%s\n"%(name) + data+=" new GwConfigInterface(%s,CFGDEFAULT_%s,%s,__CFGHIDDEN)\n"%(name,name,secret) + data+=" #else\n" + data+=" new GwConfigInterface(%s,\"%s\",%s,__CFGHIDDEN)\n"%(name,item.get('default'),secret) + data+=" #endif\n" + data+='};\n' data+='};\n' writeFileIfChanged(outFile,data) diff --git a/lib/config/GWConfig.cpp b/lib/config/GWConfig.cpp index cdbfdbe..15ecc43 100644 --- a/lib/config/GWConfig.cpp +++ b/lib/config/GWConfig.cpp @@ -159,6 +159,24 @@ void GwConfigHandler::toHex(unsigned long v, char *buffer, size_t bsize) buffer[2 * i] = 0; } +std::vector GwConfigHandler::getHidden() const{ + std::vector rt; + rt.reserve(numHidden()); + for (int i=0L;iisHidden()){ + rt.push_back(configs[i]->getName()); + }; + } + return rt; +} +int GwConfigHandler::numHidden() const{ + int rt=0; + for (int i=0L;iisHidden()) rt++; + } + return rt; +} + void GwNmeaFilter::handleToken(String token, int index){ switch(index){ case 0: diff --git a/lib/config/GWConfig.h b/lib/config/GWConfig.h index 4d0b105..f6e1db2 100644 --- a/lib/config/GWConfig.h +++ b/lib/config/GWConfig.h @@ -4,8 +4,10 @@ #include #include "GwLog.h" #include "GwConfigItem.h" +#include "GwHardware.h" #include "GwConfigDefinitions.h" #include +#include class GwConfigHandler: public GwConfigDefinitions{ @@ -28,6 +30,8 @@ class GwConfigHandler: public GwConfigDefinitions{ int getInt(const String name,int defaultv=0) const; GwConfigInterface * getConfigItem(const String name, bool dummy=false) const; bool checkPass(String hash); + std::vector getHidden() const; + int numHidden() const; /** * change the value of a config item * will become a noop after stopChanges has been called diff --git a/lib/config/GwConfigItem.h b/lib/config/GwConfigItem.h index adfb161..a6d66cd 100644 --- a/lib/config/GwConfigItem.h +++ b/lib/config/GwConfigItem.h @@ -10,12 +10,14 @@ class GwConfigInterface{ const char * initialValue; String value; bool secret=false; + bool hidden=false; public: - GwConfigInterface(const String &name, const char * initialValue, bool secret=false){ + GwConfigInterface(const String &name, const char * initialValue, bool secret=false, bool hidden=false){ this->name=name; this->initialValue=initialValue; this->value=initialValue; this->secret=secret; + this->hidden=hidden; } virtual String asString() const{ return value; @@ -41,6 +43,9 @@ class GwConfigInterface{ String getDefault() const { return initialValue; } + bool isHidden() const { + return hidden; + } friend class GwConfigHandler; }; diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 9e00ce8..5b7e278 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -144,6 +144,14 @@ #define GWSERIAL_TYPE GWSERIAL_TYPE_UNI #endif +//M5 GPS (Atomic GPS Base) +#ifdef M5_GPS_KIT + #define GWSERIAL_RX BOARD_LEFT1 + #define GWSERIAL_TYPE GWSERIAL_TYPE_RX + #define CFGGDEFAULT_serialBaud "9600" + #define CFGHIDE_serialBaud +#endif + //below we define the final device config based on the above //boards and peripherals //this allows us to easily also set them from outside diff --git a/src/main.cpp b/src/main.cpp index 09b1b03..0bc11ba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -430,11 +430,16 @@ class CapabilitiesRequest : public GwRequestMessage{ protected: virtual void processRequest(){ int numCapabilities=userCodeHandler.getCapabilities()->size(); - GwJsonDocument json(JSON_OBJECT_SIZE(numCapabilities*3+8)); + int numHidden=config.numHidden(); + GwJsonDocument json(JSON_OBJECT_SIZE(numCapabilities*3+numHidden*2+8)); for (auto it=userCodeHandler.getCapabilities()->begin(); it != userCodeHandler.getCapabilities()->end();it++){ json[it->first]=it->second; } + std::vector hiddenCfg=config.getHidden(); + for (auto it=hiddenCfg.begin();it != hiddenCfg.end();it++){ + json["HIDE"+*it]=true; + } json["serialmode"]=channels.getMode(SERIAL1_CHANNEL_ID); json["serial2mode"]=channels.getMode(SERIAL2_CHANNEL_ID); #ifdef GWBUTTON_PIN From 4fa40b98b3e2ee3efce5a81957b8febda1677035 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 5 Oct 2023 18:52:30 +0200 Subject: [PATCH 084/178] allow to set a read only mode for config items, output compiler messages for overwritten configs, add m5 gps unit defs --- extra_script.py | 28 +++++++++++++----- lib/config/GWConfig.cpp | 11 +++---- lib/config/GWConfig.h | 4 +-- lib/config/GwConfigItem.h | 20 +++++++++---- lib/hardware/GwHardware.h | 21 ++++++++++++-- src/main.cpp | 15 ++++++---- web/index.js | 60 ++++++++++++++++++++++++++++----------- 7 files changed, 114 insertions(+), 45 deletions(-) diff --git a/extra_script.py b/extra_script.py index 03ec16b..00cf107 100644 --- a/extra_script.py +++ b/extra_script.py @@ -145,20 +145,33 @@ def generateCfg(inFile,outFile,addDirs=[]): secret="false"; if item.get('type') == 'password': secret="true" - data+=" #undef __CFGHIDDEN\n" - data+=" #ifdef CFGHIDE_%s\n"%(name) - data+=" #define __CFGHIDDEN true\n" + data+=" #undef __CFGMODE\n" + data+=" #ifdef CFGMODE_%s\n"%(name) + data+=" #define __CFGMODE CFGMODE_%s\n"%(name) data+=" #else\n" - data+=" #define __CFGHIDDEN false\n" + data+=" #define __CFGMODE GwConfigInterface::NORMAL\n" data+=" #endif\n" - data+=" #ifdef CFGDEFAULT_%s\n"%(name) - data+=" new GwConfigInterface(%s,CFGDEFAULT_%s,%s,__CFGHIDDEN)\n"%(name,name,secret) + data+=" #ifdef CFGDEFAULT_%s\n"%(name) + data+=" new GwConfigInterface(%s,CFGDEFAULT_%s,%s,__CFGMODE)\n"%(name,name,secret) data+=" #else\n" - data+=" new GwConfigInterface(%s,\"%s\",%s,__CFGHIDDEN)\n"%(name,item.get('default'),secret) + data+=" new GwConfigInterface(%s,\"%s\",%s,__CFGMODE)\n"%(name,item.get('default'),secret) data+=" #endif\n" data+='};\n' data+='};\n' + data+="#ifdef CFG_MESSAGES\n" + for item in config: + name=item.get('name') + if name is None: + continue + data+="#ifdef CFGMODE_%s\n"%(name) + data+=" __MSG(\"CFGMODE_%s=\" __XSTR(CFGMODE_%s))\n"%(name,name) + data+="#endif\n" + data+="#ifdef CFGDEFAULT_%s\n"%(name) + data+=" __MSG(\"CFGDEFAULT_%s=\" CFGDEFAULT_%s)\n"%(name,name) + data+="#endif\n" + data+="#endif" + writeFileIfChanged(outFile,data) @@ -298,6 +311,7 @@ print("#prescript...") prebuild(env) board="PLATFORM_BOARD_%s"%env["BOARD"].replace("-","_").upper() print("Board=#%s#"%board) +print("BuildFlags=%s"%(" ".join(env["BUILD_FLAGS"]))) env.Append( LINKFLAGS=[ "-u", "custom_app_desc" ], CPPDEFINES=[(board,"1")] diff --git a/lib/config/GWConfig.cpp b/lib/config/GWConfig.cpp index 15ecc43..641b564 100644 --- a/lib/config/GWConfig.cpp +++ b/lib/config/GWConfig.cpp @@ -1,3 +1,4 @@ +#define CFG_MESSAGES #include "GWConfig.h" #include #include @@ -159,20 +160,20 @@ void GwConfigHandler::toHex(unsigned long v, char *buffer, size_t bsize) buffer[2 * i] = 0; } -std::vector GwConfigHandler::getHidden() const{ +std::vector GwConfigHandler::getSpecial() const{ std::vector rt; - rt.reserve(numHidden()); + rt.reserve(numSpecial()); for (int i=0L;iisHidden()){ + if (configs[i]->getType() != GwConfigInterface::NORMAL){ rt.push_back(configs[i]->getName()); }; } return rt; } -int GwConfigHandler::numHidden() const{ +int GwConfigHandler::numSpecial() const{ int rt=0; for (int i=0L;iisHidden()) rt++; + if (configs[i]->getType() != GwConfigInterface::NORMAL) rt++; } return rt; } diff --git a/lib/config/GWConfig.h b/lib/config/GWConfig.h index f6e1db2..796ae25 100644 --- a/lib/config/GWConfig.h +++ b/lib/config/GWConfig.h @@ -30,8 +30,8 @@ class GwConfigHandler: public GwConfigDefinitions{ int getInt(const String name,int defaultv=0) const; GwConfigInterface * getConfigItem(const String name, bool dummy=false) const; bool checkPass(String hash); - std::vector getHidden() const; - int numHidden() const; + std::vector getSpecial() const; + int numSpecial() const; /** * change the value of a config item * will become a noop after stopChanges has been called diff --git a/lib/config/GwConfigItem.h b/lib/config/GwConfigItem.h index a6d66cd..12f5e97 100644 --- a/lib/config/GwConfigItem.h +++ b/lib/config/GwConfigItem.h @@ -5,19 +5,25 @@ class GwConfigHandler; class GwConfigInterface{ + public: + typedef enum { + NORMAL=0, + HIDDEN=1, + READONLY=2 + } ConfigType; private: String name; const char * initialValue; String value; bool secret=false; - bool hidden=false; + ConfigType type=NORMAL; public: - GwConfigInterface(const String &name, const char * initialValue, bool secret=false, bool hidden=false){ + GwConfigInterface(const String &name, const char * initialValue, bool secret=false,ConfigType type=NORMAL){ this->name=name; this->initialValue=initialValue; this->value=initialValue; this->secret=secret; - this->hidden=hidden; + this->type=type; } virtual String asString() const{ return value; @@ -43,8 +49,8 @@ class GwConfigInterface{ String getDefault() const { return initialValue; } - bool isHidden() const { - return hidden; + ConfigType getType() const { + return type; } friend class GwConfigHandler; }; @@ -67,5 +73,7 @@ class GwNmeaFilter{ String toString(); }; - +#define __XSTR(x) __STR(x) +#define __STR(x) #x +#define __MSG(x) _Pragma (__STR(message (x))) #endif \ No newline at end of file diff --git a/lib/hardware/GwHardware.h b/lib/hardware/GwHardware.h index 5b7e278..c4d9142 100644 --- a/lib/hardware/GwHardware.h +++ b/lib/hardware/GwHardware.h @@ -17,7 +17,7 @@ #define GWSERIAL_TYPE_BI 2 #define GWSERIAL_TYPE_RX 3 #define GWSERIAL_TYPE_TX 4 - +#include #include #include "GwUserTasks.h" @@ -148,8 +148,8 @@ #ifdef M5_GPS_KIT #define GWSERIAL_RX BOARD_LEFT1 #define GWSERIAL_TYPE GWSERIAL_TYPE_RX - #define CFGGDEFAULT_serialBaud "9600" - #define CFGHIDE_serialBaud + #define CFGDEFAULT_serialBaud "9600" + #define CFGMODE_serialBaud GwConfigInterface::READONLY #endif //below we define the final device config based on the above @@ -181,6 +181,21 @@ #endif #endif +//http://docs.m5stack.com/en/unit/gps +#ifdef M5_GPS_UNIT + #ifdef GWSERIAL_TYPE + #define GWSERIAL2_RX GROOVE_PIN_2 + #define GWSERIAL2_TYPE GWSERIAL_TYPE_RX + #define CFGDEFAULT_serialBaud "9600" + #define CFGMODE_serialBaud GwConfigInterface::READONLY + #else + #define GWSERIAL_RX GROOVE_PIN_2 + #define GWSERIAL_TYPE GWSERIAL_TYPE_RX + #define CFGDEFAULT_serial2Baud "9600" + #define CFGMODE_serial2Baud GwConfigInterface::READONLY + #endif +#endif + //can kit for M5 Atom #ifdef M5_CAN_KIT #define ESP32_CAN_TX_PIN BOARD_LEFT1 diff --git a/src/main.cpp b/src/main.cpp index 0bc11ba..b928ad4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -430,15 +430,20 @@ class CapabilitiesRequest : public GwRequestMessage{ protected: virtual void processRequest(){ int numCapabilities=userCodeHandler.getCapabilities()->size(); - int numHidden=config.numHidden(); - GwJsonDocument json(JSON_OBJECT_SIZE(numCapabilities*3+numHidden*2+8)); + int numSpecial=config.numSpecial(); + logger.logDebug(GwLog::LOG,"capabilities user=%d, config=%d",numCapabilities,numSpecial); + GwJsonDocument json(JSON_OBJECT_SIZE(numCapabilities*3+numSpecial*2+8)); for (auto it=userCodeHandler.getCapabilities()->begin(); it != userCodeHandler.getCapabilities()->end();it++){ json[it->first]=it->second; } - std::vector hiddenCfg=config.getHidden(); - for (auto it=hiddenCfg.begin();it != hiddenCfg.end();it++){ - json["HIDE"+*it]=true; + std::vector specialCfg=config.getSpecial(); + for (auto it=specialCfg.begin();it != specialCfg.end();it++){ + GwConfigInterface *cfg=config.getConfigItem(*it); + if (cfg){ + logger.logDebug(GwLog::LOG,"config mode %s=%d",it->c_str(),(int)(cfg->getType())); + json["CFGMODE"+*it]=(int)cfg->getType(); + } } json["serialmode"]=channels.getMode(SERIAL1_CHANNEL_ID); json["serial2mode"]=channels.getMode(SERIAL2_CHANNEL_ID); diff --git a/web/index.js b/web/index.js index 4c49f9c..db31d8d 100644 --- a/web/index.js +++ b/web/index.js @@ -231,6 +231,7 @@ function getAllConfigs(omitPass) { let name = v.getAttribute('name'); if (!name) continue; if (name.indexOf("_") >= 0) continue; + if (v.getAttribute('disabled')) continue; let def = getConfigDefition(name); if (def.type === 'password' && ( v.value == '' || omitPass)) { continue; @@ -444,6 +445,7 @@ function createInput(configItem, frame,clazz) { let el; if (configItem.type === 'boolean' || configItem.type === 'list' || configItem.type == 'boatData') { el=addEl('select',clazz,frame); + if (configItem.readOnly) el.setAttribute('disabled',true); el.setAttribute('name', configItem.name) let slist = []; if (configItem.list) { @@ -476,6 +478,7 @@ function createInput(configItem, frame,clazz) { return createXdrInput(configItem,frame,clazz); } el = addEl('input',clazz,frame); + if (configItem.readOnly) el.setAttribute('disabled',true); el.setAttribute('name', configItem.name) if (configItem.type === 'password') { el.setAttribute('type', 'password'); @@ -591,25 +594,29 @@ function createXdrInput(configItem,frame){ {l:'bidir',v:1}, {l:'to2K',v:2}, {l:'from2K',v:3} - ] + ], + readOnly: configItem.readOnly },d,'xdrdir'); d=createXdrLine(el,'Category'); let category=createInput({ type: 'list', name: configItem.name+"_cat", - list:getXdrCategories() + list:getXdrCategories(), + readOnly: configItem.readOnly },d,'xdrcat'); d=createXdrLine(el,'Source'); let selector=createInput({ type: 'list', name: configItem.name+"_sel", - list:[] + list:[], + readOnly: configItem.readOnly },d,'xdrsel'); d=createXdrLine(el,'Field'); let field=createInput({ type:'list', name: configItem.name+'_field', - list: [] + list: [], + readOnly: configItem.readOnly },d,'xdrfield'); d=createXdrLine(el,'Instance'); let imode=createInput({ @@ -620,22 +627,26 @@ function createXdrInput(configItem,frame){ {l:'single',v:0}, {l:'ignore',v:1}, {l:'auto',v:2} - ] + ], + readOnly: configItem.readOnly },d,'xdrimode'); let instance=createInput({ type:'number', name: configItem.name+"_instance", + readOnly: configItem.readOnly },d,'xdrinstance'); d=createXdrLine(el,'Transducer'); let xdrName=createInput({ type:'text', - name: configItem.name+"_xdr" + name: configItem.name+"_xdr", + readOnly: configItem.readOnly },d,'xdrname'); d=createXdrLine(el,'Example'); let example=addEl('div','xdrexample',d,''); let data = addEl('input','xdrvalue',el); data.setAttribute('type', 'hidden'); data.setAttribute('name', configItem.name); + if (configItem.readOnly) data.setAttribute('disabled',true); let changeFunction = function () { let parts=data.value.split(','); direction.value=parts[1] || 0; @@ -730,16 +741,19 @@ function createFilterInput(configItem, frame) { let ais = createInput({ type: 'list', name: configItem.name + "_ais", - list: ['aison', 'aisoff'] + list: ['aison', 'aisoff'], + readOnly: configItem.readOnly }, el); let mode = createInput({ type: 'list', name: configItem.name + "_mode", - list: ['whitelist', 'blacklist'] + list: ['whitelist', 'blacklist'], + readOnly: configItem.readOnly }, el); let sentences = createInput({ type: 'text', name: configItem.name + "_sentences", + readOnly: configItem.readOnly }, el); let data = addEl('input',undefined,el); data.setAttribute('type', 'hidden'); @@ -767,6 +781,7 @@ function createFilterInput(configItem, frame) { changeFunction(); }); data.setAttribute('name', configItem.name); + if (configItem.readOnly) data.setAttribute('disabled',true); return data; } let moreicons=['icon-more','icon-less']; @@ -1048,8 +1063,17 @@ function createConfigDefinitions(parent, capabilities, defs,includeXdr) { }); if (!found) showItem=false; } - + let readOnly=false; + let mode=capabilities['CFGMODE'+item.name]; + if (mode == 1) { + //hide + showItem=false; + } + if (mode == 2){ + readOnly=true; + } if (showItem) { + item.readOnly=readOnly; currentCategoryPopulated=true; let row = addEl('div', 'row', categoryEl); let label = item.label || item.name; @@ -1058,7 +1082,7 @@ function createConfigDefinitions(parent, capabilities, defs,includeXdr) { let valueEl = createInput(item, valueFrame); if (!valueEl) return; valueEl.setAttribute('data-default', item.default); - valueEl.addEventListener('change', function (ev) { + if (! readOnly) valueEl.addEventListener('change', function (ev) { let el = ev.target; checkChange(el, row, item.name); }) @@ -1075,13 +1099,15 @@ function createConfigDefinitions(parent, capabilities, defs,includeXdr) { } if (item.check) valueEl.setAttribute('data-check', item.check); let btContainer = addEl('div', 'buttonContainer', row); - let bt = addEl('button', 'defaultButton', btContainer, 'X'); - bt.setAttribute('data-default', item.default); - bt.addEventListener('click', function (ev) { - valueEl.value = valueEl.getAttribute('data-default'); - let changeEvent = new Event('change'); - valueEl.dispatchEvent(changeEvent); - }) + if (!readOnly) { + let bt = addEl('button', 'defaultButton', btContainer, 'X'); + bt.setAttribute('data-default', item.default); + bt.addEventListener('click', function (ev) { + valueEl.value = valueEl.getAttribute('data-default'); + let changeEvent = new Event('change'); + valueEl.dispatchEvent(changeEvent); + }) + } bt = addEl('button', 'infoButton', btContainer, '?'); bt.addEventListener('click', function (ev) { if (item.description) { From 12288edbf8d3da5b830cdaa7058b12f8f0ed6c53 Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 5 Oct 2023 19:11:09 +0200 Subject: [PATCH 085/178] add gps kit and gps unit --- webinstall/build.yaml | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/webinstall/build.yaml b/webinstall/build.yaml index fb61984..abc2bb8 100644 --- a/webinstall/build.yaml +++ b/webinstall/build.yaml @@ -8,18 +8,24 @@ types: - label: "CAN KIT" value: M5_CAN_KIT description: "M5 Stack CAN Kit" - url: "http://docs.m5stack.com/en/atom/atom_can" + url: "https://docs.m5stack.com/en/atom/atom_can" resource: can - value: M5_SERIAL_KIT_232 description: "M5 Stack RS232 Base" label: "Atomic RS232 Base" - url: "http://docs.m5stack.com/en/atom/Atomic%20RS232%20Base" + url: "https://docs.m5stack.com/en/atom/Atomic%20RS232%20Base" resource: serial - value: M5_SERIAL_KIT_485 description: "M5 Stack RS485 Base" label: "Atomic RS485 Base" - url: "http://docs.m5stack.com/en/atom/Atomic%20RS485%20Base" + url: "https://docs.m5stack.com/en/atom/Atomic%20RS485%20Base" resource: serial + - value: M5_GPS_KIT + description: "M5 Stack Gps Kit" + label: "Gps Base" + url: "https://docs.m5stack.com/en/atom/atomicgps" + resource: serial + - &m5groovei2c type: multi key: m5groovei2c @@ -32,7 +38,7 @@ types: label: "M5 Groove CAN Units" values: - label: "CAN Unit" - url: "http://docs.m5stack.com/en/unit/can" + url: "https://docs.m5stack.com/en/unit/can" description: "M5 Can unit" value: M5_CANUNIT resource: can @@ -46,13 +52,18 @@ types: key: unit485 value: SERIAL_GROOVE_485 description: "M5 RS485 unit" - url: "http://docs.m5stack.com/en/unit/rs485" + url: "https://docs.m5stack.com/en/unit/rs485" resource: serial - label: "Tail485" value: SERIAL_GROOVE_485 key: tail485 description: "M5 Tail 485" - url: "http://docs.m5stack.com/en/atom/tail485" + url: "https://docs.m5stack.com/en/atom/tail485" + resource: serial + - label: "Gps Unit" + value: M5_GPS_UNIT + description: "M5 Gps Unit" + url: "https://docs.m5stack.com/en/unit/gps" resource: serial - &m5groove From 4af9434b29735f228f55bd5f9cbee2b4f2be193e Mon Sep 17 00:00:00 2001 From: andreas Date: Thu, 5 Oct 2023 19:37:46 +0200 Subject: [PATCH 086/178] ensure to load buildconfig from selected git sha --- webinstall/cibuild.js | 21 +++++++++++++++++---- webinstall/config.php | 4 ++-- webinstall/functions.php | 2 +- webinstall/install.php | 9 ++++++++- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index c62b852..bf67b3b 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -226,7 +226,10 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" downloadConfig: downloadConfig }; const loadConfig=async (url)=>{ - let config=await fetch(url).then((r)=>r.text()); + let config=await fetch(url).then((r)=>{ + if (!r.ok) throw new Error("unable to fetch: "+r.statusText); + return r.text() + }); let parsed=yamlLoad(config); return parsed; } @@ -423,9 +426,6 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" fetchStatus(true); setRunning(true); } - structure=await loadConfig("build.yaml"); - buildSelectors(ROOT_PATH,structure.config.children,true); - if (! running) findPipeline(); let gitParam={user:GITUSER,repo:GITREPO}; let branch=getParam('branch'); if (branch){ @@ -479,6 +479,19 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" else{ setValue('gitSha',gitSha); } + if (gitSha !== undefined){ + let url=buildUrl(GITAPI,Object.assign({},gitParam,{sha:gitSha,proxy:'webinstall/build.yaml'})); + try{ + structure=await loadConfig(url); + }catch (e){ + alert("unable to load config for selected release:\n "+e+"\n falling back to default"); + } + } + if (! structure){ + structure=await loadConfig("build.yaml"); + } + buildSelectors(ROOT_PATH,structure.config.children,true); + if (! running) findPipeline(); updateStart(); } })(); \ No newline at end of file diff --git a/webinstall/config.php b/webinstall/config.php index e63f800..2a7485b 100644 --- a/webinstall/config.php +++ b/webinstall/config.php @@ -15,12 +15,12 @@ function fillUserAndRepo($vars=null,$source=null){ } foreach (array('user','repo') as $n){ if (! isset($source[$n])){ - die("missing parameter $n"); + throw new Exception("missing parameter $n"); } $v=$source[$n]; $av=$allowed[$n]; if (! in_array($v,$av)){ - die("value $v for $n not allowed"); + throw new Exception("value $v for $n not allowed"); } $vars[$n]=$v; } diff --git a/webinstall/functions.php b/webinstall/functions.php index bb27108..c01888e 100644 --- a/webinstall/functions.php +++ b/webinstall/functions.php @@ -39,7 +39,7 @@ function addVars(&$vars,$names,$defaults=null){ foreach ($names as $n){ $v=null; if (! isset($_REQUEST[$n])){ - if ($defaults == null || ! isset($defaults[$n])) die("missing parameter $n"); + if ($defaults == null || ! isset($defaults[$n])) throw new Exception("missing parameter $n"); $v=$defaults[$n]; } else{ diff --git a/webinstall/install.php b/webinstall/install.php index c3fd6a8..d0dea66 100644 --- a/webinstall/install.php +++ b/webinstall/install.php @@ -7,6 +7,7 @@ $branchsha=API_BASE."/git/refs/heads/#branch#"; $tagsha=API_BASE."/git/refs/tags/#tag#"; $download = "https://github.com/#user#/#repo#/releases/download/#dlVersion#/#dlName#"; $manifest = "?dlName=#mName#&dlVersion=#mVersion#&user=#user#&repo=#repo#"; +$proxurl="https://raw.githubusercontent.com/#user#/#repo#/#sha#/#proxy#"; try { if (isset($_REQUEST['api'])) { $vars = fillUserAndRepo(); @@ -50,11 +51,17 @@ try { } } if (!$targetUrl) - die("unable to find $targetBase $mode\n"); + throw new Exception("unable to find $targetBase $mode\n"); #echo("download for $targetBase=$targetUrl\n"); proxy($targetUrl); exit(0); } + if (isset($_REQUEST['proxy'])){ + $vars = fillUserAndRepo(); + $vars = addVars($vars, array('sha', 'proxy')); + proxy(replaceVars($proxurl, $vars)); + exit(0); + } } catch (HTTPErrorException $h) { header($_SERVER['SERVER_PROTOCOL'] . " " . $h->code . " " . $h->getMessage()); die($h->getMessage()); From 35eb5417afa72bf49e5cff093205605cce808b1f Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 6 Oct 2023 18:40:51 +0200 Subject: [PATCH 087/178] correctly handle location header for HTTP/2 --- webinstall/functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webinstall/functions.php b/webinstall/functions.php index c01888e..477cc1d 100644 --- a/webinstall/functions.php +++ b/webinstall/functions.php @@ -5,7 +5,7 @@ function openDb(){ if (! defined('database::SERVER')) return null; try{ $db=new mysqli(database::SERVER, database::USER, database::PASS,database::DB); - $db->query("SET CHARACTER SET 'utf8'"); + #$db->query("SET CHARACTER SET 'utf8'"); return $db; }catch (Exception $e){ error_log("openDB error $e"); @@ -193,7 +193,7 @@ function proxy_impl($url, $timeout=30,$headers=null,$num = 5) } #echo ("???code=$code\n"); if ($code == 301 || $code == 302) { - if(preg_match('/Location:(.*?)\n/', $header, $matches)){ + if(preg_match('/[Ll]ocation:(.*?)\n/', $header, $matches)){ $nexturl = trim(array_pop($matches)); #echo("???nexturl=$nexturl\n"); } From 1da9fea9a452b3b97996b90143df49c8f2f8e0da Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 6 Oct 2023 18:43:18 +0200 Subject: [PATCH 088/178] clean up status handling --- webinstall/cibuild.js | 199 ++++++++++++++++++++++-------------------- 1 file changed, 102 insertions(+), 97 deletions(-) diff --git a/webinstall/cibuild.js b/webinstall/cibuild.js index bf67b3b..daaaf0b 100644 --- a/webinstall/cibuild.js +++ b/webinstall/cibuild.js @@ -1,6 +1,41 @@ import { addEl, setButtons,fillValues, setValue, buildUrl, fetchJson, setVisible, enableEl, setValues, getParam, fillSelect, forEachEl, readFile } from "./helper.js"; import {load as yamlLoad} from "https://cdn.skypack.dev/js-yaml@4.1.0"; import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" +class PipelineInfo{ + constructor(id){ + this.STFIELDS=['status','error','status_url']; + this.reset(id); + } + update(state){ + if (state.pipeline_id !== undefined && state.pipeline_id !== this.id){ + return false; + } + this.STFIELDS.forEach((i)=>{ + let v=state[i]; + if (v !== undefined)this[i]=v; + }); + } + reset(id,opt_state){ + this.id=id; + this.STFIELDS.forEach((i)=>this[i]=undefined); + this.downloadUrl=undefined; + if (opt_state) { + this.update(opt_state); + } + else{ + if (id !== undefined) this.status='fetching'; + } + } + valid(){ + return this.id !== undefined; + } + isRunning(){ + if (! this.valid()) return false; + if (this.status === undefined) return false; + return ['error','success','canceled'].indexOf(this.status) < 0; + } + +} (function(){ const STATUS_INTERVAL=2000; const CURRENT_PIPELINE='pipeline'; @@ -8,16 +43,13 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" const GITAPI="install.php"; const GITUSER="wellenvogel"; const GITREPO="esp32-nmea2000"; - let currentPipeline=undefined; - let downloadUrl=undefined; + let currentPipeline=new PipelineInfo(); let timer=undefined; let structure=undefined; let config={}; //values as read and stored let configStruct={}; //complete struct merged of config and struct let displayMode='last'; let delayedSearch=undefined; - let running=false; - let pipelineState=undefined; let gitSha=undefined; const modeStrings={ last: 'Last Build', @@ -34,37 +66,22 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" setValue('resultTitle',ms); return mode !== old; } - const showError=(text)=>{ - if (text === undefined){ - setVisible('buildError',false,true); - return; - } - setValue('buildError',text); - setVisible('buildError',true,true); - } - const hideResults = () => { - downloadUrl = undefined; - currentPipeline = undefined; - setValue('pipeline', currentPipeline); - setValue('status',''); - showError(); - setVisible('download', false, true); - setVisible('status_url', false, true); - } - const updateStart=()=>{ - if (running) { - enableEl('start',false); - return; - } + const updateStatus=()=>{ + setValues(currentPipeline,{ + id: 'pipeline' + }); + setVisible('download',currentPipeline.valid() && currentPipeline.downloadUrl!==undefined,true); + setVisible('status_url',currentPipeline.valid() && currentPipeline.status_url!==undefined,true); + setVisible('error',currentPipeline.error!==undefined,true); let e=document.getElementById('configError'); if (e.textContent) { enableEl('start',false); return; } if (displayMode != 'existing'){ - if (currentPipeline !== undefined){ + if (currentPipeline.valid()){ //check pipeline state - if (['error','success'].indexOf(pipelineState) >= 0){ + if (['error','success','canceled'].indexOf(currentPipeline.status) >= 0){ enableEl('start',true); return; } @@ -72,71 +89,51 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" return; } enableEl('start',true); + return; } - //display mnode existing - enableEl('start',currentPipeline === undefined); - } - const setRunning=(active)=>{ - running=active; - if (active){ - showError(); - downloadUrl=undefined; - setVisible('download', false, true); - setVisible('status_url', false, true); - } - updateStart(); + //display node existing + enableEl('start',!currentPipeline.valid()); } const isRunning=()=>{ - return running; + return currentPipeline.isRunning(); } const fetchStatus=(initial)=>{ - if (currentPipeline === undefined) { - setVisible('status_url',false,true); - setVisible('error',false); - setVisible('download',false,true); - setValue('status','---'); - updateStart(); + if (! currentPipeline.valid()){ + updateStatus(); return; - }; - let queryPipeline=currentPipeline; - fetchJson(API,{api:'status',pipeline:currentPipeline}) + } + let queryPipeline=currentPipeline.id; + fetchJson(API,{api:'status',pipeline:currentPipeline.id}) .then((st)=>{ - if (queryPipeline !== currentPipeline) return; + if (queryPipeline !== currentPipeline.id) return; + if (currentPipeline.id !== st.pipeline_id) return; if (st.status === undefined) st.status=st.state; - pipelineState=st.status; - setValues(st); - setVisible('status_url',st.status_url !== undefined,true); - setVisible('error',st.error !== undefined,true); - if (st.status === 'error' || st.status === 'errored'){ - setRunning(false); - setVisible('download',false,true); - updateStart(); + currentPipeline.update(st); + updateStatus(); + if (st.status === 'error' || st.status === 'errored' || st.status === 'canceled'){ return; } if (st.status === 'success'){ - setRunning(false); - updateStart(); - fetchJson(API,{api:'artifacts',pipeline:currentPipeline}) + fetchJson(API,{api:'artifacts',pipeline:currentPipeline.id}) .then((ar)=>{ if (! ar.items || ar.items.length < 1){ throw new Error("no download link"); } - downloadUrl=buildUrl(API,{ - download: currentPipeline + currentPipeline.downloadUrl=buildUrl(API,{ + download: currentPipeline.id }); - setVisible('download',true,true); + updateStatus(); }) .catch((err)=>{ - showError("Unable to get build result: "+err); - setVisible('download',false,true); + currentPipeline.update({ + status:'error', + error:"Unable to get build result: "+err + }); + updateStatus(); }); return; } - else{ - setVisible('download',false,true); - } - updateStart(); timer=window.setTimeout(fetchStatus,STATUS_INTERVAL) }) .catch((e)=>{ @@ -144,51 +141,54 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" }) } const setCurrentPipeline=(pipeline,doStore)=>{ - currentPipeline=pipeline; - setValue('pipeline',currentPipeline); + currentPipeline.reset(pipeline); if (doStore) window.localStorage.setItem(CURRENT_PIPELINE,pipeline); }; const startBuild=()=>{ let param={}; - currentPipeline=undefined; + currentPipeline.reset(undefined,{status:'requested'}); if (timer) window.clearTimeout(timer); timer=undefined; fillValues(param,['environment','buildflags']); - setValue('status','requested'); - setValue('pipeline',''); - setRunning(true); - updateStart(); + setDisplayMode('current'); + updateStatus(); if (gitSha !== undefined) param.tag=gitSha; param.config=JSON.stringify(config); fetchJson(API,Object.assign({ api:'start'},param)) .then((json)=>{ - if (json.status === 'error'){ + let status=json.status || 'error'; + if (status === 'error'){ + currentPipeline.update({status:status,error:json.error}) + updateStatus(); throw new Error("unable to create job "+(json.error||'')); } - if (!json.id) throw new Error("unable to create job, no id"); + if (!json.id) { + let error="unable to create job, no id" + currentPipeline.update({status:'error',error:error}); + updateStatus(); + throw new Error(error); + } setCurrentPipeline(json.id,true); - setValue('status',json.status); - setDisplayMode('current'); + updateStatus(); timer=window.setTimeout(fetchStatus,STATUS_INTERVAL); }) .catch((err)=>{ - setRunning(false); - setValue('status','error'); - showError(err); + currentPipeline.update({status:'error',error:err}); + updateStatus(); }); } const runDownload=()=>{ - if (! downloadUrl) return; + if (! currentPipeline.downloadUrl) return; let df=document.getElementById('dlframe'); if (df){ df.setAttribute('src',null); - df.setAttribute('src',downloadUrl); + df.setAttribute('src',currentPipeline.downloadUrl); } } const webInstall=()=>{ - if (! downloadUrl) return; - let url=buildUrl("install.html",{custom:downloadUrl}); + if (! currentPipeline.downloadUrl) return; + let url=buildUrl("install.html",{custom:currentPipeline.downloadUrl}); window.location.href=url; } const uploadConfig=()=>{ @@ -387,7 +387,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" setVisible('configError',false,true); } if (! initial) findPipeline(); - updateStart(); + updateStatus(); } let findIdx=0; const findPipeline=()=>{ @@ -408,23 +408,28 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" .then((res)=>{ if (queryIdx != findIdx) return; setCurrentPipeline(res.pipeline); - updateStart(); + if (res.pipeline) currentPipeline.status="found"; setDisplayMode('existing'); + updateStatus(); fetchStatus(true); }) - .catch((e)=>console.log("findPipeline error ",e)); + .catch((e)=>{ + console.log("findPipeline error ",e) + if (displayMode == 'existing'){ + setCurrentPipeline(); + updateStatus(); + } + }); } window.onload=async ()=>{ setButtons(btConfig); - forEachEl('#environment',(el)=>el.addEventListener('change',hideResults)); - forEachEl('#buildflags',(el)=>el.addEventListener('change',hideResults)); let pipeline=window.localStorage.getItem(CURRENT_PIPELINE); setDisplayMode('last'); if (pipeline){ setCurrentPipeline(pipeline); + updateStatus(); fetchStatus(true); - setRunning(true); } let gitParam={user:GITUSER,repo:GITREPO}; let branch=getParam('branch'); @@ -491,7 +496,7 @@ import fileDownload from "https://cdn.skypack.dev/js-file-download@0.4.12" structure=await loadConfig("build.yaml"); } buildSelectors(ROOT_PATH,structure.config.children,true); - if (! running) findPipeline(); - updateStart(); + if (! isRunning()) findPipeline(); + updateStatus(); } })(); \ No newline at end of file From 10058cfeec959700577721cccbb43a83cd72550e Mon Sep 17 00:00:00 2001 From: andreas Date: Fri, 6 Oct 2023 19:57:32 +0200 Subject: [PATCH 089/178] handle build version for ci builds --- post.py | 12 +++++------ webinstall/cibuild.css | 4 ++++ webinstall/cibuild.html | 8 +++++-- webinstall/cibuild.js | 48 ++++++++++++++++++++++++++++++++++------- webinstall/helper.js | 3 ++- 5 files changed, 58 insertions(+), 17 deletions(-) diff --git a/post.py b/post.py index 8b04ffa..8fe2f27 100644 --- a/post.py +++ b/post.py @@ -63,17 +63,17 @@ def post(source,target,env): for f in glob.glob(os.path.join(outdir,base+"*.bin")): print("removing old file %s"%f) os.unlink(f) - ofversion='' - if not version.startswith('dev'): - ofversion="-"+version - versionedFile=os.path.join(outdir,"%s%s-update.bin"%(base,ofversion)) - shutil.copyfile(firmware,versionedFile) - outfile=os.path.join(outdir,"%s%s-all.bin"%(base,ofversion)) + outfile=os.path.join(outdir,"%s-all.bin"%(base)) cmd=[python,esptool,"--chip",chip,"merge_bin","--target-offset",offset,"-o",outfile] cmd+=uploadfiles cmd+=[appoffset,firmware] print("running %s"%" ".join(cmd)) env.Execute(" ".join(cmd),"#testpost") + ofversion="-"+version + versionedFile=os.path.join(outdir,"%s%s-update.bin"%(base,ofversion)) + shutil.copyfile(firmware,versionedFile) + versioneOutFile=os.path.join(outdir,"%s%s-all.bin"%(base,ofversion)) + shutil.copyfile(outfile,versioneOutFile) env.AddPostAction( "$BUILD_DIR/${PROGNAME}.bin", post diff --git a/webinstall/cibuild.css b/webinstall/cibuild.css index fcf5312..76c8f38 100644 --- a/webinstall/cibuild.css +++ b/webinstall/cibuild.css @@ -35,6 +35,10 @@ width: 10em; opacity: 0.6; padding: 0; + flex-shrink: 0; +} +.configui .row .value{ + padding-left: 0; } .configui .since { display: block; diff --git a/webinstall/cibuild.html b/webinstall/cibuild.html index 112a8a6..36ac0fa 100644 --- a/webinstall/cibuild.html +++ b/webinstall/cibuild.html @@ -18,6 +18,10 @@ GitSha ---
+
+ Version + +
@@ -27,11 +31,11 @@
Board type - +
Build Flags - +