seems to be working

This commit is contained in:
shufps 2024-09-09 15:10:08 +02:00
parent 0931960ac1
commit 4b7a6d8146
No known key found for this signature in database
GPG Key ID: 371CB8C24D8CB455
19 changed files with 487 additions and 350 deletions

View File

@ -17,6 +17,9 @@
#include "drivers/nerd-nos/bm1397.h" #include "drivers/nerd-nos/bm1397.h"
#include "drivers/nerd-nos/serial.h" #include "drivers/nerd-nos/serial.h"
#ifdef NERD_NOS
#include "mining_nerdnos.h"
#endif
#ifdef TOUCH_ENABLE #ifdef TOUCH_ENABLE
#include "TouchHandler.h" #include "TouchHandler.h"

View File

@ -7,6 +7,7 @@
#include "freertos/task.h" #include "freertos/task.h"
#include "../devices/device.h" #include "../devices/device.h"
#include "crc.h" #include "crc.h"
#include "utils.h"
#define TYPE_JOB 0x20 #define TYPE_JOB 0x20
#define TYPE_CMD 0x40 #define TYPE_CMD 0x40
@ -48,7 +49,6 @@ typedef struct __attribute__((__packed__))
static const char *TAG = "bm1397Module"; static const char *TAG = "bm1397Module";
static uint8_t asic_response_buffer[CHUNK_SIZE]; static uint8_t asic_response_buffer[CHUNK_SIZE];
static uint32_t prev_nonce = 0;
static task_result result; static task_result result;
uint32_t increment_bitmask(const uint32_t value, const uint32_t mask); 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}; unsigned char read_address[2] = {0x00, 0x00};
// send serial data // 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) static void _send_chain_inactive(void)
@ -109,7 +109,7 @@ static void _send_chain_inactive(void)
unsigned char read_address[2] = {0x00, 0x00}; unsigned char read_address[2] = {0x00, 0x00};
// send serial data // 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) 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}; unsigned char read_address[2] = {chipAddr, 0x00};
// send serial data // 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) void BM1397_send_hash_frequency(float frequency)
@ -187,12 +187,12 @@ void BM1397_send_hash_frequency(float frequency)
for (i = 0; i < 2; i++) for (i = 0; i < 2; i++)
{ {
vTaskDelay(10 / portTICK_PERIOD_MS); 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++) for (i = 0; i < 2; i++)
{ {
vTaskDelay(10 / portTICK_PERIOD_MS); 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); 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 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 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 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_? 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); BM1397_set_job_difficulty_mask(BM1397_INITIAL_DIFFICULTY);
unsigned char init5[9] = {0x00, PLL3_PARAMETER, 0xC0, 0x70, 0x01, 0x11}; // init5 - pll3_parameter 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 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(); BM1397_set_default_baud();
@ -294,7 +294,7 @@ int BM1397_set_default_baud(void)
{ {
// default divider of 26 (11010) for 115,749 // default divider of 26 (11010) for 115,749
unsigned char baudrate[9] = {0x00, MISC_CONTROL, 0x00, 0x00, 0b01111010, 0b00110001}; // baudrate - misc_control 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; return 115749;
} }
@ -304,7 +304,7 @@ int BM1397_set_max_baud(void)
ESP_LOGI(TAG, "Setting max baud of 3125000"); ESP_LOGI(TAG, "Setting max baud of 3125000");
unsigned char baudrate[9] = {0x00, MISC_CONTROL, 0x00, 0x00, 0b01100000, 0b00110001}; unsigned char baudrate[9] = {0x00, MISC_CONTROL, 0x00, 0x00, 0b01100000, 0b00110001};
; // baudrate - misc_control ; // 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; 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 // The mask must be a power of 2 so there are no holes
// Correct: {0b00000000, 0b00000000, 0b11111111, 0b11111111} // Correct: {0b00000000, 0b00000000, 0b11111111, 0b11111111}
// Incorrect: {0b00000000, 0b00000000, 0b11100111, 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 // convert difficulty into char array
// Ex: 256 = {0b00000000, 0b00000000, 0b00000000, 0b11111111}, {0x00, 0x00, 0x00, 0xff} // 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 // So a mask of 512 looks like 0b00000000 00000000 00000001 1111111
// and not 0b00000000 00000000 10000000 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); 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) 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 // max job number is 128
// there is still some really weird logic with the job id bits for the asic to sort out // 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 // 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); 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) 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) task_result *BM1397_proccess_work(uint32_t version, uint16_t timeout)
{ {
asic_result *asic_result = BM1397_receive_work(timeout); asic_result *asic_result = BM1397_receive_work(timeout);
if (asic_result == NULL) if (asic_result == NULL)
@ -401,9 +400,6 @@ task_result *BM1397_proccess_work(uint32_t version, uint16_t timeout)
return NULL; 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_job_id = (asic_result->job_id & 0xfc) >> 2;
uint8_t rx_midstate_index = asic_result->job_id & 0x03; 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); 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.job_id = rx_job_id;
result.nonce = asic_result->nonce; result.nonce = asic_result->nonce;
result.rolled_version = rolled_version; result.rolled_version = rolled_version;

View File

@ -1,49 +1,15 @@
#pragma once #pragma once
#include <stdio.h> #include <stdio.h>
#include "common.h" #include "nerdnos.h"
#include "mining.h"
#include "crc.h" #include "crc.h"
#define CRC5_MASK 0x1F #define CRC5_MASK 0x1F
#define BM1397_INITIAL_DIFFICULTY 64
#define BM1937_SERIALTX_DEBUG false #define BM1397_SERIALTX_DEBUG false
#define BM1937_SERIALRX_DEBUG true //false #define BM1397_SERIALRX_DEBUG true //false
#define BM1397_DEBUG_WORK false //causes insane amount of debug output #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_init(uint64_t frequency, uint16_t asic_count);
void BM1397_send_work(bm_job_t * next_bm_job, uint8_t job_id); void BM1397_send_work(bm_job_t * next_bm_job, uint8_t job_id);

View File

@ -1,41 +1,6 @@
#ifndef COMMON_H_ #pragma once
#define COMMON_H_
#include <stdint.h> #include <stdint.h>
#include <stddef.h>
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); void printBufferHex(const char *prefix, const uint8_t* buf, size_t len);
#endif

View File

@ -1,18 +1,8 @@
#ifndef CRC_H_ #pragma once
#define CRC_H_
#include <stdint.h> #include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
uint8_t crc5(uint8_t *buffer, uint16_t len); uint8_t crc5(uint8_t *buffer, uint16_t len);
uint16_t crc16(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); uint16_t crc16_false(uint8_t *buffer, uint16_t len);
#ifdef __cplusplus
}
#endif
#endif // CRC_H_

