├── .gitignore ├── ATTINY85_Slave ├── ATTINY85_Slave.ino ├── sha1.cpp └── sha1.h ├── ArduinoNano_Slave ├── ArduinoNano_Helper.ino ├── ArduinoNano_Iic.ino ├── ArduinoNano_Log.ino └── ArduinoNano_Slave.ino ├── ESP8266_Master ├── ESP8266_ClientHttps.ino ├── ESP8266_ClientPool.ino ├── ESP8266_Display.ino ├── ESP8266_Helper.ino ├── ESP8266_Log.ino ├── ESP8266_Master.ino ├── ESP8266_NTP.ino ├── ESP8266_SdCard.ino ├── ESP8266_ServerHttp.ino ├── ESP8266_Slaves.ino ├── ESP8266_WiFi.ino └── data │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── duino.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── index.html │ └── site.webmanifest ├── Images ├── Duino-Coin-Rig-ESP8266-ATTINY85.png ├── Duino-Coin-Rig-ESP8266-Arduino-Nano.png └── Screenshot-Webfrontend.png ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea -------------------------------------------------------------------------------- /ATTINY85_Slave/ATTINY85_Slave.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ATTINY85_Slave 4 | * Version: 0.1 5 | * Purpose: This is the master file that starts and coordinates the slave 6 | * Author: Frank Niggemann 7 | * 8 | * This is a modified version of this software: https://github.com/ricaun/DuinoCoinI2C/tree/main/DuinoCoin_Tiny_Slave 9 | * 10 | * Hardware: ATTINY85 11 | * 12 | * Needed files: ATTINY85_Slave 13 | * sha1 14 | * 15 | * Needed libraries: ArduinoUniqueID 16 | * EEPROM 17 | * sha1 18 | * Wire 19 | */ 20 | 21 | 22 | 23 | /*********************************************************************************************************************** 24 | * Includes 25 | **********************************************************************************************************************/ 26 | 27 | #include 28 | #include 29 | #include "sha1.h" 30 | #include 31 | 32 | 33 | 34 | /*********************************************************************************************************************** 35 | * Definitions 36 | **********************************************************************************************************************/ 37 | 38 | // Possible states for the slave node 39 | #define SLAVE_STATE_UNKNOWN 0 // The ID for status UNKNOWN (Slave is in an unknown state) 40 | #define SLAVE_STATE_FREE 1 // The ID for status FREE (Slave is free for the next job) 41 | #define SLAVE_STATE_WORKING 2 // The ID for status WORKING (Slave is working on a job) 42 | #define SLAVE_STATE_READY 3 // The ID for status READY (Slave is ready with a job and has a result) 43 | #define SLAVE_STATE_RESULT_READY 4 // The ID for status RESULT READY (The result is ready to send) 44 | #define SLAVE_STATE_RESULT_SENT 5 // The ID for status RESULT SENT (The result has been sent) 45 | #define SLAVE_STATE_ERROR 6 // The ID for status ERROR (Slave has a problem) 46 | 47 | #define PIN_LED_ADDRESS 4 // The LED on this pin will blink when the connection had been established 48 | #define PIN_LED_FREE 4 // The LED on this pin shows that the node is free 49 | #define PIN_LED_WORKING 1 // The LED on this pin shows that the node is working 50 | #define PIN_LED_READY false // The LED on this pin shows that the node is ready with the job 51 | 52 | #define PIN_IIC_SDA 0 // This is the I²C SDA pin 53 | #define PIN_IIC_SCL 2 // This is the I²C SCL pin 54 | 55 | #define ADDRESS_I2C 1 // Thes I²C ID 56 | 57 | #define BUFFER_MAX 88 // The size of the main buffer 58 | #define HASH_BUFFER_SIZE 20 // The size of the hash buffer 59 | #define CHAR_END '\n' // The char that ends a line 60 | #define CHAR_DOT ',' // The char that separates the datasets 61 | 62 | 63 | 64 | /*********************************************************************************************************************** 65 | * Variables 66 | **********************************************************************************************************************/ 67 | 68 | byte iic_id = 1; 69 | int slaveState = SLAVE_STATE_UNKNOWN; 70 | bool logSerial = true; 71 | String ducoId = ""; 72 | 73 | static const char DUCOID[] PROGMEM = "DUCOID"; 74 | static const char ZEROS[] PROGMEM = "000"; 75 | 76 | static byte address; 77 | static char buffer[BUFFER_MAX]; 78 | static uint8_t buffer_position; 79 | static uint8_t buffer_length; 80 | static bool working; 81 | static bool jobdone; 82 | 83 | void(* resetFunc) (void) = 0;//declare reset function at address 0 84 | 85 | 86 | 87 | // --------------------------------------------------------------------- // 88 | // setup 89 | // --------------------------------------------------------------------- // 90 | 91 | void setup() { 92 | delay(100); 93 | if (PIN_LED_ADDRESS) { 94 | pinMode(PIN_LED_ADDRESS, OUTPUT); 95 | digitalWrite(PIN_LED_ADDRESS, LOW); 96 | } 97 | if (PIN_LED_FREE) { 98 | pinMode(PIN_LED_FREE, OUTPUT); 99 | digitalWrite(PIN_LED_FREE, LOW); 100 | } 101 | if (PIN_LED_WORKING) { 102 | pinMode(PIN_LED_WORKING, OUTPUT); 103 | digitalWrite(PIN_LED_WORKING, LOW); 104 | } 105 | if (PIN_LED_READY) { 106 | pinMode(PIN_LED_READY, OUTPUT); 107 | digitalWrite(PIN_LED_READY, LOW); 108 | } 109 | setState(SLAVE_STATE_UNKNOWN); 110 | initialize_i2c(); 111 | } 112 | 113 | 114 | 115 | // --------------------------------------------------------------------- // 116 | // loop 117 | // --------------------------------------------------------------------- // 118 | 119 | void loop() { 120 | do_work(); 121 | millis(); // ????? For some reason need this to work the i2c 122 | } 123 | 124 | 125 | 126 | // --------------------------------------------------------------------- // 127 | // run 128 | // --------------------------------------------------------------------- // 129 | 130 | boolean runEvery(unsigned long interval) 131 | { 132 | static unsigned long previousMillis = 0; 133 | unsigned long currentMillis = millis(); 134 | if (currentMillis - previousMillis >= interval) 135 | { 136 | previousMillis = currentMillis; 137 | return true; 138 | } 139 | return false; 140 | } 141 | 142 | 143 | 144 | // --------------------------------------------------------------------- // 145 | // work 146 | // --------------------------------------------------------------------- // 147 | 148 | void do_work() 149 | { 150 | if (working) 151 | { 152 | do_job(); 153 | } 154 | } 155 | 156 | void do_job() 157 | { 158 | setState(SLAVE_STATE_WORKING); 159 | unsigned long startTime = millis(); 160 | int job = work(); 161 | unsigned long endTime = millis(); 162 | unsigned int elapsedTime = endTime - startTime; 163 | memset(buffer, 0, sizeof(buffer)); 164 | char cstr[16]; 165 | 166 | // Job 167 | itoa(job, cstr, 10); 168 | strcpy(buffer, cstr); 169 | buffer[strlen(buffer)] = CHAR_DOT; 170 | 171 | // Time 172 | itoa(elapsedTime, cstr, 10); 173 | strcpy(buffer + strlen(buffer), cstr); 174 | strcpy_P(buffer + strlen(buffer), ZEROS); 175 | buffer[strlen(buffer)] = CHAR_DOT; 176 | 177 | // DUCOID 178 | strcpy_P(buffer + strlen(buffer), DUCOID); 179 | for (size_t i = 0; i < 8; i++) 180 | { 181 | itoa(UniqueID8[i], cstr, 16); 182 | if (UniqueID8[i] < 16) strcpy(buffer + strlen(buffer), "0"); 183 | strcpy(buffer + strlen(buffer), cstr); 184 | } 185 | 186 | buffer_position = 0; 187 | buffer_length = strlen(buffer); 188 | working = false; 189 | jobdone = true; 190 | setState(SLAVE_STATE_FREE); 191 | } 192 | 193 | int work() 194 | { 195 | char delimiters[] = ","; 196 | char *lastHash = strtok(buffer, delimiters); 197 | char *newHash = strtok(NULL, delimiters); 198 | char *diff = strtok(NULL, delimiters); 199 | buffer_length = 0; 200 | buffer_position = 0; 201 | return work(lastHash, newHash, atoi(diff)); 202 | } 203 | 204 | //#define HTOI(c) ((c<='9')?(c-'0'):((c<='F')?(c-'A'+10):((c<='f')?(c-'a'+10):(0)))) 205 | #define HTOI(c) ((c<='9')?(c-'0'):((c<='f')?(c-'a'+10):(0))) 206 | #define TWO_HTOI(h, l) ((HTOI(h) << 4) + HTOI(l)) 207 | //byte HTOI(char c) {return ((c<='9')?(c-'0'):((c<='f')?(c-'a'+10):(0)));} 208 | //byte TWO_HTOI(char h, char l) {return ((HTOI(h) << 4) + HTOI(l));} 209 | 210 | void HEX_TO_BYTE(char * address, char * hex, int len) 211 | { 212 | for (int i = 0; i < len; i++) address[i] = TWO_HTOI(hex[2 * i], hex[2 * i + 1]); 213 | } 214 | 215 | // DUCO-S1A hasher 216 | uint32_t work(char * lastblockhash, char * newblockhash, int difficulty) 217 | { 218 | if (difficulty > 655) return 0; 219 | HEX_TO_BYTE(newblockhash, newblockhash, HASH_BUFFER_SIZE); 220 | for (int ducos1res = 0; ducos1res < difficulty * 100 + 1; ducos1res++) 221 | { 222 | Sha1.init(); 223 | Sha1.print(lastblockhash); 224 | Sha1.print(ducos1res); 225 | if (memcmp(Sha1.result(), newblockhash, HASH_BUFFER_SIZE) == 0) 226 | { 227 | return ducos1res; 228 | } 229 | } 230 | return 0; 231 | } 232 | 233 | 234 | 235 | // --------------------------------------------------------------------- // 236 | // i2c 237 | // --------------------------------------------------------------------- // 238 | 239 | void initialize_i2c(void) { 240 | pinMode(PIN_IIC_SDA, INPUT_PULLUP); 241 | pinMode(PIN_IIC_SCL, INPUT_PULLUP); 242 | TinyWire.begin(ADDRESS_I2C); 243 | TinyWire.onReceive(iicHandlerReceive); 244 | TinyWire.onRequest(iicHandlerRequest); 245 | ducoId = getDucoId(); 246 | ledBlink(PIN_LED_ADDRESS, 250, 250); 247 | ledBlink(PIN_LED_ADDRESS, 250, 250); 248 | setState(SLAVE_STATE_FREE); 249 | } 250 | 251 | void iicHandlerReceive(int howMany) { 252 | if (howMany == 0) return; 253 | if (working) return; 254 | if (jobdone) return; 255 | 256 | while (TinyWire.available()) { 257 | char c = TinyWire.read(); 258 | buffer[buffer_length++] = c; 259 | if (buffer_length == BUFFER_MAX) buffer_length--; 260 | if (c == CHAR_END) 261 | { 262 | working = true; 263 | } 264 | } 265 | } 266 | 267 | void iicHandlerRequest() { 268 | char c = CHAR_END; 269 | if (jobdone) { 270 | c = buffer[buffer_position++]; 271 | if ( buffer_position == buffer_length) { 272 | jobdone = false; 273 | buffer_position = 0; 274 | buffer_length = 0; 275 | } 276 | } 277 | TinyWire.write(c); 278 | } 279 | 280 | void setState(int state) { 281 | if (slaveState != state) { 282 | slaveState = state; 283 | if (slaveState == SLAVE_STATE_UNKNOWN) { 284 | ledSet(false, false, false, false); 285 | } else if (slaveState == SLAVE_STATE_FREE) { 286 | ledSet(false, true, false, false); 287 | } else if (slaveState == SLAVE_STATE_WORKING) { 288 | ledSet(false, false, true, false); 289 | } else if (slaveState == SLAVE_STATE_READY) { 290 | ledSet(false, false, false, true); 291 | } else if (slaveState == SLAVE_STATE_RESULT_READY) { 292 | ledSet(false, false, false, true); 293 | } else if (slaveState == SLAVE_STATE_RESULT_SENT) { 294 | ledSet(false, true, false, false); 295 | } else if (slaveState == SLAVE_STATE_ERROR) { 296 | ledSet(false, false, false, false); 297 | } else { 298 | ledSet(false, false, false, false); 299 | } 300 | } 301 | } 302 | 303 | 304 | 305 | // --------------------------------------------------------------------- // 306 | // DUCO ID 307 | // --------------------------------------------------------------------- // 308 | 309 | String getDucoId() { 310 | String ID = "DUCOID"+getPseudoUniqueIdString(); 311 | return ID; 312 | } 313 | 314 | String getPseudoUniqueIdString() { 315 | String result = ""; 316 | byte value[8]; 317 | String hexvalue[16]; 318 | hexvalue[0] = '0'; 319 | hexvalue[1] = '1'; 320 | hexvalue[2] = '2'; 321 | hexvalue[3] = '3'; 322 | hexvalue[4] = '4'; 323 | hexvalue[5] = '5'; 324 | hexvalue[6] = '6'; 325 | hexvalue[7] = '7'; 326 | hexvalue[8] = '8'; 327 | hexvalue[9] = '9'; 328 | hexvalue[10] = 'A'; 329 | hexvalue[11] = 'B'; 330 | hexvalue[12] = 'C'; 331 | hexvalue[13] = 'D'; 332 | hexvalue[14] = 'E'; 333 | hexvalue[15] = 'F'; 334 | for (int i=0 ; i<8 ; i++) { 335 | value[i] = boot_signature_byte_get(i); 336 | } 337 | for (int i=8 ; i<128 ; i++) { 338 | byte value_before = value[(i%8)]; 339 | value[(i%8)] = (value_before+boot_signature_byte_get(i))%256; 340 | } 341 | for (int i=0 ; i<8 ; i++) { 342 | byte low = value[i] % 16; 343 | byte high = (value[i]-low) / 16; 344 | result.concat(hexvalue[high]); 345 | result.concat(hexvalue[low]); 346 | } 347 | return result; 348 | } 349 | 350 | unsigned long getStartupDelay() { 351 | byte value[8]; 352 | unsigned long milliseconds = 0; 353 | for (int i=0 ; i<8 ; i++) { 354 | value[i] = boot_signature_byte_get(i); 355 | } 356 | for (int i=8 ; i<128 ; i++) { 357 | byte value_before = value[(i%8)]; 358 | value[(i%8)] = (value_before+boot_signature_byte_get(i))%256; 359 | } 360 | for (int i=0 ; i<8 ; i++) { 361 | milliseconds += value[i]*(8^i); 362 | } 363 | milliseconds = (milliseconds%1000)*10; 364 | return milliseconds; 365 | } 366 | 367 | 368 | 369 | // --------------------------------------------------------------------- // 370 | // LED 371 | // --------------------------------------------------------------------- // 372 | 373 | void ledBlink(int pin, int msOn, int msOff) { 374 | digitalWrite(pin, HIGH); 375 | delay(msOn); 376 | digitalWrite(pin, LOW); 377 | delay(msOff); 378 | } 379 | 380 | void ledSet(bool ledAddress, bool ledFree, bool ledWorking, bool ledReady) { 381 | if (PIN_LED_ADDRESS) { 382 | if (ledAddress) { 383 | digitalWrite(PIN_LED_ADDRESS, HIGH); 384 | } else { 385 | digitalWrite(PIN_LED_ADDRESS, LOW); 386 | } 387 | } 388 | if (PIN_LED_FREE) { 389 | if (ledFree) { 390 | digitalWrite(PIN_LED_FREE, HIGH); 391 | } else { 392 | digitalWrite(PIN_LED_FREE, LOW); 393 | } 394 | } 395 | if (PIN_LED_WORKING) { 396 | if (ledWorking) { 397 | digitalWrite(PIN_LED_WORKING, HIGH); 398 | } else { 399 | digitalWrite(PIN_LED_WORKING, LOW); 400 | } 401 | } 402 | if (PIN_LED_READY) { 403 | if (ledReady) { 404 | digitalWrite(PIN_LED_READY, HIGH); 405 | } else { 406 | digitalWrite(PIN_LED_READY, LOW); 407 | } 408 | } 409 | } 410 | -------------------------------------------------------------------------------- /ATTINY85_Slave/sha1.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This code was taken from the original github crypto-master zip file and slightly 3 | modified to return a size_t type from the Sha1Class.write() call. 4 | 5 | https://github.com/Cathedrow/Cryptosuite 6 | 7 | The original code to the Cryptosuite library is contained within the 8 | Cryptosuite-master sub-directory with the needed sha1.h/cpp files copied 9 | from there and modified as described above. 10 | */ 11 | #include 12 | //#include 13 | //#include 14 | #include "sha1.h" 15 | 16 | #define SHA1_K0 0x5a827999 17 | #define SHA1_K20 0x6ed9eba1 18 | #define SHA1_K40 0x8f1bbcdc 19 | #define SHA1_K60 0xca62c1d6 20 | 21 | const uint8_t sha1InitState[] PROGMEM = { 22 | 0x01,0x23,0x45,0x67, // H0 23 | 0x89,0xab,0xcd,0xef, // H1 24 | 0xfe,0xdc,0xba,0x98, // H2 25 | 0x76,0x54,0x32,0x10, // H3 26 | 0xf0,0xe1,0xd2,0xc3 // H4 27 | }; 28 | 29 | void Sha1Class::init(void) { 30 | memcpy_P(state.b,sha1InitState,HASH_LENGTH); 31 | byteCount = 0; 32 | bufferOffset = 0; 33 | } 34 | 35 | uint32_t Sha1Class::rol32(uint32_t number, uint8_t bits) { 36 | return ((number << bits) | (number >> (32-bits))); 37 | } 38 | 39 | void Sha1Class::hashBlock() { 40 | uint8_t i; 41 | uint32_t a,b,c,d,e,t; 42 | 43 | a=state.w[0]; 44 | b=state.w[1]; 45 | c=state.w[2]; 46 | d=state.w[3]; 47 | e=state.w[4]; 48 | for (i=0; i<80; i++) { 49 | if (i>=16) { 50 | t = buffer.w[(i+13)&15] ^ buffer.w[(i+8)&15] ^ buffer.w[(i+2)&15] ^ buffer.w[i&15]; 51 | buffer.w[i&15] = rol32(t,1); 52 | } 53 | if (i<20) { 54 | t = (d ^ (b & (c ^ d))) + SHA1_K0; 55 | } else if (i<40) { 56 | t = (b ^ c ^ d) + SHA1_K20; 57 | } else if (i<60) { 58 | t = ((b & c) | (d & (b | c))) + SHA1_K40; 59 | } else { 60 | t = (b ^ c ^ d) + SHA1_K60; 61 | } 62 | t+=rol32(a,5) + e + buffer.w[i&15]; 63 | e=d; 64 | d=c; 65 | c=rol32(b,30); 66 | b=a; 67 | a=t; 68 | } 69 | state.w[0] += a; 70 | state.w[1] += b; 71 | state.w[2] += c; 72 | state.w[3] += d; 73 | state.w[4] += e; 74 | } 75 | 76 | void Sha1Class::addUncounted(uint8_t data) { 77 | buffer.b[bufferOffset ^ 3] = data; 78 | bufferOffset++; 79 | if (bufferOffset == BLOCK_LENGTH) { 80 | hashBlock(); 81 | bufferOffset = 0; 82 | } 83 | } 84 | 85 | size_t Sha1Class::write(uint8_t data) { 86 | ++byteCount; 87 | addUncounted(data); 88 | return 1; 89 | } 90 | 91 | void Sha1Class::pad() { 92 | // Implement SHA-1 padding (fips180-2 §5.1.1) 93 | 94 | // Pad with 0x80 followed by 0x00 until the end of the block 95 | addUncounted(0x80); 96 | while (bufferOffset != 56) addUncounted(0x00); 97 | 98 | // Append length in the last 8 bytes 99 | addUncounted(0); // We're only using 32 bit lengths 100 | addUncounted(0); // But SHA-1 supports 64 bit lengths 101 | addUncounted(0); // So zero pad the top bits 102 | addUncounted(byteCount >> 29); // Shifting to multiply by 8 103 | addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as 104 | addUncounted(byteCount >> 13); // byte. 105 | addUncounted(byteCount >> 5); 106 | addUncounted(byteCount << 3); 107 | } 108 | 109 | 110 | uint8_t* Sha1Class::result(void) { 111 | // Pad to complete the last block 112 | pad(); 113 | 114 | // Swap byte order back 115 | for (int i=0; i<5; i++) { 116 | uint32_t a,b; 117 | a=state.w[i]; 118 | b=a<<24; 119 | b|=(a<<8) & 0x00ff0000; 120 | b|=(a>>8) & 0x0000ff00; 121 | b|=a>>24; 122 | state.w[i]=b; 123 | } 124 | 125 | // Return pointer to hash (20 characters) 126 | return state.b; 127 | } 128 | 129 | #define HMAC_IPAD 0x36 130 | #define HMAC_OPAD 0x5c 131 | 132 | void Sha1Class::initHmac(const uint8_t* key, int keyLength) { 133 | uint8_t i; 134 | memset(keyBuffer,0,BLOCK_LENGTH); 135 | if (keyLength > BLOCK_LENGTH) { 136 | // Hash long keys 137 | init(); 138 | for (;keyLength--;) write(*key++); 139 | memcpy(keyBuffer,result(),HASH_LENGTH); 140 | } else { 141 | // Block length keys are used as is 142 | memcpy(keyBuffer,key,keyLength); 143 | } 144 | // Start inner hash 145 | init(); 146 | for (i=0; i 15 | #include "Print.h" 16 | 17 | #define HASH_LENGTH 20 18 | #define BLOCK_LENGTH 64 19 | 20 | union _buffer { 21 | uint8_t b[BLOCK_LENGTH]; 22 | uint32_t w[BLOCK_LENGTH/4]; 23 | }; 24 | union _state { 25 | uint8_t b[HASH_LENGTH]; 26 | uint32_t w[HASH_LENGTH/4]; 27 | }; 28 | 29 | class Sha1Class : public Print 30 | { 31 | public: 32 | void init(void); 33 | void initHmac(const uint8_t* secret, int secretLength); 34 | uint8_t* result(void); 35 | uint8_t* resultHmac(void); 36 | virtual size_t write(uint8_t); 37 | using Print::write; 38 | private: 39 | void pad(); 40 | void addUncounted(uint8_t data); 41 | void hashBlock(); 42 | uint32_t rol32(uint32_t number, uint8_t bits); 43 | _buffer buffer; 44 | uint8_t bufferOffset; 45 | _state state; 46 | uint32_t byteCount; 47 | uint8_t keyBuffer[BLOCK_LENGTH]; 48 | uint8_t innerHash[HASH_LENGTH]; 49 | 50 | }; 51 | extern Sha1Class Sha1; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /ArduinoNano_Slave/ArduinoNano_Helper.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ArduinoNano_Helper 4 | * Version: 0.1 5 | * Purpose: Contains all helper functions 6 | * Author: Frank Niggemann 7 | */ 8 | 9 | 10 | 11 | /*********************************************************************************************************************** 12 | * Code Helper 13 | **********************************************************************************************************************/ 14 | 15 | String getDucoId() { 16 | String ID = "DUCOID"+getPseudoUniqueIdString(); 17 | return ID; 18 | } 19 | 20 | String getPseudoUniqueIdString() { 21 | String result = ""; 22 | byte value[8]; 23 | String hexvalue[16]; 24 | hexvalue[0] = '0'; 25 | hexvalue[1] = '1'; 26 | hexvalue[2] = '2'; 27 | hexvalue[3] = '3'; 28 | hexvalue[4] = '4'; 29 | hexvalue[5] = '5'; 30 | hexvalue[6] = '6'; 31 | hexvalue[7] = '7'; 32 | hexvalue[8] = '8'; 33 | hexvalue[9] = '9'; 34 | hexvalue[10] = 'A'; 35 | hexvalue[11] = 'B'; 36 | hexvalue[12] = 'C'; 37 | hexvalue[13] = 'D'; 38 | hexvalue[14] = 'E'; 39 | hexvalue[15] = 'F'; 40 | for (int i=0 ; i<8 ; i++) { 41 | value[i] = boot_signature_byte_get(i); 42 | } 43 | for (int i=8 ; i<128 ; i++) { 44 | byte value_before = value[(i%8)]; 45 | value[(i%8)] = (value_before+boot_signature_byte_get(i))%256; 46 | } 47 | for (int i=0 ; i<8 ; i++) { 48 | byte low = value[i] % 16; 49 | byte high = (value[i]-low) / 16; 50 | logMessage("low: "+String(low)); 51 | logMessage("high: "+String(high)); 52 | result.concat(hexvalue[high]); 53 | result.concat(hexvalue[low]); 54 | } 55 | return result; 56 | } 57 | 58 | unsigned long getStartupDelay() { 59 | byte value[8]; 60 | unsigned long milliseconds = 0; 61 | for (int i=0 ; i<8 ; i++) { 62 | value[i] = boot_signature_byte_get(i); 63 | } 64 | for (int i=8 ; i<128 ; i++) { 65 | byte value_before = value[(i%8)]; 66 | value[(i%8)] = (value_before+boot_signature_byte_get(i))%256; 67 | } 68 | for (int i=0 ; i<8 ; i++) { 69 | milliseconds += value[i]*(8^i); 70 | } 71 | milliseconds = (milliseconds%1000)*10; 72 | logMessage("Startupdelay: "+String(milliseconds)+"ms"); 73 | return milliseconds; 74 | } 75 | 76 | void ledBlink(int pin, int msOn, int msOff) { 77 | digitalWrite(pin, HIGH); 78 | delay(msOn); 79 | digitalWrite(pin, LOW); 80 | delay(msOff); 81 | } 82 | 83 | void ledSet(bool ledAddress, bool ledFree, bool ledWorking, bool ledReady) { 84 | if (PIN_LED_ADDRESS) { 85 | if (ledAddress) { 86 | digitalWrite(PIN_LED_ADDRESS, HIGH); 87 | logMessage("PIN_LED_ADDRESS: ON"); 88 | } else { 89 | digitalWrite(PIN_LED_ADDRESS, LOW); 90 | logMessage("PIN_LED_ADDRESS: OFF"); 91 | } 92 | } 93 | if (PIN_LED_FREE) { 94 | if (ledFree) { 95 | digitalWrite(PIN_LED_FREE, HIGH); 96 | logMessage("PIN_LED_FREE: ON"); 97 | } else { 98 | digitalWrite(PIN_LED_FREE, LOW); 99 | logMessage("PIN_LED_FREE: OFF"); 100 | } 101 | } 102 | if (PIN_LED_WORKING) { 103 | if (ledWorking) { 104 | digitalWrite(PIN_LED_WORKING, HIGH); 105 | logMessage("PIN_LED_WORKING: ON"); 106 | } else { 107 | digitalWrite(PIN_LED_WORKING, LOW); 108 | logMessage("PIN_LED_WORKING: OFF"); 109 | } 110 | } 111 | if (PIN_LED_READY) { 112 | if (ledReady) { 113 | digitalWrite(PIN_LED_READY, HIGH); 114 | logMessage("PIN_LED_READY: ON"); 115 | } else { 116 | digitalWrite(PIN_LED_READY, LOW); 117 | logMessage("PIN_LED_READY: OFF"); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /ArduinoNano_Slave/ArduinoNano_Iic.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ArduinoNano_Iic 4 | * Version: 0.2 5 | * Purpose: Communication with the master 6 | * Author: Frank Niggemann 7 | */ 8 | 9 | 10 | 11 | /*********************************************************************************************************************** 12 | * Code Iic 13 | **********************************************************************************************************************/ 14 | 15 | void iicSetup() { 16 | pinMode(PIN_IIC_SDA, INPUT_PULLUP); 17 | pinMode(PIN_IIC_SCL, INPUT_PULLUP); 18 | 19 | Wire.begin(); 20 | for (int id=IIC_ID_MIN; id 0 && bufferRequest.indexOf('\n') != -1) { 55 | c = bufferRequest.read(); 56 | } 57 | Wire.write(c); 58 | } 59 | 60 | void iicWorker() { 61 | if (bufferReceive.available() > 0 && bufferReceive.indexOf('\n') != -1) { 62 | 63 | setState(SLAVE_STATE_WORKING); 64 | logMessage("Request: "+String(bufferReceive)); 65 | 66 | String lastblockhash = bufferReceive.readStringUntil(','); 67 | String newblockhash = bufferReceive.readStringUntil(','); 68 | unsigned int difficulty = bufferReceive.readStringUntil('\n').toInt(); 69 | unsigned long startTime = micros(); 70 | unsigned int ducos1result = 0; 71 | if (difficulty < 655) ducos1result = Ducos1a.work(lastblockhash, newblockhash, difficulty); 72 | unsigned long endTime = micros(); 73 | unsigned long elapsedTime = endTime - startTime; 74 | while (bufferRequest.available()) bufferRequest.read(); 75 | bufferRequest.print(String(ducos1result) + "," + String(elapsedTime) + "," + ducoId + "\n"); 76 | 77 | setState(SLAVE_STATE_FREE); 78 | logMessage("Result: "+String(ducos1result) + "," + String(elapsedTime) + "," + ducoId); 79 | } 80 | } 81 | 82 | /* 83 | byte iic_id = 0; 84 | StreamString bufferReceive; 85 | String stringReceive = ""; 86 | StreamString bufferRequest; 87 | String stringRequest = ""; 88 | String lastBlockHash = ""; 89 | String nextBlockHash = ""; 90 | unsigned int ducos1aResult = 0; 91 | unsigned long microtimeStart = 0; 92 | unsigned long microtimeEnd = 0; 93 | unsigned long microtimeDifference = 0; 94 | String ducoId = ""; 95 | 96 | void iicSetup() { 97 | pinMode(WIRE_SDA, INPUT_PULLUP); 98 | pinMode(WIRE_SCL, INPUT_PULLUP); 99 | 100 | Wire.begin(); 101 | for (int id=IIC_ID_MIN; id 0 && bufferReceive.indexOf('\n') != -1) { 132 | if (bufferReceive.length() < 10) { 133 | 134 | } else { 135 | lastBlockHash = bufferReceive.readStringUntil(','); 136 | nextBlockHash = bufferReceive.readStringUntil(','); 137 | unsigned int difficulty = bufferReceive.readStringUntil('\n').toInt(); 138 | logMessage("Receive lastBlockHash: "+lastBlockHash); 139 | logMessage("Receive nextBlockHash: "+nextBlockHash); 140 | logMessage("Receive difficulty: "+String(difficulty)); 141 | if (lastBlockHash!="" && nextBlockHash!="") { 142 | setState(SLAVE_STATE_WORKING); 143 | digitalWrite(PIN_LED_WORKING, HIGH); 144 | microtimeStart = micros(); 145 | ducos1aResult = 0; 146 | if (difficulty < 655){ 147 | ducos1aResult = Ducos1a.work(lastBlockHash, nextBlockHash, difficulty); 148 | } 149 | logMessage("Calculated result: "+String(ducos1aResult)); 150 | microtimeEnd = micros(); 151 | microtimeDifference = microtimeEnd - microtimeStart; 152 | setState(SLAVE_STATE_READY); 153 | digitalWrite(PIN_LED_READY, HIGH); 154 | delay(100); 155 | iicSetBufferRequestStringJobResult(); 156 | } else { 157 | setState(SLAVE_STATE_ERROR); 158 | logMessage("ERROR"); 159 | } 160 | } 161 | } 162 | } 163 | 164 | void iicHandlerRequest() { 165 | char c = '\n'; 166 | if (bufferRequest.available() > 0 && bufferRequest.indexOf('\n') != -1) { 167 | c = bufferRequest.read(); 168 | } 169 | Wire.write(c); 170 | if (slaveState == SLAVE_STATE_RESULT_READY && bufferRequest.available() == 0) { 171 | setState(SLAVE_STATE_RESULT_SENT); 172 | } 173 | } 174 | 175 | void iicSetBufferRequestStringEmpty() { 176 | String request = ""; 177 | } 178 | 179 | void iicSetBufferRequestStringJobResult() { 180 | while (bufferRequest.available()) bufferRequest.read(); 181 | String request = String(ducos1aResult)+":"+String(microtimeDifference)+":"+ducoId+"\n"; 182 | logMessage(request); 183 | bufferRequest.print(request); 184 | setState(SLAVE_STATE_RESULT_SENT); 185 | } 186 | */ 187 | -------------------------------------------------------------------------------- /ArduinoNano_Slave/ArduinoNano_Log.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ArduinoNano_Log 4 | * Version: 0.1 5 | * Purpose: Central log management 6 | * Author: Frank Niggemann 7 | */ 8 | 9 | 10 | 11 | /*********************************************************************************************************************** 12 | * Code Log 13 | **********************************************************************************************************************/ 14 | 15 | /** 16 | * Initializes the log part of the software 17 | */ 18 | void logSetup() { 19 | if (logSerial) { 20 | Serial.begin(115200); 21 | Serial.println("\n"); 22 | } 23 | } 24 | 25 | /** 26 | * Writes the message to the configured log destination(s) 27 | */ 28 | void logMessage(String message) { 29 | if (logSerial) { 30 | logMessageSerial(message); 31 | } 32 | } 33 | 34 | /** 35 | * Writes the log message to the serial stream 36 | */ 37 | void logMessageSerial(String message) { 38 | Serial.println(message); 39 | } 40 | -------------------------------------------------------------------------------- /ArduinoNano_Slave/ArduinoNano_Slave.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ArduinoNano_Slave 4 | * Version: 0.1 5 | * Purpose: This is the master file that starts and coordinates the slave 6 | * Author: Frank Niggemann 7 | * 8 | * This is a modified version of this software: https://github.com/ricaun/DuinoCoinI2C/tree/main/DuinoCoin_Tiny_Slave 9 | * 10 | * Hardware: Arduino Nano 11 | * 12 | * Needed files: ArduinoNano_Helper 13 | * ArduinoNano_Iic 14 | * ArduinoNano_Log 15 | * ArduinoNano_Slave 16 | * 17 | * Needed libraries: ArduinoUniqueID 18 | * DuinoCoin 19 | * StreamString 20 | * Wire 21 | */ 22 | 23 | 24 | 25 | /*********************************************************************************************************************** 26 | * Includes 27 | **********************************************************************************************************************/ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | 35 | 36 | /*********************************************************************************************************************** 37 | * Definitions 38 | **********************************************************************************************************************/ 39 | 40 | #define PIN_LED_ADDRESS 13 41 | #define PIN_LED_FREE false 42 | #define PIN_LED_WORKING 13 43 | #define PIN_LED_READY false 44 | 45 | #define SLAVE_STATE_UNKNOWN 0 // The ID for status UNKNOWN (Slave is in an unknown state) 46 | #define SLAVE_STATE_FREE 1 // The ID for status FREE (Slave is free for the next job) 47 | #define SLAVE_STATE_WORKING 2 // The ID for status WORKING (Slave is working on a job) 48 | #define SLAVE_STATE_READY 3 // The ID for status READY (Slave is ready with a job and has a result) 49 | #define SLAVE_STATE_RESULT_READY 4 // The ID for status READY (The result is ready to send) 50 | #define SLAVE_STATE_RESULT_SENT 5 // The ID for status READY (The result has been sent) 51 | #define SLAVE_STATE_ERROR 6 // The ID for status ERROR (Slave has a problem) 52 | 53 | #define PIN_IIC_SDA 4 // D2 - A4 - GPIO4 54 | #define PIN_IIC_SCL 5 // D1 - A5 - GPIO5 55 | #define IIC_SPEED 100000 // The speed 56 | 57 | #define IIC_ID_MIN 1 // The first possible address 58 | #define IIC_ID_MAX 50 // The last possible address 59 | 60 | 61 | 62 | /*********************************************************************************************************************** 63 | * Variables 64 | **********************************************************************************************************************/ 65 | 66 | byte iic_id = 1; 67 | StreamString bufferReceive; 68 | StreamString bufferRequest; 69 | int slaveState = SLAVE_STATE_UNKNOWN; 70 | bool logSerial = true; 71 | String ducoId = ""; 72 | 73 | 74 | 75 | /*********************************************************************************************************************** 76 | * Methods 77 | **********************************************************************************************************************/ 78 | 79 | // Methods Helper 80 | String getDucoId(); 81 | String getPseudoUniqueIdString(); 82 | unsigned long getStartupDelay(); 83 | void ledBlink(int pin, int msOn, int msOff); 84 | void ledSet(bool ledAddress, bool ledFree, bool ledWorking, bool ledReady); 85 | 86 | // Methods Iic 87 | void iicSetup(); 88 | void iicHandlerReceive(int numBytes); 89 | void iicHandlerRequest(); 90 | void iicWorker(); 91 | 92 | // Methods Log 93 | void logSetup(); 94 | void logMessage(String message); 95 | void logMessageSerial(String message); 96 | 97 | 98 | 99 | /*********************************************************************************************************************** 100 | * Code Main 101 | **********************************************************************************************************************/ 102 | 103 | void setup() { 104 | delay(100); 105 | if (PIN_LED_ADDRESS) { 106 | pinMode(PIN_LED_ADDRESS, OUTPUT); 107 | digitalWrite(PIN_LED_ADDRESS, LOW); 108 | } 109 | if (PIN_LED_FREE) { 110 | pinMode(PIN_LED_FREE, OUTPUT); 111 | digitalWrite(PIN_LED_FREE, LOW); 112 | } 113 | if (PIN_LED_WORKING) { 114 | pinMode(PIN_LED_WORKING, OUTPUT); 115 | digitalWrite(PIN_LED_WORKING, LOW); 116 | } 117 | if (PIN_LED_READY) { 118 | pinMode(PIN_LED_READY, OUTPUT); 119 | digitalWrite(PIN_LED_READY, LOW); 120 | } 121 | logSetup(); 122 | delay(getStartupDelay()); 123 | setState(SLAVE_STATE_UNKNOWN); 124 | iicSetup(); 125 | } 126 | 127 | void loop() { 128 | iicWorker(); 129 | delay(5); 130 | } 131 | 132 | void setState(int state) { 133 | if (slaveState != state) { 134 | slaveState = state; 135 | if (slaveState == SLAVE_STATE_UNKNOWN) { 136 | logMessage("Set state ID: "+String(state)+ " (UNKNOWN)"); 137 | ledSet(false, false, false, false); 138 | } else if (slaveState == SLAVE_STATE_FREE) { 139 | logMessage("Set state ID: "+String(state)+ " (FREE)"); 140 | ledSet(false, true, false, false); 141 | } else if (slaveState == SLAVE_STATE_WORKING) { 142 | logMessage("Set state ID: "+String(state)+ " (WORKING)"); 143 | ledSet(false, false, true, false); 144 | } else if (slaveState == SLAVE_STATE_READY) { 145 | logMessage("Set state ID: "+String(state)+ " (READY)"); 146 | ledSet(false, false, false, true); 147 | } else if (slaveState == SLAVE_STATE_RESULT_READY) { 148 | logMessage("Set state ID: "+String(state)+ " (RESULT_READY)"); 149 | ledSet(false, false, false, true); 150 | } else if (slaveState == SLAVE_STATE_RESULT_SENT) { 151 | logMessage("Set state ID: "+String(state)+ " (RESULT_SENT)"); 152 | ledSet(false, true, false, false); 153 | } else if (slaveState == SLAVE_STATE_ERROR) { 154 | logMessage("Set state ID: "+String(state)+ " (ERROR)"); 155 | ledSet(false, false, false, false); 156 | } else { 157 | logMessage("Set state ID: "+String(state)+ " (ERROR)"); 158 | ledSet(false, false, false, false); 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /ESP8266_Master/ESP8266_ClientHttps.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ESP8266_ClientHttps 4 | * Version: 0.1 5 | * Purpose: Connection and communication with the Duino Coin Server 6 | * Author: Frank Niggemann 7 | */ 8 | 9 | 10 | 11 | /*********************************************************************************************************************** 12 | * Code ClientHttps 13 | **********************************************************************************************************************/ 14 | 15 | /** 16 | * Initializes the HTTPS client part of the software 17 | */ 18 | void clientHttpsSetup() { 19 | logMessage("Master", "clientHttpsSetup", "MethodName", ""); 20 | https.setTimeout(30000); 21 | } 22 | 23 | /** 24 | * Reads and returns the content from the given URL 25 | * 26 | * @param String url The URL whose content is to be read and returned 27 | * 28 | * @return String The content 29 | */ 30 | String clientHttpsGetContent(String url) { 31 | logMessage("ClientHttps", "clientHttpsGetContent", "MethodName", ""); 32 | String httpsContent = ""; 33 | std::unique_ptrclientSecure(new BearSSL::WiFiClientSecure); 34 | clientSecure->setInsecure(); 35 | if (https.begin(*clientSecure, url)) 36 | { 37 | int httpsCode = https.GET(); 38 | if (httpsCode == HTTP_CODE_OK) 39 | { 40 | httpsContent = https.getString(); 41 | logMessage("ClientHttps", "clientHttpsGetContent", "MethodDetail", "HTTPS get content: "+httpsContent); 42 | } 43 | else 44 | { 45 | logMessage("ClientHttps", "clientHttpsGetContent", "MethodDetail", "HTTPS get content failed with errorcode ("+String(httpsCode)+") -> " + https.errorToString(httpsCode)); 46 | } 47 | https.end(); 48 | } 49 | return httpsContent; 50 | } 51 | 52 | /** 53 | * Requests a pool configuration from the pool server 54 | */ 55 | void clientHttpsRequestPoolConfiguration() { 56 | logMessage("ClientHttps", "clientHttpsRequestPoolConfiguration", "MethodName", ""); 57 | if (masterState != MASTER_STATE_LOADING_POOL) { 58 | setStateMaster(MASTER_STATE_LOADING_POOL); 59 | } 60 | if (serverPoolHost!="" && serverPoolPort!="") { 61 | if (masterState != MASTER_STATE_POOL_LOADED) { 62 | setStateMaster(MASTER_STATE_POOL_LOADED); 63 | } 64 | logMessage("ClientHttps", "clientHttpsRequestPoolConfiguration", "MethodDetail", "Updated pool configuration to host " + serverPoolHost + " and port " + serverPoolPort + " with name " + serverPoolName); 65 | serverPoolError = ""; 66 | return; 67 | } 68 | logMessage("ClientHttps", "clientHttpsRequestPoolConfiguration", "MethodDetail", "Request pool from " + urlRequestPool); 69 | String content = clientHttpsGetContent(urlRequestPool); 70 | if (content != "") { 71 | logMessage("ClientHttps", "clientHttpsRequestPoolConfiguration", "MethodDetail", "Return content -> " + content); 72 | DynamicJsonDocument json(1024); 73 | deserializeJson(json, content); 74 | serverPoolHost = String(json["ip"]); 75 | serverPoolPort = String(json["port"]); 76 | serverPoolName = String(json["name"]); 77 | setStateMaster(MASTER_STATE_POOL_LOADED); 78 | logMessage("ClientHttps", "clientHttpsRequestPoolConfiguration", "MethodDetail", "Updated pool configuration to host " + serverPoolHost + " and port " + serverPoolPort + " with name " + serverPoolName); 79 | serverPoolError = ""; 80 | } else { 81 | // ToDo: Create fallback solution! 82 | logMessage("ClientHttps", "clientHttpsRequestPoolConfiguration", "MethodDetail", "Connection failed! Use fallback configuration!"); 83 | } 84 | } 85 | 86 | /** 87 | * Requests the user balance for the current user and updates the value in the system 88 | */ 89 | void clientHttpsRequestUserBalance() { 90 | logMessage("ClientHttps", "clientHttpsRequestUserBalance", "MethodName", ""); 91 | String content = clientHttpsGetContent(urlRequestUserBalance+nameUser); 92 | if (content != "") { 93 | DynamicJsonDocument json(10000); 94 | deserializeJson(json, content); 95 | float ducoBalance = json["result"]["balance"]; 96 | if (balanceFirstValue == 0) { 97 | balanceFirstValue = ducoBalance; 98 | balanceFirstTimestamp = ntpGetTimestamp(); 99 | } 100 | balanceLastValue = ducoBalance; 101 | balanceLastTimestamp = ntpGetTimestamp(); 102 | } else { 103 | logMessage("ClientHttps", "clientHttpsRequestUserBalance", "MethodDetail", "Connection failed!"); 104 | } 105 | 106 | } 107 | 108 | /** 109 | * Returns the pool configuration or 'unknown' as result 110 | * 111 | * @return String The pool configuration or 'unknown' 112 | */ 113 | String clientHttpsGetPoolString() { 114 | logMessage("ClientHttps", "clientHttpsGetPoolString", "MethodName", ""); 115 | String pool = "Unknown"; 116 | if (serverPoolHost!= "" && serverPoolPort!="") { 117 | pool = serverPoolHost + ":" + serverPoolPort; 118 | } 119 | logMessage("ClientHttps", "clientHttpsGetPoolString", "MethodDetail", "Returns " + pool); 120 | return pool; 121 | } 122 | -------------------------------------------------------------------------------- /ESP8266_Master/ESP8266_ClientPool.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ESP8266_ClientPool 4 | * Version: 0.1 5 | * Purpose: Connection and communication with the Duino Coin Pool 6 | * Author: Frank Niggemann 7 | */ 8 | 9 | 10 | 11 | /*********************************************************************************************************************** 12 | * Code ClientPool 13 | **********************************************************************************************************************/ 14 | 15 | /** 16 | * Initializes the pool client part of the software 17 | */ 18 | void clientPoolSetup() { 19 | logMessage("ClientPool", "clientPoolSetup", "MethodName", ""); 20 | for (int id=SLAVE_ID_MIN ; id500) { 79 | clientPoolValidateState(id); 80 | } 81 | } 82 | } 83 | } 84 | 85 | /** 86 | * Validates the state for the client with the given ID 87 | * 88 | * @param int id The id of the client 89 | */ 90 | void clientPoolValidateState(int id) { 91 | logMessage("ClientPool", "clientPoolValidateState", "MethodName", ""); 92 | if (poolClientState[id] == CLIENT_STATE_UNKNOWN) { 93 | 94 | } else if (poolClientState[id] == CLIENT_STATE_OFFLINE) { 95 | 96 | } else if (poolClientState[id] == CLIENT_STATE_CONNECTING) { 97 | 98 | } else if (poolClientState[id] == CLIENT_STATE_ONLINE) { 99 | 100 | } else if (poolClientState[id] == CLIENT_STATE_JOB_REQUEST_SENT_TO_SERVER) { 101 | setStateClient(id, CLIENT_STATE_ONLINE); 102 | } else if (poolClientState[id] == CLIENT_STATE_JOB_REQUEST_RESULT_FROM_SERVER) { 103 | 104 | } else if (poolClientState[id] == CLIENT_STATE_JOB_SENT_TO_SLAVE) { 105 | 106 | } else if (poolClientState[id] == CLIENT_STATE_JOB_RESULT_FROM_SLAVE) { 107 | 108 | } else if (poolClientState[id] == CLIENT_STATE_JOB_RESULT_SENT_TO_SERVER) { 109 | setStateClient(id, CLIENT_STATE_JOB_RESULT_FROM_SLAVE); 110 | } else if (poolClientState[id] == CLIENT_STATE_JOB_RESULT_RESULT_FROM_SERVER) { 111 | 112 | } 113 | } 114 | 115 | /** 116 | * Logs the states of all clients with a slave 117 | */ 118 | void clientPoolLogStates() { 119 | logMessage("ClientPool", "clientPoolLogStates", "MethodName", ""); 120 | for (byte id=SLAVE_ID_MIN ; id In State: " + String(poolClientState[id])); 123 | } 124 | } 125 | } 126 | 127 | /** 128 | * Returns the number of online clients 129 | * 130 | * @return int The number of online clients 131 | */ 132 | int clientPoolClientsOnline() { 133 | logMessage("ClientPool", "clientPoolClientsOnline", "MethodName", ""); 134 | int counter = 0; 135 | for (byte id=SLAVE_ID_MIN ; id CLIENT_STATE_OFFLINE) { 137 | counter ++; 138 | } 139 | } 140 | logMessage("ClientPool", "clientPoolClientsOnline", "MethodDetail", "Return " + String(counter)); 141 | return counter; 142 | } 143 | 144 | /** 145 | * Returns the connection status of the client with the given ID 146 | * 147 | * @param int id The id of the client 148 | * 149 | * @return bool Returns true if client is connected and false if not 150 | */ 151 | bool clientPoolClientIsConnected(int id) { 152 | logMessage("ClientPool", "clientPoolClientIsConnected", "MethodName", ""); 153 | if (poolClientInstance[id].connected()) { 154 | if (poolClientState[id] != CLIENT_STATE_ONLINE) { 155 | setStateClient(id, CLIENT_STATE_ONLINE); 156 | } 157 | poolClientInstance[id].setTimeout(500); 158 | logMessage("ClientPool", "clientPoolClientIsConnected", "MethodDetail", "Client ID " + String(id) + " -> return true"); 159 | return true; 160 | } 161 | if (poolClientState[id] != CLIENT_STATE_OFFLINE) { 162 | setStateClient(id, CLIENT_STATE_OFFLINE); 163 | } 164 | logMessage("ClientPool", "clientPoolClientIsConnected", "MethodDetail", "Client ID " + String(id) + " -> return false"); 165 | return false; 166 | } 167 | 168 | /** 169 | * Tries to connect the client with the given ID to the pool 170 | * 171 | * @param int id The id of the client 172 | * 173 | * @return bool Returns true if client is connected to the pool and false if not 174 | */ 175 | bool clientPoolConnectClient(int id) { 176 | logMessage("ClientPool", "clientPoolConnectClient", "MethodName", ""); 177 | if (clientPoolClientIsConnected(id)) { 178 | return true; 179 | } 180 | if (poolClientState[id] == CLIENT_STATE_CONNECTING) { 181 | return false; 182 | } 183 | logMessage("ClientPool", "clientPoolConnectClient", "MethodDetail", "Client ID " + String(id) + " -> Try connecting!"); 184 | poolClientInstance[id].setTimeout(30000); 185 | if (!poolClientInstance[id].connect(serverPoolHost.c_str(), serverPoolPort.toInt())) { 186 | setStateClient(id, CLIENT_STATE_OFFLINE); 187 | logMessage("ClientPool", "clientPoolConnectClient", "MethodDetail", "Client ID " + String(id) + " -> return false"); 188 | return false; 189 | } 190 | setStateClient(id, CLIENT_STATE_CONNECTING); 191 | logMessage("ClientPool", "clientPoolConnectClient", "MethodDetail", "Client ID " + String(id) + " -> return true"); 192 | return true; 193 | } 194 | 195 | /** 196 | * Requests the next job for the client with the given ID 197 | * 198 | * @param int id The id of the client 199 | * 200 | * @return bool Returns true if request is sent and false if not 201 | */ 202 | bool clientPoolRequestNextJobForClient(int id) { 203 | logMessage("ClientPool", "clientPoolRequestNextJobForClient", "MethodName", ""); 204 | if (!clientPoolClientIsConnected(id)) { 205 | return false; 206 | } 207 | logMessage("ClientPool", "clientPoolRequestNextJobForClient", "MethodDetail", "Client ID " + String(id) + " -> Request next job"); 208 | poolClientInstance[id].print("JOB," + nameUser + ",AVR\n"); 209 | poolClientTimeRequestStart[id] = millis(); 210 | setStateClient(id, CLIENT_STATE_JOB_REQUEST_SENT_TO_SERVER); 211 | return true; 212 | } 213 | 214 | /** 215 | * Reads the job result for the client with the given ID 216 | * 217 | * @param int id The id of the client 218 | * @param String content The content from pool server 219 | */ 220 | void clientPoolEvaluateJobResultForClient(int id, String content) { 221 | logMessage("ClientPool", "clientPoolEvaluateJobResultForClient", "MethodName", ""); 222 | poolClientLastBlockHash[id] = splitStringAndGetValue(content, ',', 0); 223 | poolClientNextBlockHash[id] = splitStringAndGetValue(content, ',', 1); 224 | poolClientDifficulty[id] = splitStringAndGetValue(content, ',', 2); 225 | if (poolClientLastBlockHash[id].length() > 40) { 226 | int lengthVersionText = poolClientLastBlockHash[id].length()-40; 227 | poolClientServerVersion[id] = poolClientLastBlockHash[id].substring(0, lengthVersionText); 228 | poolClientLastBlockHash[id] = poolClientLastBlockHash[id].substring(lengthVersionText); 229 | } 230 | if (poolClientLastBlockHash[id].length() < 10) { 231 | logMessage("ClientPool", "clientPoolEvaluateJobResultForClient", "MethodDetail", "Client ID " + String(id) + " -> Invalid job result -> " + content); 232 | setStateClient(id, CLIENT_STATE_ONLINE); 233 | } else { 234 | logMessage("ClientPool", "clientPoolEvaluateJobResultForClient", "MethodDetail", "Client ID " + String(id) + " -> Next job -> " + poolClientLastBlockHash[id] + "," + poolClientNextBlockHash[id] + "," + poolClientDifficulty[id]); 235 | jobs_sum++; 236 | poolClientJobsSum[id]++; 237 | setStateClient(id, CLIENT_STATE_JOB_REQUEST_RESULT_FROM_SERVER); 238 | } 239 | } 240 | 241 | /** 242 | * Sends the job of the client with the given ID to the slave with the same ID 243 | * 244 | * @param int id The id of the client / slave 245 | * 246 | * @return bool Returns true if job is sent and false if not 247 | */ 248 | void clientPoolSendClientJobToSlave(int id) { 249 | logMessage("ClientPool", "clientPoolSendClientJobToSlave", "MethodName", ""); 250 | String result = slaveRequestLn(id); 251 | logMessage("ClientPool", "clientPoolSendClientJobToSlave", "MethodDetail", "Client ID " + String(id) + " -> Send job to node -> " + poolClientLastBlockHash[id] + "," + poolClientNextBlockHash[id] + "," + poolClientDifficulty[id]); 252 | slaveSendNextJob(id, poolClientLastBlockHash[id], poolClientNextBlockHash[id], poolClientDifficulty[id]); 253 | setStateClient(id, CLIENT_STATE_JOB_SENT_TO_SLAVE); 254 | } 255 | 256 | /** 257 | * Requests the job result from the slave with the given ID for the client with the same ID 258 | * 259 | * @param int id The id of the client / slave 260 | */ 261 | void clientPoolRequestClientJobResultFromSlave(int id) { 262 | logMessage("ClientPool", "clientPoolRequestClientJobResultFromSlave", "MethodName", ""); 263 | String result = slaveRequestLn(id); 264 | if (result != "") { 265 | logMessage("ClientPool", "clientPoolRequestClientJobResultFromSlave", "MethodDetail", "Client ID " + String(id) + " -> Job result from node -> " + result); 266 | if (poolClientDifficulty[id].toInt() < 655) { 267 | poolClientDucos1aResult[id] = splitStringAndGetValue(result, ',', 0); 268 | } else { 269 | poolClientDucos1aResult[id] = "0"; 270 | } 271 | poolClientMicrotimeDifference[id] = splitStringAndGetValue(result, ',', 1); 272 | poolClientDucoId[id] = splitStringAndGetValue(result, ',', 2); 273 | setStateClient(id, CLIENT_STATE_JOB_RESULT_FROM_SLAVE); 274 | } 275 | } 276 | 277 | /** 278 | * Sends the job result for the client with the given ID 279 | * 280 | * @param int id The id of the client 281 | * 282 | * @return bool Returns true if result is sent and false if not 283 | */ 284 | bool clientPoolSendJobResultForClient(int id) { 285 | logMessage("ClientPool", "clientPoolSendJobResultForClient", "MethodName", ""); 286 | if (!clientPoolClientIsConnected(id)) { 287 | return false; 288 | } 289 | 290 | float hashRate = poolClientDucos1aResult[id].toInt() / (poolClientMicrotimeDifference[id].toInt() * .000001f); 291 | if (hashRate > 209) { 292 | hashRate = 207 + (random(-20, 20) / 100.0) ; 293 | } 294 | String result = poolClientDucos1aResult[id] + "," + String(hashRate, 2) + ","+minerName+","+nameRig+" CORE " + String(id) + "," + poolClientDucoId[id]; 295 | logMessage("ClientPool", "clientPoolSendJobResultForClient", "MethodDetail", "Client ID " + String(id) + " -> Send job result to server -> " + result); 296 | poolClientInstance[id].print(result); 297 | setStateClient(id, CLIENT_STATE_JOB_RESULT_SENT_TO_SERVER); 298 | return true; 299 | } 300 | 301 | /** 302 | * Evaluates the result for the job result from the client with the given ID 303 | * 304 | * @param int id The client ID 305 | * @param String content The content from pool server 306 | * 307 | * @return String Returns the result or an empty string 308 | */ 309 | void clientPoolEvaluateResultResultForClient(int id, String content) { 310 | logMessage("ClientPool", "clientPoolEvaluateResultResultForClient", "MethodName", ""); 311 | if (content.substring(0, 3)=="BAD") { 312 | jobs_bad++; 313 | poolClientJobsBad[id]++; 314 | logMessage("ClientPool", "clientPoolEvaluateResultResultForClient", "MethodDetail", "Client ID " + String(id) + " -> Job result -> BAD"); 315 | } else if (content.substring(0, 4)=="GOOD") { 316 | jobs_good++; 317 | poolClientJobsGood[id]++; 318 | logMessage("ClientPool", "clientPoolEvaluateResultResultForClient", "MethodDetail", "Client ID " + String(id) + " -> Job result -> GOOD"); 319 | } else if (content.substring(0, 5)=="BLOCK") { 320 | jobs_blocks++; 321 | poolClientJobsBlocks[id]++; 322 | logMessage("ClientPool", "clientPoolEvaluateResultResultForClient", "MethodDetail", "Client ID " + String(id) + " -> Job result -> BLOCK"); 323 | } 324 | setStateClient(id, CLIENT_STATE_JOB_RESULT_RESULT_FROM_SERVER); 325 | } 326 | 327 | /** 328 | * Reads and returns the content from the client with the given ID 329 | * 330 | * @param int id The id of the client 331 | * 332 | * @return String The content 333 | */ 334 | String clientPoolGetContentFromClient(int id) { 335 | logMessage("ClientPool", "clientPoolGetContentFromClient", "MethodName", ""); 336 | String content = ""; 337 | String contentBefore = poolClientBuffer[id]; 338 | if (poolClientInstance[id].available()) { 339 | while (poolClientInstance[id].available()) { 340 | char nextChar = poolClientInstance[id].read(); 341 | if (nextChar != '\n') { 342 | poolClientBuffer[id] += nextChar; 343 | } else { 344 | content = poolClientBuffer[id]; 345 | poolClientBuffer[id] = ""; 346 | } 347 | } 348 | } 349 | if (contentBefore!="" && contentBefore==poolClientBuffer[id]) { 350 | content = poolClientBuffer[id]; 351 | poolClientBuffer[id] = ""; 352 | } 353 | return content; 354 | } 355 | 356 | /** 357 | * Reads and returns the content from the client with the given ID 358 | * 359 | * @param int id The id of the client 360 | */ 361 | void clientPoolGetAndEvaluateContent(int id) { 362 | logMessage("ClientPool", "clientPoolGetAndEvaluateContent", "MethodName", ""); 363 | String content = clientPoolGetContentFromClient(id); 364 | if (content != "") { 365 | logMessage("ClientPool", "clientPoolGetAndEvaluateContent", "MethodDetail", "Client ID " + String(id) + " -> Answer from pool server -> " + content); 366 | if (content.substring(0, 3)=="BAD" || content.substring(0, 4)=="GOOD" || content.substring(0, 5)=="BLOCK") { 367 | logMessage("ClientPool", "clientPoolGetAndEvaluateContent", "MethodDetail", "Client ID " + String(id) + " -> Answer classified as answer for job result validation request"); 368 | clientPoolEvaluateResultResultForClient(id, content); 369 | } else { 370 | logMessage("ClientPool", "clientPoolGetAndEvaluateContent", "MethodDetail", "Client ID " + String(id) + " -> Answer classified as answer for job request"); 371 | clientPoolEvaluateJobResultForClient(id, content); 372 | } 373 | } 374 | } 375 | 376 | /** 377 | * Sets the state for the client 378 | * 379 | * @param int id The client ID 380 | * @param int state The new state for the client 381 | */ 382 | void setStateClient(int id, int state) { 383 | logMessage("ClientPool", "setStateClient", "MethodName-"+String(id)+"-"+String(state), ""); 384 | if (poolClientState[id] != state) { 385 | poolClientLoopsWithoutStateChange[id] = 0; 386 | poolClientState[id] = state; 387 | logMessage("ClientPool", "setStateClient", "StateChange", "Client ID " + String(id) + " -> Changed state to " + String(state)); 388 | } 389 | } 390 | -------------------------------------------------------------------------------- /ESP8266_Master/ESP8266_Display.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ESP8266_Display 4 | * Version: 0.1 5 | * Purpose: Control of the display 6 | * Author: Frank Niggemann 7 | */ 8 | 9 | 10 | 11 | /*********************************************************************************************************************** 12 | * Code Display 13 | **********************************************************************************************************************/ 14 | 15 | /** 16 | * Initializes the display part of the software 17 | */ 18 | void displaySetup() { 19 | logMessage("Display", "displaySetup", "MethodName", ""); 20 | display.init(); 21 | if (OLED_FLIP_VERTICALLY) { 22 | display.flipScreenVertically(); 23 | } 24 | display.setFont(ArialMT_Plain_10); 25 | display.resetDisplay(); 26 | display.displayOn(); 27 | display.setContrast(255); 28 | display.setI2cAutoInit(true); 29 | displayClear(); 30 | displayScreenHome(); 31 | } 32 | 33 | /** 34 | * Clears the LCD display 35 | */ 36 | void displayClear() { 37 | logMessage("Display", "displayClear", "MethodName", ""); 38 | display.clear(); 39 | display.display(); 40 | delay(10); 41 | } 42 | 43 | /** 44 | * Displays the home screen on the LCD display 45 | */ 46 | void displayScreenHome() { 47 | logMessage("Display", "displayScreenHome", "MethodName", ""); 48 | display.clear(); 49 | display.setTextAlignment(TEXT_ALIGN_LEFT); 50 | String text = ""; 51 | if (masterState == MASTER_STATE_BOOTING) { 52 | text = softwareName+" V"+softwareVersion+"\n"; 53 | text+= "Booting"; 54 | } else { 55 | if (!wifiConnected()) { 56 | if (!sdCardReady()) { 57 | text = softwareName+" V"+softwareVersion+"\n"; 58 | text+= "No SD card found!"; 59 | } else { 60 | text = softwareName+" V"+softwareVersion+"\n"; 61 | text+= "WiFi: "+wifiGetIp(); 62 | } 63 | } else { 64 | text = "WiFi: "+wifiGetIp()+"\n"; 65 | if (serverPoolHost!= "" && serverPoolPort!="") { 66 | text+= "Pool: "+clientHttpsGetPoolString()+"\n"; 67 | } else { 68 | text+= "Pool: Unknown \n"; 69 | } 70 | text+= "Nodes: "+String(nodes_sum)+" / Online: "+String(nodes_online)+"\n"; 71 | text+= "Jobs: "+String(jobs_sum)+" / Blocks: "+String(jobs_blocks)+"\n"; 72 | text+= "Good: "+String(jobs_good)+" / Bad: "+String(jobs_bad); 73 | } 74 | } 75 | display.drawString(0, 0, text); 76 | display.display(); 77 | delay(10); 78 | } 79 | -------------------------------------------------------------------------------- /ESP8266_Master/ESP8266_Helper.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ESP8266_Helper 4 | * Version: 0.1 5 | * Purpose: Contains all helper functions 6 | * Author: Frank Niggemann 7 | */ 8 | 9 | 10 | 11 | /*********************************************************************************************************************** 12 | * Code Helper 13 | **********************************************************************************************************************/ 14 | 15 | /** 16 | * Splits a given text by a given separator and returns the part of the text at the given position 17 | * 18 | * @param String text The given text 19 | * @param char separator The given separator 20 | * @param int position The position to return 21 | * 22 | * @return String The part of the text at the given position 23 | */ 24 | String splitStringAndGetValue(String text, char separator, int position) { 25 | logMessage("Helper", "splitStringAndGetValue", "MethodName", ""); 26 | int found = 0; 27 | int strPosition[] = { 0, -1 }; 28 | int maxPosition = text.length() - 1; 29 | 30 | for (int i = 0; i <= maxPosition && found <= position; i++) { 31 | if (text.charAt(i) == separator || i == maxPosition) { 32 | found++; 33 | strPosition[0] = strPosition[1] + 1; 34 | strPosition[1] = (i == maxPosition) ? i+1 : i; 35 | } 36 | } 37 | return found > position ? text.substring(strPosition[0], strPosition[1]) : ""; 38 | } 39 | -------------------------------------------------------------------------------- /ESP8266_Master/ESP8266_Log.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ESP8266_Log 4 | * Version: 0.1 5 | * Purpose: Central log management 6 | * Author: Frank Niggemann 7 | */ 8 | 9 | 10 | 11 | /*********************************************************************************************************************** 12 | * Code Log 13 | **********************************************************************************************************************/ 14 | 15 | /** 16 | * Initializes the log part of the software 17 | */ 18 | void logSetup() { 19 | if (logSerial) { 20 | Serial.begin(115200); 21 | Serial.println("\n"); 22 | } 23 | } 24 | 25 | /** 26 | * Writes the message to the configured log destination(s) 27 | */ 28 | void logMessage(String partName, String methodName, String type, String message) { 29 | bool partOk = false; 30 | bool typeOk = false; 31 | 32 | if (partName=="ClientHttps" && logClientHttps) { 33 | partOk = true; 34 | } else if (partName=="ClientPool" && logClientPool) { 35 | partOk = true; 36 | } else if (partName=="Display" && logDisplay) { 37 | partOk = true; 38 | } else if (partName=="Helper" && logHelper) { 39 | partOk = true; 40 | } else if (partName=="Master" && logMaster) { 41 | partOk = true; 42 | } else if (partName=="Ntp" && logNtp) { 43 | partOk = true; 44 | } else if (partName=="SdCard" && logSdCard) { 45 | partOk = true; 46 | } else if (partName=="ServerHttp" && logServerHttp) { 47 | partOk = true; 48 | } else if (partName=="Slaves" && logSlaves) { 49 | partOk = true; 50 | } else if (partName=="WiFi" && logWiFi) { 51 | partOk = true; 52 | } 53 | 54 | if (type=="MethodName" && logMethodNames) { 55 | typeOk = true; 56 | } else if (type=="MethodDetail" && logMethodDetails) { 57 | typeOk = true; 58 | } else if (type=="StateChange" && logStateChanges) { 59 | typeOk = true; 60 | } 61 | 62 | if (partOk && typeOk && logSerial) { 63 | if (message != "") { 64 | logMessageSerial(partName+"::"+methodName+"() -> "+message); 65 | } else { 66 | logMessageSerial(partName+"::"+methodName+"()"); 67 | } 68 | } 69 | } 70 | 71 | /** 72 | * Writes the log message to the serial stream 73 | */ 74 | void logMessageSerial(String message) { 75 | Serial.println(message); 76 | } 77 | -------------------------------------------------------------------------------- /ESP8266_Master/ESP8266_Master.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ESP8266_Master 4 | * Version: 0.1 5 | * Purpose: This is the master file that starts and coordinates the services 6 | * Author: Frank Niggemann 7 | * 8 | * Hardware: ESP8266 9 | * 10 | * Needed files: ESP8266_ClientHttps 11 | * ESP8266_ClientPool 12 | * ESP8266_Display 13 | * ESP8266_Helper 14 | * ESP8266_Log 15 | * ESP8266_Master 16 | * ESP8266_NTP 17 | * ESP8266_SD 18 | * ESP8266_ServerHttp 19 | * ESP8266_Slaves 20 | * ESP8266_WiFi 21 | * 22 | * Needed libraries: Arduino 23 | * ArduinoJson 24 | * ESP8266HTTPClient 25 | * ESP8266mDNS 26 | * ESP8266WiFi 27 | * ESPAsyncTCP 28 | * ESPAsyncWebServer 29 | * NTPClient 30 | * SdFat 31 | * SPIFFSEditor 32 | * SSD1306Wire 33 | * WiFiClientSecureBearSSL 34 | * WiFiUdp 35 | * Wire 36 | */ 37 | 38 | 39 | 40 | /*********************************************************************************************************************** 41 | * Includes 42 | **********************************************************************************************************************/ 43 | 44 | #include 45 | #include 46 | #include 47 | #include // OTA libraries 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | 60 | 61 | /*********************************************************************************************************************** 62 | * Definitions 63 | **********************************************************************************************************************/ 64 | 65 | // Possible states for the master node 66 | #define MASTER_STATE_UNKNOWN 0 // The ID for status UNKNOWN (Master is in an unknown state) 67 | #define MASTER_STATE_BOOTING 1 // The ID for status BOOTING (System is booting) 68 | #define MASTER_STATE_SCANNING 2 // The ID for status SCANNING (Master scanns for slaves) 69 | #define MASTER_STATE_LOADING_CONFIG 3 // The ID for status LOADING_CONFIG (Master loads configuration) 70 | #define MASTER_STATE_CONFIG_LOADED 4 // The ID for status LOADING_CONFIG (Master loads configuration) 71 | #define MASTER_STATE_CONNECTING_WIFI 5 // The ID for status CONNECTING_WIFI (Master connects to WiFi) 72 | #define MASTER_STATE_WIFI_CONNECTED 6 // The ID for status CONNECTING_WIFI (Master connects to WiFi) 73 | #define MASTER_STATE_LOADING_POOL 7 // The ID for status LOADING_POOL (Master loads pool configuration) 74 | #define MASTER_STATE_POOL_LOADED 8 // The ID for status POOL_LOADED (Master successfully loaded pool configuration) 75 | #define MASTER_STATE_CONNECTING_CLIENTS 9 // The ID for status CONNECTING_CLIENTS (Master connects clients to the pool server) 76 | #define MASTER_STATE_CLIENTS_CONNECTED 10 // The ID for status CLIENTS_CONNECTED (Master connected clients to the pool server) 77 | #define MASTER_STATE_RUNNING 11 // The ID for status RUNNING (Master and slaves are up and running) 78 | 79 | // Possible states for the pool server clients 80 | #define CLIENT_STATE_UNKNOWN 0 // The ID for status UNKNOWN (Client is in an unknown state) 81 | #define CLIENT_STATE_OFFLINE 1 // The ID for status OFFLINE (Client is offline) 82 | #define CLIENT_STATE_CONNECTING 2 // The ID for status CONNECTING (Client is connecting to pool server) 83 | #define CLIENT_STATE_ONLINE 3 // The ID for status ONLINE (Client is connected to pool server) 84 | #define CLIENT_STATE_JOB_REQUEST_SENT_TO_SERVER 4 // The ID for status JOB_REQUEST_SENT_TO_SERVER (Client has sent a job request to the server) 85 | #define CLIENT_STATE_JOB_REQUEST_RESULT_FROM_SERVER 5 // The ID for status JOB_REQUEST_RESULT_FROM_SERVER (Server has answered the request for a job) 86 | #define CLIENT_STATE_JOB_SENT_TO_SLAVE 6 // The ID for status JOB_SENT_TO_SLAVE (Job has been sent to the slave) 87 | #define CLIENT_STATE_JOB_RESULT_FROM_SLAVE 7 // The ID for status JOB_RESULT_FROM_SLAVE (Slave has sent the result for the job) 88 | #define CLIENT_STATE_JOB_RESULT_SENT_TO_SERVER 8 // The ID for status JOB_RESULT_SENT_TO_SERVER (Client has sent the job result to the server for verification) 89 | #define CLIENT_STATE_JOB_RESULT_RESULT_FROM_SERVER 9 // The ID for status JOB_RESULT_RESULT_FROM_SERVER (Server has answered the request for verification) 90 | #define CLIENT_STATE_ERROR 10 // The ID for status ERROR (The client has an error) 91 | 92 | // Slaves 93 | #define SLAVE_ID_MIN 1 // The first possible address for a slave 94 | #define SLAVE_ID_MAX 55 // The last possible address for a slave 95 | #define WIRE_PIN_SDA D2 // The communication pin for SDA (D2 on ESP8266) 96 | #define WIRE_PIN_SCL D1 // The communication pin for SCL (D1 on ESP8266) 97 | #define WIRE_CLOCK 100000 // The I2C communication frequency 98 | 99 | // Display 100 | #define OLED_PIN_SDA D2 // The communication pin for SDA (D2 on ESP8266) 101 | #define OLED_PIN_SCL D1 // The communication pin for SCL (D1 on ESP8266) 102 | #define OLED_ADDR 0x3C // The address of the OLED display 103 | #define OLED_FLIP_VERTICALLY 0 // With 1 the content of the display will be flipped vertically 104 | 105 | // SD 106 | #define SD_PIN_CS D8 // The CS pin (D8 on ESP8266) 107 | 108 | /*********************************************************************************************************************** 109 | * Variables 110 | **********************************************************************************************************************/ 111 | 112 | // Configuration 113 | String softwareName = "DuinoCoinRig"; // The name of this software 114 | String softwareVersion = "0.3"; // The version of this software 115 | String minerName = "AVR I2C 3.0"; // The name of the miner 116 | String wifiSsid = ""; // Your WiFi SSID 117 | String wifiPassword = ""; // Your WiFi password 118 | String wifiIp = ""; // Your WiFi IP 119 | String nameUser = ""; // Your Duino Coin username 120 | String nameRig = "DuinoCoinRig"; // Your name for this rig 121 | String urlRequestPool = "https://server.duinocoin.com/getPool"; // The url to request the pool server 122 | String urlRequestUserBalance = "https://server.duinocoin.com/balances/"; // The url to request the balance 123 | String serverPoolHost = ""; // The host of the pool server 124 | String serverPoolPort = ""; // The port to connect to 125 | String serverPoolName = "Pool-Server"; // The name of the pool server 126 | bool loadConfigFromSdCard = true; // With true loads config from SD card and overwrites this config here 127 | 128 | // Communication HTTPS 129 | HTTPClient https; // The used instance of HTTPClient to request content from a HTTPS source (ClientHttps) 130 | String serverBalanceError = ""; // The last balance server error 131 | 132 | // Communication pool server 133 | WiFiClient poolClientInstance[SLAVE_ID_MAX]; // The used instancec of WiFiClient to communicate with the pool server (ClientPool) 134 | String poolClientBuffer[SLAVE_ID_MAX]; // The communication buffer to ensure that only complete datasets will be processed (ClientPool) 135 | String poolClientServerVersion[SLAVE_ID_MAX]; // The server version for the clients node, reported by the pool server (ClientPool) 136 | String poolClientLastBlockHash[SLAVE_ID_MAX]; // The last block hash for the clients node, reported by the pool server (ClientPool) 137 | String poolClientNextBlockHash[SLAVE_ID_MAX]; // The next block hash for the clients node, reported by the pool server (ClientPool) 138 | String poolClientDifficulty[SLAVE_ID_MAX]; // The difficulty for the clients node, reported by the pool server (ClientPool) 139 | int poolClientState[SLAVE_ID_MAX]; // The state of the client (ClientPool) 140 | unsigned long poolClientTimeRequestStart[SLAVE_ID_MAX]; // The time when the last request was sent to the pool server (ClientPool) 141 | String poolClientDucos1aResult[SLAVE_ID_MAX]; // The result of the Ducos1a class reported by the slave (ClientPool) 142 | String poolClientDucoId[SLAVE_ID_MAX]; // The ID of the slave (ClientPool) 143 | String poolClientMicrotimeDifference[SLAVE_ID_MAX]; // The time used to calculate the result reported by the slave (ClientPool) 144 | int poolClientLoopsWithoutStateChange[SLAVE_ID_MAX]; // The number of loops without changing the state (ClientPool) 145 | unsigned long poolClientJobsSum[SLAVE_ID_MAX]; // The number of jobs sent to the slave (ClientPool) 146 | unsigned long poolClientJobsBlocks[SLAVE_ID_MAX]; // The number of found blocks for the slave (ClientPool) 147 | unsigned long poolClientJobsGood[SLAVE_ID_MAX]; // The number of correct results for the slave (ClientPool) 148 | unsigned long poolClientJobsBad[SLAVE_ID_MAX]; // The number of wrong results for the slave (ClientPool) 149 | String serverPoolError = ""; // The last pool server error 150 | 151 | // Display 152 | SSD1306Wire display(OLED_ADDR, OLED_PIN_SDA, OLED_PIN_SCL); // The used instance of SSD1306Wire with configuraion for the used display 153 | 154 | // Log 155 | bool logSerial = true; // true -> The log will be sent to the serial communication 156 | bool logClientHttps = true; // true -> Writes the logs for Part ClientHttps 157 | bool logClientPool = true; // true -> Writes the logs for Part ClientPool 158 | bool logDisplay = true; // true -> Writes the logs for Part Display 159 | bool logHelper = true; // true -> Writes the logs for Part Helper 160 | bool logMaster = true; // true -> Writes the logs for Part Master 161 | bool logNtp = true; // true -> Writes the logs for Part Ntp 162 | bool logSdCard = true; // true -> Writes the logs for Part SdCard 163 | bool logServerHttp = true; // true -> Writes the logs for Part ServerHttp 164 | bool logSlaves = true; // true -> Writes the logs for Part Slaves 165 | bool logWiFi = true; // true -> Writes the logs for Part WiFi 166 | bool logMethodNames = true; // true -> Writes the logs for method names 167 | bool logMethodDetails = true; // true -> Writes the logs for method details 168 | bool logStateChanges = true; // true -> Writes the logs for state changes 169 | 170 | // NTP 171 | WiFiUDP ntpUDP; // The used instance of WiFiUDP for UDP communication with NTP server 172 | NTPClient timeClient(ntpUDP, "pool.ntp.org", 0); // The used instance of NTPClient with configuration 173 | 174 | // SD card 175 | SdFat sd; // The used instance of SdFat for accessing the SD card 176 | SdFile file; // The used instance of SdFile for reading a file from SD card 177 | const size_t LINE_DIM = 100; // The max length of a line in the configuration file 178 | char line[LINE_DIM]; // The array for the lines in the configuration file 179 | 180 | // Local HTTP server 181 | AsyncWebServer server(80); // The used instance of AsyncWebServer for serving a website on port 80 182 | bool webserverStarted = false; // Set to true once the local web server has started 183 | 184 | // Slaves 185 | bool slaveFound[SLAVE_ID_MAX]; // An array with all possible IDs and the information if there is a slave with this ID 186 | 187 | 188 | // Current state of the rig 189 | int masterState = MASTER_STATE_UNKNOWN; // The current state of the master 190 | int nodes_sum = 0; // The number of connected nodes 191 | int nodes_online = 0; // The number of nodes with a connection to the pool 192 | unsigned long jobs_sum = 0; // The number of jobs (complete) 193 | unsigned long jobs_blocks = 0; // The number of blocks found 194 | unsigned long jobs_good = 0; // The number of good jobs 195 | unsigned long jobs_bad = 0; // The number of bad jobs 196 | unsigned long timestampFirst = 0; // First received timestamp 197 | unsigned long timestampNow = 0; // Current timestamp 198 | unsigned long timestampUpdateLast = 0; // Timestamp for last update 199 | unsigned long timestampUpdateEvery = 30000; // Update every 30000ms 200 | unsigned long workingSeconds = 0; // The number of seconds the rig has been running 201 | unsigned long balanceFirstTimestamp = 0; // Timestamp when the first balance has been received 202 | float balanceFirstValue = 0; // The first received balance 203 | unsigned long balanceLastTimestamp = 0; // Timestamp when the last balance has been received 204 | float balanceLastValue = 0; // The last received balance 205 | 206 | 207 | 208 | /*********************************************************************************************************************** 209 | * Methods 210 | **********************************************************************************************************************/ 211 | 212 | // Methods Master 213 | void setStateMaster(int state); 214 | 215 | // Methods Client HTTPS 216 | void clientHttpsSetup(); 217 | String clientHttpsGetContent(String url); 218 | void clientHttpsRequestPoolConfiguration(); 219 | String clientHttpsGetPoolString(); 220 | void clientHttpsRequestUserBalance(); 221 | 222 | // Methods Client Pool 223 | void clientPoolSetup(); 224 | void clientPoolConnectClients(); 225 | void clientPoolRotateStates(); 226 | void clientPoolLogStates(); 227 | int clientPoolClientsOnline(); 228 | bool clientPoolClientIsConnected(int id); 229 | bool clientPoolConnectClient(int id); 230 | bool clientPoolRequestNextJobForClient(int id); 231 | String clientPoolReadJobRequestResultForClient(int id); 232 | void clientPoolSendClientJobToSlave(int id); 233 | void clientPoolRequestClientJobResultFromSlave(int id); 234 | bool clientPoolSendJobResultForClient(int id); 235 | String clientPoolReadJobResultResultForClient(int id); 236 | String clientPoolGetContentFromClient(int id); 237 | void clientPoolGetAndEvaluateContent(int id); 238 | void setStateClient(int id, int state); 239 | 240 | // Methods Display 241 | void displaySetup(); 242 | void displayClear(); 243 | void displayScreenHome(); 244 | 245 | // Methods Helper 246 | String splitStringAndGetValue(String data, char separator, int index); 247 | 248 | // Methods Logging 249 | void logSetup(); 250 | void logMessage(String partName, String methodName, String type, String message); 251 | void logMessageSerial(String message); 252 | 253 | 254 | // Methods NTP 255 | void ntpSetup(); 256 | unsigned long ntpGetTimestamp(); 257 | 258 | // Methods SD card 259 | void sdCardSetup(); 260 | bool sdCardReady(); 261 | String sdCardReadyString(); 262 | bool sdCardConfigFileExists(); 263 | String sdCardConfigFileExistsString(); 264 | void sdCardReadConfigFile(); 265 | 266 | // Methods HTTP server 267 | void serverHttpSetup(); 268 | String serverHttpApiStatus(); 269 | String serverHttpApiLog(); 270 | 271 | // Methods slaves 272 | void slavesSetup(); 273 | void slavesWireInit(); 274 | void slavesScan(); 275 | boolean slaveExists(byte id); 276 | void slaveSendMessage(byte id, String message); 277 | void slavesSendMessage(String message); 278 | void slaveSendNextJob(byte id, String resultType, String lastBlockHash, String nextBlockHash, String difficulty); 279 | void slaveSendText(byte id, String text); 280 | void slaveSendLn(byte id, String text); 281 | String slaveRequestLn(byte id); 282 | 283 | // Methods WiFi 284 | void wifiSetup(String ssid, String password); 285 | bool wifiConnected(); 286 | String wifiGetIp(); 287 | 288 | 289 | 290 | /*********************************************************************************************************************** 291 | * Code Master 292 | **********************************************************************************************************************/ 293 | 294 | /** 295 | * Initializes the software 296 | */ 297 | void setup() { 298 | delay(100); 299 | logSetup(); 300 | delay(100); 301 | setStateMaster(MASTER_STATE_BOOTING); 302 | logMessage("Master", "setStateMaster", "MethodName", "\n"+softwareName+" "+softwareVersion+"\n"+"\n"); 303 | displaySetup(); 304 | delay(12000); 305 | slavesSetup(); 306 | displayScreenHome(); 307 | if (loadConfigFromSdCard) { 308 | sdCardSetup(); 309 | } 310 | } 311 | 312 | /** 313 | * The main loop for the software 314 | */ 315 | void loop() { 316 | displayScreenHome(); 317 | 318 | if (loadConfigFromSdCard && sdCardReady() && !wifiConnected()) { 319 | sdCardReadConfigFile(); 320 | displayScreenHome(); 321 | } else if (!loadConfigFromSdCard && !wifiConnected()) { 322 | setStateMaster(MASTER_STATE_CONFIG_LOADED); 323 | displayScreenHome(); 324 | } 325 | 326 | if (masterState == MASTER_STATE_CONFIG_LOADED) { 327 | wifiSetup(wifiSsid, wifiPassword); 328 | displayScreenHome(); 329 | } 330 | 331 | if (masterState == MASTER_STATE_WIFI_CONNECTED) { 332 | clientHttpsSetup(); 333 | ntpSetup(); 334 | if (timestampFirst == 0) { 335 | timestampFirst = ntpGetTimestamp(); 336 | timestampNow = timestampFirst; 337 | timestampUpdateLast = millis(); 338 | } 339 | clientHttpsRequestUserBalance(); 340 | clientHttpsRequestPoolConfiguration(); 341 | displayScreenHome(); 342 | } 343 | 344 | if (masterState == MASTER_STATE_POOL_LOADED) { 345 | clientPoolConnectClients(); 346 | nodes_online = clientPoolClientsOnline(); 347 | displayScreenHome(); 348 | } 349 | 350 | if (masterState == MASTER_STATE_CLIENTS_CONNECTED) { 351 | setStateMaster(MASTER_STATE_RUNNING); 352 | displayScreenHome(); 353 | balanceFirstTimestamp = ntpGetTimestamp(); 354 | } 355 | 356 | if (masterState == MASTER_STATE_RUNNING) { 357 | clientPoolRotateStates(); 358 | displayScreenHome(); 359 | if (!webserverStarted) { 360 | serverHttpSetup(); 361 | webserverStarted = true; 362 | } 363 | } 364 | 365 | if ((millis() - timestampUpdateLast) > timestampUpdateEvery) { 366 | timestampNow = ntpGetTimestamp(); 367 | timestampUpdateLast = millis(); 368 | workingSeconds = timestampNow - timestampFirst; 369 | logMessage("Master", "setStateMaster", "MethodDetail", "workingSeconds = " + String(timestampNow) + " - " + String(timestampFirst) + " = " + String(workingSeconds)); 370 | if (serverPoolHost=="" && serverPoolPort=="" && nodes_online==0) { 371 | clientHttpsRequestPoolConfiguration(); 372 | } 373 | } else { 374 | workingSeconds = timestampNow - timestampFirst + (millis()/1000) - (timestampUpdateLast/1000); 375 | logMessage("Master", "setStateMaster", "MethodDetail", "workingSeconds = " + String(timestampNow) + " - " + String(timestampFirst) + " + " + String((millis()/1000)) + " - " + String((timestampUpdateLast/1000)) + " = " + String(workingSeconds)); 376 | } 377 | 378 | delay(50); 379 | } 380 | 381 | /** 382 | * Sets the master state 383 | * 384 | * @param int setStateMaster The id of the state 385 | */ 386 | void setStateMaster(int state) { 387 | logMessage("Master", "setStateMaster", "MethodName", ""); 388 | if (masterState != state) { 389 | masterState = state; 390 | logMessage("Master", "setStateMaster", "StateChange", "Master changed state to " + String(state)); 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /ESP8266_Master/ESP8266_NTP.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ESP8266_NTP 4 | * Version: 0.1 5 | * Purpose: Central NTP management 6 | * Author: Frank Niggemann 7 | */ 8 | 9 | 10 | 11 | /*********************************************************************************************************************** 12 | * Code NTP 13 | **********************************************************************************************************************/ 14 | 15 | /** 16 | * Initializes the NTP part of the software 17 | */ 18 | void ntpSetup() { 19 | logMessage("Ntp", "ntpSetup", "MethodName", ""); 20 | timeClient.begin(); 21 | } 22 | 23 | /** 24 | * Returns the current UNIX timestamp 25 | * 26 | * @return unsigned long The current UNIX timestamp 27 | */ 28 | unsigned long ntpGetTimestamp() { 29 | logMessage("Ntp", "ntpGetTimestamp", "MethodName", ""); 30 | timeClient.update(); 31 | unsigned long timestamp = timeClient.getEpochTime(); 32 | logMessage("Ntp", "ntpGetTimestamp", "MethodDetail", "Return timestamp " + String(timestamp)); 33 | return timestamp; 34 | } 35 | -------------------------------------------------------------------------------- /ESP8266_Master/ESP8266_SdCard.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ESP8266_SD 4 | * Version: 0.1 5 | * Purpose: Communication with the SD card 6 | * Author: Frank Niggemann 7 | */ 8 | 9 | 10 | 11 | /*********************************************************************************************************************** 12 | * Code SD 13 | **********************************************************************************************************************/ 14 | 15 | /** 16 | * Initializes the SD part of the software 17 | */ 18 | void sdCardSetup() { 19 | logMessage("SdCard", "sdCardSetup", "MethodName", ""); 20 | } 21 | 22 | /** 23 | * Returns true if the SD card is inserted and ready 24 | * 25 | * @return bool Returns true if the SD card is inserted and ready 26 | */ 27 | bool sdCardReady() { 28 | logMessage("SdCard", "sdCardReady", "MethodName", ""); 29 | if (!sd.begin(SD_PIN_CS, SPI_HALF_SPEED)) { 30 | return false; 31 | } 32 | return true; 33 | } 34 | 35 | /** 36 | * Returns a describing string for the ready status of the SD card 37 | * 38 | * @return String The describing string for the ready status of the SD card 39 | */ 40 | String sdCardReadyString() { 41 | logMessage("SdCard", "sdCardReadyString", "MethodName", ""); 42 | if (!sdCardReady()) { 43 | return "SD card not ready"; 44 | } 45 | return "SD card ready"; 46 | } 47 | 48 | /** 49 | * Returns true if the config file exists on the SD card 50 | * 51 | * @return bool Returns true if the config file exists on the SD card 52 | */ 53 | bool sdCardConfigFileExists() { 54 | logMessage("SdCard", "sdCardConfigFileExists", "MethodName", ""); 55 | if (!sdCardReady()) { 56 | return false; 57 | } 58 | if (!file.open("config.rig", O_READ)) { 59 | return false; 60 | } 61 | return true; 62 | } 63 | 64 | /** 65 | * Returns a describing string for the existence of the config file on the SD card 66 | * 67 | * @return String The describing string for the existence of the config file on the SD card 68 | */ 69 | String sdCardConfigFileExistsString() { 70 | logMessage("SdCard", "sdCardConfigFileExistsString", "MethodName", ""); 71 | if (sdCardConfigFileExists) { 72 | return "Config file 'config.rig' found on SD card"; 73 | } 74 | return "Config file 'config.rig' not found on SD card"; 75 | } 76 | 77 | /** 78 | * Reads the config file and sets the local configuration 79 | */ 80 | void sdCardReadConfigFile() { 81 | logMessage("SdCard", "sdCardReadConfigFile", "MethodName", ""); 82 | size_t n; 83 | if (!sdCardReady()) { 84 | logMessage("SdCard", "sdCardReadConfigFile", "MethodDetail", "No SD card found"); 85 | } else if (!sdCardConfigFileExists()) { 86 | logMessage("SdCard", "sdCardReadConfigFile", "MethodDetail", "No config file 'config.rig' found on SD card"); 87 | } else { 88 | setStateMaster(MASTER_STATE_LOADING_CONFIG); 89 | logMessage("SdCard", "sdCardReadConfigFile", "MethodDetail", "Loading config file 'config.rig'"); 90 | String config = ""; 91 | while ((n = file.fgets(line, sizeof(line))) > 0) { 92 | config = String(line); 93 | config.trim(); 94 | if (config.substring(0, 10) == "wifi_ssid=") { 95 | wifiSsid = config.substring(10); 96 | logMessage("SdCard", "sdCardReadConfigFile", "MethodDetail", "Config for WiFi SSID loaded"); 97 | } else if (config.substring(0, 14) == "wifi_password=") { 98 | wifiPassword = config.substring(14); 99 | logMessage("SdCard", "sdCardReadConfigFile", "MethodDetail", "Config for WiFi password loaded"); 100 | } else if (config.substring(0, 10) == "name_user=") { 101 | nameUser = config.substring(10); 102 | logMessage("SdCard", "sdCardReadConfigFile", "MethodDetail", "Config for user name loaded"); 103 | } else if (config.substring(0, 9) == "name_rig=") { 104 | nameRig = config.substring(9); 105 | logMessage("SdCard", "sdCardReadConfigFile", "MethodDetail", "Config for rig name loaded"); 106 | } 107 | } 108 | setStateMaster(MASTER_STATE_CONFIG_LOADED); 109 | logMessage("SdCard", "sdCardReadConfigFile", "MethodDetail", "Config file 'config.rig' loaded"); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /ESP8266_Master/ESP8266_ServerHttp.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ESP8266_ServerHttp 4 | * Version: 0.1 5 | * Purpose: The local HTTP server for the user front end 6 | * Author: Frank Niggemann 7 | */ 8 | 9 | 10 | 11 | /*********************************************************************************************************************** 12 | * Code ServerHttp 13 | **********************************************************************************************************************/ 14 | 15 | /** 16 | * Initializes the HTTP server part of the software 17 | */ 18 | void serverHttpSetup() { 19 | logMessage("ServerHttp", "serverHttpSetup", "MethodName", ""); 20 | SPIFFS.begin(); 21 | 22 | server.on("/api/status", HTTP_GET, [](AsyncWebServerRequest * request) { 23 | request->send(200, "text/plain", serverHttpApiStatus()); 24 | }); 25 | 26 | server.on("/api/log", HTTP_GET, [](AsyncWebServerRequest * request) { 27 | request->send(200, "text/plain", serverHttpApiLog()); 28 | }); 29 | 30 | server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ 31 | request->send(SPIFFS, "/index.html"); 32 | }); 33 | 34 | server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html"); 35 | 36 | server.begin(); 37 | } 38 | 39 | /** 40 | * Returns the current rig status as JSON 41 | */ 42 | String serverHttpApiStatus() { 43 | logMessage("ServerHttp", "serverHttpApiStatus", "MethodName", ""); 44 | String apiStatus = ""; 45 | if (nodes_online > 0) { 46 | apiStatus += "{"; 47 | apiStatus += "\"rig_name\":\""+nameRig+"\","; 48 | apiStatus += "\"rig_ip\":\""+wifiIp+"\","; 49 | apiStatus += "\"user_name\":\""+nameUser+"\","; 50 | apiStatus += "\"pool_address\":\""+serverPoolHost+":"+serverPoolPort+"\","; 51 | apiStatus += "\"up_and_running_since\":\""+String(workingSeconds)+"\","; 52 | apiStatus += "\"balance_first_value\":"+String(balanceFirstValue, 10)+","; 53 | apiStatus += "\"balance_first_timestamp\":"+String(balanceFirstTimestamp)+","; 54 | apiStatus += "\"nodes\":\""+String(nodes_sum)+"\","; 55 | apiStatus += "\"nodes_online\":\""+String(nodes_online)+"\","; 56 | apiStatus += "\"number_of_jobs\":\""+String(jobs_sum)+"\","; 57 | apiStatus += "\"number_of_blocks\":\""+String(jobs_blocks)+"\","; 58 | apiStatus += "\"jobs_good\":\""+String(jobs_good)+"\","; 59 | apiStatus += "\"jobs_bad\":\""+String(jobs_bad)+"\","; 60 | apiStatus += "\"core_details\":["; 61 | 62 | // ToDo ... 63 | 64 | apiStatus += "]"; 65 | apiStatus += "}"; 66 | } 67 | return apiStatus; 68 | } 69 | 70 | /** 71 | * Returns the log files 72 | */ 73 | String serverHttpApiLog() { 74 | logMessage("ServerHttp", "serverHttpApiLog", "MethodName", ""); 75 | String content = "Log"; 76 | return content; 77 | } 78 | -------------------------------------------------------------------------------- /ESP8266_Master/ESP8266_Slaves.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ESP8266_Slaves 4 | * Version: 0.1 5 | * Purpose: Communication with the slaves 6 | * Author: Frank Niggemann 7 | */ 8 | 9 | 10 | 11 | /*********************************************************************************************************************** 12 | * Code Slaves 13 | **********************************************************************************************************************/ 14 | 15 | /** 16 | * Initializes communication with the slaves 17 | */ 18 | void slavesSetup() { 19 | logMessage("Slaves", "slavesSetup", "MethodName", ""); 20 | slavesWireInit(); 21 | slavesScan(); 22 | } 23 | 24 | /** 25 | * Initializes the I²C data bus 26 | */ 27 | void slavesWireInit() { 28 | logMessage("Slaves", "slavesWireInit", "MethodName", ""); 29 | Wire.begin(WIRE_PIN_SDA, WIRE_PIN_SCL); 30 | Wire.setClock(WIRE_CLOCK); 31 | } 32 | 33 | /** 34 | * Searches for existing slaves 35 | */ 36 | void slavesScan() { 37 | logMessage("Slaves", "slavesScan", "MethodName", ""); 38 | setStateMaster(MASTER_STATE_SCANNING); 39 | logMessage("Slaves", "slavesScan", "MethodDetail", "Start scanning for slaves"); 40 | int counter = 0; 41 | for (byte id=SLAVE_ID_MIN ; id " + message); 93 | slaveSendText(id, message); 94 | } 95 | } 96 | } 97 | 98 | /** 99 | * Sends the next job to the slave with the given ID 100 | * 101 | * @param byte id The ID of the slave to which the text is to be sent 102 | * @param String hashLastBlock The hash for the last block 103 | * @param String hashNextBlock The hash for the next block 104 | * @param int difficulty The difficulty 105 | */ 106 | void slaveSendNextJob(byte id, String lastBlockHash, String nextBlockHash, String difficulty) { 107 | logMessage("Slaves", "slaveSendNextJob", "MethodName", ""); 108 | String job = lastBlockHash + "," + nextBlockHash + "," + difficulty; 109 | slaveSendLn(id, job); 110 | } 111 | 112 | /** 113 | * Sends a text to the slave with the given ID 114 | * 115 | * @param byte id The ID of the slave to which the text is to be sent 116 | * @param String text The text to be sent to the slave 117 | */ 118 | void slaveSendText(byte id, String text) { 119 | logMessage("Slaves", "slaveSendText", "MethodName", ""); 120 | logMessage("Slaves", "slaveSendText", "MethodDetail", "Send text to ID " + String(id) + " -> " + text); 121 | slavesWireInit(); 122 | for (int pos=0 ; pos " + text); 165 | return text; 166 | } 167 | -------------------------------------------------------------------------------- /ESP8266_Master/ESP8266_WiFi.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: DuinoCoinRig 3 | * File: ESP8266_WiFi 4 | * Version: 0.1 5 | * Purpose: Communication with the wireless network 6 | * Author: Frank Niggemann 7 | */ 8 | 9 | 10 | 11 | /*********************************************************************************************************************** 12 | * Code WiFi 13 | **********************************************************************************************************************/ 14 | 15 | /** 16 | * Initializes the WiFi part of the software 17 | * 18 | * @param String ssid The SSID of the WiFi networt 19 | * @param String password The password for the WiFi network with the given SSID 20 | */ 21 | void wifiSetup(String ssid, String password) { 22 | logMessage("WiFi", "wifiSetup", "MethodName", ""); 23 | if (ssid!="") { 24 | setStateMaster(MASTER_STATE_CONNECTING_WIFI); 25 | logMessage("WiFi", "wifiSetup", "MethodDetail", "Connecting to: " + String(ssid)); 26 | WiFi.mode(WIFI_STA); 27 | WiFi.begin(ssid, password); 28 | while (WiFi.status() != WL_CONNECTED) { 29 | delay(200); 30 | } 31 | setStateMaster(MASTER_STATE_WIFI_CONNECTED); 32 | wifiIp = wifiGetIp(); 33 | logMessage("WiFi", "wifiSetup", "MethodDetail", "Connected with IP address: " + wifiIp); 34 | } else { 35 | logMessage("WiFi", "wifiSetup", "MethodDetail", "Can't connect to WiFi without SSID"); 36 | } 37 | displayScreenHome(); 38 | } 39 | 40 | /** 41 | * Returns the WiFi connection status 42 | * 43 | * @return bool Returns the connection status 44 | */ 45 | bool wifiConnected() { 46 | logMessage("WiFi", "wifiConnected", "MethodName", ""); 47 | if (WiFi.status() != WL_CONNECTED) { 48 | logMessage("WiFi", "wifiConnected", "MethodDetail", "Return false"); 49 | return false; 50 | } 51 | logMessage("WiFi", "wifiConnected", "MethodDetail", "Return true"); 52 | return true; 53 | } 54 | 55 | /** 56 | * Returns the IP of the WiFi connection 57 | * 58 | * @return String Returns the IP of the current WiFi connection or "Not connected" 59 | */ 60 | String wifiGetIp() { 61 | logMessage("WiFi", "wifiGetIp", "MethodName", ""); 62 | if (!wifiConnected()) { 63 | logMessage("WiFi", "wifiGetIp", "MethodDetail", "Return 'Not connected'"); 64 | return "Not connected"; 65 | } 66 | String ip = WiFi.localIP().toString(); 67 | logMessage("WiFi", "wifiGetIp", "MethodDetail", "Return " + ip); 68 | return ip; 69 | } 70 | -------------------------------------------------------------------------------- /ESP8266_Master/data/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicrocontrollerExpert/duino-coin-rig/137fcaeb2f7b62e1e32d04bd79020f80a568c1c2/ESP8266_Master/data/android-chrome-192x192.png -------------------------------------------------------------------------------- /ESP8266_Master/data/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicrocontrollerExpert/duino-coin-rig/137fcaeb2f7b62e1e32d04bd79020f80a568c1c2/ESP8266_Master/data/android-chrome-512x512.png -------------------------------------------------------------------------------- /ESP8266_Master/data/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicrocontrollerExpert/duino-coin-rig/137fcaeb2f7b62e1e32d04bd79020f80a568c1c2/ESP8266_Master/data/apple-touch-icon.png -------------------------------------------------------------------------------- /ESP8266_Master/data/duino.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicrocontrollerExpert/duino-coin-rig/137fcaeb2f7b62e1e32d04bd79020f80a568c1c2/ESP8266_Master/data/duino.png -------------------------------------------------------------------------------- /ESP8266_Master/data/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicrocontrollerExpert/duino-coin-rig/137fcaeb2f7b62e1e32d04bd79020f80a568c1c2/ESP8266_Master/data/favicon-16x16.png -------------------------------------------------------------------------------- /ESP8266_Master/data/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicrocontrollerExpert/duino-coin-rig/137fcaeb2f7b62e1e32d04bd79020f80a568c1c2/ESP8266_Master/data/favicon-32x32.png -------------------------------------------------------------------------------- /ESP8266_Master/data/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicrocontrollerExpert/duino-coin-rig/137fcaeb2f7b62e1e32d04bd79020f80a568c1c2/ESP8266_Master/data/favicon.ico -------------------------------------------------------------------------------- /ESP8266_Master/data/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Duino Coin Rig V0.2 13 | 52 | 122 | 123 | 124 | 125 |

