From 62503f4af8f757aa4fd0a9b78cdf531d544fa9c6 Mon Sep 17 00:00:00 2001 From: shufps Date: Sat, 7 Sep 2024 20:05:10 +0200 Subject: [PATCH 01/18] nerdnos first working version --- lib/TFT_eSPI/User_Setup_Select.h | 10 +- platformio.ini | 227 +++++++++------- src/NerdMinerV2.ino.cpp | 35 ++- src/ShaTests/nerdSHA256plus.h | 4 +- src/drivers/devices/device.h | 2 + src/drivers/devices/nerdnos.h | 16 ++ src/drivers/nerd-nos/bm1397.cpp | 448 +++++++++++++++++++++++++++++++ src/drivers/nerd-nos/bm1397.h | 55 ++++ src/drivers/nerd-nos/common.h | 41 +++ src/drivers/nerd-nos/crc.cpp | 114 ++++++++ src/drivers/nerd-nos/crc.h | 18 ++ src/drivers/nerd-nos/mining.cpp | 168 ++++++++++++ src/drivers/nerd-nos/mining.h | 32 +++ src/drivers/nerd-nos/serial.cpp | 90 +++++++ src/drivers/nerd-nos/serial.h | 13 + src/drivers/nerd-nos/utils.cpp | 309 +++++++++++++++++++++ src/drivers/nerd-nos/utils.h | 35 +++ src/mining.cpp | 156 +++++++++-- src/mining.h | 3 +- src/stratum.cpp | 68 +++-- src/stratum.h | 4 +- 21 files changed, 1689 insertions(+), 159 deletions(-) create mode 100644 src/drivers/devices/nerdnos.h create mode 100644 src/drivers/nerd-nos/bm1397.cpp create mode 100644 src/drivers/nerd-nos/bm1397.h create mode 100644 src/drivers/nerd-nos/common.h create mode 100644 src/drivers/nerd-nos/crc.cpp create mode 100644 src/drivers/nerd-nos/crc.h create mode 100644 src/drivers/nerd-nos/mining.cpp create mode 100644 src/drivers/nerd-nos/mining.h create mode 100644 src/drivers/nerd-nos/serial.cpp create mode 100644 src/drivers/nerd-nos/serial.h create mode 100644 src/drivers/nerd-nos/utils.cpp create mode 100644 src/drivers/nerd-nos/utils.h diff --git a/lib/TFT_eSPI/User_Setup_Select.h b/lib/TFT_eSPI/User_Setup_Select.h index bd72af9..4436896 100644 --- a/lib/TFT_eSPI/User_Setup_Select.h +++ b/lib/TFT_eSPI/User_Setup_Select.h @@ -126,12 +126,12 @@ //#include // Setup file for ESP32/ESP8266 based SSD1351 128x128 1.5inch OLED display -//#include // Setup file for ESP32/ESP8266 based ST7789 240X280 1.69inch TFT +//#include // Setup file for ESP32/ESP8266 based ST7789 240X280 1.69inch TFT -//#include // Setup file for the ESP32 TouchDown based on ILI9488 480 x 320 TFT +//#include // Setup file for the ESP32 TouchDown based on ILI9488 480 x 320 TFT -//#include // Setup file for the ESP32 TouchDown S3 based on ILI9488 480 x 320 TFT -#ifdef NERDMINERV2 +//#include // Setup file for the ESP32 TouchDown S3 based on ILI9488 480 x 320 TFT +#if defined(NERDMINERV2) || defined(NERD_NOS) #include #endif #ifdef NERMINER_S3_AMOLED @@ -196,7 +196,7 @@ #endif // Legacy setup support, RPI_ILI9486_DRIVER form is deprecated -// Instead define RPI_DISPLAY_TYPE and also define driver (e.g. ILI9486_DRIVER) +// Instead define RPI_DISPLAY_TYPE and also define driver (e.g. ILI9486_DRIVER) #if defined (RPI_ILI9486_DRIVER) #if !defined (ILI9486_DRIVER) #define ILI9486_DRIVER diff --git a/platformio.ini b/platformio.ini index 66ea812..a090db6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,13 +11,14 @@ [platformio] globallib_dir = lib -default_envs = NerdminerV2-T-HMI, wt32-sc01, wt32-sc01-plus, han_m5stack, M5Stick-C, esp32cam, ESP32-2432S028R, ESP32_2432S028_2USB, NerminerV2, Lilygo-T-Embed, ESP32-devKitv1, NerminerV2-S3-DONGLE, NerminerV2-S3-GEEK, NerminerV2-S3-AMOLED, NerminerV2-S3-AMOLED-TOUCH, NerminerV2-T-QT, NerdminerV2-T-Display_V1, ESP32-2432S028R, M5-StampS3, ESP32-S3-devKitv1, ESP32-S3-mini-wemos, ESP32-S2-mini-wemos, ESP32-S3-mini-weact, ESP32-D0WD-V3-weact, ESP32-C3-devKitmv1, ESP32-C3-super-mini +#default_envs = NerdminerV2-T-HMI, wt32-sc01, wt32-sc01-plus, han_m5stack, M5Stick-C, esp32cam, ESP32-2432S028R, ESP32_2432S028_2USB, NerminerV2, Lilygo-T-Embed, ESP32-devKitv1, NerminerV2-S3-DONGLE, NerminerV2-S3-GEEK, NerminerV2-S3-AMOLED, NerminerV2-S3-AMOLED-TOUCH, NerminerV2-T-QT, NerdminerV2-T-Display_V1, ESP32-2432S028R, M5-StampS3, ESP32-S3-devKitv1, ESP32-S3-mini-wemos, ESP32-S2-mini-wemos, ESP32-S3-mini-weact, ESP32-D0WD-V3-weact, ESP32-C3-devKitmv1, ESP32-C3-super-mini +default_envs = NerdNOS [env:M5Stick-C] platform = espressif32@6.6.0 board = m5stick-c framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file @@ -26,16 +27,16 @@ monitor_speed = 115200 upload_speed = 1500000 # 2 x 4.5MB app, 6.875MB SPIFFS board_build.partitions = huge_app.csv -build_flags = +build_flags = -D M5STICK_C=1 ;-D DEBUG_MINING=1 -lib_deps = +lib_deps = bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 m5stack/M5StickC@^0.2.5 -lib_ignore = +lib_ignore = TFT_eSPI SD SD_MMC @@ -48,14 +49,14 @@ lib_ignore = platform = espressif32@6.6.0 board = esp-wrover-kit framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file monitor_speed = 115200 upload_speed = 921600 board_build.partitions = huge_app.csv -lib_deps = +lib_deps = fbiego/ESP32Time@^2.0.6 bblanchon/ArduinoJson@^6.21.5 lvgl/lvgl@^8.4.0 @@ -63,7 +64,7 @@ lib_deps = https://github.com/tzapu/WiFiManager.git#v2.0.17 arduino-libraries/NTPClient@^3.2.1 mathertel/OneButton@^2.5.0 -build_flags = +build_flags = -D BOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -I lib @@ -80,7 +81,7 @@ lib_ignore = platform = espressif32@6.6.0 board = esp32-s3-devkitc-1 framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file @@ -89,7 +90,7 @@ upload_speed = 921600 board_build.partitions = default_8MB.csv board_build.mcu = esp32s3 board_build.f_cpu = 240000000L -lib_deps = +lib_deps = fbiego/ESP32Time@^2.0.6 bblanchon/ArduinoJson@^6.21.5 lvgl/lvgl@^8.4.0 @@ -97,7 +98,7 @@ lib_deps = https://github.com/tzapu/WiFiManager.git#v2.0.17 arduino-libraries/NTPClient@^3.2.1 mathertel/OneButton@^2.5.0 -build_flags = +build_flags = -D BOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -I lib @@ -115,7 +116,7 @@ lib_ignore = platform = espressif32@6.6.0 board = lolin_s3_mini framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file @@ -123,21 +124,21 @@ board_build.arduino.memory_type = qio_opi monitor_speed = 115200 upload_speed = 115200 board_build.partitions = huge_app.csv -build_flags = +build_flags = -D BOARD_HAS_PSRAM -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 -D ESP32RGB=1 -D RGB_LED_PIN=47 ;-D DEBUG_MINING=1 -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 fastled/FastLED@^3.6.0 -lib_ignore = +lib_ignore = TFT_eSPI HANSOLOminerv2 @@ -160,13 +161,13 @@ build_flags = -D HAN=1 -D M5STACK_BOARD=1 ;-D DEBUG_MINING=1 -lib_deps = +lib_deps = bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 m5stack/M5Stack@^0.4.6 -lib_ignore = +lib_ignore = TFT_eSPI HANSOLOminerv2 @@ -177,26 +178,26 @@ lib_ignore = platform = espressif32@6.6.0 board = lolin_s2_mini framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file monitor_speed = 115200 upload_speed = 115200 board_build.partitions = huge_app.csv -build_flags = +build_flags = -D BOARD_HAS_PSRAM -D DEVKITV1=1 -D PIN_BUTTON_1=0 -D LED_PIN=15 ;-D DEBUG_MINING=1 -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 -lib_ignore = +lib_ignore = TFT_eSPI HANSOLOminerv2 @@ -206,7 +207,7 @@ lib_ignore = platform = espressif32@6.6.0 board = lolin_s3_mini framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file @@ -214,21 +215,21 @@ board_build.arduino.memory_type = qio_opi monitor_speed = 115200 upload_speed = 115200 board_build.partitions = huge_app.csv -build_flags = +build_flags = -D BOARD_HAS_PSRAM -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 -D ESP32RGB=1 -D RGB_LED_PIN=48 ;-D DEBUG_MINING=1 -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 fastled/FastLED@^3.6.0 -lib_ignore = +lib_ignore = TFT_eSPI HANSOLOminerv2 @@ -241,26 +242,26 @@ lib_ignore = platform = espressif32@6.6.0 board = esp32dev framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file monitor_speed = 115200 upload_speed = 115200 board_build.partitions = huge_app.csv -build_flags = +build_flags = -D DEVKITV1=1 -D PIN_BUTTON_1=0 -D LED_PIN=22 -D ACTIVE_LED=LOW -D INACTIVE_LED=HIGH -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 -lib_ignore = +lib_ignore = TFT_eSPI HANSOLOminerv2 @@ -270,27 +271,27 @@ lib_ignore = platform = espressif32@6.6.0 board = seeed_xiao_esp32c3 framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file monitor_speed = 115200 upload_speed = 115200 board_build.partitions = huge_app.csv -build_flags = +build_flags = -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 -D DEVKITV1=1 -D PIN_BUTTON_1=9 -D LED_PIN=8 ;-D DEBUG_MINING=1 -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 -lib_ignore = +lib_ignore = TFT_eSPI HANSOLOminerv2 @@ -300,28 +301,28 @@ lib_ignore = platform = espressif32@6.6.0 board = esp32-c3-devkitm-1 framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file monitor_speed = 115200 upload_speed = 115200 board_build.partitions = huge_app.csv -build_flags = +build_flags = -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 -D ESP32RGB=1 -D PIN_BUTTON_1=9 -D RGB_LED_PIN=8 ;-D DEBUG_MINING=1 -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 fastled/FastLED@^3.6.0 -lib_ignore = +lib_ignore = TFT_eSPI HANSOLOminerv2 @@ -331,7 +332,7 @@ lib_ignore = platform = espressif32@6.6.0 board = esp32-s3-devkitc-1 framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file @@ -339,21 +340,21 @@ board_build.arduino.memory_type = qio_opi monitor_speed = 115200 upload_speed = 115200 board_build.partitions = huge_app.csv -build_flags = +build_flags = -D BOARD_HAS_PSRAM -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 -D ESP32RGB=1 -D RGB_LED_PIN=48 ;-D DEBUG_MINING=1 -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 fastled/FastLED@^3.6.0 -lib_ignore = +lib_ignore = TFT_eSPI HANSOLOminerv2 @@ -363,7 +364,7 @@ lib_ignore = platform = espressif32@6.6.0 board = esp32-s3-devkitc-1 framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file @@ -375,29 +376,31 @@ upload_speed = 115200 ;board_build.partitions = default_8MB.csv board_build.partitions = huge_app.csv ;board_build.partitions = default.csv -build_flags = +build_flags = -D LV_LVGL_H_INCLUDE_SIMPLE -D BOARD_HAS_PSRAM -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 -D NERDMINERV2=1 ;-D DEBUG_MINING=1 -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 -lib_ignore = +lib_ignore = HANSOLOminerv2 ;-------------------------------------------------------------------- -[env:Lilygo-T-Embed] +;-------------------------------------------------------------------- + +[env:NerdNOS] platform = espressif32@6.6.0 board = esp32-s3-devkitc-1 framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file @@ -409,20 +412,52 @@ upload_speed = 115200 ;board_build.partitions = default_8MB.csv board_build.partitions = huge_app.csv ;board_build.partitions = default.csv -build_flags = +build_flags = + -D LV_LVGL_H_INCLUDE_SIMPLE + -D BOARD_HAS_PSRAM + -D ARDUINO_USB_MODE=1 + -D ARDUINO_USB_CDC_ON_BOOT=1 + -D NERD_NOS=1 + ;-D DEBUG_MINING=1 +lib_deps = + https://github.com/takkaO/OpenFontRender#v1.2 + bblanchon/ArduinoJson@^6.21.5 + https://github.com/tzapu/WiFiManager.git#v2.0.17 + mathertel/OneButton@^2.5.0 + arduino-libraries/NTPClient@^3.2.1 +lib_ignore = + HANSOLOminerv2 + +[env:Lilygo-T-Embed] +platform = espressif32@6.6.0 +board = esp32-s3-devkitc-1 +framework = arduino +monitor_filters = + esp32_exception_decoder + time + log2file +board_build.arduino.memory_type = qio_opi +monitor_speed = 115200 +upload_speed = 115200 +# 2 x 4.5MB app, 6.875MB SPIFFS +;board_build.partitions = large_spiffs_16MB.csv +;board_build.partitions = default_8MB.csv +board_build.partitions = huge_app.csv +;board_build.partitions = default.csv +build_flags = -D LV_LVGL_H_INCLUDE_SIMPLE -D BOARD_HAS_PSRAM -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 -D LILYGO_S3_T_EMBED=1 ;-D DEBUG_MINING=1 -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 -lib_ignore = +lib_ignore = HANSOLOminerv2 ;-------------------------------------------------------------------- @@ -431,7 +466,7 @@ lib_ignore = platform = espressif32@6.6.0 board = esp32dev framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file @@ -440,16 +475,16 @@ monitor_speed = 115200 upload_speed = 115200 # 2 x 4.5MB app, 6.875MB SPIFFS board_build.partitions = huge_app.csv -build_flags = +build_flags = -D DEVKITV1=1 ;-D DEBUG_MINING=1 -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 -lib_ignore = +lib_ignore = HANSOLOminerv2 ;-------------------------------------------------------------------- @@ -458,7 +493,7 @@ lib_ignore = platform = espressif32@6.6.0 board = esp32dev ;esp-wrover-kit framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file @@ -468,18 +503,18 @@ monitor_speed = 115200 upload_speed = 115200 # 2 x 4.5MB app, 6.875MB SPIFFS board_build.partitions = huge_app.csv -build_flags = +build_flags = ;-D DEBUG_MINING=1 # Switching from 'TDISPLAY' to 'NERDMINER_T_DISPLAY_V1' fixes font related compile errors ;-D TDISPLAY=1 -D NERDMINER_T_DISPLAY_V1=1 -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 -lib_ignore = +lib_ignore = HANSOLOminerv2 ;-------------------------------------------------------------------- @@ -489,19 +524,19 @@ platform = espressif32@6.6.0 board = lilygo-t-amoled framework = arduino board_build.partitions = huge_app.csv -build_flags = +build_flags = -DNERMINER_S3_AMOLED -DTOUCH=0 -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 https://github.com/golden-guy/Arduino_wolfssl.git#v5.5.4 -lib_ignore = +lib_ignore = HANSOLOminerv2 ;-------------------------------------------------------------------- @@ -511,19 +546,19 @@ platform = espressif32@6.6.0 board = lilygo-t-amoled framework = arduino board_build.partitions = huge_app.csv -build_flags = +build_flags = -DNERMINER_S3_AMOLED -DTOUCH=1 -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 https://github.com/golden-guy/Arduino_wolfssl.git#v5.5.4 -lib_ignore = +lib_ignore = HANSOLOminerv2 ;-------------------------------------------------------------------- @@ -533,20 +568,20 @@ platform = espressif32@6.6.0 board = esp32-s3-devkitc-1 framework = arduino board_build.partitions = huge_app.csv -build_flags = +build_flags = -DNERMINER_S3_DONGLE -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT -DTFT_BACKLIGHT_ON=LOW -DTFT_BL=38 -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 fastled/FastLED@^3.6.0 -lib_ignore = +lib_ignore = HANSOLOminerv2 ;-------------------------------------------------------------------- @@ -556,7 +591,7 @@ platform = espressif32@6.6.0 board = esp32-s3-devkitc-1 framework = arduino board_build.partitions = huge_app.csv -build_flags = +build_flags = -DNERMINER_S3_GEEK -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT @@ -566,13 +601,13 @@ build_flags = -DSDSPI_MISO=37 -DSDSPI_CS=34 -DSD_ID=HSPI -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 -lib_ignore = +lib_ignore = HANSOLOminerv2 ;-------------------------------------------------------------------- @@ -581,7 +616,7 @@ lib_ignore = platform = espressif32@6.6.0 ;(ESP32-D0WD-V3) board = esp32cam framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file @@ -589,19 +624,19 @@ monitor_speed = 115200 upload_speed = 921600 board_build.partitions = huge_app.csv board_build.arduino.memory_type = dio_qspi -build_flags = +build_flags = -D ESP32_CAM -D BOARD_HAS_PSRAM -D MONITOR_SPEED=${this.monitor_speed} ;-D DEBUG_MINING ;-D DEBUG_MEMORY -lib_deps = +lib_deps = bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 https://github.com/takkaO/OpenFontRender#v1.2 -lib_ignore = +lib_ignore = HANSOLOminerv2 ;-------------------------------------------------------------------- @@ -610,23 +645,23 @@ lib_ignore = platform = espressif32@6.6.0 board = esp32-s3-t-qt-pro framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file ;board_build.arduino.memory_type = qio_opi monitor_speed = 115200 upload_speed = 115200 -build_flags = +build_flags = -D BOARD_HAS_PSRAM -D NERMINER_T_QT=1 -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 -lib_ignore = +lib_ignore = HANSOLOminerv2 ;-------------------------------------------------------------------- @@ -643,7 +678,7 @@ monitor_speed = 115200 upload_speed = 921600 ;build_type = debug board_build.partitions = huge_app.csv -build_flags = +build_flags = ;-DDEBUG_MEMORY=1 -D ESP32_2432S028_2USB=1 -DTFT_INVERSION_ON @@ -670,7 +705,7 @@ build_flags = -DSPI_FREQUENCY=55000000 -DSPI_READ_FREQUENCY=20000000 -DSPI_TOUCH_FREQUENCY=2500000 -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 @@ -678,7 +713,7 @@ lib_deps = arduino-libraries/NTPClient@^3.2.1 bodmer/TFT_eSPI@^2.5.43 https://github.com/achillhasler/TFT_eTouch -lib_ignore = +lib_ignore = HANSOLOminerv2 ;-------------------------------------------------------------------- @@ -688,17 +723,17 @@ platform = espressif32@6.6.0 board = esp32dev framework = arduino monitor_speed = 115200 -monitor_filters = +monitor_filters = esp32_exception_decoder - time + time colorize ;debug upload_speed = 921600 ;build_type = debug board_build.partitions = huge_app.csv -build_flags = +build_flags = ;-DDEBUG_MEMORY=1 - -D ESP32_2432S028R=1 + -D ESP32_2432S028R=1 -DUSER_SETUP_LOADED=1 -DILI9341_2_DRIVER=1 -DTFT_WIDTH=240 @@ -722,8 +757,8 @@ build_flags = -DSPI_FREQUENCY=55000000 -DSPI_READ_FREQUENCY=20000000 -DSPI_TOUCH_FREQUENCY=2500000 - -lib_deps = + +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 @@ -731,7 +766,7 @@ lib_deps = arduino-libraries/NTPClient@^3.2.1 bodmer/TFT_eSPI@^2.5.43 https://github.com/achillhasler/TFT_eTouch -lib_ignore = +lib_ignore = HANSOLOminerv2 ;-------------------------------------------------------------------- @@ -768,7 +803,7 @@ lib_deps = arduino-libraries/NTPClient bodmer/TFT_eSPI @ ^2.5.31 https://github.com/achillhasler/TFT_eTouch -lib_ignore = +lib_ignore = HANSOLOminerv2 ;-------------------------------------------------------------------- @@ -780,16 +815,16 @@ framework = arduino monitor_speed = 115200 upload_speed = 115200 board_build.partitions = huge_app.csv -build_flags = +build_flags = -D NERDMINER_T_DISPLAY_V1=1 -D DEBUG_MINING=1 -lib_deps = +lib_deps = https://github.com/takkaO/OpenFontRender#v1.2 bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 https://github.com/arduino-libraries/NTPClient@^3.2.1 -lib_ignore = +lib_ignore = HANSOLOminerv2 ;-------------------------------------------------------------------- @@ -798,25 +833,25 @@ lib_ignore = platform = espressif32@6.6.0 board = m5stack-stamps3 framework = arduino -monitor_filters = +monitor_filters = esp32_exception_decoder time log2file monitor_speed = 115200 upload_speed = 115200 board_build.partitions = huge_app.csv -build_flags = +build_flags = -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 -D ESP32RGB=1 -D RGB_LED_PIN=21 -lib_deps = +lib_deps = bblanchon/ArduinoJson@^6.21.5 https://github.com/tzapu/WiFiManager.git#v2.0.17 mathertel/OneButton@^2.5.0 arduino-libraries/NTPClient@^3.2.1 fastled/FastLED@^3.6.0 -lib_ignore = +lib_ignore = TFT_eSPI SD rm67162 diff --git a/src/NerdMinerV2.ino.cpp b/src/NerdMinerV2.ino.cpp index 486e0ed..3b24b7a 100644 --- a/src/NerdMinerV2.ino.cpp +++ b/src/NerdMinerV2.ino.cpp @@ -14,6 +14,9 @@ #include "drivers/displays/display.h" #include "drivers/storage/SDCard.h" #include "timeconst.h" +#include "drivers/nerd-nos/bm1397.h" +#include "drivers/nerd-nos/serial.h" + #ifdef TOUCH_ENABLE #include "TouchHandler.h" @@ -40,7 +43,7 @@ extern monitor_data mMonitor; #ifdef SD_ID SDCard SDCrd = SDCard(SD_ID); -#else +#else SDCard SDCrd = SDCard(); #endif @@ -49,7 +52,13 @@ extern monitor_data mMonitor; unsigned long start = millis(); const char* ntpServer = "pool.ntp.org"; -//void runMonitor(void *name); +void printBufferHex(const char *prefix, const uint8_t* buf, size_t len) { + Serial.printf("%s: ", prefix); + for (size_t i = 0; i < len; i++) { + Serial.printf("%02X ", buf[i]); + } + Serial.println(); +} /********* INIT *****/ void setup() @@ -100,7 +109,7 @@ void setup() /******** INIT DISPLAY ************/ initDisplay(); - + /******** PRINT INIT SCREEN *****/ drawLoadingScreen(); delay(2*SECOND_MS); @@ -134,7 +143,19 @@ void setup() BaseType_t res2 = xTaskCreatePinnedToCore(runStratumWorker, "Stratum", 15000, (void*)name, 3, NULL,1); #endif - /******** CREATE MINER TASKS *****/ + #ifdef NERD_NOS + SERIAL_init(); + int chips = BM1397_init(200, 1); + Serial.printf("found bm1397: %d\n", chips); + //SERIAL_set_baud(BM1397_set_max_baud()); + + TaskHandle_t ASICTask = NULL; + xTaskCreate(runASIC, "Asic0", 6000, (void*)0, 1, &ASICTask); + + //esp_task_wdt_add(ASICTask); + + #else + /******** CREATE MINER TASKS *****/ //for (size_t i = 0; i < THREADS; i++) { // char *name = (char*) malloc(32); // sprintf(name, "(%d)", i); @@ -144,14 +165,16 @@ void setup() TaskHandle_t minerTask1, minerTask2 = NULL; xTaskCreate(runMiner, "Miner0", 6000, (void*)0, 1, &minerTask1); xTaskCreate(runMiner, "Miner1", 6000, (void*)1, 1, &minerTask2); - + esp_task_wdt_add(minerTask1); esp_task_wdt_add(minerTask2); - +#endif /******** MONITOR SETUP *****/ setup_monitor(); } + + void app_error_fault_handler(void *arg) { // Get stack errors char *stack = (char *)arg; diff --git a/src/ShaTests/nerdSHA256plus.h b/src/ShaTests/nerdSHA256plus.h index d3e1774..3d826ac 100644 --- a/src/ShaTests/nerdSHA256plus.h +++ b/src/ShaTests/nerdSHA256plus.h @@ -5,7 +5,7 @@ * Description: -* NerdSha256plus is a custom C implementation of sha256d based on Blockstream Jade +* NerdSha256plus is a custom C implementation of sha256d based on Blockstream Jade code https://github.com/Blockstream/Jade The folowing file can be used on any ESP32 implementation using both cores @@ -19,7 +19,7 @@ #include -struct nerdSHA256_context { +struct __attribute__((__packed__)) nerdSHA256_context { uint8_t buffer[64]; uint32_t digest[8]; }; diff --git a/src/drivers/devices/device.h b/src/drivers/devices/device.h index 5f82fef..d491fbf 100644 --- a/src/drivers/devices/device.h +++ b/src/drivers/devices/device.h @@ -3,6 +3,8 @@ #if defined(NERDMINERV2) #include "nerdMinerV2.h" +#elif defined(NERD_NOS) +#include "nerdnos.h" #elif defined(M5STICK_C) #include "M5Stick-C.h" #elif defined(DEVKITV1) diff --git a/src/drivers/devices/nerdnos.h b/src/drivers/devices/nerdnos.h new file mode 100644 index 0000000..10e1ce7 --- /dev/null +++ b/src/drivers/devices/nerdnos.h @@ -0,0 +1,16 @@ +#ifndef _NERD_MINER_V2_H +#define _NERD_MINER_V2_H + +#define PIN_BUTTON_1 0 +#define PIN_BUTTON_2 14 +#define PIN_ENABLE5V 15 + +#define T_DISPLAY + +#define NERD_NOS_GPIO_TX (GPIO_NUM_17) +#define NERD_NOS_GPIO_RX (GPIO_NUM_18) +#define NERD_NOS_GPIO_RST (GPIO_NUM_12) +#define NERD_NOS_GPIO_PEN (GPIO_NUM_13) +#define NERD_NOS_GPIO_TMP (GPIO_NUM_2) + +#endif \ No newline at end of file diff --git a/src/drivers/nerd-nos/bm1397.cpp b/src/drivers/nerd-nos/bm1397.cpp new file mode 100644 index 0000000..fab8912 --- /dev/null +++ b/src/drivers/nerd-nos/bm1397.cpp @@ -0,0 +1,448 @@ +#include +#include + +#include "bm1397.h" +#include "serial.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "../devices/device.h" +#include "crc.h" + +#define TYPE_JOB 0x20 +#define TYPE_CMD 0x40 + +#define GROUP_SINGLE 0x00 +#define GROUP_ALL 0x10 + +#define CMD_JOB 0x01 + +#define CMD_SETADDRESS 0x00 +#define CMD_WRITE 0x01 +#define CMD_READ 0x02 +#define CMD_INACTIVE 0x03 + +#define RESPONSE_CMD 0x00 +#define RESPONSE_JOB 0x80 + +#define SLEEP_TIME 20 +#define FREQ_MULT 25.0 + +#define CLOCK_ORDER_CONTROL_0 0x80 +#define CLOCK_ORDER_CONTROL_1 0x84 +#define ORDERED_CLOCK_ENABLE 0x20 +#define CORE_REGISTER_CONTROL 0x3C +#define PLL3_PARAMETER 0x68 +#define FAST_UART_CONFIGURATION 0x28 +#define TICKET_MASK 0x14 +#define MISC_CONTROL 0x18 + +typedef struct __attribute__((__packed__)) +{ + uint8_t preamble[2]; + uint32_t nonce; + uint8_t midstate_num; + uint8_t job_id; + uint8_t crc; +} asic_result; + +static const char *TAG = "bm1397Module"; + +static uint8_t asic_response_buffer[CHUNK_SIZE]; +static uint32_t prev_nonce = 0; +static task_result result; + +uint32_t increment_bitmask(const uint32_t value, const uint32_t mask); + +/// @brief +/// @param ftdi +/// @param header +/// @param data +/// @param len +static void _send_BM1397(uint8_t header, uint8_t *data, uint8_t data_len, bool debug) +{ + packet_type_t packet_type = (header & TYPE_JOB) ? JOB_PACKET : CMD_PACKET; + uint8_t total_length = (packet_type == JOB_PACKET) ? (data_len + 6) : (data_len + 5); + + // allocate memory for buffer + unsigned char *buf = (unsigned char *)malloc(total_length); + + // add the preamble + buf[0] = 0x55; + buf[1] = 0xAA; + + // add the header field + buf[2] = header; + + // add the length field + buf[3] = (packet_type == JOB_PACKET) ? (data_len + 4) : (data_len + 3); + + // add the data + memcpy(buf + 4, data, data_len); + + // add the correct crc type + if (packet_type == JOB_PACKET) + { + uint16_t crc16_total = crc16_false(buf + 2, data_len + 2); + buf[4 + data_len] = (crc16_total >> 8) & 0xFF; + buf[5 + data_len] = crc16_total & 0xFF; + } + else + { + buf[4 + data_len] = crc5(buf + 2, data_len + 2); + } + + // send serial data + SERIAL_send(buf, total_length, debug); + + free(buf); +} + +static void _send_read_address(void) +{ + unsigned char read_address[2] = {0x00, 0x00}; + // send serial data + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_READ), read_address, 2, BM1937_SERIALTX_DEBUG); +} + +static void _send_chain_inactive(void) +{ + + unsigned char read_address[2] = {0x00, 0x00}; + // send serial data + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_INACTIVE), read_address, 2, BM1937_SERIALTX_DEBUG); +} + +static void _set_chip_address(uint8_t chipAddr) +{ + + unsigned char read_address[2] = {chipAddr, 0x00}; + // send serial data + _send_BM1397((TYPE_CMD | GROUP_SINGLE | CMD_SETADDRESS), read_address, 2, BM1937_SERIALTX_DEBUG); +} + +void BM1397_send_hash_frequency(float frequency) +{ + unsigned char prefreqall[] = {0x00, 0x70, 0x0F, 0x0F, 0x0F, 0x00}; + + // default 200Mhz if it fails + unsigned char freqbufall[] = {0x00, 0x08, 0x40, 0xF0, 0x02, 0x35}; + + float deffreq = 200.0; + + float fa, fb, fc1, fc2, newf; + float f1, basef, famax = 0xf0, famin = 0x10; + uint16_t c; + int i; + + // allow a frequency 'power down' + if (frequency == 0) + { + basef = fa = 0; + fb = fc1 = fc2 = 1; + } + else + { + f1 = frequency; + fb = 2; fc1 = 1; fc2 = 5; // initial multiplier of 10 + if (f1 >= 500) + { + // halv down to '250-400' + fb = 1; + } + else if (f1 <= 150) + { + // triple up to '300-450' + fc1 = 3; + } + else if (f1 <= 250) + { + // double up to '300-500' + fc1 = 2; + } + // else f1 is 250-500 + + // f1 * fb * fc1 * fc2 is between 2500 and 5000 + // - so round up to the next 25 (freq_mult) + basef = FREQ_MULT * ceil(f1 * fb * fc1 * fc2 / FREQ_MULT); + + // fa should be between 100 (0x64) and 200 (0xC8) + fa = basef / FREQ_MULT; + } + + // code failure ... basef isn't 400 to 6000 + if (frequency != 0 && (fa < famin || fa > famax)) + { + newf = deffreq; + } + else + { + freqbufall[3] = (int)fa; + freqbufall[4] = (int)fb; + // fc1, fc2 'should' already be 1..15 + freqbufall[5] = (((int)fc1 & 0xf) << 4) + ((int)fc2 & 0xf); + + newf = basef / ((float)fb * (float)fc1 * (float)fc2); + } + + for (i = 0; i < 2; i++) + { + vTaskDelay(10 / portTICK_PERIOD_MS); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), prefreqall, 6, BM1937_SERIALTX_DEBUG); + } + for (i = 0; i < 2; i++) + { + vTaskDelay(10 / portTICK_PERIOD_MS); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), freqbufall, 6, BM1937_SERIALTX_DEBUG); + } + + vTaskDelay(10 / portTICK_PERIOD_MS); + + ESP_LOGI(TAG, "Setting Frequency to %.2fMHz (%.2f)", frequency, newf); +} + + +static uint8_t _send_init(uint64_t frequency, uint16_t asic_count) +{ + // send the init command + _send_read_address(); + + int chip_counter = 0; + while (true) { + int received = SERIAL_rx(asic_response_buffer, 11, 1000); + if (received > 0) { + ESP_LOG_BUFFER_HEX(TAG, asic_response_buffer, received); + chip_counter++; + } else { + break; + } + } + ESP_LOGI(TAG, "%i chip(s) detected on the chain, expected %i", chip_counter, asic_count); + + // send serial data + vTaskDelay(SLEEP_TIME / portTICK_PERIOD_MS); + _send_chain_inactive(); + + // split the chip address space evenly + for (uint8_t i = 0; i < asic_count; i++) { + _set_chip_address(i * (256 / asic_count)); + } + + unsigned char init[6] = {0x00, CLOCK_ORDER_CONTROL_0, 0x00, 0x00, 0x00, 0x00}; // init1 - clock_order_control0 + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init, 6, BM1937_SERIALTX_DEBUG); + + unsigned char init2[6] = {0x00, CLOCK_ORDER_CONTROL_1, 0x00, 0x00, 0x00, 0x00}; // init2 - clock_order_control1 + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init2, 6, BM1937_SERIALTX_DEBUG); + + unsigned char init3[9] = {0x00, ORDERED_CLOCK_ENABLE, 0x00, 0x00, 0x00, 0x01}; // init3 - ordered_clock_enable + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init3, 6, BM1937_SERIALTX_DEBUG); + + unsigned char init4[9] = {0x00, CORE_REGISTER_CONTROL, 0x80, 0x00, 0x80, 0x74}; // init4 - init_4_? + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init4, 6, BM1937_SERIALTX_DEBUG); + + BM1397_set_job_difficulty_mask(BM1397_INITIAL_DIFFICULTY); + + unsigned char init5[9] = {0x00, PLL3_PARAMETER, 0xC0, 0x70, 0x01, 0x11}; // init5 - pll3_parameter + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init5, 6, BM1937_SERIALTX_DEBUG); + + unsigned char init6[9] = {0x00, FAST_UART_CONFIGURATION, 0x06, 0x00, 0x00, 0x0F}; // init6 - fast_uart_configuration + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init6, 6, BM1937_SERIALTX_DEBUG); + + BM1397_set_default_baud(); + + BM1397_send_hash_frequency(frequency); + + return chip_counter; +} + +// reset the BM1397 via the RTS line +static void _reset(void) +{ + gpio_set_level(NERD_NOS_GPIO_RST, 1); + + // delay for 100ms + vTaskDelay(100 / portTICK_PERIOD_MS); + + // set the gpio pin high + gpio_set_level(NERD_NOS_GPIO_RST, 0); + + // delay for 100ms + vTaskDelay(100 / portTICK_PERIOD_MS); +} + +uint8_t BM1397_init(uint64_t frequency, uint16_t asic_count) +{ + ESP_LOGI(TAG, "Initializing BM1397"); + + memset(asic_response_buffer, 0, sizeof(asic_response_buffer)); + + gpio_set_direction(NERD_NOS_GPIO_PEN, GPIO_MODE_OUTPUT); + gpio_set_level(NERD_NOS_GPIO_PEN, 1); + + //esp_rom_gpio_pad_select_gpio(BM1397_RST_PIN); + gpio_pad_select_gpio(NERD_NOS_GPIO_RST); + gpio_set_direction(NERD_NOS_GPIO_RST, GPIO_MODE_OUTPUT); + + // reset the bm1397 + _reset(); + + return _send_init(frequency, asic_count); +} + +// Baud formula = 25M/((denominator+1)*8) +// The denominator is 5 bits found in the misc_control (bits 9-13) +int BM1397_set_default_baud(void) +{ + // default divider of 26 (11010) for 115,749 + unsigned char baudrate[9] = {0x00, MISC_CONTROL, 0x00, 0x00, 0b01111010, 0b00110001}; // baudrate - misc_control + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), baudrate, 6, BM1937_SERIALTX_DEBUG); + return 115749; +} + +int BM1397_set_max_baud(void) +{ + // divider of 0 for 3,125,000 + ESP_LOGI(TAG, "Setting max baud of 3125000"); + unsigned char baudrate[9] = {0x00, MISC_CONTROL, 0x00, 0x00, 0b01100000, 0b00110001}; + ; // baudrate - misc_control + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), baudrate, 6, BM1937_SERIALTX_DEBUG); + return 3125000; +} + +void BM1397_set_job_difficulty_mask(int difficulty) +{ + + // Default mask of 256 diff + unsigned char job_difficulty_mask[9] = {0x00, TICKET_MASK, 0b00000000, 0b00000000, 0b00000000, 0b11111111}; + + // The mask must be a power of 2 so there are no holes + // Correct: {0b00000000, 0b00000000, 0b11111111, 0b11111111} + // Incorrect: {0b00000000, 0b00000000, 0b11100111, 0b11111111} + difficulty = _largest_power_of_two(difficulty) - 1; // (difficulty - 1) if it is a pow 2 then step down to second largest for more hashrate sampling + + // convert difficulty into char array + // Ex: 256 = {0b00000000, 0b00000000, 0b00000000, 0b11111111}, {0x00, 0x00, 0x00, 0xff} + // Ex: 512 = {0b00000000, 0b00000000, 0b00000001, 0b11111111}, {0x00, 0x00, 0x01, 0xff} + for (int i = 0; i < 4; i++) + { + char value = (difficulty >> (8 * i)) & 0xFF; + // The char is read in backwards to the register so we need to reverse them + // So a mask of 512 looks like 0b00000000 00000000 00000001 1111111 + // and not 0b00000000 00000000 10000000 1111111 + + job_difficulty_mask[5 - i] = _reverse_bits(value); + } + + ESP_LOGI(TAG, "Setting job ASIC mask to %d", difficulty); + + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), job_difficulty_mask, 6, BM1937_SERIALTX_DEBUG); +} + +static uint8_t id = 0; + +uint8_t BM1397_send_work(bm_job_t *next_bm_job) +{ + job_packet job; + // max job number is 128 + // there is still some really weird logic with the job id bits for the asic to sort out + // so we have it limited to 128 and it has to increment by 4 + id = (id + 4) % 128; + + job.job_id = id; + job.num_midstates = next_bm_job->num_midstates; + memcpy(&job.starting_nonce, &next_bm_job->starting_nonce, 4); + memcpy(&job.nbits, &next_bm_job->target, 4); + memcpy(&job.ntime, &next_bm_job->ntime, 4); + memcpy(&job.merkle4, next_bm_job->merkle_root + 28, 4); + memcpy(job.midstate, next_bm_job->midstate, 32); + + if (job.num_midstates == 4) + { + memcpy(job.midstate1, next_bm_job->midstate1, 32); + memcpy(job.midstate2, next_bm_job->midstate2, 32); + memcpy(job.midstate3, next_bm_job->midstate3, 32); + } + + _send_BM1397((TYPE_JOB | GROUP_SINGLE | CMD_WRITE), (uint8_t*) &job, sizeof(job_packet), BM1397_DEBUG_WORK); + + return id; +} + +asic_result *BM1397_receive_work(uint16_t timeout) +{ + + // wait for a response, wait time is pretty arbitrary + int received = SERIAL_rx(asic_response_buffer, 9, timeout); + + if (received < 0) + { + ESP_LOGI(TAG, "Error in serial RX"); + return NULL; + } + else if (received == 0) + { + // Didn't find a solution, restart and try again + return NULL; + } + + if (received != 9 || asic_response_buffer[0] != 0xAA || asic_response_buffer[1] != 0x55) + { + ESP_LOGI(TAG, "Serial RX invalid %i", received); + ESP_LOG_BUFFER_HEX(TAG, asic_response_buffer, received); + return NULL; + } + + return (asic_result *)asic_response_buffer; +} + +task_result *BM1397_proccess_work(bm_job_t *job, uint16_t timeout) +{ + + asic_result *asic_result = BM1397_receive_work(timeout); + + if (asic_result == NULL) + { + ESP_LOGI(TAG, "return null"); + return NULL; + } + + uint8_t nonce_found = 0; + uint32_t first_nonce = 0; + + uint8_t rx_job_id = asic_result->job_id & 0xfc; + uint8_t rx_midstate_index = asic_result->job_id & 0x03; + + uint32_t rolled_version = job->version; + for (int i = 0; i < rx_midstate_index; i++) + { + rolled_version = increment_bitmask(rolled_version, 0x1fffe000); + } + + // ASIC may return the same nonce multiple times + // or one that was already found + // most of the time it behavies however + if (nonce_found == 0) + { + first_nonce = asic_result->nonce; + nonce_found = 1; + } + else if (asic_result->nonce == first_nonce) + { + // stop if we've already seen this nonce + return NULL; + } + + if (asic_result->nonce == prev_nonce) + { + return NULL; + } + else + { + prev_nonce = asic_result->nonce; + } + + result.job_id = rx_job_id; + result.nonce = asic_result->nonce; + result.rolled_version = rolled_version; + + return &result; +} diff --git a/src/drivers/nerd-nos/bm1397.h b/src/drivers/nerd-nos/bm1397.h new file mode 100644 index 0000000..7c638c9 --- /dev/null +++ b/src/drivers/nerd-nos/bm1397.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include "common.h" +#include "mining.h" +#include "crc.h" + +#define CRC5_MASK 0x1F +#define BM1397_INITIAL_DIFFICULTY 64 + +#define BM1937_SERIALTX_DEBUG false +#define BM1937_SERIALRX_DEBUG true //false +#define BM1397_DEBUG_WORK false //causes insane amount of debug output + + +typedef struct +{ + float frequency; +} bm1397Module; + +typedef enum +{ + JOB_PACKET = 0, + CMD_PACKET = 1, +} packet_type_t; + +typedef enum +{ + JOB_RESP = 0, + CMD_RESP = 1, +} response_type_t; + +typedef struct __attribute__((__packed__)) +{ + uint8_t job_id; + uint8_t num_midstates; + uint8_t starting_nonce[4]; + uint8_t nbits[4]; + uint8_t ntime[4]; + uint8_t merkle4[4]; + uint8_t midstate[32]; + uint8_t midstate1[32]; + uint8_t midstate2[32]; + uint8_t midstate3[32]; +} job_packet; + +uint8_t BM1397_init(uint64_t frequency, uint16_t asic_count); + +uint8_t BM1397_send_work(bm_job_t * next_bm_job); +void BM1397_set_job_difficulty_mask(int); +int BM1397_set_max_baud(void); +int BM1397_set_default_baud(void); +void BM1397_send_hash_frequency(float frequency); +task_result *BM1397_proccess_work(bm_job_t *job, uint16_t timeout); + diff --git a/src/drivers/nerd-nos/common.h b/src/drivers/nerd-nos/common.h new file mode 100644 index 0000000..6b98342 --- /dev/null +++ b/src/drivers/nerd-nos/common.h @@ -0,0 +1,41 @@ +#ifndef COMMON_H_ +#define COMMON_H_ + +#include + +typedef struct __attribute__((__packed__)) +{ + uint8_t job_id; + uint32_t nonce; + uint32_t rolled_version; +} task_result; + +static unsigned char _reverse_bits(unsigned char num) +{ + unsigned char reversed = 0; + int i; + + for (i = 0; i < 8; i++) { + reversed <<= 1; // Left shift the reversed variable by 1 + reversed |= num & 1; // Use bitwise OR to set the rightmost bit of reversed to the current bit of num + num >>= 1; // Right shift num by 1 to get the next bit + } + + return reversed; +} + +static int _largest_power_of_two(int num) +{ + int power = 0; + + while (num > 1) { + num = num >> 1; + power++; + } + + return 1 << power; +} + +void printBufferHex(const char *prefix, const uint8_t* buf, size_t len); + +#endif \ No newline at end of file diff --git a/src/drivers/nerd-nos/crc.cpp b/src/drivers/nerd-nos/crc.cpp new file mode 100644 index 0000000..5fc9166 --- /dev/null +++ b/src/drivers/nerd-nos/crc.cpp @@ -0,0 +1,114 @@ +#include +#include +#include +#include "crc.h" +#include "bm1397.h" + +/* compute crc5 over given number of bytes */ +// adapted from https://mightydevices.com/index.php/2018/02/reverse-engineering-antminer-s1/ +uint8_t crc5(uint8_t *buffer, uint16_t len) +{ + uint8_t i, j, k, index = 0; + uint8_t crc = CRC5_MASK; + /* registers */ + uint8_t crcin[5] = {1, 1, 1, 1, 1}; + uint8_t crcout[5] = {1, 1, 1, 1, 1}; + uint8_t din = 0; + + len *= 8; + + /* push data bits */ + for (j = 0x80, k = 0, i = 0; i < len; i++) + { + /* input bit */ + din = (buffer[index] & j) != 0; + /* shift register */ + crcout[0] = crcin[4] ^ din; + crcout[1] = crcin[0]; + crcout[2] = crcin[1] ^ crcin[4] ^ din; + crcout[3] = crcin[2]; + crcout[4] = crcin[3]; + /* next bit */ + j >>= 1, k++; + /* next byte */ + if (k == 8) + j = 0x80, k = 0, index++; + /* apply new shift register value */ + memcpy(crcin, crcout, 5); + // crcin = crcout[0]; + } + + crc = 0; + /* extract bitmask from register */ + if (crcin[4]) + crc |= 0x10; + if (crcin[3]) + crc |= 0x08; + if (crcin[2]) + crc |= 0x04; + if (crcin[1]) + crc |= 0x02; + if (crcin[0]) + crc |= 0x01; + + return crc; +} + +// kindly provided by cgminer +unsigned int crc16_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0}; + +/* CRC-16/CCITT */ +uint16_t crc16(uint8_t *buffer, uint16_t len) +{ + uint16_t crc; + + crc = 0; + while (len-- > 0) + crc = crc16_table[((crc >> 8) ^ (*buffer++)) & 0xFF] ^ (crc << 8); + + return crc; +} + +/* CRC-16/CCITT-FALSE */ +uint16_t crc16_false(uint8_t *buffer, uint16_t len) +{ + uint16_t crc; + + crc = 0xffff; + while (len-- > 0) + crc = crc16_table[((crc >> 8) ^ (*buffer++)) & 0xFF] ^ (crc << 8); + + return crc; +} diff --git a/src/drivers/nerd-nos/crc.h b/src/drivers/nerd-nos/crc.h new file mode 100644 index 0000000..1f4b1df --- /dev/null +++ b/src/drivers/nerd-nos/crc.h @@ -0,0 +1,18 @@ +#ifndef CRC_H_ +#define CRC_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +uint8_t crc5(uint8_t *buffer, uint16_t len); +uint16_t crc16(uint8_t *buffer, uint16_t len); +uint16_t crc16_false(uint8_t *buffer, uint16_t len); + +#ifdef __cplusplus +} +#endif + +#endif // CRC_H_ \ No newline at end of file diff --git a/src/drivers/nerd-nos/mining.cpp b/src/drivers/nerd-nos/mining.cpp new file mode 100644 index 0000000..5072795 --- /dev/null +++ b/src/drivers/nerd-nos/mining.cpp @@ -0,0 +1,168 @@ +#include +#include + +#include +#include +#include +#include + +#include "bm1397.h" +#include "mining.h" +#include "utils.h" +#include "stratum.h" +#include "mbedtls/sha256.h" + +///////cgminer nonce testing +/* truediffone == 0x00000000FFFF0000000000000000000000000000000000000000000000000000 + */ +static const double truediffone = 26959535291011309493156476344723991336010898738574164086137773096960.0; + +/* testing a nonce and return the diff - 0 means invalid */ +double asic_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const uint32_t rolled_version) +{ + double d64, s64, ds; + unsigned char header[80]; + + // // TODO: use the midstate hash instead of hashing the whole header + // uint32_t rolled_version = job->version; + // for (int i = 0; i < midstate_index; i++) { + // rolled_version = increment_bitmask(rolled_version, job->version_mask); + // } + + // copy data from job to header + memcpy(header, &rolled_version, 4); + memcpy(header + 4, job->prev_block_hash, 32); + memcpy(header + 36, job->merkle_root, 32); + memcpy(header + 68, &job->ntime, 4); + memcpy(header + 72, &job->target, 4); + memcpy(header + 76, &nonce, 4); + + unsigned char hash_buffer[32]; + unsigned char hash_result[32]; + + // double hash the header + mbedtls_sha256(header, 80, hash_buffer, 0); + mbedtls_sha256(hash_buffer, 32, hash_result, 0); + + d64 = truediffone; + s64 = le256todouble(hash_result); + ds = d64 / s64; + + return ds; +} + + +uint32_t increment_bitmask(const uint32_t value, const uint32_t mask) +{ + // if mask is zero, just return the original value + if (mask == 0) + return value; + + uint32_t carry = (value & mask) + (mask & -mask); // increment the least significant bit of the mask + uint32_t overflow = carry & ~mask; // find overflowed bits that are not in the mask + uint32_t new_value = (value & ~mask) | (carry & mask); // set bits according to the mask + + // Handle carry propagation + if (overflow > 0) + { + uint32_t carry_mask = (overflow << 1); // shift left to get the mask where carry should be propagated + new_value = increment_bitmask(new_value, carry_mask); // recursively handle carry propagation + } + + return new_value; +} + + +void calculate_merkle_root_hash(const char *coinbase_tx, mining_job* job, char merkle_root_hash[65]) +{ + size_t coinbase_tx_bin_len = strlen(coinbase_tx) / 2; + uint8_t coinbase_tx_bin[coinbase_tx_bin_len]; + hex2bin(coinbase_tx, coinbase_tx_bin, coinbase_tx_bin_len); + + uint8_t both_merkles[64]; + uint8_t new_root[32]; + double_sha256_bin(coinbase_tx_bin, coinbase_tx_bin_len, new_root); + memcpy(both_merkles, new_root, 32); + + for (size_t i = 0; i < job->merkle_branch.size(); i++) { + hex2bin((const char*) job->merkle_branch[i], &both_merkles[32], 32); + double_sha256_bin(both_merkles, 64, new_root); + memcpy(both_merkles, new_root, 32); + } + bin2hex(both_merkles, 32, merkle_root_hash, 65); +} + +// take a mining_notify struct with ascii hex strings and convert it to a bm_job struct +void construct_bm_job(mining_job *job, const char *merkle_root, uint32_t version_mask, bm_job_t *new_job) +{ + new_job->version = strtoul(job->version.c_str(), NULL, 16); + new_job->target = strtoul(job->nbits.c_str(), NULL, 16); + new_job->ntime = strtoul(job->ntime.c_str(), NULL, 16); + new_job->starting_nonce = 0; + + hex2bin(merkle_root, new_job->merkle_root, 32); + + // hex2bin(merkle_root, new_job.merkle_root_be, 32); + swap_endian_words(merkle_root, new_job->merkle_root_be); + reverse_bytes(new_job->merkle_root_be, 32); + + swap_endian_words(job->prev_block_hash.c_str(), new_job->prev_block_hash); + + hex2bin(job->prev_block_hash.c_str(), new_job->prev_block_hash_be, 32); + reverse_bytes(new_job->prev_block_hash_be, 32); + + ////make the midstate hash + uint8_t midstate_data[64]; + + // copy 68 bytes header data into midstate (and deal with endianess) + memcpy(midstate_data, &new_job->version, 4); // copy version + memcpy(midstate_data + 4, new_job->prev_block_hash, 32); // copy prev_block_hash + memcpy(midstate_data + 36, new_job->merkle_root, 28); // copy merkle_root + + midstate_sha256_bin(midstate_data, 64, new_job->midstate); // make the midstate hash + reverse_bytes(new_job->midstate, 32); // reverse the midstate bytes for the BM job packet + + uint32_t rolled_version = increment_bitmask(new_job->version, version_mask); + memcpy(midstate_data, &rolled_version, 4); + midstate_sha256_bin(midstate_data, 64, new_job->midstate1); + reverse_bytes(new_job->midstate1, 32); + + rolled_version = increment_bitmask(rolled_version, version_mask); + memcpy(midstate_data, &rolled_version, 4); + midstate_sha256_bin(midstate_data, 64, new_job->midstate2); + reverse_bytes(new_job->midstate2, 32); + + rolled_version = increment_bitmask(rolled_version, version_mask); + memcpy(midstate_data, &rolled_version, 4); + midstate_sha256_bin(midstate_data, 64, new_job->midstate3); + reverse_bytes(new_job->midstate3, 32); + new_job->num_midstates = 4; +} + +void asic_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_job, uint32_t extranonce_2) { + char extranonce_2_str[mWorker->extranonce2_size * 2 + 1]; // +1 zero termination + snprintf(extranonce_2_str, sizeof(extranonce_2_str), "%0*lx", (int) mWorker->extranonce2_size * 2, extranonce_2); + + // generate coinbase tx + String coinbase_tx = job->coinb1 + mWorker->extranonce1 + extranonce_2_str + job->coinb2; + + // calculate merkle root + char merkle_root[65]; + calculate_merkle_root_hash(coinbase_tx.c_str(), job, merkle_root); + + //Serial.printf("asic merkle root: %s\n", merkle_root); + // we need malloc because we will save it in the job array + construct_bm_job(job, merkle_root, 0x1fffe000, next_job); + + next_job->jobid = strdup(job->job_id.c_str()); + next_job->extranonce2 = strdup(extranonce_2_str); + //next_job->pool_diff = stratum_difficulty; +} + +uint8_t asic_send_work(bm_job_t *next_bm_job) { + return BM1397_send_work(next_bm_job); +} + +task_result *asic_proccess_work(bm_job_t *job, uint16_t timeout) { + return BM1397_proccess_work(job, timeout); +} \ No newline at end of file diff --git a/src/drivers/nerd-nos/mining.h b/src/drivers/nerd-nos/mining.h new file mode 100644 index 0000000..f25ad71 --- /dev/null +++ b/src/drivers/nerd-nos/mining.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include "common.h" +//#include "stratum.h" + + +typedef struct +{ + uint32_t version; + uint32_t version_mask; + uint8_t prev_block_hash[32]; + uint8_t prev_block_hash_be[32]; + uint8_t merkle_root[32]; + uint8_t merkle_root_be[32]; + uint32_t ntime; + uint32_t target; // aka difficulty, aka nbits + uint32_t starting_nonce; + + uint8_t num_midstates; + uint8_t midstate[32]; + uint8_t midstate1[32]; + uint8_t midstate2[32]; + uint8_t midstate3[32]; + char *jobid; + char *extranonce2; +} bm_job_t; + + +uint8_t asic_send_work(bm_job_t *next_bm_job); +task_result *asic_proccess_work(bm_job_t *job, uint16_t timeout); + +double asic_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const uint32_t rolled_version); \ No newline at end of file diff --git a/src/drivers/nerd-nos/serial.cpp b/src/drivers/nerd-nos/serial.cpp new file mode 100644 index 0000000..70f48b9 --- /dev/null +++ b/src/drivers/nerd-nos/serial.cpp @@ -0,0 +1,90 @@ +#include +#include "driver/uart.h" +#include "../devices/device.h" + +#include "common.h" +#include "serial.h" + +#define BUF_SIZE (1024) + +void SERIAL_init() { + + Serial.println("Initializing serial"); + // Configure UART1 parameters + uart_config_t uart_config = { + .baud_rate = 115200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 122, + }; + // Configure UART1 parameters + uart_param_config(UART_NUM_1, &uart_config); + // Set UART1 pins(TX: IO17, RX: I018) + uart_set_pin(UART_NUM_1, NERD_NOS_GPIO_TX, NERD_NOS_GPIO_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + + // Install UART driver (we don't need an event queue here) + // tx buffer 0 so the tx time doesn't overlap with the job wait time + // by returning before the job is written + uart_driver_install(UART_NUM_1, BUF_SIZE * 2, BUF_SIZE * 2, 0, NULL, 0); +} + +void SERIAL_set_baud(int baud) +{ + ESP_LOGI(TAG, "Changing UART baud to %i", baud); + uart_set_baudrate(UART_NUM_1, baud); +} + +int SERIAL_send(uint8_t *data, int len, bool debug) +{ + // if (debug) + // { + // printf("->"); + // prettyHex((unsigned char *)data, len); + // printf("\n"); + // } + + if (debug) { + printBufferHex("TX", data, len); + } + return uart_write_bytes(UART_NUM_1, (const char *)data, len); +} + +/// @brief waits for a serial response from the device +/// @param buf buffer to read data into +/// @param buf number of ms to wait before timing out +/// @return number of bytes read, or -1 on error +int16_t SERIAL_rx(uint8_t *buf, uint16_t size, uint16_t timeout_ms) +{ + int16_t bytes_read = uart_read_bytes(UART_NUM_1, buf, size, timeout_ms / portTICK_PERIOD_MS); + // if (bytes_read > 0) { + // printf("rx: "); + // prettyHex((unsigned char*) buf, bytes_read); + // printf("\n"); + // } + if (bytes_read > 0) { + printBufferHex("RX", buf, bytes_read); + } + return bytes_read; +} + +void SERIAL_debug_rx(void) +{ + int ret; + uint8_t buf[CHUNK_SIZE]; + + ret = SERIAL_rx(buf, 100, 20); + if (ret < 0) + { + fprintf(stderr, "unable to read data\n"); + return; + } + + memset(buf, 0, 1024); +} + +void SERIAL_clear_buffer(void) +{ + uart_flush(UART_NUM_1); +} diff --git a/src/drivers/nerd-nos/serial.h b/src/drivers/nerd-nos/serial.h new file mode 100644 index 0000000..ae165c1 --- /dev/null +++ b/src/drivers/nerd-nos/serial.h @@ -0,0 +1,13 @@ +#ifndef SERIAL_H_ +#define SERIAL_H_ + +#define CHUNK_SIZE 1024 + +int SERIAL_send(uint8_t *, int, bool); +void SERIAL_init(void); +void SERIAL_debug_rx(void); +int16_t SERIAL_rx(uint8_t *, uint16_t, uint16_t); +void SERIAL_clear_buffer(void); +void SERIAL_set_baud(int baud); + +#endif /* SERIAL_H_ */ \ No newline at end of file diff --git a/src/drivers/nerd-nos/utils.cpp b/src/drivers/nerd-nos/utils.cpp new file mode 100644 index 0000000..3cf2fbf --- /dev/null +++ b/src/drivers/nerd-nos/utils.cpp @@ -0,0 +1,309 @@ +#include "utils.h" + +#include +#include + +#include "mbedtls/sha256.h" + +#ifndef bswap_16 +#define bswap_16(a) ((((uint16_t)(a) << 8) & 0xff00) | (((uint16_t)(a) >> 8) & 0xff)) +#endif + +#ifndef bswap_32 +#define bswap_32(a) ((((uint32_t)(a) << 24) & 0xff000000) | \ + (((uint32_t)(a) << 8) & 0xff0000) | \ + (((uint32_t)(a) >> 8) & 0xff00) | \ + (((uint32_t)(a) >> 24) & 0xff)) +#endif + +/* + * General byte order swapping functions. + */ +#define bswap16(x) __bswap16(x) +#define bswap32(x) __bswap32(x) +#define bswap64(x) __bswap64(x) + +// in the other utils.cpp^^ +uint32_t swab32(uint32_t v); +void swap_endian_words(const char *hex_words, uint8_t *output); +void reverse_bytes(uint8_t *data, size_t len); +double le256todouble(const void *target); + +/* +uint32_t swab32(uint32_t v) +{ + return bswap_32(v); +} +*/ +// takes 80 bytes and flips every 4 bytes +void flip80bytes(void *dest_p, const void *src_p) +{ + uint32_t *dest = (uint32_t*) dest_p; + const uint32_t *src = (const uint32_t*) src_p; + int i; + + for (i = 0; i < 20; i++) + dest[i] = swab32(src[i]); +} + +void flip64bytes(void *dest_p, const void *src_p) +{ + uint32_t *dest = (uint32_t*) dest_p; + const uint32_t *src = (const uint32_t*) src_p; + int i; + + for (i = 0; i < 16; i++) + dest[i] = swab32(src[i]); +} + +void flip32bytes(void *dest_p, const void *src_p) +{ + uint32_t *dest = (uint32_t*) dest_p; + const uint32_t *src = (const uint32_t*) src_p; + int i; + + for (i = 0; i < 8; i++) + dest[i] = swab32(src[i]); +} + +int hex2char(uint8_t x, char *c) +{ + if (x <= 9) + { + *c = x + '0'; + } + else if (x <= 15) + { + *c = x - 10 + 'a'; + } + else + { + return -1; + } + + return 0; +} + +size_t bin2hex(const uint8_t *buf, size_t buflen, char *hex, size_t hexlen) +{ + if ((hexlen + 1) < buflen * 2) + { + return 0; + } + + for (size_t i = 0; i < buflen; i++) + { + if (hex2char(buf[i] >> 4, &hex[2 * i]) < 0) + { + return 0; + } + if (hex2char(buf[i] & 0xf, &hex[2 * i + 1]) < 0) + { + return 0; + } + } + + hex[2 * buflen] = '\0'; + return 2 * buflen; +} + +uint8_t hex2val(char c) +{ + if (c >= '0' && c <= '9') + { + return c - '0'; + } + else if (c >= 'a' && c <= 'f') + { + return c - 'a' + 10; + } + else if (c >= 'A' && c <= 'F') + { + return c - 'A' + 10; + } + else + { + return 0; + } +} + +size_t hex2bin(const char *hex, uint8_t *bin, size_t bin_len) +{ + size_t len = 0; + + while (*hex && len < bin_len) + { + bin[len] = hex2val(*hex++) << 4; + + if (!*hex) + { + len++; + break; + } + + bin[len++] |= hex2val(*hex++); + } + + return len; +} + +void print_hex(const uint8_t *b, size_t len, + const size_t in_line, const char *prefix) +{ + size_t i = 0; + const uint8_t *end = b + len; + + if (prefix == NULL) + { + prefix = ""; + } + + printf("%s", prefix); + while (b < end) + { + if (++i > in_line) + { + printf("\n%s", prefix); + i = 1; + } + printf("%02X ", (uint8_t)*b++); + } + printf("\n"); + fflush(stdout); +} + +void double_sha256(const char *hex_string, char output_hash[65]) +{ + size_t bin_len = strlen(hex_string) / 2; + uint8_t bin[bin_len]; + hex2bin(hex_string, bin, bin_len); + + unsigned char first_hash_output[32], second_hash_output[32]; + + mbedtls_sha256(bin, bin_len, first_hash_output, 0); + mbedtls_sha256(first_hash_output, 32, second_hash_output, 0); + + bin2hex(second_hash_output, 32, output_hash, 65); +} + +void double_sha256_bin(const uint8_t *data, const size_t data_len, uint8_t digest[32]) +{ + uint8_t first_hash_output[32]; + + mbedtls_sha256(data, data_len, first_hash_output, 0); + mbedtls_sha256(first_hash_output, 32, digest, 0); +} + +void single_sha256_bin(const uint8_t *data, const size_t data_len, uint8_t *dest) +{ + // mbedtls_sha256(data, data_len, dest, 0); + + // Initialize SHA256 context + mbedtls_sha256_context sha256_ctx; + mbedtls_sha256_init(&sha256_ctx); + mbedtls_sha256_starts(&sha256_ctx, 0); + + // Compute first SHA256 hash of header + mbedtls_sha256_update(&sha256_ctx, data, 64); + unsigned char hash[32]; + mbedtls_sha256_finish(&sha256_ctx, hash); + + // Compute midstate from hash + memcpy(dest, hash, 32); +} + +void midstate_sha256_bin(const uint8_t *data, const size_t data_len, uint8_t *dest) +{ + mbedtls_sha256_context midstate; + + // Calculate midstate + mbedtls_sha256_init(&midstate); + mbedtls_sha256_starts(&midstate, 0); + mbedtls_sha256_update(&midstate, data, 64); + + // memcpy(dest, midstate.state, 32); + flip32bytes(dest, midstate.state); +} +/* +void swap_endian_words(const char *hex_words, uint8_t *output) +{ + size_t hex_length = strlen(hex_words); + if (hex_length % 8 != 0) + { + fprintf(stderr, "Must be 4-byte word aligned\n"); + exit(EXIT_FAILURE); + } + + size_t binary_length = hex_length / 2; + + for (size_t i = 0; i < binary_length; i += 4) + { + for (int j = 0; j < 4; j++) + { + unsigned int byte_val; + sscanf(hex_words + (i + j) * 2, "%2x", &byte_val); + output[i + (3 - j)] = byte_val; + } + } +} +*/ +/* +void reverse_bytes(uint8_t *data, size_t len) +{ + for (int i = 0; i < len / 2; ++i) + { + uint8_t temp = data[i]; + data[i] = data[len - 1 - i]; + data[len - 1 - i] = temp; + } +} +*/ +/* +// static const double truediffone = 26959535291011309493156476344723991336010898738574164086137773096960.0; +static const double bits192 = 6277101735386680763835789423207666416102355444464034512896.0; +static const double bits128 = 340282366920938463463374607431768211456.0; +static const double bits64 = 18446744073709551616.0; + +// Converts a little endian 256 bit value to a double +double le256todouble(const void *target) +{ + uint64_t *data64; + double dcut64; + + data64 = (uint64_t *)(target + 24); + dcut64 = *data64 * bits192; + + data64 = (uint64_t *)(target + 16); + dcut64 += *data64 * bits128; + + data64 = (uint64_t *)(target + 8); + dcut64 += *data64 * bits64; + + data64 = (uint64_t *)(target); + dcut64 += *data64; + + return dcut64; +} +*/ + + +void prettyHex(unsigned char *buf, int len) +{ + int i; + printf("["); + for (i = 0; i < len - 1; i++) + { + printf("%02X ", buf[i]); + } + printf("%02X]\n", buf[len - 1]); +} + +uint32_t flip32(uint32_t val) +{ + uint32_t ret = 0; + ret |= (val & 0xFF) << 24; + ret |= (val & 0xFF00) << 8; + ret |= (val & 0xFF0000) >> 8; + ret |= (val & 0xFF000000) >> 24; + return ret; +} \ No newline at end of file diff --git a/src/drivers/nerd-nos/utils.h b/src/drivers/nerd-nos/utils.h new file mode 100644 index 0000000..a1e6a20 --- /dev/null +++ b/src/drivers/nerd-nos/utils.h @@ -0,0 +1,35 @@ +#pragma one + +#include +#include + +int hex2char(uint8_t x, char *c); + +size_t bin2hex(const uint8_t *buf, size_t buflen, char *hex, size_t hexlen); + +uint8_t hex2val(char c); +void flip80bytes(void *dest_p, const void *src_p); +void flip32bytes(void *dest_p, const void *src_p); + +size_t hex2bin(const char *hex, uint8_t *bin, size_t bin_len); + +void print_hex(const uint8_t *b, size_t len, + const size_t in_line, const char *prefix); + +void double_sha256(const char *hex_string, char output_hash[65]); + +void double_sha256_bin(const uint8_t *data, const size_t data_len, uint8_t digest[32]); + +void single_sha256_bin(const uint8_t *data, const size_t data_len, uint8_t *dest); +void midstate_sha256_bin(const uint8_t *data, const size_t data_len, uint8_t *dest); + +void swap_endian_words(const char *hex, uint8_t *output); + +void reverse_bytes(uint8_t *data, size_t len); + +double le256todouble(const void *target); + +void prettyHex(unsigned char *buf, int len); + +uint32_t flip32(uint32_t val); + diff --git a/src/mining.cpp b/src/mining.cpp index 8e529a9..20be8f9 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -14,6 +14,11 @@ #include "drivers/displays/display.h" #include "drivers/storage/storage.h" +#ifdef NERD_NOS +#include "drivers/nerd-nos/mining.h" +#endif + + nvs_handle_t stat_handle; uint32_t templates = 0; @@ -35,7 +40,7 @@ extern TSettings Settings; IPAddress serverIP(1, 1, 1, 1); //Temporally save poolIPaddres -//Global work data +//Global work data static WiFiClient client; static miner_data mMiner; //Global miner data (Create a miner class TODO) mining_subscribe mWorker; @@ -49,15 +54,15 @@ int saveIntervalsSize = sizeof(saveIntervals)/sizeof(saveIntervals[0]); int currentIntervalIndex = 0; bool checkPoolConnection(void) { - + if (client.connected()) { return true; } - + isMinerSuscribed = false; - Serial.println("Client not connected, trying to connect..."); - + Serial.println("Client not connected, trying to connect..."); + //Resolve first time pool DNS and save IP if(serverIP == IPAddress(1,1,1,1)) { WiFi.hostByName(Settings.PoolAddress.c_str(), serverIP); @@ -76,14 +81,14 @@ bool checkPoolConnection(void) { return true; } -//Implements a socketKeepAlive function and +//Implements a socketKeepAlive function and //checks if pool is not sending any data to reconnect again. //Even connection could be alive, pool could stop sending new job NOTIFY unsigned long mStart0Hashrate = 0; -bool checkPoolInactivity(unsigned int keepAliveTime, unsigned long inactivityTime){ +bool checkPoolInactivity(unsigned int keepAliveTime, unsigned long inactivityTime){ unsigned long currentKHashes = (Mhashes*1000) + hashes/1000; - unsigned long elapsedKHs = currentKHashes - totalKHashes; + unsigned long elapsedKHs = currentKHashes - totalKHashes; // If no shares sent to pool // send something to pool to hold socket oppened @@ -100,7 +105,7 @@ bool checkPoolInactivity(unsigned int keepAliveTime, unsigned long inactivityTim if(elapsedKHs == 0){ //Check if hashrate is 0 during inactivityTIme - if(mStart0Hashrate == 0) mStart0Hashrate = millis(); + if(mStart0Hashrate == 0) mStart0Hashrate = millis(); if((millis()-mStart0Hashrate) > inactivityTime) { mStart0Hashrate=0; return true;} return false; } @@ -121,18 +126,18 @@ void runStratumWorker(void *name) { #endif // connect to pool - + double currentPoolDifficulty = DEFAULT_DIFFICULTY; while(true) { - + if(WiFi.status() != WL_CONNECTED){ // WiFi is disconnected, so reconnect now mMonitor.NerdStatus = NM_Connecting; WiFi.reconnect(); vTaskDelay(5000 / portTICK_PERIOD_MS); continue; - } + } if(!checkPoolConnection()){ //If server is not reachable add random delay for connection retries @@ -148,11 +153,11 @@ void runStratumWorker(void *name) { mWorker = init_mining_subscribe(); // STEP 1: Pool server connection (SUBSCRIBE) - if(!tx_mining_subscribe(client, mWorker)) { + if(!tx_mining_subscribe(client, mWorker)) { client.stop(); - continue; + continue; } - + strcpy(mWorker.wName, Settings.BtcWallet); strcpy(mWorker.wPass, Settings.PoolPassword); // STEP 2: Pool authorize work (Block Info) @@ -172,7 +177,7 @@ void runStratumWorker(void *name) { Serial.println(" Detected more than 2 min without data form stratum server. Closing socket and reopening..."); client.stop(); isMinerSuscribed=false; - continue; + continue; } //Read pending messages from pool @@ -208,9 +213,9 @@ void runStratumWorker(void *name) { } vTaskDelay(500 / portTICK_PERIOD_MS); //Small delay - + } - + } @@ -218,7 +223,7 @@ void runStratumWorker(void *name) { //This works only with one thread, TODO -> Class or miner_data for each thread - + void runMiner(void * task_id) { unsigned int miner_id = (uint32_t)task_id; @@ -245,7 +250,7 @@ void runMiner(void * task_id) { //Prepare Premining data nerdSHA256_context nerdMidstate; //NerdShaplus uint8_t hash[32]; - + //Calcular midstate nerd_mids(&nerdMidstate, mMiner.bytearray_blockheader); //NerdShaplus @@ -265,8 +270,8 @@ void runMiner(void * task_id) { header64 = mMiner.bytearray_blockheader + 64; else header64 = mMiner.bytearray_blockheader2 + 64; - - bool is16BitShare=true; + + bool is16BitShare=true; Serial.println(">>> STARTING TO HASH NONCES"); while(true) { if (miner_id == 0) @@ -281,7 +286,7 @@ void runMiner(void * task_id) { /*Serial.print("hash1: "); for (size_t i = 0; i < 32; i++) Serial.printf("%02x", hash[i]); - Serial.println(""); + Serial.println(""); Serial.print("hash2: "); for (size_t i = 0; i < 32; i++) Serial.printf("%02x", hash2[i]); @@ -302,7 +307,7 @@ void runMiner(void * task_id) { //Check target to submit //Difficulty of 1 > 0x00000000FFFF0000000000000000000000000000000000000000000000000000 //NM2 pool diff 1e-9 > Target = diff_1 / diff_pool > 0x00003B9ACA00....00 - //Swapping diff bytes little endian >>>>>>>>>>>>>>>> 0x0000DC59D300....00 + //Swapping diff bytes little endian >>>>>>>>>>>>>>>> 0x0000DC59D300....00 //if((hash[29] <= 0xDC) && (hash[28] <= 0x59)) //0x00003B9ACA00 > diff value for 1e-9 double diff_hash = diff_from_target(hash); @@ -327,9 +332,9 @@ void runMiner(void * task_id) { } #endif Serial.println(""); - mLastTXtoPool = millis(); + mLastTXtoPool = millis(); } - + // check if 32bit share if(hash[29] !=0 || hash[28] !=0) { // increment nonce @@ -367,6 +372,103 @@ void runMiner(void * task_id) { } } +void asic_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_job, uint32_t extranonce_2); + +void runASIC(void * task_id) { + Serial.printf("[MINER] Started runASIC Task!\n"); + + uint32_t extranonce_2 = 0; + bm_job_t asic_job; + while(1) { + // wait for new job + while(1) { + if (mMiner.newJob == true) { + break; + } + vTaskDelay(100 / portTICK_PERIOD_MS); //Small delay + } + + if(mMiner.newJob) { + mMiner.newJob = false; //Clear newJob flag + } + mMiner.inRun = true; //Set inRun flag + + Serial.println(">>> STARTING TO HASH NONCES"); + uint32_t startT = micros(); + + while (mMiner.inRun) { + mMonitor.NerdStatus = NM_hashing; + extranonce_2++; + + // create the next asic job + asic_create_job(&mWorker, &mJob, &asic_job, extranonce_2); + + // send the job and + uint8_t asic_job_id = asic_send_work(&asic_job); + + // wait 30ms for the response + task_result *result = asic_proccess_work(&asic_job, 30); + + if (!result) { + // we haven't received anything in time, so send a new job + continue; + } + + if (result->job_id != asic_job_id) { + // job id mismatch + Serial.printf("ID mismatch, expected %02x, got %02x\n", asic_job_id, result->job_id); + continue; + } + + // check the nonce difficulty + double diff_hash = asic_test_nonce_value( + &asic_job, + result->nonce, + result->rolled_version); + + Serial.print(" - Current diff share: "); Serial.println(diff_hash, 12); + Serial.print(" - Current pool diff : "); Serial.println(mMiner.poolDifficulty, 12); + + // update best diff + if (diff_hash > best_diff) + best_diff = diff_hash; + + if(diff_hash > mMiner.poolDifficulty) + { + tx_mining_submit_with_version(client, mWorker, &asic_job, extranonce_2, result->nonce, result->rolled_version); + Serial.println("valid share!"); +/* + Serial.print(" - Current diff share: "); Serial.println(diff_hash,12); + Serial.print(" - Current pool diff : "); Serial.println(mMiner.poolDifficulty,12); +*/ +/* + Serial.print(" - TX SHARE: "); + for (size_t i = 0; i < 32; i++) + Serial.printf("%02x", hash[i]); +*/ + #ifdef DEBUG_MINING + Serial.println(""); + Serial.print(" - Current nonce: "); Serial.println(nonce); + Serial.print(" - Current block header: "); + for (size_t i = 0; i < 80; i++) { + Serial.printf("%02x", mMiner.bytearray_blockheader[i]); + } + #endif + Serial.println(""); + mLastTXtoPool = millis(); + } + } + Serial.println ("MINER WORK ABORTED >> waiting new job"); + mMiner.inRun = false; + uint32_t duration = micros() - startT; +/* + if (esp_task_wdt_reset() == ESP_OK) + Serial.print(">>> Resetting watchdog timer"); +*/ + } + +} + #define DELAY 100 #define REDRAW_EVERY 10 @@ -455,7 +557,7 @@ void runMonitor(void *name) seconds_elapsed = 0; if(currentIntervalIndex < saveIntervalsSize - 1) currentIntervalIndex++; - } + } } animateCurrentScreen(frame); doLedStuff(frame); diff --git a/src/mining.h b/src/mining.h index a1adf14..c010572 100644 --- a/src/mining.h +++ b/src/mining.h @@ -6,7 +6,7 @@ #define MAX_NONCE_STEP 5000000U #define MAX_NONCE 25000000U #define TARGET_NONCE 471136297U -#define DEFAULT_DIFFICULTY 1e-4 +#define DEFAULT_DIFFICULTY 64 // 1e-4 TODO-NOS #define KEEPALIVE_TIME_ms 30000 #define POOLINACTIVITY_TIME_ms 60000 @@ -15,6 +15,7 @@ void runMonitor(void *name); void runStratumWorker(void *name); void runMiner(void *name); +void runASIC(void *name); String printLocalTime(void); void resetStat(); diff --git a/src/stratum.cpp b/src/stratum.cpp index 8237a44..d5bcabf 100644 --- a/src/stratum.cpp +++ b/src/stratum.cpp @@ -30,57 +30,61 @@ bool verifyPayload (String* line){ line->trim(); if(line->isEmpty()) return false; return true; - + } bool checkError(const StaticJsonDocument doc) { - + if (!doc.containsKey("error")) return false; - + if (doc["error"].size() == 0) return false; Serial.printf("ERROR: %d | reason: %s \n", (const int) doc["error"][0], (const char*) doc["error"][1]); - return true; + return true; } // STEP 1: Pool server connection (SUBSCRIBE) - // Docs: + // Docs: // - https://cs.braiins.com/stratum-v1/docs // - https://github.com/aeternity/protocol/blob/master/STRATUM.md#mining-subscribe bool tx_mining_subscribe(WiFiClient& client, mining_subscribe& mSubscribe) { char payload[BUFFER] = {0}; - + // Subscribe id = 1; //Initialize id messages + #ifdef NERD_NOS + sprintf(payload, "{\"id\": %u, \"method\": \"mining.subscribe\", \"params\": [\"NerdNOS/%s\"]}\n", id, CURRENT_VERSION); + #else #ifndef HAN sprintf(payload, "{\"id\": %u, \"method\": \"mining.subscribe\", \"params\": [\"NerdMinerV2/%s\"]}\n", id, CURRENT_VERSION); #else sprintf(payload, "{\"id\": %u, \"method\": \"mining.subscribe\", \"params\": [\"HAN_SOLOminer/%s\"]}\n", id, CURRENT_VERSION); #endif - + #endif + Serial.printf("[WORKER] ==> Mining subscribe\n"); Serial.print(" Sending : "); Serial.println(payload); client.print(payload); - + vTaskDelay(200 / portTICK_PERIOD_MS); //Small delay - + String line = client.readStringUntil('\n'); if(!parse_mining_subscribe(line, mSubscribe)) return false; - + Serial.print(" sub_details: "); Serial.println(mSubscribe.sub_details); Serial.print(" extranonce1: "); Serial.println(mSubscribe.extranonce1); Serial.print(" extranonce2_size: "); Serial.println(mSubscribe.extranonce2_size); - if((mSubscribe.extranonce1.length() == 0) ) { - Serial.printf("[WORKER] >>>>>>>>> Work aborted\n"); + if((mSubscribe.extranonce1.length() == 0) ) { + Serial.printf("[WORKER] >>>>>>>>> Work aborted\n"); Serial.printf("extranonce1 length: %u \n", mSubscribe.extranonce1.length()); doc.clear(); doc.garbageCollect(); - return false; + return false; } return true; } @@ -89,7 +93,7 @@ bool parse_mining_subscribe(String line, mining_subscribe& mSubscribe) { if(!verifyPayload(&line)) return false; Serial.print(" Receiving: "); Serial.println(line); - + DeserializationError error = deserializeJson(doc, line); if (error || checkError(doc)) return false; @@ -122,9 +126,9 @@ bool tx_mining_auth(WiFiClient& client, const char * user, const char * pass) // Authorize id = getNextId(id); - sprintf(payload, "{\"params\": [\"%s\", \"%s\"], \"id\": %u, \"method\": \"mining.authorize\"}\n", + sprintf(payload, "{\"params\": [\"%s\", \"%s\"], \"id\": %u, \"method\": \"mining.authorize\"}\n", user, pass, id); - + Serial.printf("[WORKER] ==> Autorize work\n"); Serial.print(" Sending : "); Serial.println(payload); client.print(payload); @@ -142,7 +146,7 @@ stratum_method parse_mining_method(String line) { if(!verifyPayload(&line)) return STRATUM_PARSE_ERROR; Serial.print(" Receiving: "); Serial.println(line); - + DeserializationError error = deserializeJson(doc, line); if (error || checkError(doc)) return STRATUM_PARSE_ERROR; @@ -169,7 +173,7 @@ bool parse_mining_notify(String line, mining_job& mJob) { Serial.println(" Parsing Method [MINING NOTIFY]"); if(!verifyPayload(&line)) return false; - + DeserializationError error = deserializeJson(doc, line); if (error) return false; @@ -198,7 +202,7 @@ bool parse_mining_notify(String line, mining_job& mJob) #endif //Check if parameters where correctly received if (checkError(doc)) { - Serial.printf("[WORKER] >>>>>>>>> Work aborted\n"); + Serial.printf("[WORKER] >>>>>>>>> Work aborted\n"); return false; } return true; @@ -226,11 +230,33 @@ bool tx_mining_submit(WiFiClient& client, mining_subscribe mWorker, mining_job m return true; } +bool tx_mining_submit_with_version(WiFiClient& client, mining_subscribe mWorker, const bm_job_t* asic_job, uint32_t extranonce2, unsigned long nonce, uint32_t version) +{ + char payload[BUFFER] = {0}; + + // Submit + id = getNextId(id); + sprintf(payload, "{\"id\": %u, \"method\": \"mining.submit\", \"params\": [\"%s\",\"%s\",\"%s\",\"%08lx\",\"%08lx\",\"%08lx\"]}\n", + id, + mWorker.wName, + asic_job->jobid, + asic_job->extranonce2, + asic_job->ntime, + nonce, + version ^ asic_job->version + ); + Serial.print(" Sending : "); Serial.print(payload); + client.print(payload); + //Serial.print(" Receiving: "); Serial.println(client.readStringUntil('\n')); + + return true; +} + bool parse_mining_set_difficulty(String line, double& difficulty) { Serial.println(" Parsing Method [SET DIFFICULTY]"); if(!verifyPayload(&line)) return false; - + DeserializationError error = deserializeJson(doc, line); if (error) return false; @@ -248,7 +274,7 @@ bool tx_suggest_difficulty(WiFiClient& client, double difficulty) id = getNextId(id); sprintf(payload, "{\"id\": %d, \"method\": \"mining.suggest_difficulty\", \"params\": [%.10g]}\n", id, difficulty); - + Serial.print(" Sending : "); Serial.print(payload); return client.print(payload); diff --git a/src/stratum.h b/src/stratum.h index d8110e7..4d5ffc3 100644 --- a/src/stratum.h +++ b/src/stratum.h @@ -6,6 +6,7 @@ #include #include #include +#include "drivers/nerd-nos/mining.h" #define MAX_MERKLE_BRANCHES 32 #define HASH_SIZE 32 @@ -61,8 +62,9 @@ bool parse_mining_notify(String line, mining_job& mJob); //Method Mining.submit bool tx_mining_submit(WiFiClient& client, mining_subscribe mWorker, mining_job mJob, unsigned long nonce); +bool tx_mining_submit_with_version(WiFiClient& client, mining_subscribe mWorker, const bm_job_t* asic_job, uint32_t extranonce2, unsigned long nonce, uint32_t version); -//Difficulty Methods +//Difficulty Methods bool tx_suggest_difficulty(WiFiClient& client, double difficulty); bool parse_mining_set_difficulty(String line, double& difficulty); From 0931960ac10af32b3159cb8ca89ce57de6c2a90f Mon Sep 17 00:00:00 2001 From: shufps Date: Mon, 9 Sep 2024 07:38:02 +0200 Subject: [PATCH 02/18] proper job management --- src/drivers/nerd-nos/bm1397.cpp | 15 +++++-------- src/drivers/nerd-nos/bm1397.h | 4 ++-- src/drivers/nerd-nos/mining.cpp | 15 +++++++++---- src/drivers/nerd-nos/mining.h | 7 +++--- src/mining.cpp | 40 ++++++++++++++++++++++++--------- src/stratum.cpp | 6 ++--- src/stratum.h | 2 +- 7 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/drivers/nerd-nos/bm1397.cpp b/src/drivers/nerd-nos/bm1397.cpp index fab8912..fe1dc87 100644 --- a/src/drivers/nerd-nos/bm1397.cpp +++ b/src/drivers/nerd-nos/bm1397.cpp @@ -337,17 +337,15 @@ void BM1397_set_job_difficulty_mask(int difficulty) _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), job_difficulty_mask, 6, BM1937_SERIALTX_DEBUG); } -static uint8_t id = 0; -uint8_t BM1397_send_work(bm_job_t *next_bm_job) +void BM1397_send_work(bm_job_t *next_bm_job, uint8_t job_id) { job_packet job; // max job number is 128 // there is still some really weird logic with the job id bits for the asic to sort out // so we have it limited to 128 and it has to increment by 4 - id = (id + 4) % 128; - job.job_id = id; + job.job_id = (job_id * 4) % 128; job.num_midstates = next_bm_job->num_midstates; memcpy(&job.starting_nonce, &next_bm_job->starting_nonce, 4); memcpy(&job.nbits, &next_bm_job->target, 4); @@ -363,8 +361,6 @@ uint8_t BM1397_send_work(bm_job_t *next_bm_job) } _send_BM1397((TYPE_JOB | GROUP_SINGLE | CMD_WRITE), (uint8_t*) &job, sizeof(job_packet), BM1397_DEBUG_WORK); - - return id; } asic_result *BM1397_receive_work(uint16_t timeout) @@ -394,7 +390,7 @@ asic_result *BM1397_receive_work(uint16_t timeout) return (asic_result *)asic_response_buffer; } -task_result *BM1397_proccess_work(bm_job_t *job, uint16_t timeout) +task_result *BM1397_proccess_work(uint32_t version, uint16_t timeout) { asic_result *asic_result = BM1397_receive_work(timeout); @@ -408,10 +404,10 @@ task_result *BM1397_proccess_work(bm_job_t *job, uint16_t timeout) uint8_t nonce_found = 0; uint32_t first_nonce = 0; - uint8_t rx_job_id = asic_result->job_id & 0xfc; + uint8_t rx_job_id = (asic_result->job_id & 0xfc) >> 2; uint8_t rx_midstate_index = asic_result->job_id & 0x03; - uint32_t rolled_version = job->version; + uint32_t rolled_version = version; for (int i = 0; i < rx_midstate_index; i++) { rolled_version = increment_bitmask(rolled_version, 0x1fffe000); @@ -446,3 +442,4 @@ task_result *BM1397_proccess_work(bm_job_t *job, uint16_t timeout) return &result; } + diff --git a/src/drivers/nerd-nos/bm1397.h b/src/drivers/nerd-nos/bm1397.h index 7c638c9..5304723 100644 --- a/src/drivers/nerd-nos/bm1397.h +++ b/src/drivers/nerd-nos/bm1397.h @@ -46,10 +46,10 @@ typedef struct __attribute__((__packed__)) uint8_t BM1397_init(uint64_t frequency, uint16_t asic_count); -uint8_t BM1397_send_work(bm_job_t * next_bm_job); +void BM1397_send_work(bm_job_t * next_bm_job, uint8_t job_id); void BM1397_set_job_difficulty_mask(int); int BM1397_set_max_baud(void); int BM1397_set_default_baud(void); void BM1397_send_hash_frequency(float frequency); -task_result *BM1397_proccess_work(bm_job_t *job, uint16_t timeout); +task_result *BM1397_proccess_work(uint32_t version, uint16_t timeout); diff --git a/src/drivers/nerd-nos/mining.cpp b/src/drivers/nerd-nos/mining.cpp index 5072795..b742f8d 100644 --- a/src/drivers/nerd-nos/mining.cpp +++ b/src/drivers/nerd-nos/mining.cpp @@ -159,10 +159,17 @@ void asic_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_ //next_job->pool_diff = stratum_difficulty; } -uint8_t asic_send_work(bm_job_t *next_bm_job) { - return BM1397_send_work(next_bm_job); +void asic_send_work(bm_job_t *next_bm_job, uint8_t job_id) { + BM1397_send_work(next_bm_job, job_id); } -task_result *asic_proccess_work(bm_job_t *job, uint16_t timeout) { - return BM1397_proccess_work(job, timeout); +task_result *asic_proccess_work(uint32_t version, uint16_t timeout) { + return BM1397_proccess_work(version, timeout); +} + +void asic_free_bm_job(bm_job_t *job) { + free(job->jobid); + free(job->extranonce2); + // mark as free + job->ntime = 0; } \ No newline at end of file diff --git a/src/drivers/nerd-nos/mining.h b/src/drivers/nerd-nos/mining.h index f25ad71..4c1e666 100644 --- a/src/drivers/nerd-nos/mining.h +++ b/src/drivers/nerd-nos/mining.h @@ -26,7 +26,8 @@ typedef struct } bm_job_t; -uint8_t asic_send_work(bm_job_t *next_bm_job); -task_result *asic_proccess_work(bm_job_t *job, uint16_t timeout); +void asic_send_work(bm_job_t *next_bm_job, uint8_t job_id); +task_result *asic_proccess_work(uint32_t version, uint16_t timeout); -double asic_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const uint32_t rolled_version); \ No newline at end of file +double asic_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const uint32_t rolled_version); +void asic_free_bm_job(bm_job_t *job); \ No newline at end of file diff --git a/src/mining.cpp b/src/mining.cpp index 20be8f9..76c31dd 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -374,11 +374,13 @@ void runMiner(void * task_id) { void asic_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_job, uint32_t extranonce_2); +// we can have 32 different job ids +bm_job_t asic_jobs[32]; + void runASIC(void * task_id) { Serial.printf("[MINER] Started runASIC Task!\n"); uint32_t extranonce_2 = 0; - bm_job_t asic_job; while(1) { // wait for new job while(1) { @@ -396,33 +398,49 @@ void runASIC(void * task_id) { Serial.println(">>> STARTING TO HASH NONCES"); uint32_t startT = micros(); + memset(asic_jobs, 0, sizeof(asic_jobs)); + + // we are assuming the version doesn't change from job to job + uint32_t version = strtoul(mJob.version.c_str(), NULL, 16); + + mMonitor.NerdStatus = NM_hashing; + while (mMiner.inRun) { - mMonitor.NerdStatus = NM_hashing; extranonce_2++; + // use extranonce2 as job id + uint8_t asic_job_id = (uint8_t) (extranonce_2 % 32); + + // if it was used before, we have to free the pointers + if (asic_jobs[asic_job_id].ntime) { + asic_free_bm_job(&asic_jobs[asic_job_id]); + } + // create the next asic job - asic_create_job(&mWorker, &mJob, &asic_job, extranonce_2); + asic_create_job(&mWorker, &mJob, &asic_jobs[asic_job_id], extranonce_2); // send the job and - uint8_t asic_job_id = asic_send_work(&asic_job); + asic_send_work(&asic_jobs[asic_job_id], asic_job_id); // wait 30ms for the response - task_result *result = asic_proccess_work(&asic_job, 30); + // the pointer returned is the RS232 receive buffer :shushing-face: + // but we only have a single thread so it should be okay + task_result *result = asic_proccess_work(version, 30); + // if we haven't received anything in time, so send a new job if (!result) { - // we haven't received anything in time, so send a new job continue; } - if (result->job_id != asic_job_id) { - // job id mismatch - Serial.printf("ID mismatch, expected %02x, got %02x\n", asic_job_id, result->job_id); + // if we have received a job we don't know + if (!asic_jobs[result->job_id].ntime) { + Serial.printf("No Job found for received ID %02x\n", result->job_id); continue; } // check the nonce difficulty double diff_hash = asic_test_nonce_value( - &asic_job, + &asic_jobs[result->job_id], result->nonce, result->rolled_version); @@ -435,7 +453,7 @@ void runASIC(void * task_id) { if(diff_hash > mMiner.poolDifficulty) { - tx_mining_submit_with_version(client, mWorker, &asic_job, extranonce_2, result->nonce, result->rolled_version); + tx_mining_submit_asic(client, mWorker, &asic_jobs[result->job_id], result); Serial.println("valid share!"); /* Serial.print(" - Current diff share: "); Serial.println(diff_hash,12); diff --git a/src/stratum.cpp b/src/stratum.cpp index d5bcabf..d63619f 100644 --- a/src/stratum.cpp +++ b/src/stratum.cpp @@ -230,7 +230,7 @@ bool tx_mining_submit(WiFiClient& client, mining_subscribe mWorker, mining_job m return true; } -bool tx_mining_submit_with_version(WiFiClient& client, mining_subscribe mWorker, const bm_job_t* asic_job, uint32_t extranonce2, unsigned long nonce, uint32_t version) +bool tx_mining_submit_asic(WiFiClient& client, mining_subscribe mWorker, const bm_job_t* asic_job, task_result *result) { char payload[BUFFER] = {0}; @@ -242,8 +242,8 @@ bool tx_mining_submit_with_version(WiFiClient& client, mining_subscribe mWorker, asic_job->jobid, asic_job->extranonce2, asic_job->ntime, - nonce, - version ^ asic_job->version + result->nonce, + result->rolled_version ^ asic_job->version ); Serial.print(" Sending : "); Serial.print(payload); client.print(payload); diff --git a/src/stratum.h b/src/stratum.h index 4d5ffc3..b6951e3 100644 --- a/src/stratum.h +++ b/src/stratum.h @@ -62,7 +62,7 @@ bool parse_mining_notify(String line, mining_job& mJob); //Method Mining.submit bool tx_mining_submit(WiFiClient& client, mining_subscribe mWorker, mining_job mJob, unsigned long nonce); -bool tx_mining_submit_with_version(WiFiClient& client, mining_subscribe mWorker, const bm_job_t* asic_job, uint32_t extranonce2, unsigned long nonce, uint32_t version); +bool tx_mining_submit_asic(WiFiClient& client, mining_subscribe mWorker, const bm_job_t* asic_job, task_result *result); //Difficulty Methods bool tx_suggest_difficulty(WiFiClient& client, double difficulty); From 4b7a6d8146098e18e3396bf64ed4adb22338d735 Mon Sep 17 00:00:00 2001 From: shufps Date: Mon, 9 Sep 2024 15:10:08 +0200 Subject: [PATCH 03/18] seems to be working --- src/NerdMinerV2.ino.cpp | 3 + src/drivers/nerd-nos/bm1397.cpp | 65 +++------ src/drivers/nerd-nos/bm1397.h | 40 +----- src/drivers/nerd-nos/common.h | 39 +----- src/drivers/nerd-nos/crc.h | 12 +- src/drivers/nerd-nos/mining.cpp | 44 ++---- src/drivers/nerd-nos/mining.h | 42 +++--- src/drivers/nerd-nos/nerdnos.h | 66 +++++++++ src/drivers/nerd-nos/serial.cpp | 12 ++ src/drivers/nerd-nos/serial.h | 4 +- src/drivers/nerd-nos/utils.cpp | 48 +++++++ src/drivers/nerd-nos/utils.h | 7 +- src/mining.cpp | 126 +---------------- src/mining.h | 2 +- src/mining_nerdnos.cpp | 232 ++++++++++++++++++++++++++++++++ src/mining_nerdnos.h | 6 + src/monitor.cpp | 79 ++++++----- src/stratum.cpp | 6 + src/stratum.h | 4 +- 19 files changed, 487 insertions(+), 350 deletions(-) create mode 100644 src/drivers/nerd-nos/nerdnos.h create mode 100644 src/mining_nerdnos.cpp create mode 100644 src/mining_nerdnos.h diff --git a/src/NerdMinerV2.ino.cpp b/src/NerdMinerV2.ino.cpp index 3b24b7a..4165105 100644 --- a/src/NerdMinerV2.ino.cpp +++ b/src/NerdMinerV2.ino.cpp @@ -17,6 +17,9 @@ #include "drivers/nerd-nos/bm1397.h" #include "drivers/nerd-nos/serial.h" +#ifdef NERD_NOS +#include "mining_nerdnos.h" +#endif #ifdef TOUCH_ENABLE #include "TouchHandler.h" diff --git a/src/drivers/nerd-nos/bm1397.cpp b/src/drivers/nerd-nos/bm1397.cpp index fe1dc87..1588cf0 100644 --- a/src/drivers/nerd-nos/bm1397.cpp +++ b/src/drivers/nerd-nos/bm1397.cpp @@ -7,6 +7,7 @@ #include "freertos/task.h" #include "../devices/device.h" #include "crc.h" +#include "utils.h" #define TYPE_JOB 0x20 #define TYPE_CMD 0x40 @@ -48,7 +49,6 @@ typedef struct __attribute__((__packed__)) static const char *TAG = "bm1397Module"; static uint8_t asic_response_buffer[CHUNK_SIZE]; -static uint32_t prev_nonce = 0; static task_result result; uint32_t increment_bitmask(const uint32_t value, const uint32_t mask); @@ -101,7 +101,7 @@ static void _send_read_address(void) { unsigned char read_address[2] = {0x00, 0x00}; // send serial data - _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_READ), read_address, 2, BM1937_SERIALTX_DEBUG); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_READ), read_address, 2, BM1397_SERIALTX_DEBUG); } static void _send_chain_inactive(void) @@ -109,7 +109,7 @@ static void _send_chain_inactive(void) unsigned char read_address[2] = {0x00, 0x00}; // send serial data - _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_INACTIVE), read_address, 2, BM1937_SERIALTX_DEBUG); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_INACTIVE), read_address, 2, BM1397_SERIALTX_DEBUG); } static void _set_chip_address(uint8_t chipAddr) @@ -117,7 +117,7 @@ static void _set_chip_address(uint8_t chipAddr) unsigned char read_address[2] = {chipAddr, 0x00}; // send serial data - _send_BM1397((TYPE_CMD | GROUP_SINGLE | CMD_SETADDRESS), read_address, 2, BM1937_SERIALTX_DEBUG); + _send_BM1397((TYPE_CMD | GROUP_SINGLE | CMD_SETADDRESS), read_address, 2, BM1397_SERIALTX_DEBUG); } void BM1397_send_hash_frequency(float frequency) @@ -187,12 +187,12 @@ void BM1397_send_hash_frequency(float frequency) for (i = 0; i < 2; i++) { vTaskDelay(10 / portTICK_PERIOD_MS); - _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), prefreqall, 6, BM1937_SERIALTX_DEBUG); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), prefreqall, 6, BM1397_SERIALTX_DEBUG); } for (i = 0; i < 2; i++) { vTaskDelay(10 / portTICK_PERIOD_MS); - _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), freqbufall, 6, BM1937_SERIALTX_DEBUG); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), freqbufall, 6, BM1397_SERIALTX_DEBUG); } vTaskDelay(10 / portTICK_PERIOD_MS); @@ -228,24 +228,24 @@ static uint8_t _send_init(uint64_t frequency, uint16_t asic_count) } unsigned char init[6] = {0x00, CLOCK_ORDER_CONTROL_0, 0x00, 0x00, 0x00, 0x00}; // init1 - clock_order_control0 - _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init, 6, BM1937_SERIALTX_DEBUG); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init, 6, BM1397_SERIALTX_DEBUG); unsigned char init2[6] = {0x00, CLOCK_ORDER_CONTROL_1, 0x00, 0x00, 0x00, 0x00}; // init2 - clock_order_control1 - _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init2, 6, BM1937_SERIALTX_DEBUG); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init2, 6, BM1397_SERIALTX_DEBUG); unsigned char init3[9] = {0x00, ORDERED_CLOCK_ENABLE, 0x00, 0x00, 0x00, 0x01}; // init3 - ordered_clock_enable - _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init3, 6, BM1937_SERIALTX_DEBUG); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init3, 6, BM1397_SERIALTX_DEBUG); unsigned char init4[9] = {0x00, CORE_REGISTER_CONTROL, 0x80, 0x00, 0x80, 0x74}; // init4 - init_4_? - _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init4, 6, BM1937_SERIALTX_DEBUG); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init4, 6, BM1397_SERIALTX_DEBUG); BM1397_set_job_difficulty_mask(BM1397_INITIAL_DIFFICULTY); unsigned char init5[9] = {0x00, PLL3_PARAMETER, 0xC0, 0x70, 0x01, 0x11}; // init5 - pll3_parameter - _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init5, 6, BM1937_SERIALTX_DEBUG); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init5, 6, BM1397_SERIALTX_DEBUG); unsigned char init6[9] = {0x00, FAST_UART_CONFIGURATION, 0x06, 0x00, 0x00, 0x0F}; // init6 - fast_uart_configuration - _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init6, 6, BM1937_SERIALTX_DEBUG); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init6, 6, BM1397_SERIALTX_DEBUG); BM1397_set_default_baud(); @@ -294,7 +294,7 @@ int BM1397_set_default_baud(void) { // default divider of 26 (11010) for 115,749 unsigned char baudrate[9] = {0x00, MISC_CONTROL, 0x00, 0x00, 0b01111010, 0b00110001}; // baudrate - misc_control - _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), baudrate, 6, BM1937_SERIALTX_DEBUG); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), baudrate, 6, BM1397_SERIALTX_DEBUG); return 115749; } @@ -304,7 +304,7 @@ int BM1397_set_max_baud(void) ESP_LOGI(TAG, "Setting max baud of 3125000"); unsigned char baudrate[9] = {0x00, MISC_CONTROL, 0x00, 0x00, 0b01100000, 0b00110001}; ; // baudrate - misc_control - _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), baudrate, 6, BM1937_SERIALTX_DEBUG); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), baudrate, 6, BM1397_SERIALTX_DEBUG); return 3125000; } @@ -317,7 +317,7 @@ void BM1397_set_job_difficulty_mask(int difficulty) // The mask must be a power of 2 so there are no holes // Correct: {0b00000000, 0b00000000, 0b11111111, 0b11111111} // Incorrect: {0b00000000, 0b00000000, 0b11100111, 0b11111111} - difficulty = _largest_power_of_two(difficulty) - 1; // (difficulty - 1) if it is a pow 2 then step down to second largest for more hashrate sampling + difficulty = largest_power_of_two(difficulty) - 1; // (difficulty - 1) if it is a pow 2 then step down to second largest for more hashrate sampling // convert difficulty into char array // Ex: 256 = {0b00000000, 0b00000000, 0b00000000, 0b11111111}, {0x00, 0x00, 0x00, 0xff} @@ -329,18 +329,18 @@ void BM1397_set_job_difficulty_mask(int difficulty) // So a mask of 512 looks like 0b00000000 00000000 00000001 1111111 // and not 0b00000000 00000000 10000000 1111111 - job_difficulty_mask[5 - i] = _reverse_bits(value); + job_difficulty_mask[5 - i] = reverse_bits(value); } ESP_LOGI(TAG, "Setting job ASIC mask to %d", difficulty); - _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), job_difficulty_mask, 6, BM1937_SERIALTX_DEBUG); + _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), job_difficulty_mask, 6, BM1397_SERIALTX_DEBUG); } void BM1397_send_work(bm_job_t *next_bm_job, uint8_t job_id) { - job_packet job; + job_packet_t job; // max job number is 128 // there is still some really weird logic with the job id bits for the asic to sort out // so we have it limited to 128 and it has to increment by 4 @@ -360,7 +360,7 @@ void BM1397_send_work(bm_job_t *next_bm_job, uint8_t job_id) memcpy(job.midstate3, next_bm_job->midstate3, 32); } - _send_BM1397((TYPE_JOB | GROUP_SINGLE | CMD_WRITE), (uint8_t*) &job, sizeof(job_packet), BM1397_DEBUG_WORK); + _send_BM1397((TYPE_JOB | GROUP_SINGLE | CMD_WRITE), (uint8_t*) &job, sizeof(job_packet_t), BM1397_DEBUG_WORK); } asic_result *BM1397_receive_work(uint16_t timeout) @@ -392,7 +392,6 @@ asic_result *BM1397_receive_work(uint16_t timeout) task_result *BM1397_proccess_work(uint32_t version, uint16_t timeout) { - asic_result *asic_result = BM1397_receive_work(timeout); if (asic_result == NULL) @@ -401,9 +400,6 @@ task_result *BM1397_proccess_work(uint32_t version, uint16_t timeout) return NULL; } - uint8_t nonce_found = 0; - uint32_t first_nonce = 0; - uint8_t rx_job_id = (asic_result->job_id & 0xfc) >> 2; uint8_t rx_midstate_index = asic_result->job_id & 0x03; @@ -413,29 +409,6 @@ task_result *BM1397_proccess_work(uint32_t version, uint16_t timeout) rolled_version = increment_bitmask(rolled_version, 0x1fffe000); } - // ASIC may return the same nonce multiple times - // or one that was already found - // most of the time it behavies however - if (nonce_found == 0) - { - first_nonce = asic_result->nonce; - nonce_found = 1; - } - else if (asic_result->nonce == first_nonce) - { - // stop if we've already seen this nonce - return NULL; - } - - if (asic_result->nonce == prev_nonce) - { - return NULL; - } - else - { - prev_nonce = asic_result->nonce; - } - result.job_id = rx_job_id; result.nonce = asic_result->nonce; result.rolled_version = rolled_version; diff --git a/src/drivers/nerd-nos/bm1397.h b/src/drivers/nerd-nos/bm1397.h index 5304723..743d140 100644 --- a/src/drivers/nerd-nos/bm1397.h +++ b/src/drivers/nerd-nos/bm1397.h @@ -1,49 +1,15 @@ #pragma once #include -#include "common.h" -#include "mining.h" +#include "nerdnos.h" #include "crc.h" #define CRC5_MASK 0x1F -#define BM1397_INITIAL_DIFFICULTY 64 -#define BM1937_SERIALTX_DEBUG false -#define BM1937_SERIALRX_DEBUG true //false +#define BM1397_SERIALTX_DEBUG false +#define BM1397_SERIALRX_DEBUG true //false #define BM1397_DEBUG_WORK false //causes insane amount of debug output - -typedef struct -{ - float frequency; -} bm1397Module; - -typedef enum -{ - JOB_PACKET = 0, - CMD_PACKET = 1, -} packet_type_t; - -typedef enum -{ - JOB_RESP = 0, - CMD_RESP = 1, -} response_type_t; - -typedef struct __attribute__((__packed__)) -{ - uint8_t job_id; - uint8_t num_midstates; - uint8_t starting_nonce[4]; - uint8_t nbits[4]; - uint8_t ntime[4]; - uint8_t merkle4[4]; - uint8_t midstate[32]; - uint8_t midstate1[32]; - uint8_t midstate2[32]; - uint8_t midstate3[32]; -} job_packet; - uint8_t BM1397_init(uint64_t frequency, uint16_t asic_count); void BM1397_send_work(bm_job_t * next_bm_job, uint8_t job_id); diff --git a/src/drivers/nerd-nos/common.h b/src/drivers/nerd-nos/common.h index 6b98342..b8488ae 100644 --- a/src/drivers/nerd-nos/common.h +++ b/src/drivers/nerd-nos/common.h @@ -1,41 +1,6 @@ -#ifndef COMMON_H_ -#define COMMON_H_ +#pragma once #include - -typedef struct __attribute__((__packed__)) -{ - uint8_t job_id; - uint32_t nonce; - uint32_t rolled_version; -} task_result; - -static unsigned char _reverse_bits(unsigned char num) -{ - unsigned char reversed = 0; - int i; - - for (i = 0; i < 8; i++) { - reversed <<= 1; // Left shift the reversed variable by 1 - reversed |= num & 1; // Use bitwise OR to set the rightmost bit of reversed to the current bit of num - num >>= 1; // Right shift num by 1 to get the next bit - } - - return reversed; -} - -static int _largest_power_of_two(int num) -{ - int power = 0; - - while (num > 1) { - num = num >> 1; - power++; - } - - return 1 << power; -} +#include void printBufferHex(const char *prefix, const uint8_t* buf, size_t len); - -#endif \ No newline at end of file diff --git a/src/drivers/nerd-nos/crc.h b/src/drivers/nerd-nos/crc.h index 1f4b1df..d9f4194 100644 --- a/src/drivers/nerd-nos/crc.h +++ b/src/drivers/nerd-nos/crc.h @@ -1,18 +1,8 @@ -#ifndef CRC_H_ -#define CRC_H_ +#pragma once #include -#ifdef __cplusplus -extern "C" { -#endif - uint8_t crc5(uint8_t *buffer, uint16_t len); uint16_t crc16(uint8_t *buffer, uint16_t len); uint16_t crc16_false(uint8_t *buffer, uint16_t len); -#ifdef __cplusplus -} -#endif - -#endif // CRC_H_ \ No newline at end of file diff --git a/src/drivers/nerd-nos/mining.cpp b/src/drivers/nerd-nos/mining.cpp index b742f8d..6f2ed8e 100644 --- a/src/drivers/nerd-nos/mining.cpp +++ b/src/drivers/nerd-nos/mining.cpp @@ -11,6 +11,7 @@ #include "utils.h" #include "stratum.h" #include "mbedtls/sha256.h" +#include "../../mining.h" ///////cgminer nonce testing /* truediffone == 0x00000000FFFF0000000000000000000000000000000000000000000000000000 @@ -18,7 +19,7 @@ static const double truediffone = 26959535291011309493156476344723991336010898738574164086137773096960.0; /* testing a nonce and return the diff - 0 means invalid */ -double asic_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const uint32_t rolled_version) +double nerdnos_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const uint32_t rolled_version, uint8_t hash_result[32]) { double d64, s64, ds; unsigned char header[80]; @@ -38,7 +39,6 @@ double asic_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const ui memcpy(header + 76, &nonce, 4); unsigned char hash_buffer[32]; - unsigned char hash_result[32]; // double hash the header mbedtls_sha256(header, 80, hash_buffer, 0); @@ -51,29 +51,7 @@ double asic_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const ui return ds; } - -uint32_t increment_bitmask(const uint32_t value, const uint32_t mask) -{ - // if mask is zero, just return the original value - if (mask == 0) - return value; - - uint32_t carry = (value & mask) + (mask & -mask); // increment the least significant bit of the mask - uint32_t overflow = carry & ~mask; // find overflowed bits that are not in the mask - uint32_t new_value = (value & ~mask) | (carry & mask); // set bits according to the mask - - // Handle carry propagation - if (overflow > 0) - { - uint32_t carry_mask = (overflow << 1); // shift left to get the mask where carry should be propagated - new_value = increment_bitmask(new_value, carry_mask); // recursively handle carry propagation - } - - return new_value; -} - - -void calculate_merkle_root_hash(const char *coinbase_tx, mining_job* job, char merkle_root_hash[65]) +static void calculate_merkle_root_hash(const char *coinbase_tx, mining_job* job, char merkle_root_hash[65]) { size_t coinbase_tx_bin_len = strlen(coinbase_tx) / 2; uint8_t coinbase_tx_bin[coinbase_tx_bin_len]; @@ -93,7 +71,7 @@ void calculate_merkle_root_hash(const char *coinbase_tx, mining_job* job, char m } // take a mining_notify struct with ascii hex strings and convert it to a bm_job struct -void construct_bm_job(mining_job *job, const char *merkle_root, uint32_t version_mask, bm_job_t *new_job) +static void construct_bm_job(mining_job *job, const char *merkle_root, uint32_t version_mask, bm_job_t *new_job) { new_job->version = strtoul(job->version.c_str(), NULL, 16); new_job->target = strtoul(job->nbits.c_str(), NULL, 16); @@ -139,7 +117,7 @@ void construct_bm_job(mining_job *job, const char *merkle_root, uint32_t version new_job->num_midstates = 4; } -void asic_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_job, uint32_t extranonce_2) { +void nerdnos_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_job, uint32_t extranonce_2, uint32_t stratum_difficulty) { char extranonce_2_str[mWorker->extranonce2_size * 2 + 1]; // +1 zero termination snprintf(extranonce_2_str, sizeof(extranonce_2_str), "%0*lx", (int) mWorker->extranonce2_size * 2, extranonce_2); @@ -156,20 +134,24 @@ void asic_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_ next_job->jobid = strdup(job->job_id.c_str()); next_job->extranonce2 = strdup(extranonce_2_str); - //next_job->pool_diff = stratum_difficulty; + next_job->pool_diff = stratum_difficulty; } -void asic_send_work(bm_job_t *next_bm_job, uint8_t job_id) { +void nerdnos_send_work(bm_job_t *next_bm_job, uint8_t job_id) { BM1397_send_work(next_bm_job, job_id); } -task_result *asic_proccess_work(uint32_t version, uint16_t timeout) { +task_result *nerdnos_proccess_work(uint32_t version, uint16_t timeout) { return BM1397_proccess_work(version, timeout); } -void asic_free_bm_job(bm_job_t *job) { +void nerdnos_free_bm_job(bm_job_t *job) { free(job->jobid); free(job->extranonce2); // mark as free job->ntime = 0; +} + +void nerdnos_set_asic_difficulty(uint32_t difficulty) { + BM1397_set_job_difficulty_mask(difficulty); } \ No newline at end of file diff --git a/src/drivers/nerd-nos/mining.h b/src/drivers/nerd-nos/mining.h index 4c1e666..63098c9 100644 --- a/src/drivers/nerd-nos/mining.h +++ b/src/drivers/nerd-nos/mining.h @@ -1,33 +1,25 @@ #pragma once #include -#include "common.h" -//#include "stratum.h" +#include "nerdnos.h" +// forward declaration to resolve circular inclusions +typedef struct mining_subscribe mining_subscribe; +typedef struct mining_job mining_job; -typedef struct -{ - uint32_t version; - uint32_t version_mask; - uint8_t prev_block_hash[32]; - uint8_t prev_block_hash_be[32]; - uint8_t merkle_root[32]; - uint8_t merkle_root_be[32]; - uint32_t ntime; - uint32_t target; // aka difficulty, aka nbits - uint32_t starting_nonce; +// set the asic hardware difficulty +void nerdnos_set_asic_difficulty(uint32_t current_difficulty); - uint8_t num_midstates; - uint8_t midstate[32]; - uint8_t midstate1[32]; - uint8_t midstate2[32]; - uint8_t midstate3[32]; - char *jobid; - char *extranonce2; -} bm_job_t; +// create asic job +void nerdnos_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_job, uint32_t extranonce_2, uint32_t stratum_difficulty); +// send new work to the asic +void nerdnos_send_work(bm_job_t *next_bm_job, uint8_t job_id); -void asic_send_work(bm_job_t *next_bm_job, uint8_t job_id); -task_result *asic_proccess_work(uint32_t version, uint16_t timeout); +// receive and process responses +task_result *nerdnos_proccess_work(uint32_t version, uint16_t timeout); -double asic_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const uint32_t rolled_version); -void asic_free_bm_job(bm_job_t *job); \ No newline at end of file +// test difficulty +double nerdnos_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const uint32_t rolled_version, uint8_t hash_result[32]); + +// free allocated RAM for strings on bm_job_t +void nerdnos_free_bm_job(bm_job_t *job); diff --git a/src/drivers/nerd-nos/nerdnos.h b/src/drivers/nerd-nos/nerdnos.h new file mode 100644 index 0000000..f5c4972 --- /dev/null +++ b/src/drivers/nerd-nos/nerdnos.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +#define BM1397_INITIAL_DIFFICULTY 128 + +typedef struct __attribute__((__packed__)) +{ + uint8_t job_id; + uint32_t nonce; + uint32_t rolled_version; +} task_result; + +typedef struct +{ + uint32_t version; + uint32_t version_mask; + uint8_t prev_block_hash[32]; + uint8_t prev_block_hash_be[32]; + uint8_t merkle_root[32]; + uint8_t merkle_root_be[32]; + uint32_t ntime; + uint32_t target; // aka difficulty, aka nbits + uint32_t starting_nonce; + + uint8_t num_midstates; + uint8_t midstate[32]; + uint8_t midstate1[32]; + uint8_t midstate2[32]; + uint8_t midstate3[32]; + char *jobid; + char *extranonce2; + uint32_t pool_diff; +} bm_job_t; + +/* +typedef struct +{ + float frequency; +} bm1397Module; +*/ +typedef enum +{ + JOB_PACKET = 0, + CMD_PACKET = 1, +} packet_type_t; + +typedef enum +{ + JOB_RESP = 0, + CMD_RESP = 1, +} response_type_t; + +typedef struct __attribute__((__packed__)) +{ + uint8_t job_id; + uint8_t num_midstates; + uint8_t starting_nonce[4]; + uint8_t nbits[4]; + uint8_t ntime[4]; + uint8_t merkle4[4]; + uint8_t midstate[32]; + uint8_t midstate1[32]; + uint8_t midstate2[32]; + uint8_t midstate3[32]; +} job_packet_t; diff --git a/src/drivers/nerd-nos/serial.cpp b/src/drivers/nerd-nos/serial.cpp index 70f48b9..7a88b2c 100644 --- a/src/drivers/nerd-nos/serial.cpp +++ b/src/drivers/nerd-nos/serial.cpp @@ -51,12 +51,24 @@ int SERIAL_send(uint8_t *data, int len, bool debug) return uart_write_bytes(UART_NUM_1, (const char *)data, len); } +int SERIAL_check_for_data() { + int length; + uart_get_buffered_data_len(UART_NUM_1, (size_t*)&length); + return length; +} + + /// @brief waits for a serial response from the device /// @param buf buffer to read data into /// @param buf number of ms to wait before timing out /// @return number of bytes read, or -1 on error int16_t SERIAL_rx(uint8_t *buf, uint16_t size, uint16_t timeout_ms) { + // don't return incomplete data + if (SERIAL_check_for_data() < size) { + return 0; + } + int16_t bytes_read = uart_read_bytes(UART_NUM_1, buf, size, timeout_ms / portTICK_PERIOD_MS); // if (bytes_read > 0) { // printf("rx: "); diff --git a/src/drivers/nerd-nos/serial.h b/src/drivers/nerd-nos/serial.h index ae165c1..b4850c8 100644 --- a/src/drivers/nerd-nos/serial.h +++ b/src/drivers/nerd-nos/serial.h @@ -1,5 +1,4 @@ -#ifndef SERIAL_H_ -#define SERIAL_H_ +#pragma once #define CHUNK_SIZE 1024 @@ -10,4 +9,3 @@ int16_t SERIAL_rx(uint8_t *, uint16_t, uint16_t); void SERIAL_clear_buffer(void); void SERIAL_set_baud(int baud); -#endif /* SERIAL_H_ */ \ No newline at end of file diff --git a/src/drivers/nerd-nos/utils.cpp b/src/drivers/nerd-nos/utils.cpp index 3cf2fbf..01a47e1 100644 --- a/src/drivers/nerd-nos/utils.cpp +++ b/src/drivers/nerd-nos/utils.cpp @@ -306,4 +306,52 @@ uint32_t flip32(uint32_t val) ret |= (val & 0xFF0000) >> 8; ret |= (val & 0xFF000000) >> 24; return ret; +} + + +unsigned char reverse_bits(unsigned char num) +{ + unsigned char reversed = 0; + int i; + + for (i = 0; i < 8; i++) { + reversed <<= 1; // Left shift the reversed variable by 1 + reversed |= num & 1; // Use bitwise OR to set the rightmost bit of reversed to the current bit of num + num >>= 1; // Right shift num by 1 to get the next bit + } + + return reversed; +} + +int largest_power_of_two(int num) +{ + int power = 0; + + while (num > 1) { + num = num >> 1; + power++; + } + + return 1 << power; +} + + +uint32_t increment_bitmask(const uint32_t value, const uint32_t mask) +{ + // if mask is zero, just return the original value + if (mask == 0) + return value; + + uint32_t carry = (value & mask) + (mask & -mask); // increment the least significant bit of the mask + uint32_t overflow = carry & ~mask; // find overflowed bits that are not in the mask + uint32_t new_value = (value & ~mask) | (carry & mask); // set bits according to the mask + + // Handle carry propagation + if (overflow > 0) + { + uint32_t carry_mask = (overflow << 1); // shift left to get the mask where carry should be propagated + new_value = increment_bitmask(new_value, carry_mask); // recursively handle carry propagation + } + + return new_value; } \ No newline at end of file diff --git a/src/drivers/nerd-nos/utils.h b/src/drivers/nerd-nos/utils.h index a1e6a20..78b6852 100644 --- a/src/drivers/nerd-nos/utils.h +++ b/src/drivers/nerd-nos/utils.h @@ -1,4 +1,4 @@ -#pragma one +#pragma once #include #include @@ -33,3 +33,8 @@ void prettyHex(unsigned char *buf, int len); uint32_t flip32(uint32_t val); +unsigned char reverse_bits(unsigned char num); + +int largest_power_of_two(int num); + +uint32_t increment_bitmask(const uint32_t value, const uint32_t mask); diff --git a/src/mining.cpp b/src/mining.cpp index 76c31dd..95a6c90 100644 --- a/src/mining.cpp +++ b/src/mining.cpp @@ -14,11 +14,6 @@ #include "drivers/displays/display.h" #include "drivers/storage/storage.h" -#ifdef NERD_NOS -#include "drivers/nerd-nos/mining.h" -#endif - - nvs_handle_t stat_handle; uint32_t templates = 0; @@ -41,8 +36,8 @@ extern TSettings Settings; IPAddress serverIP(1, 1, 1, 1); //Temporally save poolIPaddres //Global work data -static WiFiClient client; -static miner_data mMiner; //Global miner data (Create a miner class TODO) +WiFiClient client; +miner_data mMiner; //Global miner data (Create a miner class TODO) mining_subscribe mWorker; mining_job mJob; monitor_data mMonitor; @@ -103,12 +98,14 @@ bool checkPoolInactivity(unsigned int keepAliveTime, unsigned long inactivityTim }*/ } +#ifndef NERD_NOS if(elapsedKHs == 0){ //Check if hashrate is 0 during inactivityTIme if(mStart0Hashrate == 0) mStart0Hashrate = millis(); if((millis()-mStart0Hashrate) > inactivityTime) { mStart0Hashrate=0; return true;} return false; } +#endif mStart0Hashrate = 0; return false; @@ -372,121 +369,6 @@ void runMiner(void * task_id) { } } -void asic_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_job, uint32_t extranonce_2); - -// we can have 32 different job ids -bm_job_t asic_jobs[32]; - -void runASIC(void * task_id) { - Serial.printf("[MINER] Started runASIC Task!\n"); - - uint32_t extranonce_2 = 0; - while(1) { - // wait for new job - while(1) { - if (mMiner.newJob == true) { - break; - } - vTaskDelay(100 / portTICK_PERIOD_MS); //Small delay - } - - if(mMiner.newJob) { - mMiner.newJob = false; //Clear newJob flag - } - mMiner.inRun = true; //Set inRun flag - - Serial.println(">>> STARTING TO HASH NONCES"); - uint32_t startT = micros(); - - memset(asic_jobs, 0, sizeof(asic_jobs)); - - // we are assuming the version doesn't change from job to job - uint32_t version = strtoul(mJob.version.c_str(), NULL, 16); - - mMonitor.NerdStatus = NM_hashing; - - while (mMiner.inRun) { - extranonce_2++; - - // use extranonce2 as job id - uint8_t asic_job_id = (uint8_t) (extranonce_2 % 32); - - // if it was used before, we have to free the pointers - if (asic_jobs[asic_job_id].ntime) { - asic_free_bm_job(&asic_jobs[asic_job_id]); - } - - // create the next asic job - asic_create_job(&mWorker, &mJob, &asic_jobs[asic_job_id], extranonce_2); - - // send the job and - asic_send_work(&asic_jobs[asic_job_id], asic_job_id); - - // wait 30ms for the response - // the pointer returned is the RS232 receive buffer :shushing-face: - // but we only have a single thread so it should be okay - task_result *result = asic_proccess_work(version, 30); - - // if we haven't received anything in time, so send a new job - if (!result) { - continue; - } - - // if we have received a job we don't know - if (!asic_jobs[result->job_id].ntime) { - Serial.printf("No Job found for received ID %02x\n", result->job_id); - continue; - } - - // check the nonce difficulty - double diff_hash = asic_test_nonce_value( - &asic_jobs[result->job_id], - result->nonce, - result->rolled_version); - - Serial.print(" - Current diff share: "); Serial.println(diff_hash, 12); - Serial.print(" - Current pool diff : "); Serial.println(mMiner.poolDifficulty, 12); - - // update best diff - if (diff_hash > best_diff) - best_diff = diff_hash; - - if(diff_hash > mMiner.poolDifficulty) - { - tx_mining_submit_asic(client, mWorker, &asic_jobs[result->job_id], result); - Serial.println("valid share!"); -/* - Serial.print(" - Current diff share: "); Serial.println(diff_hash,12); - Serial.print(" - Current pool diff : "); Serial.println(mMiner.poolDifficulty,12); -*/ -/* - Serial.print(" - TX SHARE: "); - for (size_t i = 0; i < 32; i++) - Serial.printf("%02x", hash[i]); -*/ - #ifdef DEBUG_MINING - Serial.println(""); - Serial.print(" - Current nonce: "); Serial.println(nonce); - Serial.print(" - Current block header: "); - for (size_t i = 0; i < 80; i++) { - Serial.printf("%02x", mMiner.bytearray_blockheader[i]); - } - #endif - Serial.println(""); - mLastTXtoPool = millis(); - } - } - Serial.println ("MINER WORK ABORTED >> waiting new job"); - mMiner.inRun = false; - uint32_t duration = micros() - startT; -/* - if (esp_task_wdt_reset() == ESP_OK) - Serial.print(">>> Resetting watchdog timer"); -*/ - } - -} - #define DELAY 100 #define REDRAW_EVERY 10 diff --git a/src/mining.h b/src/mining.h index c010572..930e780 100644 --- a/src/mining.h +++ b/src/mining.h @@ -20,7 +20,7 @@ String printLocalTime(void); void resetStat(); -typedef struct{ +typedef struct { uint8_t bytearray_target[32]; uint8_t bytearray_pooltarget[32]; uint8_t merkle_result[32]; diff --git a/src/mining_nerdnos.cpp b/src/mining_nerdnos.cpp new file mode 100644 index 0000000..d96fc11 --- /dev/null +++ b/src/mining_nerdnos.cpp @@ -0,0 +1,232 @@ +#include +#include +#include +#include +#include +#include +//#include "ShaTests/nerdSHA256.h" +#include "ShaTests/nerdSHA256plus.h" +#include "stratum.h" +#include "mining.h" +#include "utils.h" +#include "monitor.h" +#include "timeconst.h" +#include "drivers/displays/display.h" +#include "drivers/storage/storage.h" + +#include "drivers/nerd-nos/nerdnos.h" +#include "mining_nerdnos.h" + +extern WiFiClient client; +extern mining_subscribe mWorker; +extern mining_job mJob; +extern miner_data mMiner; +extern monitor_data mMonitor; + +extern pthread_mutex_t job_mutex; +extern double best_diff; +extern unsigned long mLastTXtoPool; + +// to track the jobs +// we can have 32 different job ids +static bm_job_t asic_jobs[32] = {0}; + +// to track hashrate +#define ASIC_HISTORY_SIZE 128 + +typedef struct { + uint32_t diffs[ASIC_HISTORY_SIZE]; + uint32_t timestamps[ASIC_HISTORY_SIZE]; + uint32_t newest; + uint32_t oldest; + uint64_t sum; + double avg_gh; + double duration; + int shares; +} history_t; + +static pthread_mutex_t job_interval_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t job_interval_cond = PTHREAD_COND_INITIALIZER; + +history_t history = {0}; + +double nerdnos_get_avg_hashrate() { + return history.avg_gh; +} + +// incremental ringbuffer based hashrate calculation +static void calculate_hashrate(history_t *history, uint32_t diff) { + // if we have wrapped around at least once our ringbuffer is full + // and we have to remove the oldest element + if (history->newest + 1 >= ASIC_HISTORY_SIZE) { + history->sum -= history->diffs[history->oldest % ASIC_HISTORY_SIZE]; + history->oldest++; + } + // add and store the newest sample + history->sum += diff; + history->diffs[history->newest % ASIC_HISTORY_SIZE] = diff; + history->timestamps[history->newest % ASIC_HISTORY_SIZE] = micros(); + + uint64_t oldest_timestamp = history->timestamps[history->oldest % ASIC_HISTORY_SIZE]; + uint64_t newest_timestamp = history->timestamps[history->newest % ASIC_HISTORY_SIZE]; + + history->duration = (double) (newest_timestamp - oldest_timestamp) / 1.0e6; + history->shares = (int) history->newest - (int) history->oldest + 1; + + if (history->duration) { + double avg = (double) (history->sum << 32llu) / history->duration; + history->avg_gh = avg / 1.0e9; + } + + history->newest++; +} + +// triggers the job creation +static void create_job_timer(TimerHandle_t xTimer) +{ + pthread_mutex_lock(&job_interval_mutex); + pthread_cond_signal(&job_interval_cond); + pthread_mutex_unlock(&job_interval_mutex); +} + +void runASIC(void * task_id) { + Serial.printf("[MINER] Started runASIC Task!\n"); + + // Create the timer + TimerHandle_t job_timer = xTimerCreate("NERDNOS_Job_Timer", NERDNOS_JOB_INTERVAL_MS / portTICK_PERIOD_MS, pdTRUE, NULL, create_job_timer); + + if (job_timer == NULL) { + Serial.println("Failed to create NERNOS timer"); + return; + } + + // Start the timer + if (xTimerStart(job_timer, 0) != pdPASS) { + Serial.println("Failed to start NERDNOS timer"); + return; + } + + uint32_t extranonce_2 = 0; + while(1) { + // wait for new job + while(1) { + if (mMiner.newJob == true) { + break; + } + vTaskDelay(100 / portTICK_PERIOD_MS); //Small delay + } + + if(mMiner.newJob) { + mMiner.newJob = false; //Clear newJob flag + } + mMiner.inRun = true; //Set inRun flag + + Serial.println(">>> STARTING TO HASH NONCES"); + uint32_t startT = micros(); + + memset(asic_jobs, 0, sizeof(asic_jobs)); + + // we are assuming the version doesn't change from job to job + uint32_t version = strtoul(mJob.version.c_str(), NULL, 16); + + mMonitor.NerdStatus = NM_hashing; + + uint32_t current_difficulty = 0; + + while (mMiner.inRun) { + // wait for the timer to start a new job + // also yields the CPU + pthread_mutex_lock(&job_interval_mutex); + pthread_cond_wait(&job_interval_cond, &job_interval_mutex); + pthread_mutex_unlock(&job_interval_mutex); + + // increment extranonce2 + extranonce_2++; + + // use extranonce2 as job id + uint8_t asic_job_id = (uint8_t) (extranonce_2 % 32); + + // if it was used before, we have to free the pointers + if (asic_jobs[asic_job_id].ntime) { + nerdnos_free_bm_job(&asic_jobs[asic_job_id]); + } + + // create the next asic job + // make sure that another task doesn't mess with the data while + // we are using it + pthread_mutex_lock(&job_mutex); + if (current_difficulty != mMiner.poolDifficulty) { + current_difficulty = mMiner.poolDifficulty; + nerdnos_set_asic_difficulty(current_difficulty); + Serial.printf("Set difficulty to %llu\n", current_difficulty); + } + nerdnos_create_job(&mWorker, &mJob, &asic_jobs[asic_job_id], extranonce_2, current_difficulty); + pthread_mutex_unlock(&job_mutex); + + // send the job and + nerdnos_send_work(&asic_jobs[asic_job_id], asic_job_id); + + // the pointer returned is the RS232 receive buffer :shushing-face: + // but we only have a single thread so it should be okay + // process all results if we have more than one + // this is okay because serial uses a buffer and (most likely^^) DMA + task_result *result = 0; + while (result = nerdnos_proccess_work(version, 1), result != NULL) { + // if we have received a job we don't know + if (!asic_jobs[result->job_id].ntime) { + Serial.printf("No Job found for received ID %02x\n", result->job_id); + continue; + } + + uint8_t hash[32]; + + // check the nonce difficulty + double diff_hash = nerdnos_test_nonce_value( + &asic_jobs[result->job_id], + result->nonce, + result->rolled_version, + hash); + + // update best diff + if (diff_hash > best_diff) + best_diff = diff_hash; + + // calculate the hashrate + if (diff_hash >= asic_jobs[result->job_id].pool_diff) { + calculate_hashrate(&history, asic_jobs[result->job_id].pool_diff); + Serial.printf("avg hashrate: %.2fGH/s (history spans %2.fs, %d shares)\n", history.avg_gh, history.duration, history.shares); + } + + if(diff_hash > mMiner.poolDifficulty) + { + tx_mining_submit_asic(client, mWorker, &asic_jobs[result->job_id], result); + Serial.println("valid share!"); + Serial.print(" - Current diff share: "); Serial.println(diff_hash,12); + Serial.print(" - Current pool diff : "); Serial.println(mMiner.poolDifficulty,12); + Serial.print(" - TX SHARE: "); + for (size_t i = 0; i < 32; i++) + Serial.printf("%02x", hash[i]); + + #ifdef DEBUG_MINING + Serial.println(""); + Serial.print(" - Current nonce: "); Serial.println(nonce); + Serial.print(" - Current block header: "); + for (size_t i = 0; i < 80; i++) { + Serial.printf("%02x", mMiner.bytearray_blockheader[i]); + } + #endif + Serial.println(""); + mLastTXtoPool = millis(); + } + } + } + Serial.println ("MINER WORK ABORTED >> waiting new job"); + mMiner.inRun = false; + uint32_t duration = micros() - startT; +/* + if (esp_task_wdt_reset() == ESP_OK) + Serial.print(">>> Resetting watchdog timer"); +*/ + } + +} diff --git a/src/mining_nerdnos.h b/src/mining_nerdnos.h new file mode 100644 index 0000000..f58f852 --- /dev/null +++ b/src/mining_nerdnos.h @@ -0,0 +1,6 @@ +#pragma once + +#define NERDNOS_JOB_INTERVAL_MS 30 + +void runASIC(void * task_id); +double nerdnos_get_avg_hashrate(); \ No newline at end of file diff --git a/src/monitor.cpp b/src/monitor.cpp index f7dc057..02b4640 100644 --- a/src/monitor.cpp +++ b/src/monitor.cpp @@ -9,6 +9,10 @@ #include "monitor.h" #include "drivers/storage/storage.h" +#ifdef NERD_NOS +#include "mining_nerdnos.h" +#endif + extern uint32_t templates; extern uint32_t hashes; extern uint32_t Mhashes; @@ -24,7 +28,7 @@ extern double best_diff; // track best diff extern monitor_data mMonitor; //from saved config -extern TSettings Settings; +extern TSettings Settings; bool invertColors = false; WiFiUDP ntpUDP; @@ -40,7 +44,7 @@ void setup_monitor(void){ /******** TIME ZONE SETTING *****/ timeClient.begin(); - + // Adjust offset depending on your zone // GMT +2 in seconds (zona horaria de Europa Central) timeClient.setTimeOffset(3600 * Settings.Timezone); @@ -55,11 +59,11 @@ void setup_monitor(void){ unsigned long mGlobalUpdate =0; void updateGlobalData(void){ - + if((mGlobalUpdate == 0) || (millis() - mGlobalUpdate > UPDATE_Global_min * 60 * 1000)){ - + if (WiFi.status() != WL_CONNECTED) return; - + //Make first API call to get global hash and current difficulty HTTPClient http; try { @@ -68,7 +72,7 @@ void updateGlobalData(void){ if (httpCode == HTTP_CODE_OK) { String payload = http.getString(); - + DynamicJsonDocument doc(1024); deserializeJson(doc, payload); String temp = ""; @@ -86,14 +90,14 @@ void updateGlobalData(void){ } http.end(); - + //Make third API call to get fees http.begin(getFees); httpCode = http.GET(); if (httpCode == HTTP_CODE_OK) { String payload = http.getString(); - + DynamicJsonDocument doc(1024); deserializeJson(doc, payload); String temp = ""; @@ -108,7 +112,7 @@ void updateGlobalData(void){ mGlobalUpdate = millis(); } - + http.end(); } catch(...) { http.end(); @@ -119,11 +123,11 @@ void updateGlobalData(void){ unsigned long mHeightUpdate = 0; String getBlockHeight(void){ - + if((mHeightUpdate == 0) || (millis() - mHeightUpdate > UPDATE_Height_min * 60 * 1000)){ - + if (WiFi.status() != WL_CONNECTED) return current_block; - + HTTPClient http; try { http.begin(getHeightAPI); @@ -136,24 +140,24 @@ String getBlockHeight(void){ current_block = payload; mHeightUpdate = millis(); - } + } http.end(); } catch(...) { http.end(); } } - + return current_block; } unsigned long mBTCUpdate = 0; String getBTCprice(void){ - + if((mBTCUpdate == 0) || (millis() - mBTCUpdate > UPDATE_BTC_min * 60 * 1000)){ - + if (WiFi.status() != WL_CONNECTED) return (String(bitcoin_price) + "$"); - + HTTPClient http; try { http.begin(getBTCAPI); @@ -170,13 +174,13 @@ String getBTCprice(void){ mBTCUpdate = millis(); } - + http.end(); } catch(...) { http.end(); } } - + return (String(bitcoin_price) + "$"); } @@ -186,7 +190,7 @@ unsigned long initialTime = 0; unsigned long mPoolUpdate = 0; void getTime(unsigned long* currentHours, unsigned long* currentMinutes, unsigned long* currentSeconds){ - + //Check if need an NTP call to check current time if((mTriggerUpdate == 0) || (millis() - mTriggerUpdate > UPDATE_PERIOD_h * 60 * 60 * 1000)){ //60 sec. * 60 min * 1000ms if(WiFi.status() == WL_CONNECTED) { @@ -206,7 +210,7 @@ void getTime(unsigned long* currentHours, unsigned long* currentMinutes, unsigne } String getDate(){ - + unsigned long elapsedTime = (millis() - mTriggerUpdate) / 1000; // Tiempo transcurrido en segundos unsigned long currentTime = initialTime + elapsedTime; // La hora actual @@ -229,15 +233,22 @@ String getTime(void){ char LocalHour[10]; sprintf(LocalHour, "%02d:%02d", currentHours, currentMinutes); - + String mystring(LocalHour); return LocalHour; } +#ifdef NERD_NOS +String getCurrentHashRate(unsigned long mElapsed) { + // we have too little space for 2 digits after the decimal point + return String(nerdnos_get_avg_hashrate(), 1); +} +#else String getCurrentHashRate(unsigned long mElapsed) { return String((1.0 * (elapsedKHs * 1000)) / mElapsed, 2); } +#endif mining_data getMiningData(unsigned long mElapsed) { @@ -328,7 +339,7 @@ String getPoolAPIUrl(void) { poolAPIUrl = String(getPublicPool); if (Settings.PoolAddress == "public-pool.io") { poolAPIUrl = "https://public-pool.io:40557/api/client/"; - } + } else { if (Settings.PoolAddress == "nerdminers.org") { poolAPIUrl = "https://pool.nerdminers.org/users/"; @@ -354,13 +365,13 @@ String getPoolAPIUrl(void) { } pool_data getPoolData(void){ - //pool_data pData; - if((mPoolUpdate == 0) || (millis() - mPoolUpdate > UPDATE_POOL_min * 60 * 1000)){ - if (WiFi.status() != WL_CONNECTED) return pData; + //pool_data pData; + if((mPoolUpdate == 0) || (millis() - mPoolUpdate > UPDATE_POOL_min * 60 * 1000)){ + if (WiFi.status() != WL_CONNECTED) return pData; //Make first API call to get global hash and current difficulty HTTPClient http; - http.setReuse(true); - try { + http.setReuse(true); + try { String btcWallet = Settings.BtcWallet; // Serial.println(btcWallet); if (btcWallet.indexOf(".")>0) btcWallet = btcWallet.substring(0,btcWallet.indexOf(".")); @@ -397,16 +408,16 @@ pool_data getPoolData(void){ double temp; if (doc.containsKey("bestDifficulty")) { - temp = doc["bestDifficulty"].as(); + temp = doc["bestDifficulty"].as(); char best_diff_string[16] = {0}; suffix_string(temp, best_diff_string, 16, 0); pData.bestDifficulty = String(best_diff_string); } doc.clear(); mPoolUpdate = millis(); - Serial.println("\n####### Pool Data OK!"); + Serial.println("\n####### Pool Data OK!"); } else { - Serial.println("\n####### Pool Data HTTP Error!"); + Serial.println("\n####### Pool Data HTTP Error!"); /* Serial.println(httpCode); String payload = http.getString(); Serial.println(payload); */ @@ -415,18 +426,18 @@ pool_data getPoolData(void){ pData.workersHash = "E"; pData.workersCount = 0; http.end(); - return pData; + return pData; } http.end(); } catch(...) { - Serial.println("####### Pool Error!"); + Serial.println("####### Pool Error!"); // mPoolUpdate = millis(); pData.bestDifficulty = "P"; pData.workersHash = "Error"; pData.workersCount = 0; http.end(); return pData; - } + } } return pData; } diff --git a/src/stratum.cpp b/src/stratum.cpp index d63619f..a5ec3ac 100644 --- a/src/stratum.cpp +++ b/src/stratum.cpp @@ -10,7 +10,9 @@ #include "utils.h" #include "version.h" +#include +pthread_mutex_t job_mutex = PTHREAD_MUTEX_INITIALIZER; StaticJsonDocument doc; unsigned long id = 1; @@ -99,9 +101,11 @@ bool parse_mining_subscribe(String line, mining_subscribe& mSubscribe) if (error || checkError(doc)) return false; if (!doc.containsKey("result")) return false; + pthread_mutex_lock(&job_mutex); mSubscribe.sub_details = String((const char*) doc["result"][0][0][1]); mSubscribe.extranonce1 = String((const char*) doc["result"][1]); mSubscribe.extranonce2_size = doc["result"][2]; + pthread_mutex_unlock(&job_mutex); return true; } @@ -179,6 +183,7 @@ bool parse_mining_notify(String line, mining_job& mJob) if (error) return false; if (!doc.containsKey("params")) return false; + pthread_mutex_lock(&job_mutex); mJob.job_id = String((const char*) doc["params"][0]); mJob.prev_block_hash = String((const char*) doc["params"][1]); mJob.coinb1 = String((const char*) doc["params"][2]); @@ -188,6 +193,7 @@ bool parse_mining_notify(String line, mining_job& mJob) mJob.nbits = String((const char*) doc["params"][6]); mJob.ntime = String((const char*) doc["params"][7]); mJob.clean_jobs = doc["params"][8]; //bool + pthread_mutex_unlock(&job_mutex); #ifdef DEBUG_MINING Serial.print(" job_id: "); Serial.println(mJob.job_id); diff --git a/src/stratum.h b/src/stratum.h index b6951e3..c0c2ff9 100644 --- a/src/stratum.h +++ b/src/stratum.h @@ -16,7 +16,7 @@ #define BUFFER_JSON_DOC 4096 #define BUFFER 1024 -typedef struct { +typedef struct mining_subscribe { String sub_details; String extranonce1; String extranonce2; @@ -25,7 +25,7 @@ typedef struct { char wPass[20]; } mining_subscribe; -typedef struct { +typedef struct mining_job { String job_id; String prev_block_hash; String coinb1; From eb9ad3c75a73a7a56d78c00e22b230e101c12351 Mon Sep 17 00:00:00 2001 From: shufps Date: Mon, 9 Sep 2024 18:51:15 +0200 Subject: [PATCH 04/18] fixed overflowing 32bit us in history --- src/mining_nerdnos.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mining_nerdnos.cpp b/src/mining_nerdnos.cpp index d96fc11..be002fd 100644 --- a/src/mining_nerdnos.cpp +++ b/src/mining_nerdnos.cpp @@ -65,7 +65,9 @@ static void calculate_hashrate(history_t *history, uint32_t diff) { // add and store the newest sample history->sum += diff; history->diffs[history->newest % ASIC_HISTORY_SIZE] = diff; - history->timestamps[history->newest % ASIC_HISTORY_SIZE] = micros(); + + // micros() wraps around after about 71.58min because it's 32bit casted from 64bit timer :facepalm: + history->timestamps[history->newest % ASIC_HISTORY_SIZE] = esp_timer_get_time(); uint64_t oldest_timestamp = history->timestamps[history->oldest % ASIC_HISTORY_SIZE]; uint64_t newest_timestamp = history->timestamps[history->newest % ASIC_HISTORY_SIZE]; From df42d18944747e9641b3446d69457574487882fa Mon Sep 17 00:00:00 2001 From: shufps Date: Mon, 9 Sep 2024 20:37:16 +0200 Subject: [PATCH 05/18] temperature monitoring of 10k ntc --- src/NerdMinerV2.ino.cpp | 2 ++ src/drivers/displays/tDisplayDriver.cpp | 14 +++++--- src/drivers/nerd-nos/adc.cpp | 48 +++++++++++++++++++++++++ src/drivers/nerd-nos/adc.h | 4 +++ src/drivers/nerd-nos/bm1397.cpp | 6 ++-- src/drivers/nerd-nos/mining.cpp | 1 - src/monitor.cpp | 10 ++++++ src/monitor.h | 5 +-- 8 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 src/drivers/nerd-nos/adc.cpp create mode 100644 src/drivers/nerd-nos/adc.h diff --git a/src/NerdMinerV2.ino.cpp b/src/NerdMinerV2.ino.cpp index 4165105..5fd9a35 100644 --- a/src/NerdMinerV2.ino.cpp +++ b/src/NerdMinerV2.ino.cpp @@ -16,6 +16,7 @@ #include "timeconst.h" #include "drivers/nerd-nos/bm1397.h" #include "drivers/nerd-nos/serial.h" +#include "drivers/nerd-nos/adc.h" #ifdef NERD_NOS #include "mining_nerdnos.h" @@ -147,6 +148,7 @@ void setup() #endif #ifdef NERD_NOS + nerdnos_adc_init(); SERIAL_init(); int chips = BM1397_init(200, 1); Serial.printf("found bm1397: %d\n", chips); diff --git a/src/drivers/displays/tDisplayDriver.cpp b/src/drivers/displays/tDisplayDriver.cpp index 3bab4a2..bb0e470 100644 --- a/src/drivers/displays/tDisplayDriver.cpp +++ b/src/drivers/displays/tDisplayDriver.cpp @@ -25,7 +25,7 @@ void tDisplay_Init(void) pinMode(PIN_ENABLE5V, OUTPUT); digitalWrite(PIN_ENABLE5V, HIGH); #endif - + tft.init(); #ifdef LILYGO_S3_T_EMBED tft.setRotation(ROTATION_270); @@ -65,9 +65,13 @@ void tDisplay_MinerScreen(unsigned long mElapsed) // Print background screen background.pushImage(0, 0, MinerWidth, MinerHeight, MinerScreen); - +#ifdef NERD_NOS + Serial.printf(">>> Completed %s share(s), %s Khashes, avg. hashrate %s GH/s, %s °C\n", + data.completedShares.c_str(), data.totalKHashes.c_str(), data.currentHashRate.c_str(), data.currentTemperature.c_str()); +#else Serial.printf(">>> Completed %s share(s), %s Khashes, avg. hashrate %s KH/s\n", data.completedShares.c_str(), data.totalKHashes.c_str(), data.currentHashRate.c_str()); +#endif // Hashrate render.setFontSize(35); @@ -211,7 +215,7 @@ void tDisplay_BTCprice(unsigned long mElapsed) { clock_data data = getClockData(mElapsed); data.currentDate ="01/12/2023"; - + //if(data.currentDate.indexOf("12/2023")>) { tDisplay_ChristmasContent(data); return; } // Print background screen @@ -231,14 +235,14 @@ void tDisplay_BTCprice(unsigned long mElapsed) render.rdrawString(data.blockHeight.c_str(), 254, 138, TFT_WHITE); // Print Hour - + background.setFreeFont(FSSB9); background.setTextSize(1); background.setTextDatum(TL_DATUM); background.setTextColor(TFT_BLACK); background.drawString(data.currentTime.c_str(), 222, 3, GFXFF); - // Print BTC Price + // Print BTC Price background.setFreeFont(FF24); background.setTextDatum(TR_DATUM); background.setTextSize(1); diff --git a/src/drivers/nerd-nos/adc.cpp b/src/drivers/nerd-nos/adc.cpp new file mode 100644 index 0000000..1699672 --- /dev/null +++ b/src/drivers/nerd-nos/adc.cpp @@ -0,0 +1,48 @@ +#include +#include +#include "driver/adc.h" +#include "esp_adc_cal.h" + +#define ADC_CHANNEL ADC1_CHANNEL_1 // GPIO2 corresponds to ADC1 channel 1 on ESP32-S3 +#define BETA 3380 // Beta value of the thermistor +#define R0 10000 // Resistance at 25°C (10kΩ) +#define ADC_MAX 4095 // Max ADC value for 12-bit resolution +#define DEFAULT_VREF 1100 // Default VREF in millivolts for ESP32 ADC + +// ADC Calibration characteristics +static esp_adc_cal_characteristics_t adc1_chars; + +void nerdnos_adc_init() { + // Configure the ADC + adc1_config_width(ADC_WIDTH_BIT_12); // Set ADC width (12-bit) + adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN_DB_11); // Set attenuation to read the full range of 0 to 3.3V + + // Characterize ADC at given attenuation + esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 0, &adc1_chars); +} + +float nerdnos_get_temperature() { + // Convert the raw ADC value to a voltage using esp_adc_cal + uint32_t voltage_mv = esp_adc_cal_raw_to_voltage(adc1_get_raw(ADC_CHANNEL), &adc1_chars); // Voltage in millivolts + + // Convert millivolts to volts + float voltage = voltage_mv / 1000.0; + + // Ensure the voltage is within a valid range + if (voltage <= 0) { + printf("Error: Invalid voltage reading.\n"); + return -273.15; // Return a clearly invalid temperature to indicate an error + } + + // Calculate the thermistor resistance using the voltage divider formula + // R_T = R0 * (Vout / (VREF - Vout)) + float thermistor_resistance = R0 * (voltage / (3.3 - voltage)); + + // Use the Beta parameter equation to calculate the temperature in Kelvin + float temperature_kelvin = (float)(BETA / (log(thermistor_resistance / R0) + (BETA / 298.15))); + + // Convert the temperature to Celsius + float temperature_celsius = temperature_kelvin - 273.15; + + return temperature_celsius; +} diff --git a/src/drivers/nerd-nos/adc.h b/src/drivers/nerd-nos/adc.h new file mode 100644 index 0000000..787ce2b --- /dev/null +++ b/src/drivers/nerd-nos/adc.h @@ -0,0 +1,4 @@ +#pragma once + +void nerdnos_adc_init(); +float nerdnos_get_temperature(); diff --git a/src/drivers/nerd-nos/bm1397.cpp b/src/drivers/nerd-nos/bm1397.cpp index 1588cf0..4f2fffa 100644 --- a/src/drivers/nerd-nos/bm1397.cpp +++ b/src/drivers/nerd-nos/bm1397.cpp @@ -63,8 +63,8 @@ static void _send_BM1397(uint8_t header, uint8_t *data, uint8_t data_len, bool d packet_type_t packet_type = (header & TYPE_JOB) ? JOB_PACKET : CMD_PACKET; uint8_t total_length = (packet_type == JOB_PACKET) ? (data_len + 6) : (data_len + 5); - // allocate memory for buffer - unsigned char *buf = (unsigned char *)malloc(total_length); + // memory for buffer + uint8_t buf[total_length]; // add the preamble buf[0] = 0x55; @@ -93,8 +93,6 @@ static void _send_BM1397(uint8_t header, uint8_t *data, uint8_t data_len, bool d // send serial data SERIAL_send(buf, total_length, debug); - - free(buf); } static void _send_read_address(void) diff --git a/src/drivers/nerd-nos/mining.cpp b/src/drivers/nerd-nos/mining.cpp index 6f2ed8e..929b072 100644 --- a/src/drivers/nerd-nos/mining.cpp +++ b/src/drivers/nerd-nos/mining.cpp @@ -129,7 +129,6 @@ void nerdnos_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *ne calculate_merkle_root_hash(coinbase_tx.c_str(), job, merkle_root); //Serial.printf("asic merkle root: %s\n", merkle_root); - // we need malloc because we will save it in the job array construct_bm_job(job, merkle_root, 0x1fffe000, next_job); next_job->jobid = strdup(job->job_id.c_str()); diff --git a/src/monitor.cpp b/src/monitor.cpp index 02b4640..376df01 100644 --- a/src/monitor.cpp +++ b/src/monitor.cpp @@ -11,6 +11,7 @@ #ifdef NERD_NOS #include "mining_nerdnos.h" +#include "drivers/nerd-nos/adc.h" #endif extern uint32_t templates; @@ -243,11 +244,19 @@ String getCurrentHashRate(unsigned long mElapsed) { // we have too little space for 2 digits after the decimal point return String(nerdnos_get_avg_hashrate(), 1); } + +String getCurrentTemperature() { + return String(nerdnos_get_temperature(), 2); +} #else String getCurrentHashRate(unsigned long mElapsed) { return String((1.0 * (elapsedKHs * 1000)) / mElapsed, 2); } + +String getCurrentTemperature() { + return String(0.0, 2); +} #endif mining_data getMiningData(unsigned long mElapsed) @@ -269,6 +278,7 @@ mining_data getMiningData(unsigned long mElapsed) data.totalMHashes = Mhashes; data.totalKHashes = totalKHashes; data.currentHashRate = getCurrentHashRate(mElapsed); + data.currentTemperature = getCurrentTemperature(); data.templates = templates; data.bestDiff = best_diff_string; data.timeMining = timeMining; diff --git a/src/monitor.h b/src/monitor.h index bf2b7b6..0ac6e79 100644 --- a/src/monitor.h +++ b/src/monitor.h @@ -12,7 +12,7 @@ //Time update period #define UPDATE_PERIOD_h 5 -//API BTC price (Update to USDT cus it's more liquidity and flow price updade) +//API BTC price (Update to USDT cus it's more liquidity and flow price updade) #define getBTCAPI "https://api.blockchain.com/v3/exchange/tickers/BTC-USDT" #define UPDATE_BTC_min 1 @@ -73,6 +73,7 @@ typedef struct { String valids; String temp; String currentTime; + String currentTemperature; }mining_data; typedef struct { @@ -81,7 +82,7 @@ typedef struct { String currentHashRate; String btcPrice; String blockHeight; - String currentTime; + String currentTime; String currentDate; }clock_data; From 1545cfabb47b6da3ae9357bcd15e1622d587da6e Mon Sep 17 00:00:00 2001 From: shufps Date: Mon, 9 Sep 2024 20:44:15 +0200 Subject: [PATCH 06/18] added vCore to log output --- src/drivers/displays/tDisplayDriver.cpp | 5 +++-- src/drivers/nerd-nos/adc.cpp | 14 +++++++++++--- src/drivers/nerd-nos/adc.h | 1 + src/monitor.cpp | 9 +++++++++ src/monitor.h | 1 + 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/drivers/displays/tDisplayDriver.cpp b/src/drivers/displays/tDisplayDriver.cpp index bb0e470..6a8201d 100644 --- a/src/drivers/displays/tDisplayDriver.cpp +++ b/src/drivers/displays/tDisplayDriver.cpp @@ -66,8 +66,9 @@ void tDisplay_MinerScreen(unsigned long mElapsed) // Print background screen background.pushImage(0, 0, MinerWidth, MinerHeight, MinerScreen); #ifdef NERD_NOS - Serial.printf(">>> Completed %s share(s), %s Khashes, avg. hashrate %s GH/s, %s °C\n", - data.completedShares.c_str(), data.totalKHashes.c_str(), data.currentHashRate.c_str(), data.currentTemperature.c_str()); + Serial.printf(">>> Completed %s share(s), %s Khashes, avg. hashrate %s GH/s, vCore: %smV, temp: %s °C\n", + data.completedShares.c_str(), data.totalKHashes.c_str(), data.currentHashRate.c_str(), + data.vcore.c_str(), data.currentTemperature.c_str()); #else Serial.printf(">>> Completed %s share(s), %s Khashes, avg. hashrate %s KH/s\n", data.completedShares.c_str(), data.totalKHashes.c_str(), data.currentHashRate.c_str()); diff --git a/src/drivers/nerd-nos/adc.cpp b/src/drivers/nerd-nos/adc.cpp index 1699672..630b177 100644 --- a/src/drivers/nerd-nos/adc.cpp +++ b/src/drivers/nerd-nos/adc.cpp @@ -3,7 +3,8 @@ #include "driver/adc.h" #include "esp_adc_cal.h" -#define ADC_CHANNEL ADC1_CHANNEL_1 // GPIO2 corresponds to ADC1 channel 1 on ESP32-S3 +#define NTC_CHANNEL ADC1_CHANNEL_1 // 10k NTC +#define VCORE_CHANNEL ADC1_CHANNEL_2 // vcore voltage #define BETA 3380 // Beta value of the thermistor #define R0 10000 // Resistance at 25°C (10kΩ) #define ADC_MAX 4095 // Max ADC value for 12-bit resolution @@ -15,7 +16,9 @@ static esp_adc_cal_characteristics_t adc1_chars; void nerdnos_adc_init() { // Configure the ADC adc1_config_width(ADC_WIDTH_BIT_12); // Set ADC width (12-bit) - adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN_DB_11); // Set attenuation to read the full range of 0 to 3.3V + adc1_config_channel_atten(NTC_CHANNEL, ADC_ATTEN_DB_11); // Set attenuation to read the full range of 0 to 3.3V + adc1_config_channel_atten(VCORE_CHANNEL, ADC_ATTEN_DB_11); // Set attenuation to read the full range of 0 to 3.3V + // Characterize ADC at given attenuation esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 0, &adc1_chars); @@ -23,7 +26,7 @@ void nerdnos_adc_init() { float nerdnos_get_temperature() { // Convert the raw ADC value to a voltage using esp_adc_cal - uint32_t voltage_mv = esp_adc_cal_raw_to_voltage(adc1_get_raw(ADC_CHANNEL), &adc1_chars); // Voltage in millivolts + uint32_t voltage_mv = esp_adc_cal_raw_to_voltage(adc1_get_raw(NTC_CHANNEL), &adc1_chars); // Voltage in millivolts // Convert millivolts to volts float voltage = voltage_mv / 1000.0; @@ -46,3 +49,8 @@ float nerdnos_get_temperature() { return temperature_celsius; } + +float nerdnos_get_vcore() { + return esp_adc_cal_raw_to_voltage(adc1_get_raw(VCORE_CHANNEL), &adc1_chars); +} + diff --git a/src/drivers/nerd-nos/adc.h b/src/drivers/nerd-nos/adc.h index 787ce2b..a54f95d 100644 --- a/src/drivers/nerd-nos/adc.h +++ b/src/drivers/nerd-nos/adc.h @@ -2,3 +2,4 @@ void nerdnos_adc_init(); float nerdnos_get_temperature(); +float nerdnos_get_vcore(); diff --git a/src/monitor.cpp b/src/monitor.cpp index 376df01..3c1afbd 100644 --- a/src/monitor.cpp +++ b/src/monitor.cpp @@ -248,6 +248,10 @@ String getCurrentHashRate(unsigned long mElapsed) { String getCurrentTemperature() { return String(nerdnos_get_temperature(), 2); } + +String getCurrentVCore() { + return String(nerdnos_get_vcore(), 2); +} #else String getCurrentHashRate(unsigned long mElapsed) { @@ -257,6 +261,10 @@ String getCurrentHashRate(unsigned long mElapsed) String getCurrentTemperature() { return String(0.0, 2); } + +String getCurrentVCore() { + return String(0.0, 2); +} #endif mining_data getMiningData(unsigned long mElapsed) @@ -279,6 +287,7 @@ mining_data getMiningData(unsigned long mElapsed) data.totalKHashes = totalKHashes; data.currentHashRate = getCurrentHashRate(mElapsed); data.currentTemperature = getCurrentTemperature(); + data.vcore = getCurrentVCore(); data.templates = templates; data.bestDiff = best_diff_string; data.timeMining = timeMining; diff --git a/src/monitor.h b/src/monitor.h index 0ac6e79..6e57336 100644 --- a/src/monitor.h +++ b/src/monitor.h @@ -74,6 +74,7 @@ typedef struct { String temp; String currentTime; String currentTemperature; + String vcore; }mining_data; typedef struct { From 9056765b21e2aa5ad3814eddc98a3786d27fff12 Mon Sep 17 00:00:00 2001 From: shufps Date: Mon, 9 Sep 2024 20:45:17 +0200 Subject: [PATCH 07/18] removed white space in log output --- src/drivers/displays/tDisplayDriver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/displays/tDisplayDriver.cpp b/src/drivers/displays/tDisplayDriver.cpp index 6a8201d..8add019 100644 --- a/src/drivers/displays/tDisplayDriver.cpp +++ b/src/drivers/displays/tDisplayDriver.cpp @@ -66,7 +66,7 @@ void tDisplay_MinerScreen(unsigned long mElapsed) // Print background screen background.pushImage(0, 0, MinerWidth, MinerHeight, MinerScreen); #ifdef NERD_NOS - Serial.printf(">>> Completed %s share(s), %s Khashes, avg. hashrate %s GH/s, vCore: %smV, temp: %s °C\n", + Serial.printf(">>> Completed %s share(s), %s Khashes, avg. hashrate %s GH/s, vCore: %smV, temp: %s°C\n", data.completedShares.c_str(), data.totalKHashes.c_str(), data.currentHashRate.c_str(), data.vcore.c_str(), data.currentTemperature.c_str()); #else From 901be540f51efe29e5c731d9cbc10a266e0e3e0c Mon Sep 17 00:00:00 2001 From: shufps Date: Tue, 10 Sep 2024 17:34:36 +0200 Subject: [PATCH 08/18] fix memoryleak, thx @wantclue --- src/mining_nerdnos.cpp | 80 ++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/src/mining_nerdnos.cpp b/src/mining_nerdnos.cpp index be002fd..181dfaa 100644 --- a/src/mining_nerdnos.cpp +++ b/src/mining_nerdnos.cpp @@ -27,9 +27,13 @@ extern pthread_mutex_t job_mutex; extern double best_diff; extern unsigned long mLastTXtoPool; -// to track the jobs + // we can have 32 different job ids -static bm_job_t asic_jobs[32] = {0}; +#define ASIC_JOB_COUNT 32 + +// to track the jobs +static bm_job_t asic_jobs[ASIC_JOB_COUNT] = {0}; + // to track hashrate #define ASIC_HISTORY_SIZE 128 @@ -54,6 +58,13 @@ double nerdnos_get_avg_hashrate() { return history.avg_gh; } +static void safe_free_job(bm_job_t *job) { + if (job && job->ntime) { + nerdnos_free_bm_job(job); + job->ntime = 0; // Only clear the ntime pointer to mark it free + } +} + // incremental ringbuffer based hashrate calculation static void calculate_hashrate(history_t *history, uint32_t diff) { // if we have wrapped around at least once our ringbuffer is full @@ -98,7 +109,7 @@ void runASIC(void * task_id) { TimerHandle_t job_timer = xTimerCreate("NERDNOS_Job_Timer", NERDNOS_JOB_INTERVAL_MS / portTICK_PERIOD_MS, pdTRUE, NULL, create_job_timer); if (job_timer == NULL) { - Serial.println("Failed to create NERNOS timer"); + Serial.println("Failed to create NERDNOS timer"); return; } @@ -111,16 +122,11 @@ void runASIC(void * task_id) { uint32_t extranonce_2 = 0; while(1) { // wait for new job - while(1) { - if (mMiner.newJob == true) { - break; - } - vTaskDelay(100 / portTICK_PERIOD_MS); //Small delay + while(!mMiner.newJob) { + vTaskDelay(100 / portTICK_PERIOD_MS); } - if(mMiner.newJob) { - mMiner.newJob = false; //Clear newJob flag - } + mMiner.newJob = false; //Clear newJob flag mMiner.inRun = true; //Set inRun flag Serial.println(">>> STARTING TO HASH NONCES"); @@ -146,12 +152,10 @@ void runASIC(void * task_id) { extranonce_2++; // use extranonce2 as job id - uint8_t asic_job_id = (uint8_t) (extranonce_2 % 32); + uint8_t asic_job_id = (uint8_t) (extranonce_2 % ASIC_JOB_COUNT); - // if it was used before, we have to free the pointers - if (asic_jobs[asic_job_id].ntime) { - nerdnos_free_bm_job(&asic_jobs[asic_job_id]); - } + // free memory if this slot was used before + safe_free_job(&asic_jobs[asic_job_id]); // create the next asic job // make sure that another task doesn't mess with the data while @@ -160,7 +164,7 @@ void runASIC(void * task_id) { if (current_difficulty != mMiner.poolDifficulty) { current_difficulty = mMiner.poolDifficulty; nerdnos_set_asic_difficulty(current_difficulty); - Serial.printf("Set difficulty to %llu\n", current_difficulty); + Serial.printf("Set difficulty to %lu\n", current_difficulty); } nerdnos_create_job(&mWorker, &mJob, &asic_jobs[asic_job_id], extranonce_2, current_difficulty); pthread_mutex_unlock(&job_mutex); @@ -172,11 +176,11 @@ void runASIC(void * task_id) { // but we only have a single thread so it should be okay // process all results if we have more than one // this is okay because serial uses a buffer and (most likely^^) DMA - task_result *result = 0; - while (result = nerdnos_proccess_work(version, 1), result != NULL) { - // if we have received a job we don't know - if (!asic_jobs[result->job_id].ntime) { - Serial.printf("No Job found for received ID %02x\n", result->job_id); + task_result *result = NULL; + while ((result = nerdnos_proccess_work(version, 1)) != NULL) { + // check if the ID is in the valid range and the slot is not empty + if (result->job_id >= ASIC_JOB_COUNT || !asic_jobs[result->job_id].ntime) { + Serial.printf("Invalid job ID or no job found for ID %02x\n", result->job_id); continue; } @@ -190,41 +194,49 @@ void runASIC(void * task_id) { hash); // update best diff - if (diff_hash > best_diff) + if (diff_hash > best_diff) { best_diff = diff_hash; + } // calculate the hashrate if (diff_hash >= asic_jobs[result->job_id].pool_diff) { calculate_hashrate(&history, asic_jobs[result->job_id].pool_diff); - Serial.printf("avg hashrate: %.2fGH/s (history spans %2.fs, %d shares)\n", history.avg_gh, history.duration, history.shares); + Serial.printf("avg hashrate: %.2fGH/s (history spans %.2fs, %d shares)\n", history.avg_gh, history.duration, history.shares); } if(diff_hash > mMiner.poolDifficulty) { tx_mining_submit_asic(client, mWorker, &asic_jobs[result->job_id], result); Serial.println("valid share!"); - Serial.print(" - Current diff share: "); Serial.println(diff_hash,12); - Serial.print(" - Current pool diff : "); Serial.println(mMiner.poolDifficulty,12); - Serial.print(" - TX SHARE: "); - for (size_t i = 0; i < 32; i++) + Serial.printf(" - Current diff share: %.3f\n", diff_hash); + Serial.printf(" - Current pool diff : %.3f\n", mMiner.poolDifficulty); + Serial.printf("Free heap after share: %u bytes\n", ESP.getFreeHeap()); + for (size_t i = 0; i < 32; i++) { Serial.printf("%02x", hash[i]); - - #ifdef DEBUG_MINING - Serial.println(""); + } + Serial.println(); +#ifdef DEBUG_MINING Serial.print(" - Current nonce: "); Serial.println(nonce); Serial.print(" - Current block header: "); for (size_t i = 0; i < 80; i++) { Serial.printf("%02x", mMiner.bytearray_blockheader[i]); } - #endif - Serial.println(""); + Serial.println(); +#endif + mLastTXtoPool = millis(); } } } - Serial.println ("MINER WORK ABORTED >> waiting new job"); + Serial.println("MINER WORK ABORTED >> waiting new job"); mMiner.inRun = false; uint32_t duration = micros() - startT; + + // clean jobs + for (int i = 0; i < ASIC_JOB_COUNT; i++) { + safe_free_job(&asic_jobs[i]); + } + /* if (esp_task_wdt_reset() == ESP_OK) Serial.print(">>> Resetting watchdog timer"); From c772ac4a9121f8bf7c4c67311be145b6666db380 Mon Sep 17 00:00:00 2001 From: shufps Date: Fri, 13 Sep 2024 15:50:34 +0200 Subject: [PATCH 09/18] debug output on malformed merkle tree branch --- src/drivers/nerd-nos/bm1397.cpp | 19 ++++++++++--------- src/drivers/nerd-nos/mining.cpp | 22 +++++++++++++++++++++- src/drivers/nerd-nos/utils.cpp | 18 ++++++++++++++++++ src/drivers/nerd-nos/utils.h | 4 ++++ 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/drivers/nerd-nos/bm1397.cpp b/src/drivers/nerd-nos/bm1397.cpp index 4f2fffa..a45a826 100644 --- a/src/drivers/nerd-nos/bm1397.cpp +++ b/src/drivers/nerd-nos/bm1397.cpp @@ -195,7 +195,7 @@ void BM1397_send_hash_frequency(float frequency) vTaskDelay(10 / portTICK_PERIOD_MS); - ESP_LOGI(TAG, "Setting Frequency to %.2fMHz (%.2f)", frequency, newf); + Serial.printf("Setting Frequency to %.2fMHz (%.2f)\n", frequency, newf); } @@ -208,13 +208,13 @@ static uint8_t _send_init(uint64_t frequency, uint16_t asic_count) while (true) { int received = SERIAL_rx(asic_response_buffer, 11, 1000); if (received > 0) { - ESP_LOG_BUFFER_HEX(TAG, asic_response_buffer, received); + //ESP_LOG_BUFFER_HEX(TAG, asic_response_buffer, received); chip_counter++; } else { break; } } - ESP_LOGI(TAG, "%i chip(s) detected on the chain, expected %i", chip_counter, asic_count); + Serial.printf("%i chip(s) detected on the chain, expected %i\n", chip_counter, asic_count); // send serial data vTaskDelay(SLEEP_TIME / portTICK_PERIOD_MS); @@ -269,7 +269,7 @@ static void _reset(void) uint8_t BM1397_init(uint64_t frequency, uint16_t asic_count) { - ESP_LOGI(TAG, "Initializing BM1397"); + Serial.println("Initializing BM1397"); memset(asic_response_buffer, 0, sizeof(asic_response_buffer)); @@ -299,7 +299,7 @@ int BM1397_set_default_baud(void) int BM1397_set_max_baud(void) { // divider of 0 for 3,125,000 - ESP_LOGI(TAG, "Setting max baud of 3125000"); + Serial.println("Setting max baud of 3125000"); unsigned char baudrate[9] = {0x00, MISC_CONTROL, 0x00, 0x00, 0b01100000, 0b00110001}; ; // baudrate - misc_control _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), baudrate, 6, BM1397_SERIALTX_DEBUG); @@ -330,7 +330,7 @@ void BM1397_set_job_difficulty_mask(int difficulty) job_difficulty_mask[5 - i] = reverse_bits(value); } - ESP_LOGI(TAG, "Setting job ASIC mask to %d", difficulty); + Serial.printf("Setting job ASIC mask to %d\n", difficulty); _send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), job_difficulty_mask, 6, BM1397_SERIALTX_DEBUG); } @@ -369,7 +369,7 @@ asic_result *BM1397_receive_work(uint16_t timeout) if (received < 0) { - ESP_LOGI(TAG, "Error in serial RX"); + Serial.println("Error in serial RX"); return NULL; } else if (received == 0) @@ -380,8 +380,9 @@ asic_result *BM1397_receive_work(uint16_t timeout) if (received != 9 || asic_response_buffer[0] != 0xAA || asic_response_buffer[1] != 0x55) { - ESP_LOGI(TAG, "Serial RX invalid %i", received); - ESP_LOG_BUFFER_HEX(TAG, asic_response_buffer, received); + Serial.println("Serial RX invalid. Resetting receive buffer ..."); + //ESP_LOG_BUFFER_HEX(TAG, asic_response_buffer, received); + SERIAL_clear_buffer(); return NULL; } diff --git a/src/drivers/nerd-nos/mining.cpp b/src/drivers/nerd-nos/mining.cpp index 929b072..f8cda28 100644 --- a/src/drivers/nerd-nos/mining.cpp +++ b/src/drivers/nerd-nos/mining.cpp @@ -51,6 +51,21 @@ double nerdnos_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const return ds; } +static void dump(mining_job *job) { + Serial.printf("job_id: %s\n", job->job_id.c_str()); + Serial.printf("prev_block_hash: %s\n", job->prev_block_hash.c_str()); + Serial.printf("coinb1: %s\n", job->coinb1.c_str()); + Serial.printf("coinb2: %s\n", job->coinb2.c_str()); + Serial.printf("nbits: %s\n", job->nbits.c_str()); + Serial.printf("version: %s\n", job->version.c_str()); + Serial.printf("ntime: %s\n", job->ntime.c_str()); + Serial.printf("taget: %lu\n", job->target); + Serial.printf("clean_jobs: %s\n", job->clean_jobs ? "true" : "false"); + for (size_t i = 0; i < job->merkle_branch.size(); i++) { + const char* m = job->merkle_branch[i]; + Serial.printf("merkle_branch[%d]: %s\n", i, m); + } +} static void calculate_merkle_root_hash(const char *coinbase_tx, mining_job* job, char merkle_root_hash[65]) { size_t coinbase_tx_bin_len = strlen(coinbase_tx) / 2; @@ -63,7 +78,12 @@ static void calculate_merkle_root_hash(const char *coinbase_tx, mining_job* job, memcpy(both_merkles, new_root, 32); for (size_t i = 0; i < job->merkle_branch.size(); i++) { - hex2bin((const char*) job->merkle_branch[i], &both_merkles[32], 32); + const char* m = job->merkle_branch[i]; + // if merkle branch is not what we expect, dump the job + if (!is_hex_string(m)) { + dump(job); + } + hex2bin(m, &both_merkles[32], 32); double_sha256_bin(both_merkles, 64, new_root); memcpy(both_merkles, new_root, 32); } diff --git a/src/drivers/nerd-nos/utils.cpp b/src/drivers/nerd-nos/utils.cpp index 01a47e1..ce409d3 100644 --- a/src/drivers/nerd-nos/utils.cpp +++ b/src/drivers/nerd-nos/utils.cpp @@ -127,6 +127,24 @@ uint8_t hex2val(char c) } } +bool is_hex_digit(char c) { + return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + +bool is_hex_string(const char* str) { + // Check if the string is exactly 64 characters long + if (strlen(str) != 64) { + return false; + } + // Check if each character is a valid hexadecimal digit + for (size_t i = 0; i < 64; i++) { + if (!is_hex_digit(str[i])) { + return false; + } + } + return true; +} + size_t hex2bin(const char *hex, uint8_t *bin, size_t bin_len) { size_t len = 0; diff --git a/src/drivers/nerd-nos/utils.h b/src/drivers/nerd-nos/utils.h index 78b6852..273a965 100644 --- a/src/drivers/nerd-nos/utils.h +++ b/src/drivers/nerd-nos/utils.h @@ -2,6 +2,7 @@ #include #include +#include int hex2char(uint8_t x, char *c); @@ -38,3 +39,6 @@ unsigned char reverse_bits(unsigned char num); int largest_power_of_two(int num); uint32_t increment_bitmask(const uint32_t value, const uint32_t mask); + +bool is_hex_digit(char c); +bool is_hex_string(const char* str); \ No newline at end of file From def93a857950ccb097a4fbb86f634f0dd6e5ffd5 Mon Sep 17 00:00:00 2001 From: shufps Date: Fri, 13 Sep 2024 18:22:41 +0200 Subject: [PATCH 10/18] copied merkle tree for nerdnos --- src/drivers/nerd-nos/mining.cpp | 8 ++++---- src/stratum.cpp | 15 ++++++++++++++- src/stratum.h | 3 ++- src/utils.cpp | 30 +++++++++++++++--------------- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/drivers/nerd-nos/mining.cpp b/src/drivers/nerd-nos/mining.cpp index f8cda28..c6c291d 100644 --- a/src/drivers/nerd-nos/mining.cpp +++ b/src/drivers/nerd-nos/mining.cpp @@ -61,8 +61,8 @@ static void dump(mining_job *job) { Serial.printf("ntime: %s\n", job->ntime.c_str()); Serial.printf("taget: %lu\n", job->target); Serial.printf("clean_jobs: %s\n", job->clean_jobs ? "true" : "false"); - for (size_t i = 0; i < job->merkle_branch.size(); i++) { - const char* m = job->merkle_branch[i]; + for (size_t i = 0; i < job->merkle_branch_size; i++) { + const char* m = job->merkle_branch[i].c_str(); Serial.printf("merkle_branch[%d]: %s\n", i, m); } } @@ -77,8 +77,8 @@ static void calculate_merkle_root_hash(const char *coinbase_tx, mining_job* job, double_sha256_bin(coinbase_tx_bin, coinbase_tx_bin_len, new_root); memcpy(both_merkles, new_root, 32); - for (size_t i = 0; i < job->merkle_branch.size(); i++) { - const char* m = job->merkle_branch[i]; + for (size_t i = 0; i < job->merkle_branch_size; i++) { + const char* m = job->merkle_branch[i].c_str(); // if merkle branch is not what we expect, dump the job if (!is_hex_string(m)) { dump(job); diff --git a/src/stratum.cpp b/src/stratum.cpp index a5ec3ac..3b0fd33 100644 --- a/src/stratum.cpp +++ b/src/stratum.cpp @@ -188,7 +188,20 @@ bool parse_mining_notify(String line, mining_job& mJob) mJob.prev_block_hash = String((const char*) doc["params"][1]); mJob.coinb1 = String((const char*) doc["params"][2]); mJob.coinb2 = String((const char*) doc["params"][3]); - mJob.merkle_branch = doc["params"][4]; + + // this only copies references to the static json buffer + // and can lead to crashes when there is a new stratum response + // and the content of the array is still needed like on NerdNOS + // that computes the merkle tree new each 30ms^^ + //mJob.merkle_branch = doc["params"][4]; + + // This copies the merkle branch + JsonArray merkle_tree = doc["params"][4]; + mJob.merkle_branch_size = merkle_tree.size(); + for (size_t i = 0; i < mJob.merkle_branch_size; i++) { + mJob.merkle_branch[i] = String((const char*) merkle_tree[i]); + } + mJob.version = String((const char*) doc["params"][5]); mJob.nbits = String((const char*) doc["params"][6]); mJob.ntime = String((const char*) doc["params"][7]); diff --git a/src/stratum.h b/src/stratum.h index c0c2ff9..8d5abca 100644 --- a/src/stratum.h +++ b/src/stratum.h @@ -31,7 +31,8 @@ typedef struct mining_job { String coinb1; String coinb2; String nbits; - JsonArray merkle_branch; + String merkle_branch[MAX_MERKLE_BRANCHES]; + size_t merkle_branch_size; String version; uint32_t target; String ntime; diff --git a/src/utils.cpp b/src/utils.cpp index 20120be..1a6a95a 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -170,13 +170,13 @@ void getNextExtranonce2(int extranonce2_size, char *extranonce2) { } miner_data init_miner_data(void){ - + miner_data newMinerData; newMinerData.poolDifficulty = DEFAULT_DIFFICULTY; newMinerData.inRun = false; newMinerData.newJob = false; - + return newMinerData; } @@ -185,14 +185,14 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ miner_data mMiner = init_miner_data(); // calculate target - target = (nbits[2:]+'00'*(int(nbits[:2],16) - 3)).zfill(64) - + char target[TARGET_BUFFER_SIZE+1]; memset(target, '0', TARGET_BUFFER_SIZE); int zeros = (int) strtol(mJob.nbits.substring(0, 2).c_str(), 0, 16) - 3; memcpy(target + zeros - 2, mJob.nbits.substring(2).c_str(), mJob.nbits.length() - 2); target[TARGET_BUFFER_SIZE] = 0; Serial.print(" target: "); Serial.println(target); - + // bytearray target size_t size_target = to_byte_array(target, 32, mMiner.bytearray_target); @@ -204,12 +204,12 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ // get extranonce2 - extranonce2 = hex(random.randint(0,2**32-1))[2:].zfill(2*extranonce2_size) //To review - char extranonce2_char[2 * mWorker.extranonce2_size+1]; + char extranonce2_char[2 * mWorker.extranonce2_size+1]; mWorker.extranonce2.toCharArray(extranonce2_char, 2 * mWorker.extranonce2_size + 1); getNextExtranonce2(mWorker.extranonce2_size, extranonce2_char); mWorker.extranonce2 = String(extranonce2_char); //mWorker.extranonce2 = "00000002"; - + //get coinbase - coinbase_hash_bin = hashlib.sha256(hashlib.sha256(binascii.unhexlify(coinbase)).digest()).digest() String coinbase = mJob.coinb1 + mWorker.extranonce1 + mWorker.extranonce2 + mJob.coinb2; Serial.print(" coinbase: "); Serial.println(coinbase); @@ -229,10 +229,10 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ mbedtls_sha256_context ctx; mbedtls_sha256_init(&ctx); - + byte interResult[32]; // 256 bit byte shaResult[32]; // 256 bit - + mbedtls_sha256_starts_ret(&ctx,0); mbedtls_sha256_update_ret(&ctx, bytearray, str_len); mbedtls_sha256_finish_ret(&ctx, interResult); @@ -249,13 +249,13 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ Serial.println(""); #endif - + // copy coinbase hash memcpy(mMiner.merkle_result, shaResult, sizeof(shaResult)); - + byte merkle_concatenated[32 * 2]; - for (size_t k=0; k < mJob.merkle_branch.size(); k++) { - const char* merkle_element = (const char*) mJob.merkle_branch[k]; + for (size_t k=0; k < mJob.merkle_branch_size; k++) { + const char* merkle_element = (const char*) mJob.merkle_branch[k].c_str(); uint8_t bytearray[32]; size_t res = to_byte_array(merkle_element, 64, bytearray); @@ -294,7 +294,7 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ #endif } // merkle root from merkle_result - + Serial.print(" merkle sha : "); char merkle_root[65]; for (int i = 0; i < 32; i++) { @@ -308,7 +308,7 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ // j.block_header = ''.join([j.version, j.prevhash, merkle_root, j.ntime, j.nbits]) String blockheader = mJob.version + mJob.prev_block_hash + String(merkle_root) + mJob.ntime + mJob.nbits + "00000000"; str_len = blockheader.length()/2; - + //uint8_t bytearray_blockheader[str_len]; res = to_byte_array(blockheader.c_str(), str_len*2, mMiner.bytearray_blockheader); @@ -375,7 +375,7 @@ miner_data calculateMiningData(mining_subscribe& mWorker, mining_job mJob){ #ifdef DEBUG_MINING - Serial.print(" >>> bytearray_blockheader : "); + Serial.print(" >>> bytearray_blockheader : "); for (size_t i = 0; i < 4; i++) Serial.printf("%02x", mMiner.bytearray_blockheader[i]); Serial.println(""); From 087253656f23ae19989604dc978d084b9f16ea37 Mon Sep 17 00:00:00 2001 From: shufps Date: Fri, 13 Sep 2024 19:09:52 +0200 Subject: [PATCH 11/18] removed global receive buffer to not loose nonces --- src/drivers/nerd-nos/bm1397.cpp | 43 +++++++++++++++------------------ src/drivers/nerd-nos/bm1397.h | 2 +- src/drivers/nerd-nos/mining.cpp | 4 +-- src/drivers/nerd-nos/mining.h | 2 +- src/drivers/nerd-nos/serial.cpp | 8 +++--- src/mining_nerdnos.cpp | 24 ++++++++---------- 6 files changed, 39 insertions(+), 44 deletions(-) diff --git a/src/drivers/nerd-nos/bm1397.cpp b/src/drivers/nerd-nos/bm1397.cpp index a45a826..5c2c45a 100644 --- a/src/drivers/nerd-nos/bm1397.cpp +++ b/src/drivers/nerd-nos/bm1397.cpp @@ -48,9 +48,6 @@ typedef struct __attribute__((__packed__)) static const char *TAG = "bm1397Module"; -static uint8_t asic_response_buffer[CHUNK_SIZE]; -static task_result result; - uint32_t increment_bitmask(const uint32_t value, const uint32_t mask); /// @brief @@ -203,10 +200,11 @@ static uint8_t _send_init(uint64_t frequency, uint16_t asic_count) { // send the init command _send_read_address(); + uint8_t buf[11] = {0}; int chip_counter = 0; while (true) { - int received = SERIAL_rx(asic_response_buffer, 11, 1000); + int received = SERIAL_rx(buf, 11, 1000); if (received > 0) { //ESP_LOG_BUFFER_HEX(TAG, asic_response_buffer, received); chip_counter++; @@ -271,8 +269,6 @@ uint8_t BM1397_init(uint64_t frequency, uint16_t asic_count) { Serial.println("Initializing BM1397"); - memset(asic_response_buffer, 0, sizeof(asic_response_buffer)); - gpio_set_direction(NERD_NOS_GPIO_PEN, GPIO_MODE_OUTPUT); gpio_set_level(NERD_NOS_GPIO_PEN, 1); @@ -361,46 +357,47 @@ void BM1397_send_work(bm_job_t *next_bm_job, uint8_t job_id) _send_BM1397((TYPE_JOB | GROUP_SINGLE | CMD_WRITE), (uint8_t*) &job, sizeof(job_packet_t), BM1397_DEBUG_WORK); } -asic_result *BM1397_receive_work(uint16_t timeout) +bool BM1397_receive_work(uint16_t timeout, asic_result *result) { + uint8_t *rcv_buf = (uint8_t*) result; // wait for a response, wait time is pretty arbitrary - int received = SERIAL_rx(asic_response_buffer, 9, timeout); + int received = SERIAL_rx(rcv_buf, 9, timeout); if (received < 0) { Serial.println("Error in serial RX"); - return NULL; + return false; } else if (received == 0) { // Didn't find a solution, restart and try again - return NULL; + return false; } - if (received != 9 || asic_response_buffer[0] != 0xAA || asic_response_buffer[1] != 0x55) + if (received != 9 || rcv_buf[0] != 0xAA || rcv_buf[1] != 0x55) { Serial.println("Serial RX invalid. Resetting receive buffer ..."); //ESP_LOG_BUFFER_HEX(TAG, asic_response_buffer, received); SERIAL_clear_buffer(); - return NULL; + return false; } + return true; - return (asic_result *)asic_response_buffer; } -task_result *BM1397_proccess_work(uint32_t version, uint16_t timeout) +bool BM1397_proccess_work(uint32_t version, uint16_t timeout, task_result *result) { - asic_result *asic_result = BM1397_receive_work(timeout); + asic_result asic_result; - if (asic_result == NULL) + if (!BM1397_receive_work(timeout, &asic_result)) { ESP_LOGI(TAG, "return null"); - return NULL; + return false; } - uint8_t rx_job_id = (asic_result->job_id & 0xfc) >> 2; - uint8_t rx_midstate_index = asic_result->job_id & 0x03; + uint8_t rx_job_id = (asic_result.job_id & 0xfc) >> 2; + uint8_t rx_midstate_index = asic_result.job_id & 0x03; uint32_t rolled_version = version; for (int i = 0; i < rx_midstate_index; i++) @@ -408,10 +405,10 @@ task_result *BM1397_proccess_work(uint32_t version, uint16_t timeout) rolled_version = increment_bitmask(rolled_version, 0x1fffe000); } - result.job_id = rx_job_id; - result.nonce = asic_result->nonce; - result.rolled_version = rolled_version; + result->job_id = rx_job_id; + result->nonce = asic_result.nonce; + result->rolled_version = rolled_version; - return &result; + return true; } diff --git a/src/drivers/nerd-nos/bm1397.h b/src/drivers/nerd-nos/bm1397.h index 743d140..02213e6 100644 --- a/src/drivers/nerd-nos/bm1397.h +++ b/src/drivers/nerd-nos/bm1397.h @@ -17,5 +17,5 @@ void BM1397_set_job_difficulty_mask(int); int BM1397_set_max_baud(void); int BM1397_set_default_baud(void); void BM1397_send_hash_frequency(float frequency); -task_result *BM1397_proccess_work(uint32_t version, uint16_t timeout); +bool BM1397_proccess_work(uint32_t version, uint16_t timeout, task_result *result); diff --git a/src/drivers/nerd-nos/mining.cpp b/src/drivers/nerd-nos/mining.cpp index c6c291d..9a1f5cb 100644 --- a/src/drivers/nerd-nos/mining.cpp +++ b/src/drivers/nerd-nos/mining.cpp @@ -160,8 +160,8 @@ void nerdnos_send_work(bm_job_t *next_bm_job, uint8_t job_id) { BM1397_send_work(next_bm_job, job_id); } -task_result *nerdnos_proccess_work(uint32_t version, uint16_t timeout) { - return BM1397_proccess_work(version, timeout); +bool nerdnos_proccess_work(uint32_t version, uint16_t timeout, task_result *result) { + return BM1397_proccess_work(version, timeout, result); } void nerdnos_free_bm_job(bm_job_t *job) { diff --git a/src/drivers/nerd-nos/mining.h b/src/drivers/nerd-nos/mining.h index 63098c9..fff9eaf 100644 --- a/src/drivers/nerd-nos/mining.h +++ b/src/drivers/nerd-nos/mining.h @@ -16,7 +16,7 @@ void nerdnos_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *ne void nerdnos_send_work(bm_job_t *next_bm_job, uint8_t job_id); // receive and process responses -task_result *nerdnos_proccess_work(uint32_t version, uint16_t timeout); +bool nerdnos_proccess_work(uint32_t version, uint16_t timeout, task_result *result); // test difficulty double nerdnos_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const uint32_t rolled_version, uint8_t hash_result[32]); diff --git a/src/drivers/nerd-nos/serial.cpp b/src/drivers/nerd-nos/serial.cpp index 7a88b2c..bbb56ff 100644 --- a/src/drivers/nerd-nos/serial.cpp +++ b/src/drivers/nerd-nos/serial.cpp @@ -51,8 +51,8 @@ int SERIAL_send(uint8_t *data, int len, bool debug) return uart_write_bytes(UART_NUM_1, (const char *)data, len); } -int SERIAL_check_for_data() { - int length; +size_t SERIAL_check_for_data() { + size_t length; uart_get_buffered_data_len(UART_NUM_1, (size_t*)&length); return length; } @@ -65,7 +65,9 @@ int SERIAL_check_for_data() { int16_t SERIAL_rx(uint8_t *buf, uint16_t size, uint16_t timeout_ms) { // don't return incomplete data - if (SERIAL_check_for_data() < size) { + size_t available = SERIAL_check_for_data(); + if (available && available < size) { + Serial.printf("not returning parts of data ... %d vs %d\n", (int) available, (int) size); return 0; } diff --git a/src/mining_nerdnos.cpp b/src/mining_nerdnos.cpp index 181dfaa..290e4b8 100644 --- a/src/mining_nerdnos.cpp +++ b/src/mining_nerdnos.cpp @@ -172,15 +172,11 @@ void runASIC(void * task_id) { // send the job and nerdnos_send_work(&asic_jobs[asic_job_id], asic_job_id); - // the pointer returned is the RS232 receive buffer :shushing-face: - // but we only have a single thread so it should be okay - // process all results if we have more than one - // this is okay because serial uses a buffer and (most likely^^) DMA - task_result *result = NULL; - while ((result = nerdnos_proccess_work(version, 1)) != NULL) { + task_result result = {0}; + while (nerdnos_proccess_work(version, 1, &result)) { // check if the ID is in the valid range and the slot is not empty - if (result->job_id >= ASIC_JOB_COUNT || !asic_jobs[result->job_id].ntime) { - Serial.printf("Invalid job ID or no job found for ID %02x\n", result->job_id); + if (result.job_id >= ASIC_JOB_COUNT || !asic_jobs[result.job_id].ntime) { + Serial.printf("Invalid job ID or no job found for ID %02x\n", result.job_id); continue; } @@ -188,9 +184,9 @@ void runASIC(void * task_id) { // check the nonce difficulty double diff_hash = nerdnos_test_nonce_value( - &asic_jobs[result->job_id], - result->nonce, - result->rolled_version, + &asic_jobs[result.job_id], + result.nonce, + result.rolled_version, hash); // update best diff @@ -199,14 +195,14 @@ void runASIC(void * task_id) { } // calculate the hashrate - if (diff_hash >= asic_jobs[result->job_id].pool_diff) { - calculate_hashrate(&history, asic_jobs[result->job_id].pool_diff); + if (diff_hash >= asic_jobs[result.job_id].pool_diff) { + calculate_hashrate(&history, asic_jobs[result.job_id].pool_diff); Serial.printf("avg hashrate: %.2fGH/s (history spans %.2fs, %d shares)\n", history.avg_gh, history.duration, history.shares); } if(diff_hash > mMiner.poolDifficulty) { - tx_mining_submit_asic(client, mWorker, &asic_jobs[result->job_id], result); + tx_mining_submit_asic(client, mWorker, &asic_jobs[result.job_id], &result); Serial.println("valid share!"); Serial.printf(" - Current diff share: %.3f\n", diff_hash); Serial.printf(" - Current pool diff : %.3f\n", mMiner.poolDifficulty); From 8efa73b1ca878f6373fbecbec50137bbe708a3d7 Mon Sep 17 00:00:00 2001 From: shufps Date: Fri, 13 Sep 2024 20:37:46 +0200 Subject: [PATCH 12/18] fixed history timestamps (uint64_t instead of uint32_t) --- src/mining_nerdnos.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mining_nerdnos.cpp b/src/mining_nerdnos.cpp index 290e4b8..38f815f 100644 --- a/src/mining_nerdnos.cpp +++ b/src/mining_nerdnos.cpp @@ -40,7 +40,7 @@ static bm_job_t asic_jobs[ASIC_JOB_COUNT] = {0}; typedef struct { uint32_t diffs[ASIC_HISTORY_SIZE]; - uint32_t timestamps[ASIC_HISTORY_SIZE]; + uint64_t timestamps[ASIC_HISTORY_SIZE]; uint32_t newest; uint32_t oldest; uint64_t sum; @@ -78,7 +78,7 @@ static void calculate_hashrate(history_t *history, uint32_t diff) { history->diffs[history->newest % ASIC_HISTORY_SIZE] = diff; // micros() wraps around after about 71.58min because it's 32bit casted from 64bit timer :facepalm: - history->timestamps[history->newest % ASIC_HISTORY_SIZE] = esp_timer_get_time(); + history->timestamps[history->newest % ASIC_HISTORY_SIZE] = (uint64_t) esp_timer_get_time(); uint64_t oldest_timestamp = history->timestamps[history->oldest % ASIC_HISTORY_SIZE]; uint64_t newest_timestamp = history->timestamps[history->newest % ASIC_HISTORY_SIZE]; From 745a9716845b75d3b2bb0effecfa481ff83cf655 Mon Sep 17 00:00:00 2001 From: shufps Date: Fri, 13 Sep 2024 20:38:40 +0200 Subject: [PATCH 13/18] show mV without decimal places --- src/monitor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/monitor.cpp b/src/monitor.cpp index 3c1afbd..b0a613c 100644 --- a/src/monitor.cpp +++ b/src/monitor.cpp @@ -250,7 +250,7 @@ String getCurrentTemperature() { } String getCurrentVCore() { - return String(nerdnos_get_vcore(), 2); + return String(nerdnos_get_vcore(), 0); } #else String getCurrentHashRate(unsigned long mElapsed) @@ -263,7 +263,7 @@ String getCurrentTemperature() { } String getCurrentVCore() { - return String(0.0, 2); + return String(0.0, 0); } #endif From 2f288edbe6e12850febe0f9a56468ec11c670a19 Mon Sep 17 00:00:00 2001 From: shufps Date: Fri, 13 Sep 2024 20:43:08 +0200 Subject: [PATCH 14/18] removed additional debugging again --- src/drivers/nerd-nos/mining.cpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/drivers/nerd-nos/mining.cpp b/src/drivers/nerd-nos/mining.cpp index 9a1f5cb..f24dc13 100644 --- a/src/drivers/nerd-nos/mining.cpp +++ b/src/drivers/nerd-nos/mining.cpp @@ -51,21 +51,6 @@ double nerdnos_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const return ds; } -static void dump(mining_job *job) { - Serial.printf("job_id: %s\n", job->job_id.c_str()); - Serial.printf("prev_block_hash: %s\n", job->prev_block_hash.c_str()); - Serial.printf("coinb1: %s\n", job->coinb1.c_str()); - Serial.printf("coinb2: %s\n", job->coinb2.c_str()); - Serial.printf("nbits: %s\n", job->nbits.c_str()); - Serial.printf("version: %s\n", job->version.c_str()); - Serial.printf("ntime: %s\n", job->ntime.c_str()); - Serial.printf("taget: %lu\n", job->target); - Serial.printf("clean_jobs: %s\n", job->clean_jobs ? "true" : "false"); - for (size_t i = 0; i < job->merkle_branch_size; i++) { - const char* m = job->merkle_branch[i].c_str(); - Serial.printf("merkle_branch[%d]: %s\n", i, m); - } -} static void calculate_merkle_root_hash(const char *coinbase_tx, mining_job* job, char merkle_root_hash[65]) { size_t coinbase_tx_bin_len = strlen(coinbase_tx) / 2; @@ -79,10 +64,6 @@ static void calculate_merkle_root_hash(const char *coinbase_tx, mining_job* job, for (size_t i = 0; i < job->merkle_branch_size; i++) { const char* m = job->merkle_branch[i].c_str(); - // if merkle branch is not what we expect, dump the job - if (!is_hex_string(m)) { - dump(job); - } hex2bin(m, &both_merkles[32], 32); double_sha256_bin(both_merkles, 64, new_root); memcpy(both_merkles, new_root, 32); From f7932e97e6d8c919c12f5c12a68229cdece7bd06 Mon Sep 17 00:00:00 2001 From: WantClue Date: Fri, 13 Sep 2024 22:52:25 +0200 Subject: [PATCH 15/18] Add temperature reading to Asic run and disable if overheat --- src/mining_nerdnos.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/mining_nerdnos.cpp b/src/mining_nerdnos.cpp index 38f815f..35de4e8 100644 --- a/src/mining_nerdnos.cpp +++ b/src/mining_nerdnos.cpp @@ -16,6 +16,8 @@ #include "drivers/nerd-nos/nerdnos.h" #include "mining_nerdnos.h" +#include "drivers/nerd-nos/adc.h" +#include "drivers/nerd-nos/bm1397.h" extern WiFiClient client; extern mining_subscribe mWorker; @@ -38,6 +40,10 @@ static bm_job_t asic_jobs[ASIC_JOB_COUNT] = {0}; // to track hashrate #define ASIC_HISTORY_SIZE 128 +// define temperature readings +#define MAX_SAFE_TEMP 80.0 // Define maximum safe temperature (in Celsius) +#define TEMP_CHECK_INTERVAL 5000 // Check temperature every 5 seconds (in milliseconds) + typedef struct { uint32_t diffs[ASIC_HISTORY_SIZE]; uint64_t timestamps[ASIC_HISTORY_SIZE]; @@ -120,6 +126,8 @@ void runASIC(void * task_id) { } uint32_t extranonce_2 = 0; + unsigned long lastTempCheck = 0; + while(1) { // wait for new job while(!mMiner.newJob) { @@ -142,6 +150,28 @@ void runASIC(void * task_id) { uint32_t current_difficulty = 0; while (mMiner.inRun) { + // Temperature check + unsigned long currentTime = millis(); + if (currentTime - lastTempCheck > TEMP_CHECK_INTERVAL) { + float currentTemp = nerdnos_get_temperature(); // Get ASIC temperature + + if (currentTemp > MAX_SAFE_TEMP) { + gpio_set_level(NERD_NOS_GPIO_PEN, 0); // Disable Buck + Serial.println("ASIC temperature too high. Disabling power."); + + // Wait for temperature to drop + while (nerdnos_get_temperature() > (MAX_SAFE_TEMP - 15)) { // 15 degree hysteresis + vTaskDelay(2000 / portTICK_PERIOD_MS); // Check every 2 second + } + + gpio_set_level(NERD_NOS_GPIO_PEN, 1); // Enable Buck again + Serial.println("Temperature safe. Re-enabling ASIC."); + BM1397_init(200, 1); // Re-Init ASIC + } + + lastTempCheck = currentTime; + } + // wait for the timer to start a new job // also yields the CPU pthread_mutex_lock(&job_interval_mutex); From fb36c60738ebe3407a6bbc071a7f46b31b09adbb Mon Sep 17 00:00:00 2001 From: shufps Date: Sat, 14 Sep 2024 07:26:07 +0200 Subject: [PATCH 16/18] fixes chip id response of bm1397 --- src/drivers/nerd-nos/bm1397.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/drivers/nerd-nos/bm1397.cpp b/src/drivers/nerd-nos/bm1397.cpp index 5c2c45a..0eeade9 100644 --- a/src/drivers/nerd-nos/bm1397.cpp +++ b/src/drivers/nerd-nos/bm1397.cpp @@ -48,6 +48,8 @@ typedef struct __attribute__((__packed__)) static const char *TAG = "bm1397Module"; +static const uint8_t chip_id[] = {0xAA, 0x55, 0x13, 0x97, 0x18, 0x00}; + uint32_t increment_bitmask(const uint32_t value, const uint32_t mask); /// @brief @@ -204,9 +206,8 @@ static uint8_t _send_init(uint64_t frequency, uint16_t asic_count) int chip_counter = 0; while (true) { - int received = SERIAL_rx(buf, 11, 1000); - if (received > 0) { - //ESP_LOG_BUFFER_HEX(TAG, asic_response_buffer, received); + int received = SERIAL_rx(buf, 9, 1000); + if (received > 0 && !memcmp(chip_id, buf, sizeof(chip_id))) { chip_counter++; } else { break; From de10ecad994d15e9196e7e870a6ba022480ca6e6 Mon Sep 17 00:00:00 2001 From: shufps Date: Sat, 14 Sep 2024 09:39:16 +0200 Subject: [PATCH 17/18] avoid using serial while mutex is locked --- src/mining_nerdnos.cpp | 54 ++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/mining_nerdnos.cpp b/src/mining_nerdnos.cpp index 35de4e8..cade337 100644 --- a/src/mining_nerdnos.cpp +++ b/src/mining_nerdnos.cpp @@ -150,34 +150,34 @@ void runASIC(void * task_id) { uint32_t current_difficulty = 0; while (mMiner.inRun) { - // Temperature check - unsigned long currentTime = millis(); - if (currentTime - lastTempCheck > TEMP_CHECK_INTERVAL) { - float currentTemp = nerdnos_get_temperature(); // Get ASIC temperature - - if (currentTemp > MAX_SAFE_TEMP) { - gpio_set_level(NERD_NOS_GPIO_PEN, 0); // Disable Buck - Serial.println("ASIC temperature too high. Disabling power."); - - // Wait for temperature to drop - while (nerdnos_get_temperature() > (MAX_SAFE_TEMP - 15)) { // 15 degree hysteresis - vTaskDelay(2000 / portTICK_PERIOD_MS); // Check every 2 second - } - - gpio_set_level(NERD_NOS_GPIO_PEN, 1); // Enable Buck again - Serial.println("Temperature safe. Re-enabling ASIC."); - BM1397_init(200, 1); // Re-Init ASIC - } - - lastTempCheck = currentTime; - } - // wait for the timer to start a new job // also yields the CPU pthread_mutex_lock(&job_interval_mutex); pthread_cond_wait(&job_interval_cond, &job_interval_mutex); pthread_mutex_unlock(&job_interval_mutex); + // Temperature check + unsigned long currentTime = millis(); + if (currentTime - lastTempCheck > TEMP_CHECK_INTERVAL) { + float currentTemp = nerdnos_get_temperature(); // Get ASIC temperature + + if (currentTemp > MAX_SAFE_TEMP) { + gpio_set_level(NERD_NOS_GPIO_PEN, 0); // Disable Buck + Serial.println("ASIC temperature too high. Disabling power."); + + // Wait for temperature to drop + while (nerdnos_get_temperature() > (MAX_SAFE_TEMP - 15)) { // 15 degree hysteresis + vTaskDelay(2000 / portTICK_PERIOD_MS); // Check every 2 second + } + + gpio_set_level(NERD_NOS_GPIO_PEN, 1); // Enable Buck again + Serial.println("Temperature safe. Re-enabling ASIC."); + BM1397_init(200, 1); // Re-Init ASIC + } + + lastTempCheck = currentTime; + } + // increment extranonce2 extranonce_2++; @@ -191,13 +191,15 @@ void runASIC(void * task_id) { // make sure that another task doesn't mess with the data while // we are using it pthread_mutex_lock(&job_mutex); - if (current_difficulty != mMiner.poolDifficulty) { - current_difficulty = mMiner.poolDifficulty; + nerdnos_create_job(&mWorker, &mJob, &asic_jobs[asic_job_id], extranonce_2, mMiner.poolDifficulty); + pthread_mutex_unlock(&job_mutex); + + // don't send difficulty while the job mutex is locked + if (current_difficulty != asic_jobs[asic_job_id].pool_diff) { + current_difficulty = asic_jobs[asic_job_id].pool_diff; nerdnos_set_asic_difficulty(current_difficulty); Serial.printf("Set difficulty to %lu\n", current_difficulty); } - nerdnos_create_job(&mWorker, &mJob, &asic_jobs[asic_job_id], extranonce_2, current_difficulty); - pthread_mutex_unlock(&job_mutex); // send the job and nerdnos_send_work(&asic_jobs[asic_job_id], asic_job_id); From 93cc2a1a88995b9d46061e4cda9169c8e971c0cf Mon Sep 17 00:00:00 2001 From: shufps Date: Sat, 14 Sep 2024 10:55:34 +0200 Subject: [PATCH 18/18] uses non-blocking read for asic response --- src/drivers/nerd-nos/bm1397.cpp | 9 +++---- src/drivers/nerd-nos/serial.cpp | 44 ++++++++++++++++----------------- src/drivers/nerd-nos/serial.h | 2 +- 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/drivers/nerd-nos/bm1397.cpp b/src/drivers/nerd-nos/bm1397.cpp index 0eeade9..a3cbea4 100644 --- a/src/drivers/nerd-nos/bm1397.cpp +++ b/src/drivers/nerd-nos/bm1397.cpp @@ -362,24 +362,23 @@ bool BM1397_receive_work(uint16_t timeout, asic_result *result) { uint8_t *rcv_buf = (uint8_t*) result; - // wait for a response, wait time is pretty arbitrary - int received = SERIAL_rx(rcv_buf, 9, timeout); + // non blocking read + int received = SERIAL_rx_non_blocking(rcv_buf, 9); if (received < 0) { Serial.println("Error in serial RX"); return false; } - else if (received == 0) + else if (!received) { - // Didn't find a solution, restart and try again + // we have not received any data return false; } if (received != 9 || rcv_buf[0] != 0xAA || rcv_buf[1] != 0x55) { Serial.println("Serial RX invalid. Resetting receive buffer ..."); - //ESP_LOG_BUFFER_HEX(TAG, asic_response_buffer, received); SERIAL_clear_buffer(); return false; } diff --git a/src/drivers/nerd-nos/serial.cpp b/src/drivers/nerd-nos/serial.cpp index bbb56ff..d37dcc9 100644 --- a/src/drivers/nerd-nos/serial.cpp +++ b/src/drivers/nerd-nos/serial.cpp @@ -57,6 +57,28 @@ size_t SERIAL_check_for_data() { return length; } +/// @brief waits for a serial response from the device +/// @param buf buffer to read data into +/// @param buf number of ms to wait before timing out +/// @return number of bytes read, or -1 on error +int16_t SERIAL_rx_non_blocking(uint8_t *buf, uint16_t size) { + // check how much data we have + size_t available = SERIAL_check_for_data(); + + // no data available, return 0 + if (!available) { + return 0; + } + + // check for incomplete data + if (available && available < size) { + Serial.printf("not returning incomplete data ... %d vs %d\n", (int) available, (int) size); + return 0; + } + // timeout 0 means non_blocking read + return SERIAL_rx(buf, size, 0); +} + /// @brief waits for a serial response from the device /// @param buf buffer to read data into @@ -64,13 +86,6 @@ size_t SERIAL_check_for_data() { /// @return number of bytes read, or -1 on error int16_t SERIAL_rx(uint8_t *buf, uint16_t size, uint16_t timeout_ms) { - // don't return incomplete data - size_t available = SERIAL_check_for_data(); - if (available && available < size) { - Serial.printf("not returning parts of data ... %d vs %d\n", (int) available, (int) size); - return 0; - } - int16_t bytes_read = uart_read_bytes(UART_NUM_1, buf, size, timeout_ms / portTICK_PERIOD_MS); // if (bytes_read > 0) { // printf("rx: "); @@ -83,21 +98,6 @@ int16_t SERIAL_rx(uint8_t *buf, uint16_t size, uint16_t timeout_ms) return bytes_read; } -void SERIAL_debug_rx(void) -{ - int ret; - uint8_t buf[CHUNK_SIZE]; - - ret = SERIAL_rx(buf, 100, 20); - if (ret < 0) - { - fprintf(stderr, "unable to read data\n"); - return; - } - - memset(buf, 0, 1024); -} - void SERIAL_clear_buffer(void) { uart_flush(UART_NUM_1); diff --git a/src/drivers/nerd-nos/serial.h b/src/drivers/nerd-nos/serial.h index b4850c8..5a13515 100644 --- a/src/drivers/nerd-nos/serial.h +++ b/src/drivers/nerd-nos/serial.h @@ -4,8 +4,8 @@ int SERIAL_send(uint8_t *, int, bool); void SERIAL_init(void); -void SERIAL_debug_rx(void); int16_t SERIAL_rx(uint8_t *, uint16_t, uint16_t); +int16_t SERIAL_rx_non_blocking(uint8_t *buf, uint16_t size); void SERIAL_clear_buffer(void); void SERIAL_set_baud(int baud);