View File

@ -11,6 +11,7 @@
#include "utils.h" #include "utils.h"
#include "stratum.h" #include "stratum.h"
#include "mbedtls/sha256.h" #include "mbedtls/sha256.h"
#include "../../mining.h"
///////cgminer nonce testing ///////cgminer nonce testing
/* truediffone == 0x00000000FFFF0000000000000000000000000000000000000000000000000000 /* truediffone == 0x00000000FFFF0000000000000000000000000000000000000000000000000000
@ -18,7 +19,7 @@
static const double truediffone = 26959535291011309493156476344723991336010898738574164086137773096960.0; static const double truediffone = 26959535291011309493156476344723991336010898738574164086137773096960.0;
/* testing a nonce and return the diff - 0 means invalid */ /* 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; double d64, s64, ds;
unsigned char header[80]; 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); memcpy(header + 76, &nonce, 4);
unsigned char hash_buffer[32]; unsigned char hash_buffer[32];
unsigned char hash_result[32];
// double hash the header // double hash the header
mbedtls_sha256(header, 80, hash_buffer, 0); 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; return ds;
} }
static void calculate_merkle_root_hash(const char *coinbase_tx, mining_job* job, char merkle_root_hash[65])
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; size_t coinbase_tx_bin_len = strlen(coinbase_tx) / 2;
uint8_t coinbase_tx_bin[coinbase_tx_bin_len]; 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 // 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->version = strtoul(job->version.c_str(), NULL, 16);
new_job->target = strtoul(job->nbits.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; 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 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); 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->jobid = strdup(job->job_id.c_str());
next_job->extranonce2 = strdup(extranonce_2_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); 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); 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->jobid);
free(job->extranonce2); free(job->extranonce2);
// mark as free // mark as free
job->ntime = 0; job->ntime = 0;
} }
void nerdnos_set_asic_difficulty(uint32_t difficulty) {
BM1397_set_job_difficulty_mask(difficulty);
}

View File

@ -1,33 +1,25 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include "common.h" #include "nerdnos.h"
//#include "stratum.h"
// forward declaration to resolve circular inclusions
typedef struct mining_subscribe mining_subscribe;
typedef struct mining_job mining_job;
typedef struct // set the asic hardware difficulty
{ void nerdnos_set_asic_difficulty(uint32_t current_difficulty);
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; // create asic job
uint8_t midstate[32]; void nerdnos_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_job, uint32_t extranonce_2, uint32_t stratum_difficulty);
uint8_t midstate1[32];
uint8_t midstate2[32];
uint8_t midstate3[32];
char *jobid;
char *extranonce2;
} bm_job_t;
// 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); // receive and process responses
task_result *asic_proccess_work(uint32_t version, uint16_t timeout); 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); // test difficulty
void asic_free_bm_job(bm_job_t *job); 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);

View File

@ -0,0 +1,66 @@
#pragma once
#include <stdint.h>
#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;

View File

@ -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); 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 /// @brief waits for a serial response from the device
/// @param buf buffer to read data into /// @param buf buffer to read data into
/// @param buf number of ms to wait before timing out /// @param buf number of ms to wait before timing out
/// @return number of bytes read, or -1 on error /// @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 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); int16_t bytes_read = uart_read_bytes(UART_NUM_1, buf, size, timeout_ms / portTICK_PERIOD_MS);
// if (bytes_read > 0) { // if (bytes_read > 0) {
// printf("rx: "); // printf("rx: ");

View File

@ -1,5 +1,4 @@
#ifndef SERIAL_H_ #pragma once
#define SERIAL_H_
#define CHUNK_SIZE 1024 #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_clear_buffer(void);
void SERIAL_set_baud(int baud); void SERIAL_set_baud(int baud);
#endif /* SERIAL_H_ */