STATUS %RIGNAME% (%RIGIP%)

126 |
127 |
128 |
129 |
130 |
131 | 132 |
133 | Pool 134 |
135 |
136 | %POOLADDRESS% 137 |
138 |
139 | 140 |
141 | Up and working since 142 |
143 |
144 | %UPANDWORKING% 145 |
146 |
147 |
148 |
149 |
150 |
Current balance: %CURRENTBALANCE%
Estimated values
151 |
152 |
153 |
154 |
%DUCOSPERHOUR% ᕲ / Hour
155 |
%DUCOSPERDAY% ᕲ / Day
156 |
%DUCOSPERWEEK% ᕲ / Week
157 |
%DUCOSPERYEAR% ᕲ / Year
158 |
159 |
160 |
161 |
162 |
163 | Number of Nodes 164 |

%NODES%

165 |
166 |
167 |
168 | Nodes online 169 |

%NODESONLINE%

170 |
171 |
172 |
173 |
174 |
175 |
176 | Number of Jobs 177 |

%JOBS%

178 |
179 |
180 |
181 | Number of blocks 182 |

%BLOCKS%

183 |
184 |
185 |
186 | Jobs GOOD 187 |

%JOBSGOOD%

188 |
189 |
190 |
191 | Jobs BAD 192 |