View File

@ -307,3 +307,51 @@ uint32_t flip32(uint32_t val)
ret |= (val & 0xFF000000) >> 24; ret |= (val & 0xFF000000) >> 24;
return ret; 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;
}

View File

@ -1,4 +1,4 @@
#pragma one #pragma once
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@ -33,3 +33,8 @@ void prettyHex(unsigned char *buf, int len);
uint32_t flip32(uint32_t val); 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);

View File

@ -14,11 +14,6 @@
#include "drivers/displays/display.h" #include "drivers/displays/display.h"
#include "drivers/storage/storage.h" #include "drivers/storage/storage.h"
#ifdef NERD_NOS
#include "drivers/nerd-nos/mining.h"
#endif
nvs_handle_t stat_handle; nvs_handle_t stat_handle;
uint32_t templates = 0; uint32_t templates = 0;
@ -41,8 +36,8 @@ extern TSettings Settings;
IPAddress serverIP(1, 1, 1, 1); //Temporally save poolIPaddres IPAddress serverIP(1, 1, 1, 1); //Temporally save poolIPaddres
//Global work data //Global work data
static WiFiClient client; WiFiClient client;
static miner_data mMiner; //Global miner data (Create a miner class TODO) miner_data mMiner; //Global miner data (Create a miner class TODO)
mining_subscribe mWorker; mining_subscribe mWorker;
mining_job mJob; mining_job mJob;
monitor_data mMonitor; monitor_data mMonitor;
@ -103,12 +98,14 @@ bool checkPoolInactivity(unsigned int keepAliveTime, unsigned long inactivityTim
}*/ }*/
} }
#ifndef NERD_NOS
if(elapsedKHs == 0){ if(elapsedKHs == 0){
//Check if hashrate is 0 during inactivityTIme //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;} if((millis()-mStart0Hashrate) > inactivityTime) { mStart0Hashrate=0; return true;}
return false; return false;
} }
#endif
mStart0Hashrate = 0; mStart0Hashrate = 0;
return false; 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 DELAY 100
#define REDRAW_EVERY 10 #define REDRAW_EVERY 10

View File

@ -20,7 +20,7 @@ String printLocalTime(void);
void resetStat(); void resetStat();
typedef struct{ typedef struct {
uint8_t bytearray_target[32]; uint8_t bytearray_target[32];
uint8_t bytearray_pooltarget[32]; uint8_t bytearray_pooltarget[32];
uint8_t merkle_result[32]; uint8_t merkle_result[32];

232
src/mining_nerdnos.cpp Normal file
View File

@ -0,0 +1,232 @@
#include <Arduino.h>
#include <ArduinoJson.h>
#include <WiFi.h>
#include <esp_task_wdt.h>
#include <nvs_flash.h>
#include <nvs.h>
//#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");
*/
}
}

6
src/mining_nerdnos.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#define NERDNOS_JOB_INTERVAL_MS 30
void runASIC(void * task_id);
double nerdnos_get_avg_hashrate();

View File

@ -9,6 +9,10 @@
#include "monitor.h" #include "monitor.h"
#include "drivers/storage/storage.h" #include "drivers/storage/storage.h"
#ifdef NERD_NOS
#include "mining_nerdnos.h"
#endif
extern uint32_t templates; extern uint32_t templates;
extern uint32_t hashes; extern uint32_t hashes;
extern uint32_t Mhashes; extern uint32_t Mhashes;
@ -234,10 +238,17 @@ String getTime(void){
return 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) String getCurrentHashRate(unsigned long mElapsed)
{ {
return String((1.0 * (elapsedKHs * 1000)) / mElapsed, 2); return String((1.0 * (elapsedKHs * 1000)) / mElapsed, 2);
} }
#endif
mining_data getMiningData(unsigned long mElapsed) mining_data getMiningData(unsigned long mElapsed)
{ {

View File

@ -10,7 +10,9 @@
#include "utils.h" #include "utils.h"
#include "version.h" #include "version.h"
#include <pthread.h>
pthread_mutex_t job_mutex = PTHREAD_MUTEX_INITIALIZER;
StaticJsonDocument<BUFFER_JSON_DOC> doc; StaticJsonDocument<BUFFER_JSON_DOC> doc;
unsigned long id = 1; unsigned long id = 1;
@ -99,9 +101,11 @@ bool parse_mining_subscribe(String line, mining_subscribe& mSubscribe)
if (error || checkError(doc)) return false; if (error || checkError(doc)) return false;
if (!doc.containsKey("result")) 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.sub_details = String((const char*) doc["result"][0][0][1]);
mSubscribe.extranonce1 = String((const char*) doc["result"][1]); mSubscribe.extranonce1 = String((const char*) doc["result"][1]);
mSubscribe.extranonce2_size = doc["result"][2]; mSubscribe.extranonce2_size = doc["result"][2];
pthread_mutex_unlock(&job_mutex);
return true; return true;
} }
@ -179,6 +183,7 @@ bool parse_mining_notify(String line, mining_job& mJob)
if (error) return false; if (error) return false;
if (!doc.containsKey("params")) return false; if (!doc.containsKey("params")) return false;
pthread_mutex_lock(&job_mutex);
mJob.job_id = String((const char*) doc["params"][0]); mJob.job_id = String((const char*) doc["params"][0]);
mJob.prev_block_hash = String((const char*) doc["params"][1]); mJob.prev_block_hash = String((const char*) doc["params"][1]);
mJob.coinb1 = String((const char*) doc["params"][2]); 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.nbits = String((const char*) doc["params"][6]);
mJob.ntime = String((const char*) doc["params"][7]); mJob.ntime = String((const char*) doc["params"][7]);
mJob.clean_jobs = doc["params"][8]; //bool mJob.clean_jobs = doc["params"][8]; //bool
pthread_mutex_unlock(&job_mutex);
#ifdef DEBUG_MINING #ifdef DEBUG_MINING
Serial.print(" job_id: "); Serial.println(mJob.job_id); Serial.print(" job_id: "); Serial.println(mJob.job_id);

View File

@ -16,7 +16,7 @@
#define BUFFER_JSON_DOC 4096 #define BUFFER_JSON_DOC 4096
#define BUFFER 1024 #define BUFFER 1024
typedef struct { typedef struct mining_subscribe {
String sub_details; String sub_details;
String extranonce1; String extranonce1;
String extranonce2; String extranonce2;
@ -25,7 +25,7 @@ typedef struct {
char wPass[20]; char wPass[20];
} mining_subscribe; } mining_subscribe;
typedef struct { typedef struct mining_job {
String job_id; String job_id;
String prev_block_hash; String prev_block_hash;
String coinb1; String coinb1;