%JOBSBAD%

193 |
194 |
195 |
196 |
%COREDETAILS%
197 |
198 |
199 |
200 |

About

201 |

This software was written and is maintained by Frank Niggemann for Microcontroller-Expert. The software is free and can be used by anyone under the MIT license.
The Duino Coin is a fun coin and no liability is assumed! Microcontroller-Expert provides this software free of charge. There is no guarantee that this software will work correctly on all hardware. Therefore, no liability is accepted for any damage!

202 |

Links

203 | 210 |
211 |
212 |
213 |
214 | 215 | 216 | 217 | -------------------------------------------------------------------------------- /ESP8266_Master/data/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /Images/Duino-Coin-Rig-ESP8266-ATTINY85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicrocontrollerExpert/duino-coin-rig/137fcaeb2f7b62e1e32d04bd79020f80a568c1c2/Images/Duino-Coin-Rig-ESP8266-ATTINY85.png -------------------------------------------------------------------------------- /Images/Duino-Coin-Rig-ESP8266-Arduino-Nano.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicrocontrollerExpert/duino-coin-rig/137fcaeb2f7b62e1e32d04bd79020f80a568c1c2/Images/Duino-Coin-Rig-ESP8266-Arduino-Nano.png -------------------------------------------------------------------------------- /Images/Screenshot-Webfrontend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MicrocontrollerExpert/duino-coin-rig/137fcaeb2f7b62e1e32d04bd79020f80a568c1c2/Images/Screenshot-Webfrontend.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 MicrocontrollerExpert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # duino-coin-rig 2 | The planned cooperation with the Duino Coin team failed and therefore this will not be an official code for an official rig. It is not yet clear how this project will develop further! 3 | 4 | ### Table of contents 5 | - [Hardware](#hardware) 6 | * [Currently supported hardware](#currently-supported-hardware) 7 | * [Planned additional SBCs](#planned-additional-sbcs) 8 | * [Plannes additional MCs](#plannes-additional-mcs) 9 | - [Component wiring](#component-wiring) 10 | * [ESP8266 to Arduino Nano](#esp8266-to-arduino-nano) 11 | * [ESP8266 to ATTINY85](#esp8266-to-attiny85) 12 | * [ESP8266 to SSD1306 display](#esp8266-to-ssd1306-display) 13 | * [ESP8266 to SD card](#esp8266-to-sd-card) 14 | + [3.3V SD card adapter](#33v-sd-card-adapter) 15 | + [5V SD card adapter](#5v-sd-card-adapter) 16 | 17 | ### Screenshot Webfrontend 18 | Duino Coin Rig Screenshot Webfrontend 19 | 20 | 21 | ## Hardware 22 | ### Currently supported hardware 23 | - ESP8266 24 | - Arduino Nano 25 | 26 | ### Planned additional SBCs 27 | - ESP32 28 | - Arduino Micro 29 | - Arduino Uno 30 | - Arduino Mega 31 | 32 | ### Plannes additional MCs 33 | - ATtiny85 34 | 35 | 36 | 37 | ## Component wiring 38 | 39 | ### ESP8266 to Arduino Nano 40 | Tested with ESP8266 D1 Mini, but should theoretically work with ESP8266 NodeMCU as well. 41 | 42 | Because the ESP8266 works with a 3.3V signal and the Arduino Nanos need a 5V signal, you have to connect a logic level converter in between. This converts the 3.3V signals from the ESP8266 to 5V for the Arduino Nanos and the 5V signals from the Arduino Nanos to 3.3V for the ESP8266. Without this conversion, the components could not communicate with each other. 43 | 44 | | Type | ESP8266 D1 Mini | ESP8266 NodeMCU | - | Logic Level Converter | - | Arduino Nano | 45 | |:-----:| :-----: | :-----: | :-----: | :-----: | :-----: | :-----: | 46 | | GND | GND | GND | - | GND - GND | - | GND | 47 | | Voltage | 3.3V | 3.3V | - | 3V - 5V | - | 5V | 48 | | Voltage | 5V | VIN | - | - | - | 5V | 49 | | SCL | D1 | D1 | - | LV1 - HV1 | - | A5 | 50 | | SDA | D2 | D2 | - | LV2 - HV2 | - | A4 | 51 | 52 | Duino Coin Rig ESP8266 Arduino Nano 53 | 54 | 55 | ### ESP8266 to ATTINY85 56 | Tested with ESP8266 D1 Mini, but should theoretically work with ESP8266 NodeMCU as well. 57 | 58 | Because the ESP8266 works with a 3.3V signal and the ATTINYs need a 5V signal, you have to connect a logic level converter in between. This converts the 3.3V signals from the ESP8266 to 5V for the ATTINYs and the 5V signals from the ATTINYs to 3.3V for the ESP8266. Without this conversion, the components could not communicate with each other. 59 | 60 | | Type | ESP8266 D1 Mini | ESP8266 NodeMCU | - | Logic Level Converter | - | ATTINY85 | 61 | |:-----:| :-----: | :-----: | :-----: | :-----: | :-----: | :-----: | 62 | | GND | GND | GND | - | GND - GND | - | PIN4 | 63 | | Voltage | 3.3V | 3.3V | - | 3V - 5V | - | PIN8 | 64 | | Voltage | 5V | VIN | - | - | - | PIN8 | 65 | | SCL | D1 | D1 | - | LV1 - HV1 | - | PIN7 | 66 | | SDA | D2 | D2 | - | LV2 - HV2 | - | PIN5 | 67 | | G-LED | - | - | - | - | - | PIN3 | 68 | | R-LED | - | - | - | - | - | PIN6 | 69 | 70 | Duino Coin Rig ESP8266 ATTINY85 71 | 72 | 73 | ### ESP8266 to SSD1306 display 74 | Tested with ESP8266 D1 Mini, but should theoretically work with ESP8266 NodeMCU as well. 75 | 76 | The display is connected directly to the ESP8266 because no logic level converter is required. 77 | 78 | | Type | ESP8266 D1 Mini | ESP8266 NodeMCU | SSD1306 | 79 | |:-----:| :-----: | :-----: | :-----: | 80 | | GND | GND | GND | GND | 81 | | Voltage | 3.3V | 3.3V | VCC | 82 | | SCL | D1 | D1 | SCL | 83 | | SDA | D2 | D2 | SDA | 84 | 85 | 86 | ### ESP8266 to SD card 87 | Tested with ESP8266 D1 Mini, but should theoretically work with ESP8266 NodeMCU as well. 88 | 89 | Depending on the SD card adapter, it must be connected to either the 3.3V or the 5V connection of the ESP8266. We present it in two different tables, although all other connections are identical. 90 | 91 | 92 | #### 3.3V SD card adapter 93 | | Type | ESP8266 D1 Mini | ESP8266 NodeMCU | 3.3V SD card adapter | 94 | |:-----:| :-----: | :-----: | :-----: | 95 | | GND | GND | GND | GND | GND | 96 | | Voltage | 3.3V | 3.3V | VCC | 97 | | SCK | D5 | D5 | SCK | 98 | | MISO | D6 | D6 | MISO | 99 | | MOSI | D7 | D7 | MOSI | 100 | | CS | D8 | D8 | CS | 101 | 102 | 103 | #### 5V SD card adapter 104 | | Type | ESP8266 D1 Mini | ESP8266 NodeMCU | 5V SD card adapter | 105 | |:-----:| :-----: | :-----: | :-----: | 106 | | GND | GND | GND | GND | GND | 107 | | Voltage | 5V | VIN | VCC | 108 | | SCK | D5 | D5 | SCK | 109 | | MISO | D6 | D6 | MISO | 110 | | MOSI | D7 | D7 | MOSI | 111 | | CS | D8 | D8 | CS | 112 | 113 | 114 | ## Required Libraries 115 | You need these libraries to be able to compile the code. 116 | 117 | - Arduino 118 | - ArduinoJson (https://github.com/bblanchon/ArduinoJson) 119 | - ESP8266HTTPClient (https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266HTTPClient) 120 | - ESP8266mDNS (https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266mDNS) 121 | - ESP8266WiFi (https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WiFi) 122 | - ESPAsyncTCP (https://github.com/me-no-dev/ESPAsyncTCP) 123 | - ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) 124 | - NTPClient (https://github.com/arduino-libraries/NTPClient) 125 | - SdFat (https://github.com/greiman/SdFat) 126 | - SPIFFSEditor 127 | - SSD1306Wire 128 | - WiFiClientSecureBearSSL (https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WiFi/src) 129 | - WiFiUdp 130 | - Wire 131 | 132 | ## Configuration 133 | ### Configuration without SD card 134 | Without the SD card adapter and a SD card you have to configure this software in the code. To do this, you need to adjust the following variables in the ESP8266_Master file. 135 | 136 | | Variable | Description | 137 | |:-----:| :-----: | 138 | | wifiSsid | The SSID (name) of your WiFi network | 139 | | wifiPassword | The password of your WiFi network | 140 | | nameUser | The name you use for your wallet | 141 | | nameRig | The name of your rig | 142 | 143 | ### Configuration with SD card 144 | If you want to load your configuration from an SD card, you need to create a text file called "config.rig" on the SD card and fill in this information: 145 | ``` 146 | wifi_ssid={The SSID (name) of your WiFi network} 147 | wifi_password={The password of your WiFi network} 148 | name_user={The name you use for your wallet} 149 | name_rig={The name of your rig} 150 | ``` 151 | 152 | It could then look like this: 153 | ``` 154 | wifi_ssid=MyHomeWiFi 155 | wifi_password=secret123 156 | name_user=Duinouser 157 | name_rig=Duinorig 158 | ``` 159 | --------------------------------------------------------------------------------