├── requirements.txt ├── Resources ├── attiny_rig_bb.png ├── pico_oneI2CM_twoI2CS_bb.png └── pico_twoI2CM_twoI2CS_bb.png ├── DuinoCoin_Tiny_Slave_trinket ├── trinketloader_2015-06-09.zip ├── sha1.h ├── sha1.cpp └── DuinoCoin_Tiny_Slave_trinket.ino ├── DuinoCoin_RPI_Pico_DualCore ├── precompiled │ ├── DuinoCoin_RPI_Pico_DualCore.ino.rpipico.uf2 │ └── config.txt ├── sha1 │ ├── basic.h │ ├── constants.c │ ├── sha1.h │ ├── default.h │ ├── sha1.c │ ├── types.c │ ├── types.h │ ├── constants.h │ ├── hash.h │ └── hash.c ├── backend.cpp ├── config.h ├── sha1.h ├── sha1.cpp ├── pico_utils.ino ├── kdb.ino ├── core0.ino ├── DuinoCoin_RPI_Pico_DualCore.ino └── core1.ino ├── DuinoCoin_ATTiny_Slave ├── duco_hash.h ├── duco_hash.cpp └── DuinoCoin_ATTiny_Slave.ino ├── DuinoCoin_RPI_Tiny_Slave ├── duco_hash.h ├── DuinoCoinSpeed.h ├── duco_hash.cpp └── DuinoCoin_RPI_Tiny_Slave.ino ├── README.md └── AVR_Miner_RPI_TB.py /requirements.txt: -------------------------------------------------------------------------------- 1 | colorama 2 | requests 3 | pypresence 4 | websocket-client 5 | smbus 6 | -------------------------------------------------------------------------------- /Resources/attiny_rig_bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JK-Rolling/DuinoCoinI2C_RPI/HEAD/Resources/attiny_rig_bb.png -------------------------------------------------------------------------------- /Resources/pico_oneI2CM_twoI2CS_bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JK-Rolling/DuinoCoinI2C_RPI/HEAD/Resources/pico_oneI2CM_twoI2CS_bb.png -------------------------------------------------------------------------------- /Resources/pico_twoI2CM_twoI2CS_bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JK-Rolling/DuinoCoinI2C_RPI/HEAD/Resources/pico_twoI2CM_twoI2CS_bb.png -------------------------------------------------------------------------------- /DuinoCoin_Tiny_Slave_trinket/trinketloader_2015-06-09.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JK-Rolling/DuinoCoinI2C_RPI/HEAD/DuinoCoin_Tiny_Slave_trinket/trinketloader_2015-06-09.zip -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/precompiled/DuinoCoin_RPI_Pico_DualCore.ino.rpipico.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JK-Rolling/DuinoCoinI2C_RPI/HEAD/DuinoCoin_RPI_Pico_DualCore/precompiled/DuinoCoin_RPI_Pico_DualCore.ino.rpipico.uf2 -------------------------------------------------------------------------------- /DuinoCoin_ATTiny_Slave/duco_hash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define SHA1_BLOCK_LEN 64 6 | #define SHA1_HASH_LEN 20 7 | 8 | struct duco_hash_state_t { 9 | uint8_t buffer[SHA1_BLOCK_LEN]; 10 | uint8_t result[SHA1_HASH_LEN]; 11 | uint32_t tempState[5]; 12 | 13 | uint8_t block_offset; 14 | uint8_t total_bytes; 15 | }; 16 | 17 | void duco_hash_init(duco_hash_state_t * hasher, char const * prevHash); 18 | 19 | uint8_t const * duco_hash_try_nonce(duco_hash_state_t * hasher, char const * nonce); 20 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Tiny_Slave/duco_hash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "DuinoCoinSpeed.h" 3 | #include 4 | 5 | #define SHA1_BLOCK_LEN 64 6 | #define SHA1_HASH_LEN 20 7 | 8 | struct duco_hash_state_t { 9 | uint8_t buffer[SHA1_BLOCK_LEN]; 10 | uint8_t result[SHA1_HASH_LEN]; 11 | uint32_t tempState[5]; 12 | 13 | uint8_t block_offset; 14 | uint8_t total_bytes; 15 | }; 16 | 17 | void duco_hash_init(duco_hash_state_t * hasher, char const * prevHash); 18 | 19 | uint8_t const * duco_hash_try_nonce(duco_hash_state_t * hasher, char const * nonce); 20 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/precompiled/config.txt: -------------------------------------------------------------------------------- 1 | File: DuinoCoin_RPI_Pico_DualCore.ino.rpipico.uf2 2 | was compiled with settings below. 3 | Once loaded into RPi Pico, it should auto self-assign I2C address starting from 0x8 4 | 5 | #define DEV_INDEX 0 6 | #define I2CS_START_ADDRESS 8 7 | #define I2CS_FIND_ADDR true // >>> see kdb before setting it to true <<< 8 | #define WIRE_CLOCK 1000000 // >>> see kdb before changing this I2C clock frequency <<< 9 | #define I2C0_SDA 20 10 | #define I2C0_SCL 21 11 | #define I2C1_SDA 26 12 | #define I2C1_SCL 27 13 | #define CRC8_EN true 14 | #define WDT_EN true 15 | #define CORE_BATON_EN false 16 | #define LED_EN true 17 | #define SENSOR_EN true 18 | #define SINGLE_CORE_ONLY false // >>> see kdb before setting it to true <<< 19 | #define WORKER_NAME "rp2040" 20 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/sha1/basic.h: -------------------------------------------------------------------------------- 1 | // This file is part of cryptosuite2. // 2 | // // 3 | // cryptosuite2 is free software: you can redistribute it and/or modify // 4 | // it under the terms of the GNU General Public License as published by // 5 | // the Free Software Foundation, either version 3 of the License, or // 6 | // (at your option) any later version. // 7 | // // 8 | // cryptosuite2 is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with cryptosuite2. If not, see . // 15 | // // 16 | 17 | #ifndef SHA1_BASIC_H_ 18 | #define SHA1_BASIC_H_ 19 | 20 | #include "default.h" 21 | #include 22 | 23 | #define sha1_rotl(bits,word) (((word) << (bits)) | ((word) >> (32 - (bits)))) 24 | 25 | #endif 26 | 27 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/backend.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of cryptosuite2. // 2 | // // 3 | // cryptosuite2 is free software: you can redistribute it and/or modify // 4 | // it under the terms of the GNU General Public License as published by // 5 | // the Free Software Foundation, either version 3 of the License, or // 6 | // (at your option) any later version. // 7 | // // 8 | // cryptosuite2 is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with cryptosuite2. If not, see . // 15 | // // 16 | 17 | //#pragma GCC optimize ("-Ofast") 18 | #include "config.h" 19 | 20 | #ifndef SHA1_DISABLED 21 | #include "sha1/constants.c" 22 | #include "sha1/hash.c" 23 | #include "sha1/types.c" 24 | #include "sha1/sha1.c" 25 | #endif 26 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/config.h: -------------------------------------------------------------------------------- 1 | // This file is part of cryptosuite2. // 2 | // // 3 | // cryptosuite2 is free software: you can redistribute it and/or modify // 4 | // it under the terms of the GNU General Public License as published by // 5 | // the Free Software Foundation, either version 3 of the License, or // 6 | // (at your option) any later version. // 7 | // // 8 | // cryptosuite2 is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with cryptosuite2. If not, see . // 15 | // // 16 | 17 | //#pragma GCC optimize ("-Ofast") 18 | 19 | // include the module config first, 20 | // overwrite it in the arduino interface config. 21 | #include "sha1/default.h" 22 | 23 | #ifndef SHA_CONFIG_H_ 24 | #define SHA_CONFIG_H_ 25 | 26 | // No changes yet. 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /DuinoCoin_Tiny_Slave_trinket/sha1.h: -------------------------------------------------------------------------------- 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 | #pragma GCC optimize ("-O2") 12 | #ifndef Sha1_h 13 | #define Sha1_h 14 | 15 | #include 16 | #include "Print.h" 17 | 18 | #define HASH_LENGTH 20 19 | #define BLOCK_LENGTH 64 20 | 21 | union _buffer { 22 | uint8_t b[BLOCK_LENGTH]; 23 | uint32_t w[BLOCK_LENGTH/4]; 24 | }; 25 | union _state { 26 | uint8_t b[HASH_LENGTH]; 27 | uint32_t w[HASH_LENGTH/4]; 28 | }; 29 | 30 | class Sha1Class : public Print 31 | { 32 | public: 33 | void init(void); 34 | void initHmac(const uint8_t* secret, int secretLength); 35 | uint8_t* result(void); 36 | uint8_t* resultHmac(void); 37 | virtual size_t write(uint8_t); 38 | using Print::write; 39 | private: 40 | void pad(); 41 | void addUncounted(uint8_t data); 42 | void hashBlock(); 43 | uint32_t rol32(uint32_t number, uint8_t bits); 44 | _buffer buffer; 45 | uint8_t bufferOffset; 46 | _state state; 47 | uint32_t byteCount; 48 | uint8_t keyBuffer[BLOCK_LENGTH]; 49 | uint8_t innerHash[HASH_LENGTH]; 50 | 51 | }; 52 | extern Sha1Class Sha1; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/sha1/constants.c: -------------------------------------------------------------------------------- 1 | // This file is part of cryptosuite2. // 2 | // // 3 | // cryptosuite2 is free software: you can redistribute it and/or modify // 4 | // it under the terms of the GNU General Public License as published by // 5 | // the Free Software Foundation, either version 3 of the License, or // 6 | // (at your option) any later version. // 7 | // // 8 | // cryptosuite2 is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with cryptosuite2. If not, see . // 15 | // // 16 | 17 | #include "constants.h" 18 | 19 | const uint32_t sha1_init_state[SHA1_HASH_LEN / 4] PROGMEM = 20 | { 21 | 0x67452301, 0xefcdab89, 0x98badcfe, 22 | 0x10325476, 0xc3d2e1f0 23 | }; 24 | 25 | const uint32_t sha1_constants[4] PROGMEM = 26 | { 27 | 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 28 | }; 29 | 30 | 31 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/sha1/sha1.h: -------------------------------------------------------------------------------- 1 | // This file is part of cryptosuite2. // 2 | // // 3 | // cryptosuite2 is free software: you can redistribute it and/or modify // 4 | // it under the terms of the GNU General Public License as published by // 5 | // the Free Software Foundation, either version 3 of the License, or // 6 | // (at your option) any later version. // 7 | // // 8 | // cryptosuite2 is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with cryptosuite2. If not, see . // 15 | // // 16 | 17 | #ifndef SHA1_SHA1_H_ 18 | #define SHA1_SHA1_H_ 19 | 20 | #include "default.h" 21 | #include "types.h" 22 | #include "hash.h" 23 | #include 24 | //#include 25 | 26 | #ifndef ssize_t 27 | #define ssize_t long int 28 | #endif 29 | 30 | ssize_t sha1_hasher_write(sha1_hasher_t hasher, const void * buf, size_t count); 31 | 32 | #endif 33 | 34 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/sha1/default.h: -------------------------------------------------------------------------------- 1 | // This file is part of cryptosuite2. // 2 | // // 3 | // cryptosuite2 is free software: you can redistribute it and/or modify // 4 | // it under the terms of the GNU General Public License as published by // 5 | // the Free Software Foundation, either version 3 of the License, or // 6 | // (at your option) any later version. // 7 | // // 8 | // cryptosuite2 is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with cryptosuite2. If not, see . // 15 | // // 16 | 17 | // This file is the module config header 18 | // when the arduino interface is NOT used. 19 | // 20 | // If you want to use the library with Arduino, 21 | // edit sha/config.h. If you use a proper build system, 22 | // use this config file for sha1 and sha/sha256/default.h 23 | // for sha256. 24 | #ifndef SHA1_DEFAULT_H_ 25 | #define SHA1_DEFAULT_H_ 26 | 27 | #define SHA1_ENABLE_HMAC 28 | 29 | 30 | #endif 31 | 32 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/sha1/sha1.c: -------------------------------------------------------------------------------- 1 | // This file is part of cryptosuite2. // 2 | // // 3 | // cryptosuite2 is free software: you can redistribute it and/or modify // 4 | // it under the terms of the GNU General Public License as published by // 5 | // the Free Software Foundation, either version 3 of the License, or // 6 | // (at your option) any later version. // 7 | // // 8 | // cryptosuite2 is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with cryptosuite2. If not, see . // 15 | // // 16 | 17 | #include "sha1.h" 18 | 19 | ssize_t sha1_hasher_write(sha1_hasher_t hasher, const void * buf, size_t count) 20 | { 21 | size_t written = 0; 22 | uint8_t chk_result; 23 | char c; 24 | while(written < count) 25 | { 26 | c = ((char *) buf)[written]; 27 | chk_result = sha1_hasher_putc(hasher, c); 28 | if(chk_result != c) 29 | { 30 | return -1; 31 | } 32 | written++; 33 | } 34 | return written; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Tiny_Slave/DuinoCoinSpeed.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Description: 3 | * This file will provide additional information to the compiler 4 | * By defining SPEED value, the optimization can be changed during macro expansion phase 5 | * Default SPEED value is 2 (-O2) 6 | * 7 | * Example Usage: 8 | * #include "DuinoCoinSpeed.h" 9 | * // this should gives 10 | * // #pragma GCC optimize ("-O2") 11 | * 12 | * Author: JK Rolling 13 | * Date: 2022/01/20 14 | * 15 | */ 16 | 17 | #ifndef DUINOCOINSPEED_H_ 18 | #define DUINOCOINSPEED_H_ 19 | 20 | #define OPTIMIZATION(x) _Pragma (#x) 21 | 22 | // user to change SPEED preference 23 | #ifndef SPEED 24 | #define SPEED 4 25 | #endif 26 | 27 | // 0 -O0 <- reduce compilation time 28 | // 1 -Os <- optimize for size 29 | // 2 -O2 <- Fast 30 | // 3 -O3 <- Faster 31 | // 4 -Ofast <- Fastest 32 | // g -Og <- optimize debugging experience 33 | #if SPEED==0 34 | OPTIMIZATION (GCC optimize ("-O0")) 35 | #elif SPEED==1 36 | OPTIMIZATION (GCC optimize ("-Os")) 37 | #elif SPEED==2 38 | OPTIMIZATION (GCC optimize ("-O2")) 39 | #elif SPEED==3 40 | OPTIMIZATION (GCC optimize ("-O3")) 41 | #elif SPEED==4 42 | OPTIMIZATION (GCC optimize ("-Ofast")) 43 | #elif SPEED==g 44 | OPTIMIZATION (GCC optimize ("-Og")) 45 | #else 46 | #error "Invalid SPEED value. Use 0/1/2/3/4/g" 47 | #endif 48 | 49 | #endif 50 | 51 | /* 52 | * Tested on Arduino UNO 53 | * 0 --> 260H/s 54 | * 1 --> 314H/s 55 | * 2 --> 328H/s 56 | * 3 --> 342H/s 57 | * 4 --> 342H/s 58 | * g --> 260H/s 59 | * 60 | */ 61 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/sha1/types.c: -------------------------------------------------------------------------------- 1 | // This file is part of cryptosuite2. // 2 | // // 3 | // cryptosuite2 is free software: you can redistribute it and/or modify // 4 | // it under the terms of the GNU General Public License as published by // 5 | // the Free Software Foundation, either version 3 of the License, or // 6 | // (at your option) any later version. // 7 | // // 8 | // cryptosuite2 is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with cryptosuite2. If not, see . // 15 | // // 16 | 17 | #include "types.h" 18 | #include 19 | #include 20 | #include "constants.h" 21 | 22 | sha1_hasher_t sha1_hasher_new(void) 23 | { 24 | sha1_hasher_t hasher = (sha1_hasher_t) malloc(sizeof(struct sha1_hasher_s)); 25 | if(!hasher) 26 | { 27 | return NULL; 28 | } 29 | sha1_hasher_init(hasher); 30 | return hasher; 31 | } 32 | 33 | void sha1_hasher_init(sha1_hasher_t hasher) 34 | { 35 | uint8_t i; 36 | for(i = 0; i < SHA1_HASH_LEN / 4; i++) 37 | { 38 | hasher->state.words[i] = pgm_read_dword(sha1_init_state + i); 39 | } 40 | hasher->block_offset = 0; 41 | hasher->total_bytes = 0; 42 | hasher->_lock = 0; 43 | 44 | } 45 | 46 | 47 | void sha1_hasher_del(sha1_hasher_t hasher) 48 | { 49 | free(hasher); 50 | } 51 | 52 | 53 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/sha1.h: -------------------------------------------------------------------------------- 1 | // This file is part of cryptosuite2. // 2 | // // 3 | // cryptosuite2 is free software: you can redistribute it and/or modify // 4 | // it under the terms of the GNU General Public License as published by // 5 | // the Free Software Foundation, either version 3 of the License, or // 6 | // (at your option) any later version. // 7 | // // 8 | // cryptosuite2 is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with cryptosuite2. If not, see . // 15 | // // 16 | 17 | //#pragma GCC optimize ("-Ofast") 18 | 19 | #include "config.h" 20 | 21 | #ifndef Sha1_h 22 | #define Sha1_h 23 | 24 | #include 25 | #include "Print.h" 26 | #include "sha1/sha1.h" 27 | 28 | #ifndef SHA1_DISABLE_WRAPPER 29 | class Sha1Wrapper : public Print 30 | { 31 | public: 32 | void init(void); 33 | uint8_t * result(void); 34 | #ifdef SHA1_ENABLE_HMAC 35 | void initHmac(const uint8_t * secret, uint16_t secretLength); 36 | uint8_t * resultHmac(void); 37 | #endif 38 | virtual size_t write(uint8_t); 39 | using Print::write; 40 | private: 41 | struct sha1_hasher_s _hasher; 42 | 43 | }; 44 | 45 | extern Sha1Wrapper Sha1; 46 | extern Sha1Wrapper core0_Sha1; 47 | extern Sha1Wrapper core1_Sha1; 48 | #endif 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/sha1.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of cryptosuite2. // 2 | // // 3 | // cryptosuite2 is free software: you can redistribute it and/or modify // 4 | // it under the terms of the GNU General Public License as published by // 5 | // the Free Software Foundation, either version 3 of the License, or // 6 | // (at your option) any later version. // 7 | // // 8 | // cryptosuite2 is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with cryptosuite2. If not, see . // 15 | // // 16 | 17 | //#pragma GCC optimize ("-Ofast") 18 | 19 | #include "sha1.h" 20 | 21 | #ifndef SHA1_DISABLED 22 | #ifndef SHA1_DISABLE_WRAPPER 23 | void Sha1Wrapper::init(void) 24 | { 25 | sha1_hasher_init(&_hasher); 26 | } 27 | 28 | #ifdef SHA1_ENABLE_HMAC 29 | void Sha1Wrapper::initHmac(const uint8_t * secret, uint16_t secretLength) 30 | { 31 | sha1_hasher_init_hmac(&_hasher, secret, secretLength); 32 | } 33 | 34 | uint8_t * Sha1Wrapper::resultHmac(void) 35 | { 36 | return sha1_hasher_gethmac(&_hasher); 37 | } 38 | #endif 39 | 40 | 41 | size_t Sha1Wrapper::write(uint8_t byte) 42 | { 43 | if(sha1_hasher_putc(&_hasher, byte) == byte) 44 | { 45 | return 1; 46 | } 47 | return 0; 48 | } 49 | 50 | uint8_t * Sha1Wrapper::result(void) 51 | { 52 | return sha1_hasher_gethash(&_hasher); 53 | } 54 | 55 | Sha1Wrapper Sha1; 56 | Sha1Wrapper core0_Sha1; 57 | Sha1Wrapper core1_Sha1; 58 | 59 | #endif 60 | #endif 61 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/sha1/types.h: -------------------------------------------------------------------------------- 1 | // This file is part of cryptosuite2. // 2 | // // 3 | // cryptosuite2 is free software: you can redistribute it and/or modify // 4 | // it under the terms of the GNU General Public License as published by // 5 | // the Free Software Foundation, either version 3 of the License, or // 6 | // (at your option) any later version. // 7 | // // 8 | // cryptosuite2 is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with cryptosuite2. If not, see . // 15 | // // 16 | 17 | #ifndef SHA1_TYPES_H_ 18 | #define SHA1_TYPES_H_ 19 | 20 | #include "default.h" 21 | #include 22 | #include 23 | #include "constants.h" 24 | 25 | 26 | typedef union 27 | { 28 | uint8_t bytes[SHA1_HASH_LEN]; 29 | uint32_t words[SHA1_HASH_LEN / 4]; 30 | 31 | } sha1_state_t; 32 | 33 | typedef union 34 | { 35 | uint8_t bytes[SHA1_BLOCK_LEN]; 36 | uint32_t words[SHA1_BLOCK_LEN / 4]; 37 | 38 | } sha1_block_t; 39 | 40 | typedef struct __attribute__((__packed__)) sha1_hasher_s 41 | { 42 | sha1_state_t state; 43 | sha1_block_t buffer; 44 | 45 | uint8_t block_offset; 46 | uint64_t total_bytes; 47 | uint8_t _lock; 48 | #ifdef SHA1_ENABLE_HMAC 49 | uint8_t hmac_key_buffer[SHA1_BLOCK_LEN]; 50 | uint8_t hmac_inner_hash[SHA1_HASH_LEN]; 51 | #endif 52 | } * sha1_hasher_t; 53 | 54 | sha1_hasher_t sha1_hasher_new(void); 55 | void sha1_hasher_del(sha1_hasher_t hasher); 56 | void sha1_hasher_init(sha1_hasher_t hasher); 57 | 58 | 59 | #endif 60 | 61 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/sha1/constants.h: -------------------------------------------------------------------------------- 1 | // This file is part of cryptosuite2. // 2 | // // 3 | // cryptosuite2 is free software: you can redistribute it and/or modify // 4 | // it under the terms of the GNU General Public License as published by // 5 | // the Free Software Foundation, either version 3 of the License, or // 6 | // (at your option) any later version. // 7 | // // 8 | // cryptosuite2 is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with cryptosuite2. If not, see . // 15 | // // 16 | 17 | #ifndef SHA1_CONSTANTS_H_ 18 | #define SHA1_CONSTANTS_H_ 19 | 20 | #include "default.h" 21 | #include 22 | 23 | #define SHA1_BLOCK_LEN 64 24 | #define SHA1_HASH_LEN 20 25 | 26 | #include "Arduino.h" 27 | 28 | extern const uint32_t sha1_init_state[SHA1_HASH_LEN / 4] PROGMEM; 29 | 30 | extern const uint32_t sha1_constants[4] PROGMEM; 31 | 32 | 33 | // From RFC3174 (http://www.faqs.org/rfcs/rfc3174.html) 34 | // (Section 5): 35 | // 36 | // A sequence of constant words K(0), K(1), ... , K(79) is used in the 37 | // SHA-1. In hex these are given by 38 | // 39 | // K(t) = 5A827999 ( 0 <= t <= 19) 40 | // 41 | // K(t) = 6ED9EBA1 (20 <= t <= 39) 42 | // 43 | // K(t) = 8F1BBCDC (40 <= t <= 59) 44 | // 45 | // K(t) = CA62C1D6 (60 <= t <= 79). 46 | // 47 | // This can be achieved using an integer division by 20 and only 4 constants. 48 | 49 | #define sha1_k(i) pgm_read_dword(sha1_constants + (i / 20)) 50 | 51 | 52 | #ifdef SHA1_ENABLE_HMAC 53 | #define SHA1_HMAC_IPAD 0x36 54 | #define SHA1_HMAC_OPAD 0x5c 55 | #endif 56 | 57 | #endif 58 | 59 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/sha1/hash.h: -------------------------------------------------------------------------------- 1 | // This file is part of cryptosuite2. // 2 | // // 3 | // cryptosuite2 is free software: you can redistribute it and/or modify // 4 | // it under the terms of the GNU General Public License as published by // 5 | // the Free Software Foundation, either version 3 of the License, or // 6 | // (at your option) any later version. // 7 | // // 8 | // cryptosuite2 is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with cryptosuite2. If not, see . // 15 | // // 16 | 17 | #ifndef SHA1_HASH_H_ 18 | #define SHA1_HASH_H_ 19 | 20 | 21 | #include "default.h" 22 | #include "constants.h" 23 | #include "types.h" 24 | #include "basic.h" 25 | 26 | void sha1_hash_block(sha1_hasher_t hasher); 27 | 28 | void sha1_hasher_add_byte(sha1_hasher_t hasher, uint8_t byte); 29 | 30 | /** 31 | * NOTE: once the block has been pad'ed the hasher will 32 | * produce nonsense data. Therefore putc will return EOF 33 | * once the hasher has been pad'ed (this happens, when 34 | * sha1_hasher_gethash or sha1_hasher_gethmac are invoced). 35 | * */ 36 | uint8_t sha1_hasher_putc(sha1_hasher_t hasher, uint8_t byte); 37 | 38 | void sha1_hasher_pad(sha1_hasher_t hasher); 39 | 40 | /** 41 | * NOTE: this will NOT return a copy of the data but 42 | * a REFERENCE! One MUST NOT free the result. 43 | * 44 | * Also this modifies the state of the hasher. The 45 | * hasher has an internal lock ensuring that writing 46 | * to the hasher fails after this operation. 47 | * */ 48 | uint8_t * sha1_hasher_gethash(sha1_hasher_t hasher); 49 | 50 | #ifdef SHA1_ENABLE_HMAC 51 | void sha1_hasher_init_hmac(sha1_hasher_t hasher, const uint8_t * key, size_t key_len); 52 | uint8_t * sha1_hasher_gethmac(sha1_hasher_t hasher); 53 | #endif 54 | 55 | #endif 56 | 57 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/pico_utils.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * pico_utils.ino 3 | * created by JK Rolling 4 | * 27 06 2022 5 | * 6 | * Dual core and dual I2C (400kHz/1MHz) 7 | * DuinoCoin worker 8 | * 9 | * See kdb.ino on Arduino IDE settings to compile for RP2040 10 | * 11 | * with inspiration from 12 | * * Revox (Duino-Coin founder) 13 | * * Ricaun (I2C Nano sketch) 14 | */ 15 | 16 | #pragma GCC optimize ("-Ofast") 17 | 18 | #include "pico/unique_id.h" 19 | #include "hardware/structs/rosc.h" 20 | #include "hardware/adc.h" 21 | 22 | #ifndef LED_EN 23 | #define LED_EN false 24 | #endif 25 | 26 | // https://stackoverflow.com/questions/51731313/cross-platform-crc8-function-c-and-python-parity-check 27 | uint8_t crc8( uint8_t *addr, uint8_t len) { 28 | uint8_t crc=0; 29 | for (uint8_t i=0; i>= 1; 34 | if (mix) 35 | crc ^= 0x8C; 36 | inbyte >>= 1; 37 | } 38 | } 39 | return crc; 40 | } 41 | 42 | uint8_t calc_crc8( String msg ) { 43 | int msg_len = msg.length() + 1; 44 | char char_array[msg_len]; 45 | msg.toCharArray(char_array, msg_len); 46 | return crc8((uint8_t *)char_array,msg.length()); 47 | } 48 | 49 | String get_DUCOID() { 50 | int len = 2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1; 51 | uint8_t buff[len] = ""; 52 | pico_get_unique_board_id_string((char *)buff, len); 53 | String uniqueID = String ((char *)buff, strlen((char *)buff)); 54 | return "DUCOID"+uniqueID; 55 | } 56 | 57 | void enable_internal_temperature_sensor() { 58 | adc_init(); 59 | adc_set_temp_sensor_enabled(true); 60 | adc_select_input(0x4); 61 | } 62 | 63 | double read_temperature() { 64 | uint16_t adcValue = adc_read(); 65 | double temperature; 66 | temperature = 3.3f / 0x1000; 67 | temperature *= adcValue; 68 | // celcius degree 69 | temperature = 27.0 - ((temperature - 0.706)/ 0.001721); 70 | // fahrenheit degree 71 | // temperature = temperature * 9 / 5 + 32; 72 | return temperature; 73 | } 74 | 75 | double read_humidity() { 76 | // placeholder for future external sensor 77 | return 0.0; 78 | } 79 | 80 | /* Von Neumann extractor: 81 | From the input stream, this extractor took bits, 82 | two at a time (first and second, then third and fourth, and so on). 83 | If the two bits matched, no output was generated. 84 | If the bits differed, the value of the first bit was output. 85 | */ 86 | uint32_t rnd_whitened(void){ 87 | uint32_t k, random = 0; 88 | uint32_t random_bit1, random_bit2; 89 | volatile uint32_t *rnd_reg=(uint32_t *)(ROSC_BASE + ROSC_RANDOMBIT_OFFSET); 90 | 91 | for (k = 0; k < 32; k++) { 92 | while(1) { 93 | random_bit1=0x00000001 & (*rnd_reg); 94 | random_bit2=0x00000001 & (*rnd_reg); 95 | if (random_bit1 != random_bit2) break; 96 | } 97 | random = random + random_bit1; 98 | random = random << 1; 99 | } 100 | return random; 101 | } 102 | 103 | void Blink(uint8_t count, uint8_t pin = LED_BUILTIN) { 104 | if (!LED_EN) return; 105 | uint8_t state = LOW; 106 | 107 | for (int x=0; x<(count << 1); ++x) { 108 | analogWrite(pin, state ^= LED_BRIGHTNESS); 109 | sleep_ms(50); 110 | } 111 | } 112 | 113 | float LED_FADE = 0; 114 | bool repeating_timer_callback(struct repeating_timer *t) { 115 | if (LED_FADE >= 0) { 116 | LED_FADE = (LED_FADE * 0.9) - 0.1; 117 | } 118 | if (LED_FADE <= 1) { 119 | LED_FADE = 0; 120 | } 121 | analogWrite(LED_PIN, LED_FADE); 122 | return true; 123 | } 124 | 125 | void BlinkFade(void) { 126 | if (!LED_EN) return; 127 | LED_FADE = 255; 128 | } 129 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Tiny_Slave/duco_hash.cpp: -------------------------------------------------------------------------------- 1 | #include "duco_hash.h" 2 | 3 | #define sha1_rotl(bits,word) (((word) << (bits)) | ((word) >> (32 - (bits)))) 4 | 5 | void duco_hash_block(duco_hash_state_t * hasher) { 6 | // NOTE: keeping this static improves performance quite a lot 7 | static uint32_t w[16]; 8 | 9 | for (uint8_t i = 0, i4 = 0; i < 16; i++, i4 += 4) { 10 | w[i] = (uint32_t(hasher->buffer[i4]) << 24) | 11 | (uint32_t(hasher->buffer[i4 + 1]) << 16) | 12 | (uint32_t(hasher->buffer[i4 + 2]) << 8) | 13 | (uint32_t(hasher->buffer[i4 + 3])); 14 | } 15 | 16 | uint32_t a = hasher->tempState[0]; 17 | uint32_t b = hasher->tempState[1]; 18 | uint32_t c = hasher->tempState[2]; 19 | uint32_t d = hasher->tempState[3]; 20 | uint32_t e = hasher->tempState[4]; 21 | 22 | for (uint8_t i = 10; i < 80; i++) { 23 | if (i >= 16) { 24 | w[i & 15] = sha1_rotl(1, w[(i-3) & 15] ^ w[(i-8) & 15] ^ w[(i-14) & 15] ^ w[(i-16) & 15]); 25 | } 26 | 27 | uint32_t temp = sha1_rotl(5, a) + e + w[i & 15]; 28 | if (i < 20) { 29 | temp += (b & c) | ((~b) & d); 30 | temp += 0x5a827999; 31 | } else if(i < 40) { 32 | temp += b ^ c ^ d; 33 | temp += 0x6ed9eba1; 34 | } else if(i < 60) { 35 | temp += (b & c) | (b & d) | (c & d); 36 | temp += 0x8f1bbcdc; 37 | } else { 38 | temp += b ^ c ^ d; 39 | temp += 0xca62c1d6; 40 | } 41 | 42 | e = d; 43 | d = c; 44 | c = sha1_rotl(30, b); 45 | b = a; 46 | a = temp; 47 | } 48 | 49 | a += 0x67452301; 50 | b += 0xefcdab89; 51 | c += 0x98badcfe; 52 | d += 0x10325476; 53 | e += 0xc3d2e1f0; 54 | 55 | hasher->result[0 * 4 + 0] = a >> 24; 56 | hasher->result[0 * 4 + 1] = a >> 16; 57 | hasher->result[0 * 4 + 2] = a >> 8; 58 | hasher->result[0 * 4 + 3] = a; 59 | hasher->result[1 * 4 + 0] = b >> 24; 60 | hasher->result[1 * 4 + 1] = b >> 16; 61 | hasher->result[1 * 4 + 2] = b >> 8; 62 | hasher->result[1 * 4 + 3] = b; 63 | hasher->result[2 * 4 + 0] = c >> 24; 64 | hasher->result[2 * 4 + 1] = c >> 16; 65 | hasher->result[2 * 4 + 2] = c >> 8; 66 | hasher->result[2 * 4 + 3] = c; 67 | hasher->result[3 * 4 + 0] = d >> 24; 68 | hasher->result[3 * 4 + 1] = d >> 16; 69 | hasher->result[3 * 4 + 2] = d >> 8; 70 | hasher->result[3 * 4 + 3] = d; 71 | hasher->result[4 * 4 + 0] = e >> 24; 72 | hasher->result[4 * 4 + 1] = e >> 16; 73 | hasher->result[4 * 4 + 2] = e >> 8; 74 | hasher->result[4 * 4 + 3] = e; 75 | } 76 | 77 | void duco_hash_init(duco_hash_state_t * hasher, char const * prevHash) { 78 | for (uint8_t i = 0; i < 40; i++) { 79 | hasher->buffer[i] = prevHash[i]; 80 | } 81 | 82 | if (prevHash == (void*)(0xffffffff)) { 83 | // NOTE: THIS IS NEVER CALLED 84 | // This is here to keep a live reference to hash_block 85 | // Otherwise GCC tries to inline it entirely into the main loop 86 | // Which causes massive perf degradation 87 | duco_hash_block(nullptr); 88 | } 89 | 90 | // Do first 10 rounds as these are going to be the same all time 91 | 92 | uint32_t a = 0x67452301; 93 | uint32_t b = 0xefcdab89; 94 | uint32_t c = 0x98badcfe; 95 | uint32_t d = 0x10325476; 96 | uint32_t e = 0xc3d2e1f0; 97 | 98 | static uint32_t w[10]; 99 | 100 | for (uint8_t i = 0, i4 = 0; i < 10; i++, i4 += 4) { 101 | w[i] = (uint32_t(hasher->buffer[i4]) << 24) | 102 | (uint32_t(hasher->buffer[i4 + 1]) << 16) | 103 | (uint32_t(hasher->buffer[i4 + 2]) << 8) | 104 | (uint32_t(hasher->buffer[i4 + 3])); 105 | } 106 | 107 | for (uint8_t i = 0; i < 10; i++) { 108 | uint32_t temp = sha1_rotl(5, a) + e + w[i & 15]; 109 | temp += (b & c) | ((~b) & d); 110 | temp += 0x5a827999; 111 | 112 | e = d; 113 | d = c; 114 | c = sha1_rotl(30, b); 115 | b = a; 116 | a = temp; 117 | } 118 | 119 | hasher->tempState[0] = a; 120 | hasher->tempState[1] = b; 121 | hasher->tempState[2] = c; 122 | hasher->tempState[3] = d; 123 | hasher->tempState[4] = e; 124 | } 125 | 126 | void duco_hash_set_nonce(duco_hash_state_t * hasher, char const * nonce) { 127 | uint8_t * b = hasher->buffer; 128 | 129 | uint8_t off = SHA1_HASH_LEN * 2; 130 | for (uint8_t i = 0; i < 10 && nonce[i] != 0; i++) { 131 | b[off++] = nonce[i]; 132 | } 133 | 134 | uint8_t total_bytes = off; 135 | 136 | b[off++] = 0x80; 137 | while (off < 62) { 138 | b[off++] = 0; 139 | } 140 | 141 | b[62] = total_bytes >> 5; 142 | b[63] = total_bytes << 3; 143 | } 144 | 145 | uint8_t const * duco_hash_try_nonce(duco_hash_state_t * hasher, char const * nonce) { 146 | duco_hash_set_nonce(hasher, nonce); 147 | duco_hash_block(hasher); 148 | 149 | return hasher->result; 150 | } 151 | -------------------------------------------------------------------------------- /DuinoCoin_Tiny_Slave_trinket/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 | #pragma GCC optimize ("-O2") 12 | #include 13 | //#include 14 | //#include 15 | #include "sha1.h" 16 | 17 | #define SHA1_K0 0x5a827999 18 | #define SHA1_K20 0x6ed9eba1 19 | #define SHA1_K40 0x8f1bbcdc 20 | #define SHA1_K60 0xca62c1d6 21 | 22 | const uint8_t sha1InitState[] PROGMEM = { 23 | 0x01,0x23,0x45,0x67, // H0 24 | 0x89,0xab,0xcd,0xef, // H1 25 | 0xfe,0xdc,0xba,0x98, // H2 26 | 0x76,0x54,0x32,0x10, // H3 27 | 0xf0,0xe1,0xd2,0xc3 // H4 28 | }; 29 | 30 | void Sha1Class::init(void) { 31 | memcpy_P(state.b,sha1InitState,HASH_LENGTH); 32 | byteCount = 0; 33 | bufferOffset = 0; 34 | } 35 | 36 | uint32_t Sha1Class::rol32(uint32_t number, uint8_t bits) { 37 | return ((number << bits) | (number >> (32-bits))); 38 | } 39 | 40 | void Sha1Class::hashBlock() { 41 | uint8_t i; 42 | uint32_t a,b,c,d,e,t; 43 | 44 | a=state.w[0]; 45 | b=state.w[1]; 46 | c=state.w[2]; 47 | d=state.w[3]; 48 | e=state.w[4]; 49 | for (i=0; i<80; i++) { 50 | if (i>=16) { 51 | t = buffer.w[(i+13)&15] ^ buffer.w[(i+8)&15] ^ buffer.w[(i+2)&15] ^ buffer.w[i&15]; 52 | buffer.w[i&15] = rol32(t,1); 53 | } 54 | if (i<20) { 55 | t = (d ^ (b & (c ^ d))) + SHA1_K0; 56 | } else if (i<40) { 57 | t = (b ^ c ^ d) + SHA1_K20; 58 | } else if (i<60) { 59 | t = ((b & c) | (d & (b | c))) + SHA1_K40; 60 | } else { 61 | t = (b ^ c ^ d) + SHA1_K60; 62 | } 63 | t+=rol32(a,5) + e + buffer.w[i&15]; 64 | e=d; 65 | d=c; 66 | c=rol32(b,30); 67 | b=a; 68 | a=t; 69 | } 70 | state.w[0] += a; 71 | state.w[1] += b; 72 | state.w[2] += c; 73 | state.w[3] += d; 74 | state.w[4] += e; 75 | } 76 | 77 | void Sha1Class::addUncounted(uint8_t data) { 78 | buffer.b[bufferOffset ^ 3] = data; 79 | bufferOffset++; 80 | if (bufferOffset == BLOCK_LENGTH) { 81 | hashBlock(); 82 | bufferOffset = 0; 83 | } 84 | } 85 | 86 | size_t Sha1Class::write(uint8_t data) { 87 | ++byteCount; 88 | addUncounted(data); 89 | return 1; 90 | } 91 | 92 | void Sha1Class::pad() { 93 | // Implement SHA-1 padding (fips180-2 §5.1.1) 94 | 95 | // Pad with 0x80 followed by 0x00 until the end of the block 96 | addUncounted(0x80); 97 | while (bufferOffset != 56) addUncounted(0x00); 98 | 99 | // Append length in the last 8 bytes 100 | addUncounted(0); // We're only using 32 bit lengths 101 | addUncounted(0); // But SHA-1 supports 64 bit lengths 102 | addUncounted(0); // So zero pad the top bits 103 | addUncounted(byteCount >> 29); // Shifting to multiply by 8 104 | addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as 105 | addUncounted(byteCount >> 13); // byte. 106 | addUncounted(byteCount >> 5); 107 | addUncounted(byteCount << 3); 108 | } 109 | 110 | 111 | uint8_t* Sha1Class::result(void) { 112 | // Pad to complete the last block 113 | pad(); 114 | 115 | // Swap byte order back 116 | for (uint8_t i=0; i<5; i++) { 117 | uint32_t a,b; 118 | a=state.w[i]; 119 | b=a<<24; 120 | b|=(a<<8) & 0x00ff0000; 121 | b|=(a>>8) & 0x0000ff00; 122 | b|=a>>24; 123 | state.w[i]=b; 124 | } 125 | 126 | // Return pointer to hash (20 characters) 127 | return state.b; 128 | } 129 | 130 | #define HMAC_IPAD 0x36 131 | #define HMAC_OPAD 0x5c 132 | 133 | void Sha1Class::initHmac(const uint8_t* key, int keyLength) { 134 | uint8_t i; 135 | memset(keyBuffer,0,BLOCK_LENGTH); 136 | if (keyLength > BLOCK_LENGTH) { 137 | // Hash long keys 138 | init(); 139 | for (;keyLength--;) write(*key++); 140 | memcpy(keyBuffer,result(),HASH_LENGTH); 141 | } else { 142 | // Block length keys are used as is 143 | memcpy(keyBuffer,key,keyLength); 144 | } 145 | // Start inner hash 146 | init(); 147 | for (i=0; i> (32 - (bits)))) 6 | 7 | void duco_hash_block(duco_hash_state_t * hasher) { 8 | // NOTE: keeping this static improves performance quite a lot 9 | static uint32_t w[16]; 10 | 11 | for (uint8_t i = 0, i4 = 0; i < 16; i++, i4 += 4) { 12 | w[i] = (uint32_t(hasher->buffer[i4]) << 24) | 13 | (uint32_t(hasher->buffer[i4 + 1]) << 16) | 14 | (uint32_t(hasher->buffer[i4 + 2]) << 8) | 15 | (uint32_t(hasher->buffer[i4 + 3])); 16 | } 17 | 18 | uint32_t a = hasher->tempState[0]; 19 | uint32_t b = hasher->tempState[1]; 20 | uint32_t c = hasher->tempState[2]; 21 | uint32_t d = hasher->tempState[3]; 22 | uint32_t e = hasher->tempState[4]; 23 | 24 | for (uint8_t i = 10; i < 80; i++) { 25 | if (i >= 16) { 26 | w[i & 15] = sha1_rotl(1, w[(i-3) & 15] ^ w[(i-8) & 15] ^ w[(i-14) & 15] ^ w[(i-16) & 15]); 27 | } 28 | 29 | uint32_t temp = sha1_rotl(5, a) + e + w[i & 15]; 30 | if (i < 20) { 31 | temp += (b & c) | ((~b) & d); 32 | temp += 0x5a827999; 33 | } else if(i < 40) { 34 | temp += b ^ c ^ d; 35 | temp += 0x6ed9eba1; 36 | } else if(i < 60) { 37 | temp += (b & c) | (b & d) | (c & d); 38 | temp += 0x8f1bbcdc; 39 | } else { 40 | temp += b ^ c ^ d; 41 | temp += 0xca62c1d6; 42 | } 43 | 44 | e = d; 45 | d = c; 46 | c = sha1_rotl(30, b); 47 | b = a; 48 | a = temp; 49 | } 50 | 51 | a += 0x67452301; 52 | b += 0xefcdab89; 53 | c += 0x98badcfe; 54 | d += 0x10325476; 55 | e += 0xc3d2e1f0; 56 | 57 | hasher->result[0 * 4 + 0] = a >> 24; 58 | hasher->result[0 * 4 + 1] = a >> 16; 59 | hasher->result[0 * 4 + 2] = a >> 8; 60 | hasher->result[0 * 4 + 3] = a; 61 | hasher->result[1 * 4 + 0] = b >> 24; 62 | hasher->result[1 * 4 + 1] = b >> 16; 63 | hasher->result[1 * 4 + 2] = b >> 8; 64 | hasher->result[1 * 4 + 3] = b; 65 | hasher->result[2 * 4 + 0] = c >> 24; 66 | hasher->result[2 * 4 + 1] = c >> 16; 67 | hasher->result[2 * 4 + 2] = c >> 8; 68 | hasher->result[2 * 4 + 3] = c; 69 | hasher->result[3 * 4 + 0] = d >> 24; 70 | hasher->result[3 * 4 + 1] = d >> 16; 71 | hasher->result[3 * 4 + 2] = d >> 8; 72 | hasher->result[3 * 4 + 3] = d; 73 | hasher->result[4 * 4 + 0] = e >> 24; 74 | hasher->result[4 * 4 + 1] = e >> 16; 75 | hasher->result[4 * 4 + 2] = e >> 8; 76 | hasher->result[4 * 4 + 3] = e; 77 | } 78 | 79 | void duco_hash_init(duco_hash_state_t * hasher, char const * prevHash) { 80 | for (uint8_t i = 0; i < 40; i++) { 81 | hasher->buffer[i] = prevHash[i]; 82 | } 83 | 84 | if (prevHash == (void*)(0xffffffff)) { 85 | // NOTE: THIS IS NEVER CALLED 86 | // This is here to keep a live reference to hash_block 87 | // Otherwise GCC tries to inline it entirely into the main loop 88 | // Which causes massive perf degradation 89 | duco_hash_block(nullptr); 90 | } 91 | 92 | // Do first 10 rounds as these are going to be the same all time 93 | 94 | uint32_t a = 0x67452301; 95 | uint32_t b = 0xefcdab89; 96 | uint32_t c = 0x98badcfe; 97 | uint32_t d = 0x10325476; 98 | uint32_t e = 0xc3d2e1f0; 99 | 100 | static uint32_t w[10]; 101 | 102 | for (uint8_t i = 0, i4 = 0; i < 10; i++, i4 += 4) { 103 | w[i] = (uint32_t(hasher->buffer[i4]) << 24) | 104 | (uint32_t(hasher->buffer[i4 + 1]) << 16) | 105 | (uint32_t(hasher->buffer[i4 + 2]) << 8) | 106 | (uint32_t(hasher->buffer[i4 + 3])); 107 | } 108 | 109 | for (uint8_t i = 0; i < 10; i++) { 110 | uint32_t temp = sha1_rotl(5, a) + e + w[i & 15]; 111 | temp += (b & c) | ((~b) & d); 112 | temp += 0x5a827999; 113 | 114 | e = d; 115 | d = c; 116 | c = sha1_rotl(30, b); 117 | b = a; 118 | a = temp; 119 | } 120 | 121 | hasher->tempState[0] = a; 122 | hasher->tempState[1] = b; 123 | hasher->tempState[2] = c; 124 | hasher->tempState[3] = d; 125 | hasher->tempState[4] = e; 126 | } 127 | 128 | void duco_hash_set_nonce(duco_hash_state_t * hasher, char const * nonce) { 129 | uint8_t * b = hasher->buffer; 130 | 131 | uint8_t off = SHA1_HASH_LEN * 2; 132 | for (uint8_t i = 0; i < 10 && nonce[i] != 0; i++) { 133 | b[off++] = nonce[i]; 134 | } 135 | 136 | uint8_t total_bytes = off; 137 | 138 | b[off++] = 0x80; 139 | while (off < 62) { 140 | b[off++] = 0; 141 | } 142 | 143 | b[62] = total_bytes >> 5; 144 | b[63] = total_bytes << 3; 145 | } 146 | 147 | uint8_t const * duco_hash_try_nonce(duco_hash_state_t * hasher, char const * nonce) { 148 | duco_hash_set_nonce(hasher, nonce); 149 | duco_hash_block(hasher); 150 | 151 | return hasher->result; 152 | } 153 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/kdb.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * kdb - knowledge database 3 | * the answer here is based on experiment and findings by 'JK Rolling' 4 | * you may or may not see the same things as board package/sketch is constantly evolving with time 5 | * 6 | * > Arduino IDE settings 7 | * >> Description: 8 | * Recommended settings in IDE to enable smoother miner bring up 9 | * 1. File -> Preferences -> Additional Boards Manager URLs: 10 | * https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json 11 | * 2. Tools -> Boards -> Board Manager 12 | * type "RP2040" in search box and select board from Earle F. Philhower, III 13 | * click "Install" 14 | * 3. Tools -> Boards -> Raspberry Pi RP2040 Boards -> Raspberry Pi Pico 15 | * 4. Tools -> CPU Speed -> 100MHz 16 | * (tested version 2.2.1 at June 2022) 17 | * 18 | * > I2C0_SDA / I2C0_SCL / I2C1_SDA / I2C1_SCL 19 | * >> Description: 20 | * Refers to I2C pins on Pico. The I2C pins always comes in pair. e.g. I2C0_SDA and I2C0_SCL. 21 | * DO NOT mixed and match them. BAD example - (I2C1_SDA and I2C0_SCL) or (I2C0_SDA(GP20) and I2C0_SCL(GP17)) 22 | * Valid I2C pairs: 23 | * - [I2C0_SDA,I2C0_SCL]: [0,1];[4,5];[8,9];[12,13];[16,17];[20,21]; 24 | * - [I2C1_SDA,I2C1_SCL]: [2,3];[6,7];[10,11];[14,15];[18,19];[26,27]; 25 | * 26 | * > I2CS_FIND_ADDR 27 | * >> Description: 28 | * with value "true", worker will auto self-assign I2CS address 29 | * However, worker may find themself have clashing address between core0/core1 30 | * or with other worker. This is due to the random delay before I2C bus scan 31 | * might be too close to one another 32 | * >> Resolution: 33 | * set to "false" so worker will have fixed I2CS address 34 | * workers will have more deterministic behavior for Python miner to communicate with 35 | * 36 | * > CRC8_EN 37 | * >> Description: 38 | * Provide I2C data integrity checks so both RP2040 and Python can decide to discard/re-request 39 | * job/result. Recommended value 'true'. 40 | * Setting it to 'false' can reduce I2C transaction length rougly 4 bytes or 4.5%. Do this only 41 | * if the I2C network is placed in electrically quiet area 42 | * 43 | * > WDT_EN 44 | * >> Description: 45 | * Kick RP2040 back into initial state when it is stucked for whatever reason. 46 | * Current timeout value is maxed at 8.3s. Reduce this value by changing WDT_PERIOD 47 | * Recommended value is 'true' 48 | * set to 'false' will disable WDT and RP2040 will not restart if hung 49 | * 50 | * > CORE_BATON_EN 51 | * >> Description: 52 | * with value of "true", baton here limits only one core is active during SHA1 hashing. 53 | * with value of "false", the true dual-core capability is unleashed, which in theory 54 | * should double the share rate. Also open up possibility of supporting 55 | * true dual I2C slave where 2 independent jobs can be received and hashed simultaneously. 56 | * RPi master I2C0 need external pull resistor. You may calculate the best resistance. 57 | * usually 4k7Ohms will work 58 | * However, one or both core will lock up and not responsive to USB or I2C in 2.0.3 59 | * >> Resolution: 60 | * Update board package to 2.2.1 or newer 61 | * 62 | * > WIRE_CLOCK 63 | * >> Description: 64 | * clock here will determine I2C SCL clock frequency. supported 100kHz, 400kHz, 1MHz 65 | * Using 100KHz(100000), rp2040 seems to struggle with data loss/corruption 66 | * >> Resolution: 67 | * Use 400kHz(400000) or 1MHz(1000000) for rp2040. see table below for clarity 68 | * It'll reduce I2C data flight time for faster clock 69 | * |-----------|--------------------|---------------------------| 70 | * | I2CM freq | RP2040 I2CS freq | Note | 71 | * |-----------|--------------------|---------------------------| 72 | * | 100kHz | 100kHz/400kHz/1MHz | I2CS 100kHz is not stable | 73 | * | 400kHz | 400kHz/1MHz | | 74 | * | 1MHz | 1MHz | | 75 | * |-----------|--------------------|---------------------------| 76 | * *result collected in table above is based on my testbench. I2CM=RPi Zero W 2; I2CS=RPi Pico 77 | * ESP I2C master of 100kHz(with repeated write length of 8) seems to work well with rp2040 1MHz 78 | * 79 | * > LED_EN 80 | * >> Description: 81 | * value 'true' will enable the led to blink whenever a share is done 82 | * value 'false' will disable the led 83 | * 84 | * > SENSOR_EN 85 | * >> Description: 86 | * value 'true' will enable the internal temperature reporting to DuinoCoin IoT section. 87 | * currently reporting degree celcius. for degree fahrenheit, look for read_temperature() function 88 | * in pico_utils.ino, comment out degree equation and uncomment fahrenheit equation. 89 | * value 'false' will disbale internal temperature reporting 90 | * This feature doesn't limit to internal temperature sensor. one may connect external sensor 91 | * and write code for it so it can report to DuinoCoin online wallet IoT section 92 | * 93 | * > SINGLE_CORE_ONLY 94 | * >> Description: 95 | * value 'true' will disable core1. Only 1 I2CS address will show up and the single core hash rate 96 | * will increase by ~10% 97 | * value 'false' will active dual core. 98 | * This parameter will render CORE_BATON_EN unused 99 | * 100 | * > WORKER_NAME 101 | * >> Description: 102 | * Python will read this name and print on screen before worker start 103 | * put a unique name if needed, else no harm done if untouched 104 | */ 105 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/sha1/hash.c: -------------------------------------------------------------------------------- 1 | // This file is part of cryptosuite2. // 2 | // // 3 | // cryptosuite2 is free software: you can redistribute it and/or modify // 4 | // it under the terms of the GNU General Public License as published by // 5 | // the Free Software Foundation, either version 3 of the License, or // 6 | // (at your option) any later version. // 7 | // // 8 | // cryptosuite2 is distributed in the hope that it will be useful, // 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 11 | // GNU General Public License for more details. // 12 | // // 13 | // You should have received a copy of the GNU General Public License // 14 | // along with cryptosuite2. If not, see . // 15 | // // 16 | 17 | #include "hash.h" 18 | #include 19 | #include 20 | 21 | 22 | void sha1_hash_block(sha1_hasher_t hasher) 23 | { 24 | uint8_t i; 25 | uint32_t a, b, c, d, e, temp; 26 | 27 | // XXX: Omit initializing the message schedule. 28 | // See how I did this below. 29 | // Allocating the message schedule would eat 2k RAM 30 | // which is a no-go on an AVR. 31 | uint8_t i4; 32 | // On x86 we have to change the byte order, because... 33 | // I actually do not know. 34 | for(i = i4 = 0; i < 16; i++, i4 += 4) 35 | { 36 | hasher->buffer.words[i] = (((uint32_t)hasher->buffer.bytes[i4]) << 24) | 37 | (((uint32_t)hasher->buffer.bytes[i4 + 1]) << 16) | 38 | (((uint32_t)hasher->buffer.bytes[i4 + 2]) << 8) | 39 | (((uint32_t)hasher->buffer.bytes[i4 + 3])); 40 | } 41 | 42 | a = hasher->state.words[0]; 43 | b = hasher->state.words[1]; 44 | c = hasher->state.words[2]; 45 | d = hasher->state.words[3]; 46 | e = hasher->state.words[4]; 47 | 48 | for(i = 0; i < 80; i++) 49 | { 50 | // XXX: 51 | // This part of the computation omits the message schedule 52 | // W as described in https://tools.ietf.org/html/rfc4634 53 | // The first 16 words of the message schedule is just the block 54 | // anyways and the computation of the message schedule uses only 55 | // the last 16 words, so we can do that. 56 | if( i >= 16 ) 57 | { 58 | hasher->buffer.words[i & 15] = sha1_rotl(1 59 | , hasher->buffer.words[(i - 3)& 15] 60 | ^ hasher->buffer.words[(i - 8)& 15] 61 | ^ hasher->buffer.words[(i - 14)& 15] 62 | ^ hasher->buffer.words[(i - 16)& 15]); 63 | } 64 | 65 | temp = sha1_rotl(5, a) + e + hasher->buffer.words[i & 15] + sha1_k(i); 66 | if(i < 20) 67 | { 68 | temp += (b & c) | ((~b) & d); 69 | } 70 | else if(i < 40) 71 | { 72 | temp += b ^ c ^ d; 73 | } 74 | else if(i < 60) 75 | { 76 | temp += (b & c) | (b & d) | (c & d); 77 | } 78 | else 79 | { 80 | temp += b ^ c ^ d; 81 | } 82 | 83 | 84 | 85 | 86 | 87 | e = d; 88 | d = c; 89 | c = sha1_rotl(30, b); 90 | b = a; 91 | a = temp; 92 | } 93 | hasher->state.words[0] += a; 94 | hasher->state.words[1] += b; 95 | hasher->state.words[2] += c; 96 | hasher->state.words[3] += d; 97 | hasher->state.words[4] += e; 98 | 99 | } 100 | 101 | 102 | 103 | void sha1_hasher_add_byte(sha1_hasher_t hasher, uint8_t byte) 104 | { 105 | hasher->buffer.bytes[hasher->block_offset] = byte; 106 | hasher->block_offset++; 107 | if(hasher->block_offset == SHA1_BLOCK_LEN) 108 | { 109 | sha1_hash_block(hasher); 110 | hasher->block_offset = 0; 111 | } 112 | } 113 | 114 | /** 115 | * NOTE: once the block has been pad'ed the hasher will 116 | * produce nonsense data. Therefore putc will return EOF 117 | * once the hasher has been pad'ed (this happens, when 118 | * sha1_hasher_gethash or sha1_hasher_gethmac are invoced). 119 | * */ 120 | uint8_t sha1_hasher_putc(sha1_hasher_t hasher, uint8_t byte) 121 | { 122 | if(hasher->_lock) 123 | { 124 | return EOF; 125 | } 126 | hasher->total_bytes++; 127 | sha1_hasher_add_byte(hasher, byte); 128 | return byte; 129 | 130 | } 131 | 132 | 133 | 134 | void sha1_hasher_pad(sha1_hasher_t hasher) 135 | { 136 | hasher->_lock = 1; 137 | sha1_hasher_add_byte(hasher, 0x80); 138 | while(hasher->block_offset != 56) 139 | { 140 | sha1_hasher_add_byte(hasher, 0); 141 | } 142 | 143 | // FIXME: 144 | // Use a loop for this. 145 | sha1_hasher_add_byte(hasher, hasher->total_bytes * 8 >> 56); 146 | sha1_hasher_add_byte(hasher, hasher->total_bytes * 8 >> 48); 147 | sha1_hasher_add_byte(hasher, hasher->total_bytes * 8 >> 40); 148 | sha1_hasher_add_byte(hasher, hasher->total_bytes * 8 >> 32); 149 | sha1_hasher_add_byte(hasher, hasher->total_bytes * 8 >> 24); 150 | sha1_hasher_add_byte(hasher, hasher->total_bytes * 8 >> 16); 151 | sha1_hasher_add_byte(hasher, hasher->total_bytes * 8 >> 8); 152 | sha1_hasher_add_byte(hasher, hasher->total_bytes * 8); 153 | 154 | } 155 | 156 | uint8_t * sha1_hasher_gethash(sha1_hasher_t hasher) 157 | { 158 | sha1_hasher_pad(hasher); 159 | uint8_t i; 160 | 161 | // switch byte order. 162 | for(i = 0; i < (SHA1_HASH_LEN / 4); i++) 163 | { 164 | uint32_t a, b; 165 | a = hasher->state.words[i]; 166 | b = a << 24; 167 | b |= ( a << 8) & 0x00ff0000; 168 | b |= ( a >> 8) & 0x0000ff00; 169 | b |= a >> 24; 170 | hasher->state.words[i] = b; 171 | } 172 | return hasher->state.bytes; 173 | } 174 | 175 | 176 | #ifdef SHA1_ENABLE_HMAC 177 | void sha1_hasher_init_hmac(sha1_hasher_t hasher, const uint8_t * key, size_t key_len) 178 | { 179 | uint8_t i; 180 | memset(hasher->hmac_key_buffer, 0, SHA1_BLOCK_LEN); 181 | 182 | if(key_len > SHA1_BLOCK_LEN) 183 | { 184 | sha1_hasher_init(hasher); 185 | while(key_len--) 186 | { 187 | sha1_hasher_putc(hasher, *key++); 188 | } 189 | memcpy(hasher->hmac_key_buffer, 190 | sha1_hasher_gethash(hasher), 191 | SHA1_HASH_LEN); 192 | } 193 | else 194 | { 195 | memcpy(hasher->hmac_key_buffer, key, key_len); 196 | } 197 | sha1_hasher_init(hasher); 198 | for(i = 0; i < SHA1_BLOCK_LEN; i++) 199 | { 200 | sha1_hasher_putc(hasher, hasher->hmac_key_buffer[i] ^ SHA1_HMAC_IPAD); 201 | } 202 | 203 | } 204 | uint8_t * sha1_hasher_gethmac(sha1_hasher_t hasher) 205 | { 206 | uint8_t i; 207 | memcpy(hasher->hmac_inner_hash, sha1_hasher_gethash(hasher), 208 | SHA1_HASH_LEN); 209 | sha1_hasher_init(hasher); 210 | 211 | for(i = 0; i < SHA1_BLOCK_LEN; i++) 212 | { 213 | sha1_hasher_putc(hasher, hasher->hmac_key_buffer[i] ^ SHA1_HMAC_OPAD); 214 | } 215 | for(i = 0; i < SHA1_HASH_LEN; i++) 216 | { 217 | sha1_hasher_putc(hasher, hasher->hmac_inner_hash[i]); 218 | } 219 | return sha1_hasher_gethash(hasher); 220 | } 221 | #endif 222 | 223 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/core0.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * core0.ino 3 | * created by JK Rolling 4 | * 27 06 2022 5 | * 6 | * Dual core and dual I2C (400kHz/1MHz) 7 | * DuinoCoin worker 8 | * 9 | * See kdb.ino on Arduino IDE settings to compile for RP2040 10 | * 11 | * with inspiration from 12 | * * Revox (Duino-Coin founder) 13 | * * Ricaun (I2C Nano sketch) 14 | */ 15 | 16 | #pragma GCC optimize ("-Ofast") 17 | 18 | StreamString core0_bufferReceive; 19 | StreamString core0_bufferRequest; 20 | Sha1Wrapper core0_Sha1_base; 21 | 22 | void core0_setup_i2c() { 23 | byte addr = 2 * DEV_INDEX + I2CS_START_ADDRESS; 24 | I2C0.setSDA(I2C0_SDA); 25 | I2C0.setSCL(I2C0_SCL); 26 | I2C0.setClock(WIRE_CLOCK); 27 | if (I2CS_FIND_ADDR) { 28 | do { 29 | addr = core0_find_i2c(); 30 | // search again if address clashes with other core 31 | } while (addr == i2c1_addr); 32 | } 33 | I2C0.begin(addr); 34 | I2C0.onReceive(core0_receiveEvent); 35 | I2C0.onRequest(core0_requestEvent); 36 | 37 | printMsgln("core0 I2C0 addr:"+String(addr)); 38 | } 39 | 40 | byte core0_find_i2c() { 41 | unsigned long time_delay = (unsigned long) rnd_whitened() >> 13; 42 | sleep_us(time_delay); 43 | I2C0.begin(); 44 | byte addr; 45 | for (addr = I2CS_START_ADDRESS; addr < I2CS_MAX; addr++) { 46 | I2C0.beginTransmission(addr); 47 | int error = I2C0.endTransmission(); 48 | if (error != 0) break; 49 | } 50 | I2C0.end(); 51 | return addr; 52 | } 53 | 54 | void core0_receiveEvent(int howMany) { 55 | if (howMany == 0) return; 56 | core0_bufferReceive.write(I2C0.read()); 57 | while (I2C0.available()) I2C0.read(); 58 | } 59 | 60 | void core0_requestEvent() { 61 | char c = '\n'; 62 | if (core0_bufferRequest.available() > 0 && core0_bufferRequest.indexOf('\n') != -1) { 63 | c = core0_bufferRequest.read(); 64 | } 65 | I2C0.write(c); 66 | } 67 | 68 | void core0_abort_loop() { 69 | SerialPrintln("core0 detected crc8 hash mismatch. Re-request job.."); 70 | while (core0_bufferReceive.available()) core0_bufferReceive.read(); 71 | core0_bufferRequest.print("#\n"); 72 | if (WDT_EN && wdt_pet) { 73 | watchdog_update(); 74 | } 75 | Blink(BLINK_BAD_CRC, LED_PIN); 76 | } 77 | 78 | void core0_send(String data) { 79 | core0_bufferRequest.print(DUMMY_DATA + data + "\n"); 80 | } 81 | 82 | bool core0_loop() { 83 | 84 | if (core0_bufferReceive.available() > 0 && 85 | core0_bufferReceive.indexOf("$") != -1) { 86 | String action = core0_bufferReceive.readStringUntil(','); 87 | String field = core0_bufferReceive.readStringUntil('$'); 88 | String response; 89 | 90 | if (action == "get") { 91 | switch (tolower(field[0])) { 92 | case 't': // temperature 93 | if (SENSOR_EN) response = String(read_temperature()); 94 | else response = SENSOR_EN; 95 | printMsg("core0 temperature: "); 96 | break; 97 | case 'h': // humidity 98 | if (SENSOR_EN) response = String(read_humidity()); 99 | else response = SENSOR_EN; 100 | printMsg("core0 humidity: "); 101 | break; 102 | case 'f': // i2c clock frequency 103 | response = String(WIRE_CLOCK); 104 | printMsg("core0 WIRE_CLOCK: " ); 105 | break; 106 | case 'c': // crc8 status 107 | response = String(CRC8_EN); 108 | printMsg("core0 CRC8_EN: "); 109 | break; 110 | case 'b': // core_baton 111 | response = String(CORE_BATON_EN); 112 | printMsg("core0 CORE_BATON_EN: "); 113 | break; 114 | case 's' : // single core only 115 | response = String(SINGLE_CORE_ONLY); 116 | printMsg("core0 SINGLE_CORE_ONLY: "); 117 | break; 118 | case 'n' : // worker name 119 | response = String(WORKER_NAME); 120 | printMsg("WORKER_NAME: "); 121 | break; 122 | default: 123 | response = "unkn"; 124 | printMsgln("core0 command: " + field); 125 | printMsg("core0 response: "); 126 | } 127 | printMsgln(response); 128 | core0_send(response + "$"); 129 | } 130 | else if (action == "set") { 131 | // not used at the moment 132 | } 133 | if (WDT_EN && wdt_pet) { 134 | watchdog_update(); 135 | } 136 | } 137 | 138 | // do work here 139 | if (core0_bufferReceive.available() > 0 && core0_bufferReceive.indexOf('\n') != -1) { 140 | 141 | if (core0_bufferReceive.available() > 0 && 142 | core0_bufferReceive.indexOf("$") != -1) { 143 | core0_bufferReceive.readStringUntil('$'); 144 | } 145 | 146 | printMsg("core0 job recv : " + core0_bufferReceive); 147 | 148 | // Read last block hash 149 | String lastblockhash = core0_bufferReceive.readStringUntil(','); 150 | // Read expected hash 151 | String newblockhash = core0_bufferReceive.readStringUntil(','); 152 | 153 | unsigned int difficulty; 154 | if (CRC8_EN) { 155 | difficulty = core0_bufferReceive.readStringUntil(',').toInt(); 156 | uint8_t received_crc8 = core0_bufferReceive.readStringUntil('\n').toInt(); 157 | String data = lastblockhash + "," + newblockhash + "," + String(difficulty) + ","; 158 | uint8_t expected_crc8 = calc_crc8(data); 159 | 160 | if (received_crc8 != expected_crc8) { 161 | core0_abort_loop(); 162 | return false; 163 | } 164 | } 165 | else { 166 | // Read difficulty 167 | difficulty = core0_bufferReceive.readStringUntil('\n').toInt(); 168 | } 169 | 170 | // clear in case of excessive jobs 171 | while (core0_bufferReceive.available() > 0 && core0_bufferReceive.indexOf('\n') != -1) { 172 | core0_bufferReceive.readStringUntil('\n'); 173 | } 174 | 175 | // Start time measurement 176 | unsigned long startTime = micros(); 177 | // Call DUCO-S1A hasher 178 | unsigned int ducos1result = 0; 179 | if (difficulty < DIFF_MAX) ducos1result = core0_ducos1a(lastblockhash, newblockhash, difficulty); 180 | // End time measurement 181 | unsigned long endTime = micros(); 182 | // Calculate elapsed time 183 | unsigned long elapsedTime = endTime - startTime; 184 | // Send result back to the program with share time 185 | while (core0_bufferRequest.available()) core0_bufferRequest.read(); 186 | 187 | String result = String(ducos1result) + "," + String(elapsedTime) + "," + String(DUCOID); 188 | 189 | // calculate crc8 for result 190 | if (CRC8_EN) { 191 | result += ","; 192 | result += String(calc_crc8(result)); 193 | } 194 | core0_send(result); 195 | // prepend non-alnum data (to be discarded in py) for improved data integrity 196 | //core0_bufferRequest.print(" " + result + "\n"); 197 | 198 | return true; 199 | } 200 | return false; 201 | } 202 | 203 | // DUCO-S1A hasher from Revox 204 | uint32_t core0_ducos1a(String lastblockhash, String newblockhash, 205 | uint32_t difficulty) { 206 | 207 | uint8_t job[job_maxsize]; 208 | newblockhash.toUpperCase(); 209 | const char *c = newblockhash.c_str(); 210 | uint8_t final_len = newblockhash.length() >> 1; 211 | for (uint8_t i = 0, j = 0; j < final_len; i += 2, j++) 212 | job[j] = ((((c[i] & 0x1F) + 9) % 25) << 4) + ((c[i + 1] & 0x1F) + 9) % 25; 213 | 214 | // Difficulty loop 215 | core0_Sha1_base.init(); 216 | core0_Sha1_base.print(lastblockhash); 217 | for (uint32_t ducos1res = 0; ducos1res < difficulty * 100 + 1; ducos1res++) { 218 | core0_Sha1 = core0_Sha1_base; 219 | core0_Sha1.print(String(ducos1res)); 220 | // Get SHA1 result 221 | uint8_t *hash_bytes = core0_Sha1.result(); 222 | if (memcmp(hash_bytes, job, SHA1_HASH_LEN * sizeof(char)) == 0) { 223 | // If expected hash is equal to the found hash, return the result 224 | return ducos1res; 225 | } 226 | if (WDT_EN && wdt_pet) { 227 | if (core0_max_elapsed(millis(), wdt_period_half)) { 228 | watchdog_update(); 229 | } 230 | } 231 | } 232 | return 0; 233 | } 234 | 235 | String core0_response() { 236 | return core0_bufferRequest; 237 | } 238 | 239 | bool core0_max_elapsed(unsigned long current, unsigned long max_elapsed) { 240 | static unsigned long _start = 0; 241 | 242 | if ((current - _start) > max_elapsed) { 243 | _start = current; 244 | return true; 245 | } 246 | return false; 247 | } 248 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/DuinoCoin_RPI_Pico_DualCore.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * DuinoCoin_RPI_Pico_DualCore.ino 3 | * created by JK Rolling 4 | * 27 06 2022 5 | * 6 | * Dual core and dual I2C (400kHz/1MHz) 7 | * DuinoCoin worker 8 | * 9 | * See kdb.ino on Arduino IDE settings to compile for RP2040 10 | * 11 | * with inspiration from 12 | * * Revox (Duino-Coin founder) 13 | * * Ricaun (I2C Nano sketch) 14 | */ 15 | 16 | #pragma GCC optimize ("-Ofast") 17 | 18 | #include 19 | #include // https://github.com/ricaun/StreamJoin 20 | #include "pico/mutex.h" 21 | extern "C" { 22 | #include 23 | }; 24 | #include "sha1.h" 25 | 26 | /****************** USER MODIFICATION START ****************/ 27 | #define DEV_INDEX 0 28 | #define I2CS_START_ADDRESS 8 29 | #define I2CS_FIND_ADDR false // >>> see kdb before setting it to true <<< 30 | #define WIRE_CLOCK 1000000 // >>> see kdb before changing this I2C clock frequency <<< 31 | #define I2C0_SDA 20 32 | #define I2C0_SCL 21 33 | #define I2C1_SDA 26 34 | #define I2C1_SCL 27 35 | #define CRC8_EN true 36 | #define WDT_EN true 37 | #define CORE_BATON_EN false 38 | #define LED_EN true 39 | #define SENSOR_EN true 40 | #define SINGLE_CORE_ONLY false // >>> see kdb before setting it to true <<< 41 | #define WORKER_NAME "rp2040" 42 | /****************** USER MODIFICATION END ******************/ 43 | /*---------------------------------------------------------*/ 44 | /****************** FINE TUNING START **********************/ 45 | #define LED_PIN LED_BUILTIN 46 | #define LED_BRIGHTNESS 255 // 1-255 47 | #define BLINK_SHARE_FOUND 1 48 | #define BLINK_SETUP_COMPLETE 2 49 | #define BLINK_BAD_CRC 3 50 | #define WDT_PERIOD 8300 51 | // USB-Serial --> Serial 52 | // UART0-Serial --> Serial1 (preferred, connect FTDI RX pin to Pico GP0) 53 | #define SERIAL_LOGGER Serial1 54 | #define I2C0 Wire 55 | #define I2C1 Wire1 56 | #define I2CS_MAX 32 57 | #define DIFF_MAX 1000 58 | #define DUMMY_DATA " " 59 | #define MCORE_WDT_THRESHOLD 10 60 | /****************** FINE TUNING END ************************/ 61 | 62 | #ifdef SERIAL_LOGGER 63 | #define SerialBegin() SERIAL_LOGGER.begin(115200); 64 | #define SerialPrint(x) SERIAL_LOGGER.print(x); 65 | #define SerialPrintln(x) SERIAL_LOGGER.println(x); 66 | #else 67 | #define SerialBegin() 68 | #define SerialPrint(x) 69 | #define SerialPrintln(x) 70 | #endif 71 | 72 | #if SINGLE_CORE_ONLY 73 | #define DISABLE_2ND_CORE 74 | #endif 75 | 76 | // prototype 77 | void Blink(uint8_t count, uint8_t pin); 78 | void BlinkFade(void); 79 | bool repeating_timer_callback(struct repeating_timer *t); 80 | 81 | static String DUCOID; 82 | static mutex_t serial_mutex; 83 | static bool core_baton; 84 | static bool wdt_pet = true; 85 | static uint16_t wdt_period_half = WDT_PERIOD/2; 86 | // 40+40+20+3 is the maximum size of a job 87 | const uint16_t job_maxsize = 104; 88 | byte i2c1_addr=0; 89 | bool core0_started = false, core1_started = false; 90 | uint32_t core0_shares = 0, core0_shares_ss = 0, core0_shares_local = 0; 91 | uint32_t core1_shares = 0, core1_shares_ss = 0, core1_shares_local = 0; 92 | struct repeating_timer timer; 93 | 94 | // Core0 95 | void setup() { 96 | bool print_on_por = true; 97 | SerialBegin(); 98 | 99 | // core status indicator 100 | if (LED_EN) { 101 | pinMode(LED_PIN, OUTPUT); 102 | add_repeating_timer_ms(10, repeating_timer_callback, NULL, &timer); 103 | } 104 | 105 | // initialize pico internal temperature sensor 106 | if (SENSOR_EN) { 107 | enable_internal_temperature_sensor(); 108 | } 109 | 110 | // initialize mutex 111 | mutex_init(&serial_mutex); 112 | 113 | // initialize watchdog 114 | if (WDT_EN) { 115 | if (watchdog_caused_reboot()) { 116 | printMsgln(F("\nRebooted by Watchdog!")); 117 | Blink(BLINK_SETUP_COMPLETE, LED_PIN); 118 | print_on_por = false; 119 | } 120 | // Enable the watchdog, requiring the watchdog to be updated every 8000ms or the chip will reboot 121 | // Maximum of 0x7fffff, which is approximately 8.3 seconds 122 | // second arg is pause on debug which means the watchdog will pause when stepping through code 123 | watchdog_enable(WDT_PERIOD, 1); 124 | } 125 | 126 | // let core0 run first 127 | core_baton = true; 128 | 129 | DUCOID = get_DUCOID(); 130 | core0_setup_i2c(); 131 | Blink(BLINK_SETUP_COMPLETE, LED_PIN); 132 | if (print_on_por) { 133 | printMsgln("I2CS_START_ADDRESS: "+String(I2CS_START_ADDRESS)); 134 | printMsgln("I2CS_FIND_ADDR: "+String(I2CS_FIND_ADDR)); 135 | printMsgln("DEV_INDEX: "+String(DEV_INDEX)); 136 | printMsgln("WIRE_CLOCK: "+String(WIRE_CLOCK)); 137 | printMsgln("I2C0_SDA: "+String(I2C0_SDA)); 138 | printMsgln("I2C0_SCL: "+String(I2C0_SCL)); 139 | printMsgln("I2C1_SDA: "+String(I2C1_SDA)); 140 | printMsgln("I2C1_SCL: "+String(I2C1_SCL)); 141 | printMsgln("CRC8_EN: "+String(CRC8_EN)); 142 | printMsgln("WDT_EN: "+String(WDT_EN)); 143 | printMsgln("CORE_BATON_EN: "+String(CORE_BATON_EN)); 144 | printMsgln("LED_EN: "+String(LED_EN)); 145 | printMsgln("SENSOR_EN: "+String(SENSOR_EN)); 146 | printMsgln("SINGLE_CORE_ONLY: "+String(SINGLE_CORE_ONLY)); 147 | } 148 | printMsgln("core0 startup done!"); 149 | } 150 | 151 | void loop() { 152 | if (core_baton || !CORE_BATON_EN) { 153 | if (core0_loop()) { 154 | core0_started = true; 155 | printMsg(F("core0 job done :")); 156 | printMsg(core0_response()); 157 | BlinkFade(); 158 | if (WDT_EN && wdt_pet) { 159 | watchdog_update(); 160 | } 161 | 162 | if (core0_started && core1_started && !SINGLE_CORE_ONLY) { 163 | core0_shares++; 164 | if (core1_shares != core1_shares_ss) { 165 | core1_shares_ss = core1_shares; 166 | core1_shares_local = 0; 167 | } 168 | else { 169 | printMsgln("core0: core1 " + String(MCORE_WDT_THRESHOLD - core1_shares_local) + " remaining count to WDT disable"); 170 | 171 | if (core1_shares_local >= MCORE_WDT_THRESHOLD) { 172 | printMsgln("core0: Detected core1 hung. Disable WDT"); 173 | wdt_pet = false; 174 | } 175 | if ((MCORE_WDT_THRESHOLD - core1_shares_local) != 0) { 176 | core1_shares_local++; 177 | } 178 | } 179 | } 180 | } 181 | if (!SINGLE_CORE_ONLY) 182 | core_baton = false; 183 | } 184 | } 185 | 186 | #ifndef DISABLE_2ND_CORE 187 | // Core1 188 | void setup1() { 189 | sleep_ms(100); 190 | core1_setup_i2c(); 191 | Blink(BLINK_SETUP_COMPLETE, LED_PIN); 192 | printMsgln("core1 startup done!"); 193 | } 194 | 195 | void loop1() { 196 | if (!core_baton || !CORE_BATON_EN) { 197 | if (core1_loop()) { 198 | core1_started = true; 199 | printMsg(F("core1 job done :")); 200 | printMsg(core1_response()); 201 | BlinkFade(); 202 | if (WDT_EN && wdt_pet) { 203 | watchdog_update(); 204 | } 205 | 206 | if (core0_started && core1_started && !SINGLE_CORE_ONLY) { 207 | core1_shares++; 208 | if (core0_shares != core0_shares_ss) { 209 | core0_shares_ss = core0_shares; 210 | core0_shares_local = 0; 211 | } 212 | else { 213 | printMsgln("core1: core0 " + String(MCORE_WDT_THRESHOLD - core0_shares_local) + " remaining count to WDT disable"); 214 | if (core0_shares_local >= MCORE_WDT_THRESHOLD) { 215 | printMsgln("core1: Detected core0 hung. Disable WDT"); 216 | wdt_pet = false; 217 | } 218 | if ((MCORE_WDT_THRESHOLD - core0_shares_local) != 0) { 219 | core0_shares_local++; 220 | } 221 | } 222 | } 223 | } 224 | core_baton = true; 225 | } 226 | } 227 | #endif 228 | 229 | // protect scarce resource 230 | void printMsg(String msg) { 231 | uint32_t owner; 232 | if (!mutex_try_enter(&serial_mutex, &owner)) { 233 | if (owner == get_core_num()) return; 234 | mutex_enter_blocking(&serial_mutex); 235 | } 236 | SerialPrint(msg); 237 | mutex_exit(&serial_mutex); 238 | } 239 | 240 | void printMsgln(String msg) { 241 | printMsg(msg+"\n"); 242 | } 243 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Pico_DualCore/core1.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * core1.ino 3 | * created by JK Rolling 4 | * 27 06 2022 5 | * 6 | * Dual core and dual I2C (400kHz/1MHz) 7 | * DuinoCoin worker 8 | * 9 | * See kdb.ino on Arduino IDE settings to compile for RP2040 10 | * 11 | * with inspiration from 12 | * * Revox (Duino-Coin founder) 13 | * * Ricaun (I2C Nano sketch) 14 | */ 15 | 16 | #pragma GCC optimize ("-Ofast") 17 | 18 | StreamString core1_bufferReceive; 19 | StreamString core1_bufferRequest; 20 | Sha1Wrapper core1_Sha1_base; 21 | 22 | void core1_setup_i2c() { 23 | byte addr = 2 * DEV_INDEX + I2CS_START_ADDRESS + 1; 24 | I2C1.setSDA(I2C1_SDA); 25 | I2C1.setSCL(I2C1_SCL); 26 | I2C1.setClock(WIRE_CLOCK); 27 | if (I2CS_FIND_ADDR) { 28 | addr = core1_find_i2c(); 29 | i2c1_addr = addr; 30 | } 31 | I2C1.begin(addr); 32 | I2C1.onReceive(core1_receiveEvent); 33 | I2C1.onRequest(core1_requestEvent); 34 | 35 | printMsgln("core1 I2C1 addr:"+String(addr)); 36 | } 37 | 38 | byte core1_find_i2c() { 39 | unsigned long time_delay = (unsigned long) rnd_whitened() >> 13; 40 | sleep_us(time_delay); 41 | I2C1.begin(); 42 | byte addr; 43 | for (addr = I2CS_START_ADDRESS; addr < I2CS_MAX; addr++) { 44 | I2C1.beginTransmission(addr); 45 | int error = I2C1.endTransmission(); 46 | if (error != 0) break; 47 | } 48 | I2C1.end(); 49 | return addr; 50 | } 51 | 52 | void core1_receiveEvent(int howMany) { 53 | if (howMany == 0) return; 54 | core1_bufferReceive.write(I2C1.read()); 55 | while (I2C1.available()) I2C1.read(); 56 | } 57 | 58 | void core1_requestEvent() { 59 | char c = '\n'; 60 | if (core1_bufferRequest.available() > 0 && core1_bufferRequest.indexOf('\n') != -1) { 61 | c = core1_bufferRequest.read(); 62 | } 63 | I2C1.write(c); 64 | } 65 | 66 | void core1_abort_loop() { 67 | SerialPrintln("core1 detected crc8 hash mismatch. Re-request job.."); 68 | while (core1_bufferReceive.available()) core1_bufferReceive.read(); 69 | core1_bufferRequest.print("#\n"); 70 | if (WDT_EN && wdt_pet) { 71 | watchdog_update(); 72 | } 73 | Blink(BLINK_BAD_CRC, LED_PIN); 74 | } 75 | 76 | void core1_send(String data) { 77 | core1_bufferRequest.print(DUMMY_DATA + data + "\n"); 78 | } 79 | 80 | bool core1_loop() { 81 | 82 | if (core1_bufferReceive.available() > 0 && 83 | core1_bufferReceive.indexOf("$") != -1) { 84 | String action = core1_bufferReceive.readStringUntil(','); 85 | String field = core1_bufferReceive.readStringUntil('$'); 86 | String response; 87 | 88 | if (action == "get") { 89 | switch (tolower(field[0])) { 90 | case 't': // temperature 91 | if (SENSOR_EN) response = String(read_temperature()); 92 | else response = SENSOR_EN; 93 | printMsg("core1 temperature: "); 94 | break; 95 | case 'h': // humidity 96 | if (SENSOR_EN) response = String(read_humidity()); 97 | else response = SENSOR_EN; 98 | printMsg("core0 humidity: "); 99 | break; 100 | case 'f': // i2c clock frequency 101 | response = String(WIRE_CLOCK); 102 | printMsg("core1 WIRE_CLOCK: "); 103 | break; 104 | case 'c': // crc8 status 105 | response = String(CRC8_EN); 106 | printMsg("core1 CRC8_EN: "); 107 | break; 108 | case 'b': // core_baton 109 | response = String(CORE_BATON_EN); 110 | printMsg("core1 CORE_BATON_EN: "); 111 | break; 112 | case 's' : // single core only 113 | response = String(SINGLE_CORE_ONLY); 114 | printMsg("core1 SINGLE_CORE_ONLY: "); 115 | break; 116 | case 'n' : // worker name 117 | response = String(WORKER_NAME); 118 | printMsg("WORKER_NAME: "); 119 | break; 120 | default: 121 | response = "unkn"; 122 | printMsgln("core1 command: " + field); 123 | printMsg("core1 response: "); 124 | } 125 | printMsgln(response); 126 | core1_send(response + "$"); 127 | } 128 | else if (action == "set") { 129 | // not used at the moment 130 | } 131 | if (WDT_EN && wdt_pet) { 132 | watchdog_update(); 133 | } 134 | } 135 | 136 | // do work here 137 | if (core1_bufferReceive.available() > 0 && core1_bufferReceive.indexOf('\n') != -1) { 138 | 139 | if (core1_bufferReceive.available() > 0 && 140 | core1_bufferReceive.indexOf("$") != -1) { 141 | core1_bufferReceive.readStringUntil('$'); 142 | } 143 | 144 | printMsg("core1 job recv : " + core1_bufferReceive); 145 | 146 | // Read last block hash 147 | String lastblockhash = core1_bufferReceive.readStringUntil(','); 148 | // Read expected hash 149 | String newblockhash = core1_bufferReceive.readStringUntil(','); 150 | 151 | unsigned int difficulty; 152 | if (CRC8_EN) { 153 | difficulty = core1_bufferReceive.readStringUntil(',').toInt(); 154 | uint8_t received_crc8 = core1_bufferReceive.readStringUntil('\n').toInt(); 155 | String data = lastblockhash + "," + newblockhash + "," + String(difficulty) + ","; 156 | uint8_t expected_crc8 = calc_crc8(data); 157 | 158 | if (received_crc8 != expected_crc8) { 159 | core1_abort_loop(); 160 | return false; 161 | } 162 | } 163 | else { 164 | // Read difficulty 165 | difficulty = core1_bufferReceive.readStringUntil('\n').toInt(); 166 | } 167 | 168 | // clear in case of excessive jobs 169 | while (core1_bufferReceive.available() > 0 && core1_bufferReceive.indexOf('\n') != -1) { 170 | core1_bufferReceive.readStringUntil('\n'); 171 | } 172 | 173 | // Start time measurement 174 | unsigned long startTime = micros(); 175 | // Call DUCO-S1A hasher 176 | unsigned int ducos1result = 0; 177 | if (difficulty < DIFF_MAX) ducos1result = core1_ducos1a(lastblockhash, newblockhash, difficulty); 178 | // End time measurement 179 | unsigned long endTime = micros(); 180 | // Calculate elapsed time 181 | unsigned long elapsedTime = endTime - startTime; 182 | // Send result back to the program with share time 183 | while (core1_bufferRequest.available()) core1_bufferRequest.read(); 184 | 185 | String result = String(ducos1result) + "," + String(elapsedTime) + "," + String(DUCOID); 186 | 187 | // calculate crc8 for result 188 | if (CRC8_EN) { 189 | result += ","; 190 | result += String(calc_crc8(result)); 191 | } 192 | core1_send(result); 193 | // prepend non-alnum data (to be discarded in py) for improved data integrity 194 | //core1_bufferRequest.print(" " + result + "\n"); 195 | 196 | return true; 197 | } 198 | return false; 199 | } 200 | 201 | // DUCO-S1A hasher from Revox 202 | uint32_t core1_ducos1a(String lastblockhash, String newblockhash, 203 | uint32_t difficulty) { 204 | // 40+40+20+3 is the maximum size of a job 205 | //const uint16_t job_maxsize = 104; 206 | uint8_t job[job_maxsize]; 207 | //Sha1Wrapper core1_Sha1_base; 208 | newblockhash.toUpperCase(); 209 | const char *c = newblockhash.c_str(); 210 | uint8_t final_len = newblockhash.length() >> 1; 211 | for (uint8_t i = 0, j = 0; j < final_len; i += 2, j++) 212 | job[j] = ((((c[i] & 0x1F) + 9) % 25) << 4) + ((c[i + 1] & 0x1F) + 9) % 25; 213 | 214 | // Difficulty loop 215 | core1_Sha1_base.init(); 216 | core1_Sha1_base.print(lastblockhash); 217 | for (uint32_t ducos1res = 0; ducos1res < difficulty * 100 + 1; ducos1res++) { 218 | core1_Sha1 = core1_Sha1_base; 219 | core1_Sha1.print(String(ducos1res)); 220 | // Get SHA1 result 221 | uint8_t *hash_bytes = core1_Sha1.result(); 222 | if (memcmp(hash_bytes, job, SHA1_HASH_LEN * sizeof(char)) == 0) { 223 | // If expected hash is equal to the found hash, return the result 224 | return ducos1res; 225 | } 226 | if (WDT_EN && wdt_pet) { 227 | if (core1_max_elapsed(millis(), wdt_period_half)) { 228 | watchdog_update(); 229 | } 230 | } 231 | } 232 | return 0; 233 | } 234 | 235 | String core1_response() { 236 | return core1_bufferRequest; 237 | } 238 | 239 | bool core1_max_elapsed(unsigned long current, unsigned long max_elapsed) { 240 | static unsigned long _start = 0; 241 | 242 | if ((current - _start) > max_elapsed) { 243 | _start = current; 244 | return true; 245 | } 246 | return false; 247 | } 248 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DuinoCoinI2C_RPI 2 | This project design to mine [Duino-Coin](https://github.com/revoxhere/duino-coin) using Raspberry Pi or equivalent SBC as a master and Arduino/ATtiny85 as a slave. 3 | 4 | Using the I2C communication to connect all the boards and make a scalable communication between the master and the slaves. 5 | 6 | ## Video Tutorial 7 | 8 | Raspberry Pi AVR I2C Bus 1 [tutorial video](https://youtu.be/bZ2XwPpYtiw) 9 | 10 | Raspberry Pi AVR I2C Bus 0 [tutorial video](https://youtu.be/ywO7j4yqIlg) 11 | 12 | Raspberry Pi Pico HW Part1 [tutorial video](https://youtu.be/yu4R4ktdubY) 13 | 14 | Raspberry Pi Pico SW Part2 [tutorial video](https://youtu.be/eBEd8jxf3TE) 15 | 16 | ## Python Environment Setup 17 | 18 | ### Linux 19 | 20 | ```BASH 21 | sudo apt update 22 | sudo apt install python3 python3-pip git i2c-tools python3-smbus screen -y # Install dependencies 23 | git clone https://github.com/JK-Rolling/DuinoCoinI2C_RPI.git # Clone DuinoCoinI2C_RPI repository 24 | cd DuinoCoinI2C_RPI 25 | python3 -m pip install -r requirements.txt # Install pip dependencies 26 | ```` 27 | 28 | Use `sudo raspi-config` to enable I2C. Refer detailed steps at [raspberry-pi-i2c](https://pimylifeup.com/raspberry-pi-i2c/) 29 | 30 | For RPI I2C Bus 0, extra step is needed, `sudo nano /boot/config.txt` and add `dtparam=i2c_vc=on`, save and reboot 31 | 32 | For RPi Pico as worker, the RPi master is strongly recomended to use 400kHz/1MHz SCL clock frequency. again, `sudo nano /boot/config.txt`, 33 | add `dtparam=i2c_baudrate=1000000` and `dtparam=i2c_vc_baudrate=1000000` for 1MHz. Change to 400000 if you prefer 400kHz SCL. 34 | 35 | Finally, connect your I2C AVR miner and launch the software (e.g. `python3 ./AVR_Miner_RPI.py`) 36 | 37 | ### Windows 38 | 39 | 1. Download and install [Python 3](https://www.python.org/downloads/) (add Python and Pip to Windows PATH) 40 | 2. Download [the DuinoCoinI2C_RPI](https://github.com/JK-Rolling/DuinoCoinI2C_RPI/releases) 41 | 3. Extract the zip archive you've downloaded and open the folder in command prompt 42 | 4. In command prompt type `py -m pip install -r requirements.txt` to install required pip dependencies 43 | 44 | Finally, connect your I2C AVR miner and launch the software (e.g. `python3 ./AVR_Miner_RPI.py` or `py AVR_Miner_RPI.py` in the command prompt) 45 | 46 | ## Version 47 | 48 | DuinoCoinI2C_RPI Version 3.3 49 | 50 | # Arduino - Slave 51 | 52 | Arduino shall use `DuinoCoin_RPI_Tiny_Slave` sketch. LLC is required if Arduino is operating at 5V and master at 3.3V. Arduino also have builtin temperature sensor that can be used for IoT reporting. This feature is enabled for fun as the ADC is uncalibrated. Once calibrated, the temperature can be pretty accurate according to some. Set `TEMPERATURE_OFFSET` and `FILTER_LP` to calibrate. 53 | 54 | Occasionally slaves might hang and not responding to master. quick workaround is to press the reset button on the slave to bring it back. 55 | 56 | Once in a blue moon, one of the slave might pull down the whole bus with it. power cycling the rig is the fastest way to bring it back. 57 | 58 | To solve these issues permanently, update Nano with Optiboot bootloader. WDT will auto reset the board if there is no activity within 8s. 59 | 60 | Change this line to `true` to activate the WDT. works for Nano clone as well. **make sure the Nano is using Optiboot** 61 | 62 | `#define WDT_EN true` 63 | 64 | feature can be turn on/off individually to cater for your specific scenario. set to false to disable. 65 | ```C 66 | #define I2CS_FIND_ADDR false 67 | #define WDT_EN true 68 | #define CRC8_EN true 69 | #define LED_EN true 70 | #define SENSOR_EN true 71 | ``` 72 | |`#define`| Note | 73 | |:-| :---- | 74 | |DEV_INDEX|device index to help assign I2C address| 75 | |I2CS_START_ADDRESS|this and `DEV_INDEX` summation will produce final I2C address| 76 | |I2CS_FIND_ADDR|Scan available I2CS address and self-assign, overriding `DEV_INDEX`. Recommend to set `false`| 77 | |WDT_EN|Auto self-reset every 8s in case of inactivity| 78 | |CRC8_EN|I2C CRC8 insertion and data integrity checks| 79 | |LED_EN|Blink when share is found| 80 | |SENSOR_EN|Report chip internal temperature in degree Celsius| 81 | 82 | each SBC have different starting I2CS address from `i2cdetect` command. Address that is not shown is still usable. To change the I2CS starting address, modify `I2CS_START_ADDRESS` 83 | 84 | For disabled `I2CS_FIND_ADDR`, manually assign I2CS address by updating `DEV_INDEX`. `I2CS_FIND_ADDR` is best effort basis, it may or may not work. 85 | 86 | # ATtiny85 - Slave 87 | 88 | Use `DuinoCoin_ATTiny_Slave` for ATtiny. LLC is required if worker and host is operating at different voltage. 4k7 pullup resistors for `SDA/SCL` pins are strongly recommended if host do not have it built-in. The TWI/I2C/IIC seems to work well with SCL 100KHz `WIRE_CLOCK 100000`. Default compiled sketch size for ATtiny85 is 5630 bytes for program storage space and 351 bytes for dynamic memory. Estimated hashrate 267H/s for ATTiny85-20PU running at 3.3V or 5V. ATTiny85 also have builtin temperature sensor that can be used for IoT reporting. This feature is enabled for fun as the ADC is uncalibrated. Once calibrated, the temperature can be pretty accurate according to some. Set `TEMPERATURE_OFFSET` and `TEMPERATURE_COEFF` to calibrate. Note: floating point is costly, only proceed if you know what you're doing. 89 | 90 | add `http://drazzy.com/package_drazzy.com_index.json` to `Additional Board Manager URLs` in Arduino IDE, then go to board manager and search for `attiny` and install ATTinyCore from Spence Konde 91 | 92 | ATTiny85 for example, the system clock is default at 1MHz. This needs to be changed to get good hashrate 93 | 94 | You may use dedicated ATTiny programmer or any Uno/Nano to set the fuse via `Tools --> Burn Bootloader`. See table below on setting that worked for me on ATtiny85. Finally upload sketch using `Sketch --> Upload Using Programmer` 95 | 96 | |Attribute|Value| 97 | |:-|:-| 98 | |Board|ATtiny25/45/85 (No Bootloader)| 99 | |Chip|ATtiny85| 100 | |Clock Source|16.5 MHz (PLL,tweaked)| 101 | |Timer 1 Clock|CPU| 102 | |LTO|Enabled| 103 | |millis()/micros()|Enabled| 104 | |Save EEPROM|EEPROM retained| 105 | |B.O.D Level|Disabled| 106 | 107 | ## ATTiny85 Wiring 108 | 109 | attiny85 110 | 111 | ## Reducing sketch / RAM usage 112 | For some smaller devices, to get smallest possible sketch size without hacking, set `CRC8_EN false`, `WDT_EN false`, `SENSOR_EN false` and change to `#pragma GCC optimize ("-Os")` for all files. You should get 4232 bytes use of program storage space and 343 bytes of dynamic memory, which produce about 222H/s at 16MHz 113 | 114 | to get smallest possible sketch size with hacking, use the above settings and hack file `Arduino15\packages\ATTinyCore\hardware\avr\1.5.2\libraries\Wire\src\USI_TWI_Slave\USI_TWI_Slave.h`. This should further reduce the dynamic memory usage to 319 bytes. *RELIABILITY NOT GUARANTEED* 115 | 116 | ```C 117 | // change value from 16 to 4. somehow value of 1/2 broke TWI 118 | #define TWI_RX_BUFFER_SIZE (4) 119 | #define TWI_TX_BUFFER_SIZE (4) 120 | ``` 121 | 122 | # Adafruit Trinket ATtiny85 - Slave 123 | 124 | Similar to ATtiny85, but Trinket came with bootloader which leave only 5310 Bytes for sketch. Use `DuinoCoin_Tiny_Slave_trinket` sketch to start mining. Estimated hashrate at 258 H/s, consuming 97% of available program space (5146 Bytes). 125 | 126 | VBAT+ pin on the Trinket 5V will accept 3.3V input just fine, so LLC not required if the master is using 3.3V logic level. 127 | 128 | ## Removing bootloader 129 | 130 | You may use Arduino Nano/Uno or ATtiny programmer to remove the bootloader to regain full 8KB storage, load `DuinoCoin_ATTiny_Slave` instead to get 267 H/s (+3.5%) 131 | 132 | Load File > Examples > ArduinoISP into Uno/Nano and connect as below. Open `DuinoCoin_ATTiny_Slave`, choose the Uno/Nano from Tools > Port, choose Tools > Programmer > Arduino as ISP, finally, Sketch > Upload Using Programmer 133 | 134 | | Trinket | | Uno/Nano | 135 | | :----: | :-----: | :-----: | 136 | |VBAT+ | <---> | 5V | 137 | |GND|<--->|GND| 138 | |RST|<--->|#10| 139 | |#0|<--->|#11| 140 | |#1|<--->|#12| 141 | |#2|<--->|#13| 142 | 143 | ## Repair bootloader 144 | 145 | Follow the wiring in `Removing bootloader`, download the [trinketloader_2015-06-09.zip](https://github.com/JK-Rolling/DuinoCoinI2C_RPI/raw/main/DuinoCoin_Tiny_Slave_trinket/trinketloader_2015-06-09.zip), extract all files, load the `trinketloader` into Uno/Nano, open Serial console at 9600 baudrate, hit `G` and enter. bootloader should be uploaded into the Trinket and once again, sketch can be uploaded into Trinket via USB 146 | 147 | # Raspberry Pi Pico - Slave 148 | 149 | Use Pico dual core code for Raspberry Pi Pico. Logic Level Converter (LLC) is not required as both RPi and Pico operates at 3.3V. 150 | 151 | Read `kdb.ino` for detailed setup steps in Arduino IDE 152 | 153 | ## RP2040 board package from Earle F. Philhower, III 154 | 155 | For good out-of-box experience, add `https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json` to `Additional Board Manager URLs` in Arduino IDE, then search for RP2040 Boards **version 2.2.1 or above**. Install it from Arduino IDE board manager 156 | 157 | Default I2C pin `SDA0 - GP20` `SCL0 - GP21` `SDA1 - GP26` `SCL1 - GP27` 158 | 159 | 160 | ## Relocating I2C pins 161 | 162 | User can change the I2C pin by modifying pin value in `DuinoCoin_RPI_Pico_DualCore.ino` 163 | 164 | `#define I2C0_SDA 20` 165 | `#define I2C0_SCL 21` 166 | `#define I2C1_SDA 26` 167 | `#define I2C1_SCL 27` 168 | 169 | The motivation to change the default I2C pin is to make the board rig building friendly because now the I2C pin have the same side as the `Vsys` pin 170 | 171 | ## Library Dependency 172 | 173 | * [ArduinoUniqueID](https://github.com/ricaun/ArduinoUniqueID) (Handle the chip ID) 174 | * [StreamString](https://github.com/ricaun/StreamJoin) 175 | 176 | ## I2C Address 177 | 178 | The I2C Address on the Arduino is hardcoded by user. if an address already exists on the I2C bus, the behavior is undefined 179 | 180 | Change the value on the define for each worker for unique address: 181 | ``` 182 | #define DEV_INDEX 1 183 | ``` 184 | 185 | # Raspberry PI or SBC - Master 186 | 187 | The master requests the job on the `DuinoCoin` server and sends the work to the slave (Arduino). 188 | 189 | After the job is done, the slave sends back the response to the master (SBC) and then sends back to the `DuinoCoin` server. 190 | 191 | ## IoT Feature 192 | 193 | RPi Pico have built in temperature sensor that made IoT reporting possible. 194 | 195 | For other worker, it is possible to add external sensor and enhance worker sketch to read them. But it is outside of the scope. 196 | 197 | ## CRC8 Feature 198 | 199 | During setup, Python will auto discover worker CRC8 status. This option applies to all workers. 200 | 201 | CRC8 feature is ON by default. To disable it, use `#define CRC8_EN false` 202 | 203 | ## Max Client/Slave 204 | 205 | The code theoretically supports up to 119 clients on Raspberry PI (Bullseye OS) on single I2C bus 206 | 207 | Slave addresses range from 0x0..0x77 208 | 209 | Some reported that I2C addresses that did not shows up from `i2cdetect` are accessible 210 | 211 | RPi have 2 I2C buses which bring up the count up to 254 (theoretical). This requires 2 separate instances of Python miner with it's own Settings.cfg file. Duplicate the directory into 2 and start the setup from there. Instance 1 should use i2cbus 1 (RPi have builtin 1k8 pullup). Instance 2 should use i2cbus 0 (where external 4k7 pullup resistor are on) 212 | 213 | ## Enable I2C on Raspberry PI 214 | 215 | Google or refer to [raspberry-pi-i2c](https://pimylifeup.com/raspberry-pi-i2c/) 216 | 217 | For RPI I2C Bus 0, there might not be pull up resistor built in. It relies on the pull up from Nano. 218 | 219 | For other I2C slave that do not have pull up capability, add 4k7 Ohm resistor to both SDA and SCL line on I2C bus 0. 220 | 221 | **Note:** If you see bad shares, it could be due to a bug in [RPI I2C hardware](https://github.com/raspberrypi/linux/issues/254) 222 | 223 | # Connection Pinouts 224 | 225 | Connect the pins of the Raspberry PI on the Arduino like the table/images below, use a [Logic Level Converter](https://www.sparkfun.com/products/12009) to connect between the SBC and Arduino/ATtiny85 226 | 227 | || RPI | Logic Level Converter | Arduino | 228 | |:-:| :----: | :-----: | :-----: | 229 | ||3.3V | <---> | 5V | 230 | ||GND | <---> | GND | 231 | |`SDA`| PIN 3 | <---> | A4 | 232 | |`SCL`| PIN 5 | <---> | A5 | 233 | 234 | || RPI | Logic Level Converter | ATtiny85 | 235 | |:-:| :----: | :-----: | :-----: | 236 | ||3.3V | <---> | 5V | 237 | ||GND | <---> | GND | 238 | |`SDA`| PIN 3 | <---> | PB0 | 239 | |`SCL`| PIN 5 | <---> | PB2 | 240 | 241 | ## For dual I2C master and dual I2C slave interface 242 | 243 | pico 244 | 245 | || RPI (dual I2C) || Pico | 246 | |:-:| :----: | :-----: | :-----: | 247 | ||3.3V or 5V | <---> | VSYS* | 248 | ||GND | <---> | GND | 249 | |`SDA1`| PIN 3 | <---> | GP26 | 250 | |`SCL1`| PIN 5 | <---> | GP27 | 251 | |`SDA0`| PIN 27 | <---> | GP20 | 252 | |`SCL0`| PIN 28 | <---> | GP21 | 253 | 254 | ## For single I2C master and dual I2C slave interface 255 | 256 | pico 257 | 258 | || RPI (single I2C) || Pico | 259 | |:-:| :----: | :-----: | :-----: | 260 | ||3.3V or 5V | <---> | VSYS* | 261 | ||GND | <---> | GND | 262 | |`SDA1`| PIN 3 | <---> | GP26, GP20 | 263 | |`SCL1`| PIN 5 | <---> | GP27, GP21 | 264 | 265 | \* VSYS accept voltage range from 1.8V to 5.5V. Voltage regulator onboard Pico will generate 3.3V for rp2040 use 266 | 267 | ## Benchmarks of tested devices 268 | 269 | | Device | Average hashrate
(all threads) | Mining
threads | 270 | |-----------------------------------------------------------|-----------------------------------|-------------------| 271 | | Arduino Pro Mini, Uno, Nano etc.
(Atmega 328p/pb/16u2) | 268 H/s | 1 | 272 | | Adafruit Trinket 5V Attiny85 | 258 H/s | 1 | 273 | | Attiny85 | 267 H/s | 1 | 274 | | Raspberry Pi Pico | 4.7 kH/s (100MHz) | 2 | 275 | | Raspberry Pi Pico | 5.4 kH/s (120MHz) | 2 | 276 | 277 | # License and Terms of service 278 | 279 | All refers back to original [Duino-Coin licensee and terms of service](https://github.com/revoxhere/duino-coin) 280 | -------------------------------------------------------------------------------- /DuinoCoin_ATTiny_Slave/DuinoCoin_ATTiny_Slave.ino: -------------------------------------------------------------------------------- 1 | /* 2 | DoinoCoin_ATTiny_Slave.ino 3 | adapted from Luiz H. Cassettari 4 | by JK Rolling 5 | */ 6 | 7 | #include // https://github.com/ricaun/ArduinoUniqueID 8 | #include 9 | #include 10 | #include 11 | #include "duco_hash.h" 12 | 13 | /****************** USER MODIFICATION START ****************/ 14 | #define ADDRESS_I2C 8 // manual I2C address assignment 15 | #define CRC8_EN true 16 | #define WDT_EN true 17 | #define SENSOR_EN false // use ATTiny85 internal temperature sensor 18 | #define LED_EN false // brightness controllable on pin 1 19 | /****************** USER MODIFICATION END ******************/ 20 | /*---------------------------------------------------------*/ 21 | /****************** FINE TUNING START **********************/ 22 | #define WORKER_NAME "t85" 23 | #define WIRE_MAX 32 24 | #define WIRE_CLOCK 100000 25 | #define TEMPERATURE_OFFSET 287 // calibrate ADC here 26 | #define TEMPERATURE_COEFF 1 // calibrate ADC further 27 | #define LED_PIN 1 28 | #define LED_BRIGHTNESS 8 // 1-255 29 | /****************** FINE TUNING END ************************/ 30 | //#define EEPROM_ADDRESS 0 31 | #if defined(ARDUINO_AVR_UNO) | defined(ARDUINO_AVR_PRO) 32 | #define SERIAL_LOGGER Serial 33 | #endif 34 | 35 | // ATtiny85 - http://drazzy.com/package_drazzy.com_index.json 36 | // SCL - PB2 - 2 37 | // SDA - PB0 - 0 38 | 39 | #ifdef SERIAL_LOGGER 40 | #define SerialBegin() SERIAL_LOGGER.begin(115200); 41 | #define SerialPrint(x) SERIAL_LOGGER.print(x); 42 | #define SerialPrintln(x) SERIAL_LOGGER.println(x); 43 | #else 44 | #define SerialBegin() 45 | #define SerialPrint(x) 46 | #define SerialPrintln(x) 47 | #endif 48 | 49 | #if LED_EN 50 | #define LedBegin() DDRB |= (1 << LED_PIN); 51 | #if LED_BRIGHTNESS == 255 52 | #define LedHigh() PORTB |= (1 << LED_PIN); 53 | #define LedLow() PORTB &= ~(1 << LED_PIN); 54 | #else 55 | #define LedHigh() analogWrite(LED_PIN, LED_BRIGHTNESS); 56 | #define LedLow() analogWrite(LED_PIN, 0); 57 | #endif 58 | #define LedBlink() LedHigh(); delay(100); LedLow(); delay(100); 59 | #else 60 | #define LedBegin() 61 | #define LedHigh() 62 | #define LedLow() 63 | #define LedBlink() 64 | #endif 65 | 66 | #if CRC8_EN 67 | #define BUFFER_MAX 89 68 | #else 69 | #define BUFFER_MAX 86 70 | #endif 71 | #define HASH_BUFFER_SIZE 20 72 | #define CHAR_END '\n' 73 | #define CHAR_DOT ',' 74 | 75 | static const char DUCOID[] PROGMEM = "DUCOID"; 76 | static const char ZEROS[] PROGMEM = "000"; 77 | static const char WK_NAME[] PROGMEM = WORKER_NAME; 78 | static const char UNKN[] PROGMEM = "unkn"; 79 | static const char ONE[] PROGMEM = "1"; 80 | static const char ZERO[] PROGMEM = "0"; 81 | 82 | static byte address; 83 | static char buffer[BUFFER_MAX]; 84 | static uint8_t buffer_position; 85 | static uint8_t buffer_length; 86 | static bool working; 87 | static bool jobdone; 88 | 89 | void(* resetFunc) (void) = 0;//declare reset function at address 0 90 | 91 | // --------------------------------------------------------------------- // 92 | // setup 93 | // --------------------------------------------------------------------- // 94 | 95 | void setup() { 96 | if (SENSOR_EN) { 97 | // Setup the Analog to Digital Converter - one ADC conversion 98 | // is read and discarded 99 | ADCSRA &= ~(_BV(ADATE) |_BV(ADIE)); // Clear auto trigger and interrupt enable 100 | ADCSRA |= _BV(ADEN); // Enable AD and start conversion 101 | ADMUX = 0xF | _BV( REFS1 ); // ADC4 (Temp Sensor) and Ref voltage = 1.1V; 102 | delay(3); // Settling time min 1ms 103 | getADC(); // discard first read 104 | } 105 | SerialBegin(); 106 | if (WDT_EN) { 107 | wdt_disable(); 108 | wdt_enable(WDTO_4S); 109 | } 110 | initialize_i2c(); 111 | LedBegin(); 112 | LedBlink(); 113 | LedBlink(); 114 | LedBlink(); 115 | } 116 | 117 | // --------------------------------------------------------------------- // 118 | // loop 119 | // --------------------------------------------------------------------- // 120 | 121 | void loop() { 122 | do_work(); 123 | millis(); // ????? For some reason need this to work the i2c 124 | #ifdef SERIAL_LOGGER 125 | if (SERIAL_LOGGER.available()) 126 | { 127 | #ifdef EEPROM_ADDRESS 128 | EEPROM.write(EEPROM_ADDRESS, SERIAL_LOGGER.parseInt()); 129 | #endif 130 | resetFunc(); 131 | } 132 | #endif 133 | } 134 | 135 | // --------------------------------------------------------------------- // 136 | // run 137 | // --------------------------------------------------------------------- // 138 | 139 | boolean runEvery(unsigned long interval) 140 | { 141 | static unsigned long previousMillis = 0; 142 | unsigned long currentMillis = millis(); 143 | if (currentMillis - previousMillis >= interval) 144 | { 145 | previousMillis = currentMillis; 146 | return true; 147 | } 148 | return false; 149 | } 150 | 151 | // --------------------------------------------------------------------- // 152 | // work 153 | // --------------------------------------------------------------------- // 154 | 155 | void do_work() 156 | { 157 | if (working) 158 | { 159 | LedHigh(); 160 | SerialPrintln(buffer); 161 | 162 | if (buffer[0] == '&') 163 | { 164 | resetFunc(); 165 | } 166 | 167 | if (buffer[0] == 'g') { 168 | // i2c_cmd 169 | //pos 0123[4]5678 170 | // get,[c]rc8$ 171 | // get,[b]aton$ 172 | // get,[s]inglecore$ 173 | // get,[f]req$ 174 | char f = buffer[4]; 175 | switch (tolower(f)) { 176 | case 't': // temperature 177 | if (SENSOR_EN) ltoa(getTemperature(), buffer, 8); 178 | else strcpy_P(buffer, ZERO); 179 | SerialPrint("SENSOR_EN: "); 180 | break; 181 | case 'f': // i2c clock frequency 182 | ltoa(WIRE_CLOCK, buffer, 10); 183 | SerialPrint("WIRE_CLOCK: "); 184 | break; 185 | case 'c': // crc8 status 186 | if (CRC8_EN) strcpy_P(buffer, ONE); 187 | else strcpy_P(buffer, ZERO); 188 | SerialPrint("CRC8_EN: "); 189 | break; 190 | case 'n': // worker name 191 | strcpy_P(buffer, WK_NAME); 192 | SerialPrint("WORKER: "); 193 | break; 194 | default: 195 | strcpy_P(buffer, UNKN); 196 | SerialPrint("command: "); 197 | SerialPrintln(f); 198 | SerialPrint("response: "); 199 | } 200 | SerialPrintln(buffer); 201 | buffer_position = 0; 202 | buffer_length = strlen(buffer); 203 | working = false; 204 | jobdone = true; 205 | if (WDT_EN) wdt_reset(); 206 | return; 207 | } 208 | do_job(); 209 | } 210 | LedLow(); 211 | } 212 | 213 | void do_job() 214 | { 215 | unsigned long startTime = millis(); 216 | unsigned int job = work(); 217 | unsigned long endTime = millis(); 218 | unsigned int elapsedTime = endTime - startTime; 219 | if (job<5) elapsedTime = job*(1<<2); 220 | 221 | memset(buffer, 0, sizeof(buffer)); 222 | char cstr[16]; 223 | 224 | // Job 225 | if (job == 0) 226 | strcpy(cstr,"#"); // re-request job 227 | else 228 | itoa(job, cstr, 10); 229 | strcpy(buffer, cstr); 230 | buffer[strlen(buffer)] = CHAR_DOT; 231 | 232 | // Time 233 | itoa(elapsedTime, cstr, 10); 234 | strcpy(buffer + strlen(buffer), cstr); 235 | strcpy_P(buffer + strlen(buffer), ZEROS); 236 | buffer[strlen(buffer)] = CHAR_DOT; 237 | 238 | // DUCOID 239 | strcpy_P(buffer + strlen(buffer), DUCOID); 240 | for (size_t i = 0; i < 8; i++) 241 | { 242 | itoa(UniqueID8[i], cstr, 16); 243 | if (UniqueID8[i] < 16) strcpy(buffer + strlen(buffer), "0"); 244 | strcpy(buffer + strlen(buffer), cstr); 245 | } 246 | 247 | #if CRC8_EN 248 | char gen_crc8[3]; 249 | buffer[strlen(buffer)] = CHAR_DOT; 250 | 251 | // CRC8 252 | itoa(crc8((uint8_t *)buffer, strlen(buffer)), gen_crc8, 10); 253 | strcpy(buffer + strlen(buffer), gen_crc8); 254 | #endif 255 | 256 | SerialPrintln(buffer); 257 | 258 | buffer_position = 0; 259 | buffer_length = strlen(buffer); 260 | working = false; 261 | jobdone = true; 262 | 263 | if (WDT_EN) wdt_reset(); 264 | } 265 | 266 | uint16_t work() 267 | { 268 | // calculate CRC8 269 | #if CRC8_EN 270 | char *delim_ptr = strchr(buffer, CHAR_DOT); 271 | delim_ptr = strchr(delim_ptr+1, CHAR_DOT); 272 | uint8_t job_length = strchr(delim_ptr+1, CHAR_DOT) - &buffer[0] + 1; 273 | uint8_t calc_crc8 = crc8((uint8_t *)buffer, job_length); 274 | #endif 275 | 276 | // tokenize 277 | const char *delim = ","; 278 | char *lastHash = strtok(buffer, delim); 279 | char *newHash = strtok(NULL, delim); 280 | char *diff = strtok(NULL, delim); 281 | 282 | // validate integrity 283 | #if CRC8_EN 284 | char *rx_crc8 = strtok(NULL, delim); 285 | if (calc_crc8 != atoi(rx_crc8)) { 286 | return 0; 287 | } 288 | #endif 289 | 290 | buffer_length = 0; 291 | buffer_position = 0; 292 | return work(lastHash, newHash, atoi(diff)); 293 | } 294 | 295 | //#define HTOI(c) ((c<='9')?(c-'0'):((c<='F')?(c-'A'+10):((c<='f')?(c-'a'+10):(0)))) 296 | #define HTOI(c) ((c<='9')?(c-'0'):((c<='f')?(c-'a'+10):(0))) 297 | #define TWO_HTOI(h, l) ((HTOI(h) << 4) + HTOI(l)) 298 | //byte HTOI(char c) {return ((c<='9')?(c-'0'):((c<='f')?(c-'a'+10):(0)));} 299 | //byte TWO_HTOI(char h, char l) {return ((HTOI(h) << 4) + HTOI(l));} 300 | 301 | void HEX_TO_BYTE(char * address, char * hex, uint8_t len) 302 | { 303 | for (uint8_t i = 0; i < len; i++) address[i] = TWO_HTOI(hex[2 * i], hex[2 * i + 1]); 304 | } 305 | 306 | // DUCO-S1A hasher 307 | uint16_t work(char * lastblockhash, char * newblockhash, uint8_t difficulty) 308 | { 309 | if (difficulty > 655) return 0; 310 | HEX_TO_BYTE(newblockhash, newblockhash, HASH_BUFFER_SIZE); 311 | static duco_hash_state_t hash; 312 | duco_hash_init(&hash, lastblockhash); 313 | char nonceStr[10 + 1]; 314 | for (uint16_t nonce = 0; nonce < difficulty*100+1; nonce++) { 315 | ultoa(nonce, nonceStr, 10); 316 | uint8_t const * hash_bytes = duco_hash_try_nonce(&hash, nonceStr); 317 | if (memcmp(hash_bytes, newblockhash, HASH_BUFFER_SIZE) == 0) { 318 | return nonce; 319 | } 320 | if (WDT_EN) { 321 | if (runEvery(2000)) wdt_reset(); 322 | } 323 | } 324 | 325 | return 0; 326 | } 327 | 328 | // --------------------------------------------------------------------- // 329 | // i2c 330 | // --------------------------------------------------------------------- // 331 | 332 | void initialize_i2c(void) { 333 | address = ADDRESS_I2C; 334 | 335 | #ifdef EEPROM_ADDRESS 336 | address = EEPROM.read(EEPROM_ADDRESS); 337 | if (address == 0 || address > 127) { 338 | address = ADDRESS_I2C; 339 | EEPROM.write(EEPROM_ADDRESS, address); 340 | } 341 | #endif 342 | 343 | SerialPrint("Wire begin "); 344 | SerialPrintln(address); 345 | Wire.begin(address); 346 | Wire.onReceive(onReceiveJob); 347 | Wire.onRequest(onRequestResult); 348 | } 349 | 350 | void onReceiveJob(uint8_t howMany) { 351 | if (howMany == 0) return; 352 | if (working) return; 353 | if (jobdone) return; 354 | 355 | char c = Wire.read(); 356 | buffer[buffer_length++] = c; 357 | if (buffer_length == BUFFER_MAX) buffer_length--; 358 | if (c == CHAR_END || c == '$') { 359 | working = true; 360 | } 361 | // flush remaining data 362 | while (Wire.available()) { 363 | Wire.read(); 364 | } 365 | } 366 | 367 | void onRequestResult() { 368 | char c = CHAR_END; 369 | if (jobdone) { 370 | c = buffer[buffer_position++]; 371 | if ( buffer_position == buffer_length) { 372 | jobdone = false; 373 | buffer_position = 0; 374 | buffer_length = 0; 375 | memset(buffer, 0, sizeof(buffer)); 376 | } 377 | } 378 | Wire.write(c); 379 | } 380 | 381 | #if CRC8_EN 382 | // https://stackoverflow.com/questions/51731313/cross-platform-crc8-function-c-and-python-parity-check 383 | uint8_t crc8( uint8_t *addr, uint8_t len) { 384 | uint8_t crc=0; 385 | for (uint8_t i=0; i>= 1; 390 | if (mix) 391 | crc ^= 0x8C; 392 | inbyte >>= 1; 393 | } 394 | } 395 | return crc; 396 | } 397 | #endif 398 | 399 | // Calibration of the temperature sensor has to be changed for your own ATtiny85 400 | // per tech note: http://www.atmel.com/Images/doc8108.pdf 401 | uint16_t chipTemp(uint16_t raw) { 402 | return((raw - TEMPERATURE_OFFSET) / TEMPERATURE_COEFF); 403 | } 404 | 405 | // Common code for both sources of an ADC conversion 406 | uint16_t getADC() { 407 | ADCSRA |= _BV(ADSC); // Start conversion 408 | while((ADCSRA & _BV(ADSC))); // Wait until conversion is finished 409 | return ADC; // 10-bits 410 | } 411 | 412 | uint16_t getTemperature() { 413 | uint8_t vccIndex, vccGuess, vccLevel; 414 | uint16_t rawTemp=0, rawVcc; 415 | 416 | // Measure temperature 417 | ADCSRA |= _BV(ADEN); // Enable AD and start conversion 418 | ADMUX = 0xF | _BV( REFS1 ); // ADC4 (Temp Sensor) and Ref voltage = 1.1V; 419 | delay(3); // Settling time min 1ms. give it 3 420 | 421 | // acumulate ADC samples 422 | for (uint8_t i=0; i<16; i++) { 423 | rawTemp += getADC(); 424 | } 425 | rawTemp = rawTemp >> 4; // take median value 426 | 427 | ADCSRA &= ~(_BV(ADEN)); // disable ADC 428 | 429 | rawVcc = getRawVcc(); 430 | 431 | // probably using 3.3V Vcc if less than 4V 432 | if ((rawVcc * 10) < 40) { 433 | vccGuess = 33; 434 | vccLevel = 16; 435 | } 436 | // maybe 5V Vcc 437 | else { 438 | vccGuess = 50; 439 | vccLevel = 33; 440 | } 441 | //index 0..13 for vcc 1.7 ... 3.0 442 | //index 0..33 for vcc 1.7 ... 5.0 443 | vccIndex = min(max(17,(uint8_t)(rawVcc * 10)),vccGuess) - 17; 444 | 445 | // Temperature compensation using the chip voltage 446 | // with 3.0 V VCC is 1 lower than measured with 1.7 V VCC 447 | return chipTemp(rawTemp) + vccIndex / vccLevel; 448 | } 449 | 450 | uint16_t getRawVcc() { 451 | uint16_t rawVcc = 0; 452 | 453 | // Measure chip voltage (Vcc) 454 | ADCSRA |= _BV(ADEN); // Enable ADC 455 | ADMUX = 0x0c | _BV(REFS2); // Use Vcc as voltage reference, 456 | // bandgap reference as ADC input 457 | delay(3); // Settling time min 1 ms. give it 3 458 | 459 | // accumulate ADC samples 460 | for (uint8_t i=0; i<16; i++) { 461 | rawVcc += getADC(); 462 | } 463 | rawVcc = rawVcc >> 4; // take median value 464 | 465 | ADCSRA &= ~(_BV(ADEN)); // disable ADC 466 | 467 | // single ended conversion formula. ADC = Vin * 1024 / Vref, where Vin = 1.1V 468 | // 1.1 * (1<<5) = 35.20. take 35. fp calculation is expensive 469 | // divide using right shift 470 | rawVcc = (1024 * 35 / rawVcc) >> 5; 471 | 472 | return rawVcc; 473 | } 474 | -------------------------------------------------------------------------------- /DuinoCoin_Tiny_Slave_trinket/DuinoCoin_Tiny_Slave_trinket.ino: -------------------------------------------------------------------------------- 1 | /* 2 | DoinoCoin_ATTiny_Slave.ino 3 | adapted from Luiz H. Cassettari 4 | by JK Rolling 5 | */ 6 | 7 | #pragma GCC optimize ("-O2") 8 | #include // https://github.com/ricaun/ArduinoUniqueID 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "sha1.h" 14 | 15 | /****************** USER MODIFICATION START ****************/ 16 | #define ADDRESS_I2C 8 // manual I2C address assignment 17 | #define CRC8_EN true 18 | #define WDT_EN true 19 | #define SENSOR_EN false // use ATTiny85 internal temperature sensor (+346 Bytes) 20 | #define LED_EN true 21 | /****************** USER MODIFICATION END ******************/ 22 | /*---------------------------------------------------------*/ 23 | /****************** FINE TUNING START **********************/ 24 | #define WORKER_NAME "trinket" 25 | #define WIRE_MAX 32 26 | #define WIRE_CLOCK 100000 27 | #define TEMPERATURE_OFFSET 299 // calibrate ADC here 28 | #define TEMPERATURE_COEFF 1 // calibrate ADC further 29 | #define LED_PIN LED_BUILTIN 30 | #define LED_BRIGHTNESS 255 // 1-255 31 | /****************** FINE TUNING END ************************/ 32 | //#define EEPROM_ADDRESS 0 33 | #if defined(ARDUINO_AVR_UNO) | defined(ARDUINO_AVR_PRO) 34 | #define SERIAL_LOGGER Serial 35 | #endif 36 | 37 | // ATtiny85 - https://adafruit.github.io/arduino-board-index/package_adafruit_index.json 38 | // SCL - PB2 - 2 39 | // SDA - PB0 - 0 40 | 41 | #ifdef SERIAL_LOGGER 42 | #define SerialBegin() SERIAL_LOGGER.begin(115200); 43 | #define SerialPrint(x) SERIAL_LOGGER.print(x); 44 | #define SerialPrintln(x) SERIAL_LOGGER.println(x); 45 | #else 46 | #define SerialBegin() 47 | #define SerialPrint(x) 48 | #define SerialPrintln(x) 49 | #endif 50 | 51 | #if LED_EN 52 | #define LedBegin() DDRB |= (1 << LED_PIN); 53 | #if LED_BRIGHTNESS == 255 54 | #define LedHigh() PORTB |= (1 << LED_PIN); 55 | #define LedLow() PORTB &= ~(1 << LED_PIN); 56 | #else 57 | #define LedHigh() analogWrite(LED_PIN, LED_BRIGHTNESS); 58 | #define LedLow() analogWrite(LED_PIN, 0); 59 | #endif 60 | #define LedBlink() LedHigh(); delay(100); LedLow(); delay(100); 61 | #else 62 | #define LedBegin() 63 | #define LedHigh() 64 | #define LedLow() 65 | #define LedBlink() 66 | #endif 67 | 68 | #define BUFFER_MAX 88 69 | #define HASH_BUFFER_SIZE 20 70 | #define CHAR_END '\n' 71 | #define CHAR_DOT ',' 72 | 73 | static const char DUCOID[] PROGMEM = "DUCOID"; 74 | static const char ZEROS[] PROGMEM = "000"; 75 | static const char WK_NAME[] PROGMEM = WORKER_NAME; 76 | static const char UNKN[] PROGMEM = "unkn"; 77 | static const char ONE[] PROGMEM = "1"; 78 | static const char ZERO[] PROGMEM = "0"; 79 | 80 | static byte address; 81 | static char buffer[BUFFER_MAX]; 82 | static uint8_t buffer_position; 83 | static uint8_t buffer_length; 84 | static bool working; 85 | static bool jobdone; 86 | 87 | void(* resetFunc) (void) = 0;//declare reset function at address 0 88 | 89 | // --------------------------------------------------------------------- // 90 | // setup 91 | // --------------------------------------------------------------------- // 92 | 93 | void setup() { 94 | clock_prescale_set(clock_div_1); 95 | if (SENSOR_EN) { 96 | // Setup the Analog to Digital Converter - one ADC conversion 97 | // is read and discarded 98 | ADCSRA &= ~(_BV(ADATE) |_BV(ADIE)); // Clear auto trigger and interrupt enable 99 | ADCSRA |= _BV(ADEN); // Enable AD and start conversion 100 | ADMUX = 0xF | _BV( REFS1 ); // ADC4 (Temp Sensor) and Ref voltage = 1.1V; 101 | delay(3); // Settling time min 1ms 102 | getADC(); // discard first read 103 | } 104 | SerialBegin(); 105 | if (WDT_EN) { 106 | wdt_disable(); 107 | wdt_enable(WDTO_8S); 108 | } 109 | initialize_i2c(); 110 | LedBegin(); 111 | LedBlink(); 112 | LedBlink(); 113 | LedBlink(); 114 | } 115 | 116 | // --------------------------------------------------------------------- // 117 | // loop 118 | // --------------------------------------------------------------------- // 119 | 120 | void loop() { 121 | do_work(); 122 | millis(); // ????? For some reason need this to work the i2c 123 | #ifdef SERIAL_LOGGER 124 | if (SERIAL_LOGGER.available()) 125 | { 126 | #ifdef EEPROM_ADDRESS 127 | EEPROM.write(EEPROM_ADDRESS, SERIAL_LOGGER.parseInt()); 128 | #endif 129 | resetFunc(); 130 | } 131 | #endif 132 | TinyWireS_stop_check(); 133 | } 134 | 135 | // --------------------------------------------------------------------- // 136 | // run 137 | // --------------------------------------------------------------------- // 138 | 139 | boolean runEvery(unsigned long interval) 140 | { 141 | static unsigned long previousMillis = 0; 142 | unsigned long currentMillis = millis(); 143 | if (currentMillis - previousMillis >= interval) 144 | { 145 | previousMillis = currentMillis; 146 | return true; 147 | } 148 | return false; 149 | } 150 | 151 | // --------------------------------------------------------------------- // 152 | // work 153 | // --------------------------------------------------------------------- // 154 | 155 | void do_work() 156 | { 157 | if (working) 158 | { 159 | LedHigh(); 160 | SerialPrintln(buffer); 161 | 162 | if (buffer[0] == '&') 163 | { 164 | resetFunc(); 165 | } 166 | 167 | if (buffer[0] == 'g') { 168 | // i2c_cmd 169 | //pos 0123[4]5678 170 | // get,[c]rc8$ 171 | // get,[b]aton$ 172 | // get,[s]inglecore$ 173 | // get,[f]req$ 174 | char f = buffer[4]; 175 | switch (tolower(f)) { 176 | case 't': // temperature 177 | if (SENSOR_EN) ltoa(getTemperature(), buffer, 8); 178 | else strcpy_P(buffer, ZERO); 179 | SerialPrint("SENSOR_EN: "); 180 | break; 181 | case 'f': // i2c clock frequency 182 | ltoa(WIRE_CLOCK, buffer, 10); 183 | SerialPrint("WIRE_CLOCK: "); 184 | break; 185 | case 'c': // crc8 status 186 | if (CRC8_EN) strcpy_P(buffer, ONE); 187 | else strcpy_P(buffer, ZERO); 188 | SerialPrint("CRC8_EN: "); 189 | break; 190 | case 'n': // worker name 191 | strcpy_P(buffer, WK_NAME); 192 | SerialPrint("WORKER: "); 193 | break; 194 | default: 195 | strcpy_P(buffer, UNKN); 196 | SerialPrint("command: "); 197 | SerialPrintln(f); 198 | SerialPrint("response: "); 199 | } 200 | SerialPrintln(buffer); 201 | buffer_position = 0; 202 | buffer_length = strlen(buffer); 203 | working = false; 204 | jobdone = true; 205 | if (WDT_EN) wdt_reset(); 206 | return; 207 | } 208 | do_job(); 209 | } 210 | LedLow(); 211 | } 212 | 213 | void do_job() 214 | { 215 | unsigned long startTime = millis(); 216 | unsigned int job = work(); 217 | unsigned long endTime = millis(); 218 | unsigned int elapsedTime = endTime - startTime; 219 | //if (job<5) elapsedTime = job*(1<<2); 220 | 221 | memset(buffer, 0, sizeof(buffer)); 222 | char cstr[16]; 223 | 224 | // Job 225 | if (job == 0) 226 | strcpy(cstr,"#"); // re-request job 227 | else 228 | itoa(job, cstr, 10); 229 | strcpy(buffer, cstr); 230 | buffer[strlen(buffer)] = CHAR_DOT; 231 | 232 | // Time 233 | itoa(elapsedTime, cstr, 10); 234 | strcpy(buffer + strlen(buffer), cstr); 235 | strcpy_P(buffer + strlen(buffer), ZEROS); 236 | buffer[strlen(buffer)] = CHAR_DOT; 237 | 238 | // DUCOID 239 | strcpy_P(buffer + strlen(buffer), DUCOID); 240 | for (size_t i = 0; i < 8; i++) 241 | { 242 | itoa(UniqueID8[i], cstr, 16); 243 | if (UniqueID8[i] < 16) strcpy(buffer + strlen(buffer), "0"); 244 | strcpy(buffer + strlen(buffer), cstr); 245 | } 246 | 247 | #if CRC8_EN 248 | char gen_crc8[3]; 249 | buffer[strlen(buffer)] = CHAR_DOT; 250 | 251 | // CRC8 252 | itoa(crc8((uint8_t *)buffer, strlen(buffer)), gen_crc8, 10); 253 | strcpy(buffer + strlen(buffer), gen_crc8); 254 | #endif 255 | 256 | SerialPrintln(buffer); 257 | 258 | buffer_position = 0; 259 | buffer_length = strlen(buffer); 260 | working = false; 261 | jobdone = true; 262 | 263 | if (WDT_EN) wdt_reset(); 264 | } 265 | 266 | uint16_t work() 267 | { 268 | char delimiters[] = ","; 269 | char *lastHash = strtok(buffer, delimiters); 270 | char *newHash = strtok(NULL, delimiters); 271 | char *diff = strtok(NULL, delimiters); 272 | 273 | #if CRC8_EN 274 | char *received_crc8 = strtok(NULL, delimiters); 275 | // do crc8 checks here 276 | uint8_t job_length = 3; // 3 commas 277 | job_length += strlen(lastHash) + strlen(newHash) + strlen(diff); 278 | char buffer_temp[job_length+1]; 279 | strcpy(buffer_temp, lastHash); 280 | strcat(buffer_temp, delimiters); 281 | strcat(buffer_temp, newHash); 282 | strcat(buffer_temp, delimiters); 283 | strcat(buffer_temp, diff); 284 | strcat(buffer_temp, delimiters); 285 | 286 | if (atoi(received_crc8) != crc8((uint8_t *)buffer_temp,job_length)) { 287 | // data corrupted 288 | SerialPrintln("CRC8 mismatched. Abort.."); 289 | return 0; 290 | } 291 | #endif 292 | 293 | buffer_length = 0; 294 | buffer_position = 0; 295 | return work(lastHash, newHash, atoi(diff)); 296 | } 297 | 298 | //#define HTOI(c) ((c<='9')?(c-'0'):((c<='F')?(c-'A'+10):((c<='f')?(c-'a'+10):(0)))) 299 | #define HTOI(c) ((c<='9')?(c-'0'):((c<='f')?(c-'a'+10):(0))) 300 | #define TWO_HTOI(h, l) ((HTOI(h) << 4) + HTOI(l)) 301 | //byte HTOI(char c) {return ((c<='9')?(c-'0'):((c<='f')?(c-'a'+10):(0)));} 302 | //byte TWO_HTOI(char h, char l) {return ((HTOI(h) << 4) + HTOI(l));} 303 | 304 | void HEX_TO_BYTE(char * address, char * hex, uint8_t len) 305 | { 306 | for (uint8_t i = 0; i < len; i++) address[i] = TWO_HTOI(hex[2 * i], hex[2 * i + 1]); 307 | } 308 | 309 | // DUCO-S1A hasher 310 | uint16_t work(char * lastblockhash, char * newblockhash, uint8_t difficulty) 311 | { 312 | if (difficulty > 655) return 0; 313 | HEX_TO_BYTE(newblockhash, newblockhash, HASH_BUFFER_SIZE); 314 | for (uint16_t ducos1res = 0; ducos1res < difficulty * 100 + 1; ducos1res++) 315 | { 316 | Sha1.init(); 317 | Sha1.print(lastblockhash); 318 | Sha1.print(ducos1res); 319 | if (memcmp(Sha1.result(), newblockhash, HASH_BUFFER_SIZE) == 0) 320 | { 321 | return ducos1res; 322 | } 323 | if (WDT_EN) { 324 | if (runEvery(2000)) wdt_reset(); 325 | } 326 | } 327 | return 0; 328 | } 329 | 330 | // --------------------------------------------------------------------- // 331 | // i2c 332 | // --------------------------------------------------------------------- // 333 | 334 | void initialize_i2c(void) { 335 | address = ADDRESS_I2C; 336 | 337 | #ifdef EEPROM_ADDRESS 338 | address = EEPROM.read(EEPROM_ADDRESS); 339 | if (address == 0 || address > 127) { 340 | address = ADDRESS_I2C; 341 | EEPROM.write(EEPROM_ADDRESS, address); 342 | } 343 | #endif 344 | 345 | SerialPrint("Wire begin "); 346 | SerialPrintln(address); 347 | TinyWireS.begin(address); 348 | TinyWireS.onReceive(onReceiveJob); 349 | TinyWireS.onRequest(onRequestResult); 350 | } 351 | 352 | void onReceiveJob(uint8_t howMany) { 353 | if (howMany == 0) return; 354 | if (working) return; 355 | if (jobdone) return; 356 | 357 | char c = TinyWireS.read(); 358 | buffer[buffer_length++] = c; 359 | if (buffer_length == BUFFER_MAX) buffer_length--; 360 | if (c == CHAR_END || c == '$') { 361 | working = true; 362 | } 363 | while (TinyWireS.available()) { 364 | TinyWireS.read(); 365 | } 366 | } 367 | 368 | void onRequestResult() { 369 | char c = CHAR_END; 370 | if (jobdone) { 371 | c = buffer[buffer_position++]; 372 | if ( buffer_position == buffer_length) { 373 | jobdone = false; 374 | buffer_position = 0; 375 | buffer_length = 0; 376 | memset(buffer, 0, sizeof(buffer)); 377 | } 378 | } 379 | TinyWireS.write(c); 380 | } 381 | 382 | #if CRC8_EN 383 | // https://stackoverflow.com/questions/51731313/cross-platform-crc8-function-c-and-python-parity-check 384 | uint8_t crc8( uint8_t *addr, uint8_t len) { 385 | uint8_t crc=0; 386 | for (uint8_t i=0; i>= 1; 391 | if (mix) 392 | crc ^= 0x8C; 393 | inbyte >>= 1; 394 | } 395 | } 396 | return crc; 397 | } 398 | #endif 399 | 400 | // Calibration of the temperature sensor has to be changed for your own ATtiny85 401 | // per tech note: http://www.atmel.com/Images/doc8108.pdf 402 | uint16_t chipTemp(uint16_t raw) { 403 | return((raw - TEMPERATURE_OFFSET) / TEMPERATURE_COEFF); 404 | } 405 | 406 | // Common code for both sources of an ADC conversion 407 | uint16_t getADC() { 408 | ADCSRA |= _BV(ADSC); // Start conversion 409 | while((ADCSRA & _BV(ADSC))); // Wait until conversion is finished 410 | return ADC; // 10-bits 411 | } 412 | 413 | uint16_t getTemperature() { 414 | uint8_t vccIndex, vccGuess, vccLevel; 415 | uint16_t rawTemp=0, rawVcc; 416 | 417 | // Measure temperature 418 | ADCSRA |= _BV(ADEN); // Enable AD and start conversion 419 | ADMUX = 0xF | _BV( REFS1 ); // ADC4 (Temp Sensor) and Ref voltage = 1.1V; 420 | delay(3); // Settling time min 1ms. give it 3 421 | 422 | // acumulate ADC samples 423 | for (uint8_t i=0; i<16; i++) { 424 | rawTemp += getADC(); 425 | } 426 | rawTemp = rawTemp >> 4; // take median value 427 | 428 | ADCSRA &= ~(_BV(ADEN)); // disable ADC 429 | 430 | rawVcc = getRawVcc(); 431 | 432 | // probably using 3.3V Vcc if less than 4V 433 | if ((rawVcc * 10) < 40) { 434 | vccGuess = 33; 435 | vccLevel = 16; 436 | } 437 | // maybe 5V Vcc 438 | else { 439 | vccGuess = 50; 440 | vccLevel = 33; 441 | } 442 | //index 0..13 for vcc 1.7 ... 3.0 443 | //index 0..33 for vcc 1.7 ... 5.0 444 | vccIndex = min(max(17,(uint8_t)(rawVcc * 10)),vccGuess) - 17; 445 | 446 | // Temperature compensation using the chip voltage 447 | // with 3.0 V VCC is 1 lower than measured with 1.7 V VCC 448 | return chipTemp(rawTemp) + vccIndex / vccLevel; 449 | } 450 | 451 | uint16_t getRawVcc() { 452 | uint16_t rawVcc = 0; 453 | 454 | // Measure chip voltage (Vcc) 455 | ADCSRA |= _BV(ADEN); // Enable ADC 456 | ADMUX = 0x0c | _BV(REFS2); // Use Vcc as voltage reference, 457 | // bandgap reference as ADC input 458 | delay(3); // Settling time min 1 ms. give it 3 459 | 460 | // accumulate ADC samples 461 | for (uint8_t i=0; i<16; i++) { 462 | rawVcc += getADC(); 463 | } 464 | rawVcc = rawVcc >> 4; // take median value 465 | 466 | ADCSRA &= ~(_BV(ADEN)); // disable ADC 467 | 468 | // single ended conversion formula. ADC = Vin * 1024 / Vref, where Vin = 1.1V 469 | // 1.1 * (1<<5) = 35.20. take 35. fp calculation is expensive 470 | // divide using right shift 471 | rawVcc = (1024 * 35 / rawVcc) >> 5; 472 | 473 | return rawVcc; 474 | } 475 | -------------------------------------------------------------------------------- /DuinoCoin_RPI_Tiny_Slave/DuinoCoin_RPI_Tiny_Slave.ino: -------------------------------------------------------------------------------- 1 | /* 2 | DoinoCoin_Tiny_Slave.ino 3 | adapted from Luiz H. Cassettari 4 | by JK Rolling 5 | */ 6 | #include // https://github.com/ricaun/ArduinoUniqueID 7 | #include 8 | #include "duco_hash.h" 9 | 10 | /****************** USER MODIFICATION START ****************/ 11 | #define DEV_INDEX 0 12 | #define I2CS_START_ADDRESS 8 13 | #define I2CS_FIND_ADDR false 14 | #define WDT_EN false // recommended 'true', but do not turn on if using old bootloader 15 | #define CRC8_EN true 16 | #define LED_EN true 17 | #define SENSOR_EN true 18 | /****************** USER MODIFICATION END ******************/ 19 | /*---------------------------------------------------------*/ 20 | /****************** FINE TUNING START **********************/ 21 | #define LED_PIN LED_BUILTIN 22 | #define LED_BRIGHTNESS 255 // 1-255 23 | #define BLINK_SHARE_FOUND 1 24 | #define BLINK_SETUP_COMPLETE 2 25 | #define BLINK_BAD_CRC 3 26 | //#define SERIAL_LOGGER Serial 27 | #define I2CS_MAX 32 28 | #define WORKER_NAME "atmega328p" 29 | #define WIRE_CLOCK 100000 30 | #define TEMPERATURE_OFFSET 338 31 | #define FILTER_LP 0.1 32 | /****************** FINE TUNING END ************************/ 33 | 34 | #if WDT_EN 35 | #include 36 | #endif 37 | 38 | // ATtiny85 - http://drazzy.com/package_drazzy.com_index.json 39 | // SCL - PB2 - 2 40 | // SDA - PB0 - 0 41 | 42 | // Nano/UNO 43 | // SCL - A5 44 | // SDA - A4 45 | 46 | #ifdef SERIAL_LOGGER 47 | #define SerialBegin() SERIAL_LOGGER.begin(115200); 48 | #define SerialPrint(x) SERIAL_LOGGER.print(x); 49 | #define SerialPrintln(x) SERIAL_LOGGER.println(x); 50 | #else 51 | #define SerialBegin() 52 | #define SerialPrint(x) 53 | #define SerialPrintln(x) 54 | #endif 55 | 56 | #define BUFFER_MAX 90 57 | #define HASH_BUFFER_SIZE 20 58 | #define CHAR_END '\n' 59 | #define CHAR_DOT ',' 60 | 61 | static const char DUCOID[] PROGMEM = "DUCOID"; 62 | static const char ZEROS[] PROGMEM = "000"; 63 | static const char WK_NAME[] PROGMEM = WORKER_NAME; 64 | static const char UNKN[] PROGMEM = "unkn"; 65 | static const char ONE[] PROGMEM = "1"; 66 | static const char ZERO[] PROGMEM = "0"; 67 | 68 | static byte address; 69 | static char buffer[BUFFER_MAX]; 70 | static uint8_t buffer_position; 71 | static uint8_t buffer_length; 72 | static bool working; 73 | static bool jobdone; 74 | static double temperature_filter; 75 | 76 | void(* resetFunc) (void) = 0;//declare reset function at address 0 77 | void Blink(uint8_t count, uint8_t pin, uint8_t dly); 78 | 79 | // --------------------------------------------------------------------- // 80 | // setup 81 | // --------------------------------------------------------------------- // 82 | void setup() { 83 | SerialBegin(); 84 | 85 | if (SENSOR_EN) { 86 | analogReference (INTERNAL); 87 | temperature_filter = readTemperature(); 88 | } 89 | 90 | if (LED_EN) { 91 | pinMode(LED_PIN, OUTPUT); 92 | } 93 | 94 | #if WDT_EN 95 | wdt_disable(); 96 | wdt_enable(WDTO_4S); 97 | #endif 98 | 99 | initialize_i2c(); 100 | 101 | Blink(BLINK_SETUP_COMPLETE, LED_PIN, 50); 102 | SerialPrintln("Startup Done!"); 103 | } 104 | 105 | // --------------------------------------------------------------------- // 106 | // loop 107 | // --------------------------------------------------------------------- // 108 | 109 | void loop() { 110 | do_work(); 111 | } 112 | 113 | // --------------------------------------------------------------------- // 114 | // run 115 | // --------------------------------------------------------------------- // 116 | boolean runEvery(unsigned long interval) 117 | { 118 | static unsigned long previousMillis = 0; 119 | unsigned long currentMillis = millis(); 120 | if (currentMillis - previousMillis >= interval) 121 | { 122 | previousMillis = currentMillis; 123 | return true; 124 | } 125 | return false; 126 | } 127 | 128 | // --------------------------------------------------------------------- // 129 | // work 130 | // --------------------------------------------------------------------- // 131 | void do_work() 132 | { 133 | if (working) 134 | { 135 | led_on(); 136 | SerialPrintln(buffer); 137 | 138 | if (buffer[0] == '&') { 139 | resetFunc(); 140 | } 141 | 142 | // worker related command 143 | if (buffer[0] == 'g') { 144 | // i2c_cmd 145 | //pos 0123[4]5678 146 | // get,[c]rc8$ 147 | // get,[b]aton$ 148 | // get,[s]inglecore$ 149 | // get,[f]req$ 150 | char f = buffer[4]; 151 | switch (tolower(f)) { 152 | case 't': // temperature 153 | if (SENSOR_EN) { 154 | temperature_filter = ( FILTER_LP * readTemperature()) + (( 1.0 - FILTER_LP ) * temperature_filter); 155 | ltoa(temperature_filter, buffer, 8); 156 | } 157 | else strcpy_P(buffer, ZERO); 158 | SerialPrint("SENSOR_EN: "); 159 | break; 160 | case 'f': // i2c clock frequency 161 | ltoa(WIRE_CLOCK, buffer, 10); 162 | SerialPrint("WIRE_CLOCK: "); 163 | break; 164 | case 'c': // crc8 status 165 | if (CRC8_EN) strcpy_P(buffer, ONE); 166 | else strcpy_P(buffer, ZERO); 167 | SerialPrint("CRC8_EN: "); 168 | break; 169 | case 'n': // worker name 170 | strcpy_P(buffer, WK_NAME); 171 | SerialPrint("WORKER: "); 172 | break; 173 | default: 174 | strcpy_P(buffer, UNKN); 175 | SerialPrint("command: "); 176 | SerialPrintln(f); 177 | SerialPrint("response: "); 178 | } 179 | SerialPrintln(buffer); 180 | buffer_position = 0; 181 | buffer_length = strlen(buffer); 182 | working = false; 183 | jobdone = true; 184 | #if WDT_EN 185 | wdt_reset(); 186 | #endif 187 | return; 188 | } 189 | else if (buffer[0] == 's') { 190 | // not used at the moment 191 | } 192 | 193 | #if I2CS_FIND_ADDR 194 | if (buffer[0] == '@') { 195 | address = find_i2c(); 196 | memset(buffer, 0, sizeof(buffer)); 197 | buffer_position = 0; 198 | buffer_length = 0; 199 | working = false; 200 | jobdone = false; 201 | return; 202 | } 203 | #endif 204 | 205 | do_job(); 206 | } 207 | led_off(); 208 | } 209 | 210 | void do_job() 211 | { 212 | unsigned long startTime = millis(); 213 | int job = work(); 214 | unsigned long endTime = millis(); 215 | unsigned int elapsedTime = endTime - startTime; 216 | if (job<5) elapsedTime = job*(1<<2); 217 | 218 | memset(buffer, 0, sizeof(buffer)); 219 | char cstr[16]; 220 | 221 | // Job 222 | if (job == 0) 223 | strcpy(cstr,"#"); // re-request job 224 | else 225 | itoa(job, cstr, 10); 226 | strcpy(buffer, cstr); 227 | buffer[strlen(buffer)] = CHAR_DOT; 228 | 229 | // Time 230 | itoa(elapsedTime, cstr, 10); 231 | strcpy(buffer + strlen(buffer), cstr); 232 | strcpy_P(buffer + strlen(buffer), ZEROS); 233 | buffer[strlen(buffer)] = CHAR_DOT; 234 | 235 | // DUCOID 236 | strcpy_P(buffer + strlen(buffer), DUCOID); 237 | for (size_t i = 0; i < 8; i++) 238 | { 239 | itoa(UniqueID8[i], cstr, 16); 240 | if (UniqueID8[i] < 16) strcpy(buffer + strlen(buffer), "0"); 241 | strcpy(buffer + strlen(buffer), cstr); 242 | } 243 | 244 | #if CRC8_EN 245 | char gen_crc8[3]; 246 | buffer[strlen(buffer)] = CHAR_DOT; 247 | 248 | // CRC8 249 | itoa(crc8((uint8_t *)buffer, strlen(buffer)), gen_crc8, 10); 250 | strcpy(buffer + strlen(buffer), gen_crc8); 251 | #endif 252 | 253 | SerialPrintln(buffer); 254 | 255 | buffer_position = 0; 256 | buffer_length = strlen(buffer); 257 | working = false; 258 | jobdone = true; 259 | 260 | #if WDT_EN 261 | wdt_reset(); 262 | #endif 263 | } 264 | 265 | int work() 266 | { 267 | 268 | #if CRC8_EN 269 | char *delim_ptr = strchr(buffer, CHAR_DOT); 270 | delim_ptr = strchr(delim_ptr+1, CHAR_DOT); 271 | uint8_t job_length = strchr(delim_ptr+1, CHAR_DOT) - &buffer[0] + 1; 272 | uint8_t calc_crc8 = crc8((uint8_t *)buffer, job_length); 273 | #endif 274 | 275 | // tokenize 276 | const char *delim = ","; 277 | char *lastHash = strtok(buffer, delim); 278 | char *newHash = strtok(NULL, delim); 279 | char *diff = strtok(NULL, delim); 280 | 281 | // validate integrity 282 | #if CRC8_EN 283 | char *rx_crc8 = strtok(NULL, delim); 284 | if (calc_crc8 != atoi(rx_crc8)) { 285 | // data corrupted 286 | SerialPrintln("CRC8 mismatched. Abort.."); 287 | return 0; 288 | } 289 | #endif 290 | 291 | buffer_length = 0; 292 | buffer_position = 0; 293 | return work(lastHash, newHash, atoi(diff)); 294 | } 295 | 296 | //#define HTOI(c) ((c<='9')?(c-'0'):((c<='F')?(c-'A'+10):((c<='f')?(c-'a'+10):(0)))) 297 | #define HTOI(c) ((c<='9')?(c-'0'):((c<='f')?(c-'a'+10):(0))) 298 | #define TWO_HTOI(h, l) ((HTOI(h) << 4) + HTOI(l)) 299 | //byte HTOI(char c) {return ((c<='9')?(c-'0'):((c<='f')?(c-'a'+10):(0)));} 300 | //byte TWO_HTOI(char h, char l) {return ((HTOI(h) << 4) + HTOI(l));} 301 | 302 | void HEX_TO_BYTE(char * address, char * hex, int len) 303 | { 304 | for (uint8_t i = 0; i < len; i++) address[i] = TWO_HTOI(hex[2 * i], hex[2 * i + 1]); 305 | } 306 | 307 | // DUCO-S1A hasher 308 | uint32_t work(char * lastblockhash, char * newblockhash, int difficulty) 309 | { 310 | if (difficulty > 655) return 0; 311 | HEX_TO_BYTE(newblockhash, newblockhash, HASH_BUFFER_SIZE); 312 | static duco_hash_state_t hash; 313 | duco_hash_init(&hash, lastblockhash); 314 | 315 | char nonceStr[10 + 1]; 316 | for (int nonce = 0; nonce < difficulty*100+1; nonce++) { 317 | ultoa(nonce, nonceStr, 10); 318 | 319 | uint8_t const * hash_bytes = duco_hash_try_nonce(&hash, nonceStr); 320 | if (memcmp(hash_bytes, newblockhash, HASH_BUFFER_SIZE) == 0) { 321 | return nonce; 322 | } 323 | #if WDT_EN 324 | if (runEvery(2000)) 325 | wdt_reset(); 326 | #endif 327 | } 328 | 329 | return 0; 330 | } 331 | 332 | // --------------------------------------------------------------------- // 333 | // i2c 334 | // --------------------------------------------------------------------- // 335 | 336 | void initialize_i2c(void) { 337 | address = DEV_INDEX + I2CS_START_ADDRESS; 338 | 339 | #if I2CS_FIND_ADDR 340 | address = find_i2c(); 341 | #endif 342 | 343 | Wire.setClock(WIRE_CLOCK); 344 | Wire.begin(address); 345 | Wire.onReceive(onReceiveJob); 346 | Wire.onRequest(onRequestResult); 347 | 348 | SerialPrint("Wire begin "); 349 | SerialPrintln(address); 350 | } 351 | 352 | void onReceiveJob(int howMany) { 353 | if (howMany == 0) return; 354 | if (working) return; 355 | if (jobdone) return; 356 | 357 | for (int i=0; i < howMany; i++) { 358 | if (i == 0) { 359 | char c = Wire.read(); 360 | buffer[buffer_length++] = c; 361 | if (buffer_length == BUFFER_MAX) buffer_length--; 362 | if ((c == CHAR_END) || (c == '$')) 363 | { 364 | working = true; 365 | } 366 | } 367 | else { 368 | // dump the rest 369 | Wire.read(); 370 | } 371 | } 372 | } 373 | 374 | void onRequestResult() { 375 | char c = CHAR_END; 376 | if (jobdone) { 377 | c = buffer[buffer_position++]; 378 | if ( buffer_position == buffer_length) { 379 | jobdone = false; 380 | buffer_position = 0; 381 | buffer_length = 0; 382 | memset(buffer, 0, sizeof(buffer)); 383 | } 384 | } 385 | Wire.write(c); 386 | } 387 | 388 | // --------------------------------------------------------------------- // 389 | // I2CS_FIND_ADDR 390 | // --------------------------------------------------------------------- // 391 | #if I2CS_FIND_ADDR 392 | byte find_i2c() 393 | { 394 | address = I2CS_START_ADDRESS; 395 | unsigned long time = (unsigned long) getTrueRotateRandomByte() * 1000 + (unsigned long) getTrueRotateRandomByte(); 396 | delayMicroseconds(time); 397 | Wire.setClock(WIRE_CLOCK); 398 | Wire.begin(); 399 | int address; 400 | for (address = I2CS_START_ADDRESS; address < I2CS_MAX; address++) 401 | { 402 | Wire.beginTransmission(address); 403 | int error = Wire.endTransmission(); 404 | if (error != 0) 405 | { 406 | break; 407 | } 408 | } 409 | Wire.end(); 410 | return address; 411 | } 412 | 413 | // --------------------------------------------------------------- 414 | // True Random Numbers 415 | // https://gist.github.com/bloc97/b55f684d17edd8f50df8e918cbc00f94 416 | // --------------------------------------------------------------- 417 | #if defined(ARDUINO_AVR_PRO) 418 | #define ANALOG_RANDOM A6 419 | #else 420 | #define ANALOG_RANDOM A1 421 | #endif 422 | 423 | const int waitTime = 16; 424 | 425 | byte lastByte = 0; 426 | byte leftStack = 0; 427 | byte rightStack = 0; 428 | 429 | byte rotate(byte b, int r) { 430 | return (b << r) | (b >> (8 - r)); 431 | } 432 | 433 | void pushLeftStack(byte bitToPush) { 434 | leftStack = (leftStack << 1) ^ bitToPush ^ leftStack; 435 | } 436 | 437 | void pushRightStackRight(byte bitToPush) { 438 | rightStack = (rightStack >> 1) ^ (bitToPush << 7) ^ rightStack; 439 | } 440 | 441 | byte getTrueRotateRandomByte() { 442 | byte finalByte = 0; 443 | 444 | byte lastStack = leftStack ^ rightStack; 445 | 446 | for (int i = 0; i < 4; i++) { 447 | delayMicroseconds(waitTime); 448 | int leftBits = analogRead(ANALOG_RANDOM); 449 | 450 | delayMicroseconds(waitTime); 451 | int rightBits = analogRead(ANALOG_RANDOM); 452 | 453 | finalByte ^= rotate(leftBits, i); 454 | finalByte ^= rotate(rightBits, 7 - i); 455 | 456 | for (int j = 0; j < 8; j++) { 457 | byte leftBit = (leftBits >> j) & 1; 458 | byte rightBit = (rightBits >> j) & 1; 459 | 460 | if (leftBit != rightBit) { 461 | if (lastStack % 2 == 0) { 462 | pushLeftStack(leftBit); 463 | } else { 464 | pushRightStackRight(leftBit); 465 | } 466 | } 467 | } 468 | 469 | } 470 | lastByte ^= (lastByte >> 3) ^ (lastByte << 5) ^ (lastByte >> 4); 471 | lastByte ^= finalByte; 472 | 473 | return lastByte ^ leftStack ^ rightStack; 474 | } 475 | #endif 476 | 477 | #if CRC8_EN 478 | // https://stackoverflow.com/questions/51731313/cross-platform-crc8-function-c-and-python-parity-check 479 | uint8_t crc8( uint8_t *addr, uint8_t len) { 480 | uint8_t crc=0; 481 | for (uint8_t i=0; i>= 1; 486 | if (mix) 487 | crc ^= 0x8C; 488 | inbyte >>= 1; 489 | } 490 | } 491 | return crc; 492 | } 493 | #endif 494 | 495 | void led_on() { 496 | if (!LED_EN) return; 497 | digitalWrite(LED_PIN, HIGH); 498 | } 499 | 500 | void led_off() { 501 | if (!LED_EN) return; 502 | digitalWrite(LED_PIN, LOW); 503 | } 504 | 505 | void Blink(uint8_t count, uint8_t pin = LED_BUILTIN, uint8_t dly = 50) { 506 | if (!LED_EN) return; 507 | uint8_t state = LOW; 508 | 509 | for (int x=0; x<(count << 1); ++x) { 510 | analogWrite(pin, state ^= LED_BRIGHTNESS); 511 | delay(dly); 512 | } 513 | } 514 | 515 | static int readTemperature() { 516 | ADMUX = 0xC8; // activate interal temperature sensor, 517 | // using 1.1V ref. voltage 518 | ADCSRA |= _BV(ADSC); // start the conversion 519 | while (bit_is_set(ADCSRA, ADSC)); // ADSC is cleared when the conversion 520 | // finishes 521 | 522 | // combine bytes & correct for temperature offset (approximate) 523 | return ( (ADCL | (ADCH << 8)) - TEMPERATURE_OFFSET); 524 | } 525 | -------------------------------------------------------------------------------- /AVR_Miner_RPI_TB.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | RPI I2C Unofficial AVR Miner 3.4 © MIT licensed 4 | Modified by JK-Rolling 5 | 20210919 6 | 7 | Full credit belong to 8 | https://duinocoin.com 9 | https://github.com/revoxhere/duino-coin 10 | Duino-Coin Team & Community 2019-current 11 | """ 12 | 13 | from os import _exit, mkdir 14 | from os import name as osname 15 | from os import path 16 | from os import system as ossystem 17 | from platform import machine as osprocessor 18 | from platform import system 19 | import sys 20 | 21 | from configparser import ConfigParser 22 | from pathlib import Path 23 | 24 | from json import load as jsonload 25 | from random import choice 26 | from locale import LC_ALL, getdefaultlocale, getlocale, setlocale 27 | 28 | from re import sub 29 | from socket import socket 30 | from datetime import datetime 31 | from statistics import mean 32 | from signal import SIGINT, signal 33 | from time import ctime, sleep, strptime, time 34 | import pip 35 | 36 | from subprocess import DEVNULL, Popen, check_call, call 37 | from threading import Thread 38 | from threading import Lock as thread_lock 39 | from threading import Semaphore 40 | 41 | import base64 as b64 42 | 43 | import os 44 | import random 45 | printlock = Semaphore(value=1) 46 | i2clock = Semaphore(value=1) 47 | 48 | 49 | # Python <3.5 check 50 | f"Your Python version is too old. Duino-Coin Miner requires version 3.6 or above. Update your packages and try again" 51 | 52 | 53 | def install(package): 54 | try: 55 | pip.main(["install", package]) 56 | except AttributeError: 57 | check_call([sys.executable, '-m', 'pip', 'install', package]) 58 | call([sys.executable, __file__]) 59 | 60 | try: 61 | from smbus import SMBus 62 | except ModuleNotFoundError: 63 | print("SMBus is not installed. " 64 | + "Miner will try to automatically install it " 65 | + "If it fails, please manually execute " 66 | + "python3 -m pip install smbus") 67 | install('smbus') 68 | 69 | try: 70 | import requests 71 | except ModuleNotFoundError: 72 | print("Requests is not installed. " 73 | + "Miner will try to automatically install it " 74 | + "If it fails, please manually execute " 75 | + "python3 -m pip install requests") 76 | install('requests') 77 | 78 | try: 79 | from colorama import Back, Fore, Style, init 80 | init(autoreset=True) 81 | except ModuleNotFoundError: 82 | print("Colorama is not installed. " 83 | + "Miner will try to automatically install it " 84 | + "If it fails, please manually execute " 85 | + "python3 -m pip install colorama") 86 | install("colorama") 87 | 88 | try: 89 | from pypresence import Presence 90 | except ModuleNotFoundError: 91 | print("Pypresence is not installed. " 92 | + "Miner will try to automatically install it " 93 | + "If it fails, please manually execute " 94 | + "python3 -m pip install pypresence") 95 | install("pypresence") 96 | 97 | 98 | def now(): 99 | return datetime.now() 100 | 101 | 102 | def port_num(com): 103 | #return str(''.join(filter(str.isdigit, com))) 104 | return "{:02x}".format(int(com,16)) 105 | 106 | 107 | class Settings: 108 | VER = '3.5' 109 | SOC_TIMEOUT = 15 110 | REPORT_TIME = 120 111 | AVR_TIMEOUT = 3 # diff 6 * 100 / 268 h/s = 2.24 s 112 | DELAY_START = 30 # 30 seconds start delay between worker to help kolka sync efficiency drop 113 | IoT_EN = "n" 114 | DATA_DIR = "Duino-Coin AVR Miner " + str(VER) 115 | SEPARATOR = "," 116 | ENCODING = "utf-8" 117 | I2C_WR_RDDCY = 2 118 | WORKER_CFG_SHARED = "y" 119 | try: 120 | # Raspberry Pi latin users can't display this character 121 | "‖".encode(sys.stdout.encoding) 122 | BLOCK = " ‖ " 123 | except: 124 | BLOCK = " | " 125 | PICK = "" 126 | COG = " @" 127 | if (osname != "nt" 128 | or bool(osname == "nt" 129 | and os.environ.get("WT_SESSION"))): 130 | # Windows' cmd does not support emojis, shame! 131 | # And some codecs same, for example the Latin-1 encoding don`t support emoji 132 | try: 133 | "⛏ ⚙".encode(sys.stdout.encoding) # if the terminal support emoji 134 | PICK = " ⛏" 135 | COG = " ⚙" 136 | except UnicodeEncodeError: # else 137 | PICK = "" 138 | COG = " @" 139 | 140 | def check_mining_key(user_settings): 141 | user_settings = user_settings["AVR Miner"] 142 | 143 | if user_settings["mining_key"] == "None": 144 | # user manually reset .cfg file key. re-encode it 145 | mining_key = user_settings["mining_key"] 146 | user_settings["mining_key"] = b64.b64encode(mining_key.encode("utf-8")).decode('utf-8') 147 | config["AVR Miner"] = user_settings 148 | with open(Settings.DATA_DIR + '/Settings.cfg', 149 | "w") as configfile: 150 | config.write(configfile) 151 | print("sys0", 152 | Style.RESET_ALL + get_string("config_saved"), 153 | "info") 154 | sleep(1.5) 155 | 156 | key = b64.b64decode(user_settings["mining_key"]).decode('utf-8') 157 | response = requests.get( 158 | "https://server.duinocoin.com/mining_key" 159 | + "?u=" + user_settings["username"] 160 | + "&k=" + key, 161 | timeout=10 162 | ).json() 163 | 164 | if not response["success"]: 165 | pretty_print( 166 | "sys0", 167 | get_string("invalid_mining_key"), 168 | "error") 169 | 170 | retry = input("You want to retry? (y/n): ") 171 | if retry == "y" or retry == "Y": 172 | mining_key = input("Enter your mining key: ") 173 | user_settings["mining_key"] = b64.b64encode(mining_key.encode("utf-8")).decode('utf-8') 174 | config["AVR Miner"] = user_settings 175 | 176 | with open(Settings.DATA_DIR + '/Settings.cfg', 177 | "w") as configfile: 178 | config.write(configfile) 179 | print("sys0", 180 | Style.RESET_ALL + get_string("config_saved"), 181 | "info") 182 | sleep(1.5) 183 | check_mining_key(config) 184 | else: 185 | return 186 | 187 | 188 | class Client: 189 | """ 190 | Class helping to organize socket connections 191 | """ 192 | def connect(pool: tuple): 193 | s = socket() 194 | s.settimeout(Settings.SOC_TIMEOUT) 195 | s.connect((pool)) 196 | return s 197 | 198 | def send(s, msg: str): 199 | sent = s.sendall(str(msg).encode(Settings.ENCODING)) 200 | return True 201 | 202 | def recv(s, limit: int = 128): 203 | data = s.recv(limit).decode(Settings.ENCODING).rstrip("\n") 204 | return data 205 | 206 | def fetch_pool(): 207 | while True: 208 | pretty_print("net0", " " + get_string("connection_search"), 209 | "info") 210 | try: 211 | response = requests.get( 212 | "https://server.duinocoin.com/getPool", 213 | timeout=10).json() 214 | 215 | if response["success"] == True: 216 | pretty_print("net0", get_string("connecting_node") 217 | + response["name"], 218 | "info") 219 | 220 | NODE_ADDRESS = response["ip"] 221 | NODE_PORT = response["port"] 222 | debug_output(f"Fetched pool: {response['name']}") 223 | return (NODE_ADDRESS, NODE_PORT) 224 | 225 | elif "message" in response: 226 | pretty_print(f"Warning: {response['message']}" 227 | + ", retrying in 15s", "warning", "net0") 228 | sleep(15) 229 | else: 230 | raise Exception( 231 | "no response - IP ban or connection error") 232 | except Exception as e: 233 | if "Expecting value" in str(e): 234 | pretty_print("net0", get_string("node_picker_unavailable") 235 | + f"15s {Style.RESET_ALL}({e})", 236 | "warning") 237 | else: 238 | pretty_print("net0", get_string("node_picker_error") 239 | + f"15s {Style.RESET_ALL}({e})", 240 | "error") 241 | sleep(15) 242 | 243 | 244 | class Donate: 245 | def load(donation_level): 246 | if donation_level > 0: 247 | if osname == 'nt': 248 | if not Path( 249 | f"{Settings.DATA_DIR}/Donate.exe").is_file(): 250 | url = ('https://server.duinocoin.com/' 251 | + 'donations/DonateExecutableWindows.exe') 252 | r = requests.get(url, timeout=15) 253 | with open(f"{Settings.DATA_DIR}/Donate.exe", 254 | 'wb') as f: 255 | f.write(r.content) 256 | elif osname == "posix": 257 | if osprocessor() == "aarch64": 258 | url = ('https://server.duinocoin.com/' 259 | + 'donations/DonateExecutableAARCH64') 260 | elif osprocessor() == "armv7l": 261 | url = ('https://server.duinocoin.com/' 262 | + 'donations/DonateExecutableAARCH32') 263 | else: 264 | url = ('https://server.duinocoin.com/' 265 | + 'donations/DonateExecutableLinux') 266 | if not Path( 267 | f"{Settings.DATA_DIR}/Donate").is_file(): 268 | r = requests.get(url, timeout=15) 269 | with open(f"{Settings.DATA_DIR}/Donate", 270 | "wb") as f: 271 | f.write(r.content) 272 | 273 | def start(donation_level): 274 | donation_settings = requests.get( 275 | "https://server.duinocoin.com/donations/settings.json").json() 276 | 277 | if os.name == 'nt': 278 | cmd = (f'cd "{Settings.DATA_DIR}" & Donate.exe ' 279 | + f'-o {donation_settings["url"]} ' 280 | + f'-u {donation_settings["user"]} ' 281 | + f'-p {donation_settings["pwd"]} ' 282 | + f'-s 4 -e {donation_level*2}') 283 | elif os.name == 'posix': 284 | cmd = (f'cd "{Settings.DATA_DIR}" && chmod +x Donate ' 285 | + '&& nice -20 ./Donate ' 286 | + f'-o {donation_settings["url"]} ' 287 | + f'-u {donation_settings["user"]} ' 288 | + f'-p {donation_settings["pwd"]} ' 289 | + f'-s 4 -e {donation_level*2}') 290 | 291 | if donation_level <= 0: 292 | pretty_print( 293 | 'sys0', Fore.YELLOW 294 | + get_string('free_network_warning').lstrip() 295 | + get_string('donate_warning').replace("\n", "\n\t\t") 296 | + Fore.GREEN + 'https://duinocoin.com/donate' 297 | + Fore.YELLOW + get_string('learn_more_donate'), 298 | 'warning') 299 | sleep(5) 300 | 301 | if donation_level > 0: 302 | debug_output(get_string('starting_donation')) 303 | donateExecutable = Popen(cmd, shell=True, stderr=DEVNULL) 304 | pretty_print('sys0', 305 | get_string('thanks_donation').replace("\n", "\n\t\t"), 306 | 'error') 307 | 308 | 309 | shares = [0, 0, 0] 310 | bad_crc8 = 0 311 | i2c_retry_count = 0 312 | hashrate_mean = [] 313 | ping_mean = [] 314 | diff = 0 315 | shuffle_ports = "y" 316 | donator_running = False 317 | job = '' 318 | debug = 'n' 319 | discord_presence = 'y' 320 | rig_identifier = 'None' 321 | donation_level = 0 322 | hashrate = 0 323 | config = ConfigParser() 324 | mining_start_time = time() 325 | worker_cfg_global = {"valid":False} 326 | 327 | if not path.exists(Settings.DATA_DIR): 328 | mkdir(Settings.DATA_DIR) 329 | 330 | if not Path(Settings.DATA_DIR + '/Translations.json').is_file(): 331 | url = ('https://raw.githubusercontent.com/' 332 | + 'revoxhere/' 333 | + 'duino-coin/master/Resources/' 334 | + 'AVR_Miner_langs.json') 335 | r = requests.get(url, timeout=5) 336 | with open(Settings.DATA_DIR + '/Translations.json', 'wb') as f: 337 | f.write(r.content) 338 | 339 | # Load language file 340 | with open(Settings.DATA_DIR + '/Translations.json', 'r', 341 | encoding='utf8') as lang_file: 342 | lang_file = jsonload(lang_file) 343 | 344 | # OS X invalid locale hack 345 | if system() == 'Darwin': 346 | if getlocale()[0] is None: 347 | setlocale(LC_ALL, 'en_US.UTF-8') 348 | 349 | try: 350 | if not Path(Settings.DATA_DIR + '/Settings.cfg').is_file(): 351 | locale = getdefaultlocale()[0] 352 | if locale.startswith('es'): 353 | lang = 'spanish' 354 | elif locale.startswith('sk'): 355 | lang = 'slovak' 356 | elif locale.startswith('ru'): 357 | lang = 'russian' 358 | elif locale.startswith('pl'): 359 | lang = 'polish' 360 | elif locale.startswith('de'): 361 | lang = 'german' 362 | elif locale.startswith('fr'): 363 | lang = 'french' 364 | elif locale.startswith('tr'): 365 | lang = 'turkish' 366 | elif locale.startswith('it'): 367 | lang = 'italian' 368 | elif locale.startswith('pt'): 369 | lang = 'portuguese' 370 | elif locale.startswith('zh'): 371 | lang = 'chinese_simplified' 372 | elif locale.startswith('th'): 373 | lang = 'thai' 374 | elif locale.startswith('az'): 375 | lang = 'azerbaijani' 376 | elif locale.startswith('nl'): 377 | lang = 'dutch' 378 | elif locale.startswith('ko'): 379 | lang = 'korean' 380 | elif locale.startswith("id"): 381 | lang = "indonesian" 382 | elif locale.startswith("cz"): 383 | lang = "czech" 384 | else: 385 | lang = 'english' 386 | else: 387 | try: 388 | config.read(Settings.DATA_DIR + '/Settings.cfg') 389 | lang = config["AVR Miner"]['language'] 390 | except Exception: 391 | lang = 'english' 392 | except: 393 | lang = 'english' 394 | 395 | 396 | def get_string(string_name: str): 397 | if string_name in lang_file[lang]: 398 | return lang_file[lang][string_name] 399 | elif string_name in lang_file['english']: 400 | return lang_file['english'][string_name] 401 | else: 402 | return string_name 403 | 404 | 405 | def get_prefix(symbol: str, 406 | val: float, 407 | accuracy: int): 408 | """ 409 | H/s, 1000 => 1 kH/s 410 | """ 411 | if val >= 1_000_000_000_000: # Really? 412 | val = str(round((val / 1_000_000_000_000), accuracy)) + " T" 413 | elif val >= 1_000_000_000: 414 | val = str(round((val / 1_000_000_000), accuracy)) + " G" 415 | elif val >= 1_000_000: 416 | val = str(round((val / 1_000_000), accuracy)) + " M" 417 | elif val >= 1_000: 418 | val = str(round((val / 1_000))) + " k" 419 | else: 420 | if symbol: 421 | val = str(round(val)) + " " 422 | else: 423 | val = str(round(val)) 424 | return val + symbol 425 | 426 | 427 | def debug_output(text: str): 428 | if debug == 'y': 429 | print(Style.RESET_ALL + Fore.WHITE 430 | + now().strftime(Style.DIM + '%H:%M:%S.%f ') 431 | + Style.NORMAL + f'DEBUG: {text}') 432 | 433 | def ondemand_print(text: str): 434 | print(Style.RESET_ALL + Fore.WHITE 435 | + now().strftime(Style.DIM + '%H:%M:%S.%f ') 436 | + Style.NORMAL + f'DEBUG: {text}') 437 | 438 | def title(title: str): 439 | if osname == 'nt': 440 | """ 441 | Changing the title in Windows' cmd 442 | is easy - just use the built-in 443 | title command 444 | """ 445 | ossystem('title ' + title) 446 | else: 447 | """ 448 | Most *nix terminals use 449 | this escape sequence to change 450 | the console window title 451 | """ 452 | try: 453 | print('\33]0;' + title + '\a', end='') 454 | sys.stdout.flush() 455 | except Exception as e: 456 | print(e) 457 | 458 | 459 | def handler(signal_received, frame): 460 | pretty_print( 461 | 'sys0', get_string('sigint_detected') 462 | + Style.NORMAL + Fore.RESET 463 | + get_string('goodbye'), 'warning') 464 | 465 | _exit(0) 466 | 467 | 468 | # Enable signal handler 469 | signal(SIGINT, handler) 470 | 471 | 472 | def load_config(): 473 | global username 474 | global donation_level 475 | global avrport 476 | global hashrate_list 477 | global debug 478 | global rig_identifier 479 | global discord_presence 480 | global shuffle_ports 481 | global SOC_TIMEOUT 482 | global i2c 483 | 484 | if not Path(str(Settings.DATA_DIR) + '/Settings.cfg').is_file(): 485 | print( 486 | Style.BRIGHT + get_string('basic_config_tool') 487 | + Settings.DATA_DIR 488 | + get_string('edit_config_file_warning')) 489 | 490 | print( 491 | Style.RESET_ALL + get_string('dont_have_account') 492 | + Fore.YELLOW + get_string('wallet') + Fore.RESET 493 | + get_string('register_warning')) 494 | 495 | correct_username = False 496 | while not correct_username: 497 | username = input( 498 | Style.RESET_ALL + Fore.YELLOW 499 | + get_string('ask_username') 500 | + Fore.RESET + Style.BRIGHT) 501 | if not username: 502 | username = choice(["revox", "Bilaboz"]) 503 | 504 | #jk r = requests.get(f"https://server.duinocoin.com/users/{username}", 505 | #jk timeout=Settings.SOC_TIMEOUT).json() 506 | #jk correct_username = r["success"] 507 | #jk if not correct_username: 508 | #jk print(get_string("incorrect_username")) 509 | correct_username = True 510 | 511 | mining_key = input(Style.RESET_ALL + Fore.YELLOW 512 | + get_string("ask_mining_key") 513 | + Fore.RESET + Style.BRIGHT) 514 | if not mining_key: 515 | mining_key = "None" 516 | mining_key = b64.b64encode(mining_key.encode("utf-8")).decode('utf-8') 517 | 518 | i2c = input( 519 | Style.RESET_ALL + Fore.YELLOW 520 | + 'Enter your choice of I2C bus (e.g. 1): ' 521 | + Fore.RESET + Style.BRIGHT) 522 | i2c = int(i2c) 523 | 524 | if ossystem(f'i2cdetect -y {i2c}') != 0: 525 | print(Style.RESET_ALL + Fore.RED 526 | + 'I2C is disabled. Exiting..') 527 | _exit(1) 528 | else : 529 | print(Style.RESET_ALL + Fore.YELLOW 530 | + 'i2cdetect has found I2C addresses above') 531 | 532 | avrport = '' 533 | while True: 534 | current_port = input( 535 | Style.RESET_ALL + Fore.YELLOW 536 | + 'Enter your I2C slave address (e.g. 8): ' 537 | + Fore.RESET + Style.BRIGHT) 538 | 539 | avrport += current_port 540 | confirmation = input( 541 | Style.RESET_ALL + Fore.YELLOW 542 | + get_string('ask_anotherport') 543 | + Fore.RESET + Style.BRIGHT) 544 | 545 | if confirmation == 'y' or confirmation == 'Y': 546 | avrport += ',' 547 | else: 548 | break 549 | 550 | Settings.IoT_EN = input( 551 | Style.RESET_ALL + Fore.YELLOW 552 | + 'Do you want to turn on Duino IoT feature? (Y/n): ' 553 | + Fore.RESET + Style.BRIGHT) 554 | Settings.IoT_EN = Settings.IoT_EN.lower() 555 | if len(Settings.IoT_EN) == 0: Settings.IoT_EN = "y" 556 | elif Settings.IoT_EN != "y": Settings.IoT_EN = "n" 557 | 558 | rig_identifier = input( 559 | Style.RESET_ALL + Fore.YELLOW 560 | + get_string('ask_rig_identifier') 561 | + Fore.RESET + Style.BRIGHT) 562 | if rig_identifier == 'y' or rig_identifier == 'Y': 563 | rig_identifier = input( 564 | Style.RESET_ALL + Fore.YELLOW 565 | + get_string('ask_rig_name') 566 | + Fore.RESET + Style.BRIGHT) 567 | else: 568 | rig_identifier = 'None' 569 | 570 | donation_level = '0' 571 | if osname == 'nt' or osname == 'posix': 572 | donation_level = input( 573 | Style.RESET_ALL + Fore.YELLOW 574 | + get_string('ask_donation_level') 575 | + Fore.RESET + Style.BRIGHT) 576 | 577 | donation_level = sub(r'\D', '', donation_level) 578 | if donation_level == '': 579 | donation_level = 1 580 | if float(donation_level) > int(5): 581 | donation_level = 5 582 | if float(donation_level) < int(0): 583 | donation_level = 0 584 | donation_level = int(donation_level) 585 | 586 | config["AVR Miner"] = { 587 | 'username': username, 588 | 'avrport': avrport, 589 | 'donate': donation_level, 590 | 'language': lang, 591 | 'identifier': rig_identifier, 592 | 'debug': 'n', 593 | "soc_timeout": 45, 594 | "avr_timeout": Settings.AVR_TIMEOUT, 595 | "delay_start": Settings.DELAY_START, 596 | "duinoiot_en": Settings.IoT_EN, 597 | "discord_presence": "y", 598 | "periodic_report": Settings.REPORT_TIME, 599 | "shuffle_ports": "y", 600 | "mining_key": mining_key, 601 | "i2c": i2c, 602 | "i2c_wr_rddcy": Settings.I2C_WR_RDDCY, 603 | "worker_cfg_shared":Settings.WORKER_CFG_SHARED} 604 | 605 | with open(str(Settings.DATA_DIR) 606 | + '/Settings.cfg', 'w') as configfile: 607 | config.write(configfile) 608 | 609 | avrport = avrport.split(',') 610 | print(Style.RESET_ALL + get_string('config_saved')) 611 | hashrate_list = [0] * len(avrport) 612 | 613 | else: 614 | config.read(str(Settings.DATA_DIR) + '/Settings.cfg') 615 | username = config["AVR Miner"]['username'] 616 | avrport = config["AVR Miner"]['avrport'] 617 | avrport = avrport.replace(" ", "").split(',') 618 | donation_level = int(config["AVR Miner"]['donate']) 619 | debug = config["AVR Miner"]['debug'].lower() 620 | rig_identifier = config["AVR Miner"]['identifier'] 621 | Settings.SOC_TIMEOUT = int(config["AVR Miner"]["soc_timeout"]) 622 | Settings.AVR_TIMEOUT = float(config["AVR Miner"]["avr_timeout"]) 623 | Settings.DELAY_START = int(config["AVR Miner"]["delay_start"]) 624 | Settings.IoT_EN = config["AVR Miner"]["duinoiot_en"].lower() 625 | discord_presence = config["AVR Miner"]["discord_presence"] 626 | shuffle_ports = config["AVR Miner"]["shuffle_ports"] 627 | Settings.REPORT_TIME = int(config["AVR Miner"]["periodic_report"]) 628 | hashrate_list = [0] * len(avrport) 629 | i2c = int(config["AVR Miner"]["i2c"]) 630 | Settings.I2C_WR_RDDCY = int(config["AVR Miner"]["i2c_wr_rddcy"]) 631 | Settings.WORKER_CFG_SHARED = config["AVR Miner"]["worker_cfg_shared"].lower() 632 | 633 | 634 | def greeting(): 635 | global greeting 636 | print(Style.RESET_ALL) 637 | 638 | current_hour = strptime(ctime(time())).tm_hour 639 | if current_hour < 12: 640 | greeting = get_string('greeting_morning') 641 | elif current_hour == 12: 642 | greeting = get_string('greeting_noon') 643 | elif current_hour > 12 and current_hour < 18: 644 | greeting = get_string('greeting_afternoon') 645 | elif current_hour >= 18: 646 | greeting = get_string('greeting_evening') 647 | else: 648 | greeting = get_string('greeting_back') 649 | 650 | print( 651 | Style.DIM + Fore.MAGENTA 652 | + Settings.BLOCK + Fore.YELLOW 653 | + Style.BRIGHT + '\n Unofficial Duino-Coin RPI I2C AVR Miner' 654 | + Style.RESET_ALL + Fore.MAGENTA 655 | + f' {Settings.VER}' + Fore.RESET 656 | + ' 2021-2022') 657 | 658 | print( 659 | Style.DIM + Fore.MAGENTA 660 | + Settings.BLOCK + Style.NORMAL + Fore.MAGENTA 661 | + 'https://github.com/JK-Rolling ' 662 | + 'https://github.com/revoxhere/duino-coin') 663 | 664 | if lang != "english": 665 | print( 666 | Style.DIM + Fore.MAGENTA 667 | + Settings.BLOCK + Style.NORMAL 668 | + Fore.RESET + lang.capitalize() 669 | + " translation: " + Fore.MAGENTA 670 | + get_string("translation_autor")) 671 | 672 | print( 673 | Style.DIM + Fore.MAGENTA 674 | + Settings.BLOCK + Style.NORMAL 675 | + Fore.RESET + get_string('avr_on_port') 676 | + Style.BRIGHT + Fore.YELLOW 677 | + ' '.join(avrport)) 678 | 679 | if osname == 'nt' or osname == 'posix': 680 | print( 681 | Style.DIM + Fore.MAGENTA + Settings.BLOCK 682 | + Style.NORMAL + Fore.RESET 683 | + get_string('donation_level') + Style.BRIGHT 684 | + Fore.YELLOW + str(donation_level)) 685 | 686 | print( 687 | Style.DIM + Fore.MAGENTA 688 | + Settings.BLOCK + Style.NORMAL 689 | + Fore.RESET + get_string('algorithm') 690 | + Style.BRIGHT + Fore.YELLOW 691 | + 'DUCO-S1A ⚙ AVR diff') 692 | 693 | if rig_identifier != "None": 694 | print( 695 | Style.DIM + Fore.MAGENTA 696 | + Settings.BLOCK + Style.NORMAL 697 | + Fore.RESET + get_string('rig_identifier') 698 | + Style.BRIGHT + Fore.YELLOW + rig_identifier) 699 | 700 | print( 701 | Style.DIM + Fore.MAGENTA 702 | + Settings.BLOCK + Style.NORMAL 703 | + Fore.RESET + str(greeting) + ', ' 704 | + Style.BRIGHT + Fore.YELLOW 705 | + str(username) + '!\n') 706 | 707 | 708 | def init_rich_presence(): 709 | # Initialize Discord rich presence 710 | global RPC 711 | try: 712 | RPC = Presence(905158274490441808) 713 | RPC.connect() 714 | Thread(target=update_rich_presence).start() 715 | except Exception as e: 716 | #print("Error launching Discord RPC thread: " + str(e)) 717 | pass 718 | 719 | 720 | def update_rich_presence(): 721 | startTime = int(time()) 722 | while True: 723 | try: 724 | total_hashrate = get_prefix("H/s", sum(hashrate_list), 2) 725 | RPC.update(details="Hashrate: " + str(total_hashrate), 726 | start=mining_start_time, 727 | state=str(shares[0]) + "/" 728 | + str(shares[0] + shares[1]) 729 | + " accepted shares", 730 | large_image="avrminer", 731 | large_text="Duino-Coin, " 732 | + "a coin that can be mined with almost everything" 733 | + ", including AVR boards", 734 | buttons=[{"label": "Visit duinocoin.com", 735 | "url": "https://duinocoin.com"}, 736 | {"label": "Join the Discord", 737 | "url": "https://discord.gg/k48Ht5y"}]) 738 | except Exception as e: 739 | print("Error updating Discord RPC thread: " + str(e)) 740 | 741 | sleep(15) 742 | 743 | 744 | def pretty_print(sender: str = "sys0", 745 | msg: str = None, 746 | state: str = "success"): 747 | """ 748 | Produces nicely formatted CLI output for messages: 749 | HH:MM:S |sender| msg 750 | """ 751 | if sender.startswith("net"): 752 | bg_color = Back.BLUE 753 | elif sender.startswith("avr"): 754 | bg_color = Back.MAGENTA 755 | else: 756 | bg_color = Back.GREEN 757 | 758 | if state == "success": 759 | fg_color = Fore.GREEN 760 | elif state == "info": 761 | fg_color = Fore.BLUE 762 | elif state == "error": 763 | fg_color = Fore.RED 764 | else: 765 | fg_color = Fore.YELLOW 766 | 767 | with thread_lock(): 768 | printlock.acquire() 769 | print(Fore.WHITE + datetime.now().strftime(Style.DIM + "%H:%M:%S ") 770 | + bg_color + Style.BRIGHT + " " + sender + " " 771 | + Back.RESET + " " + fg_color + msg.strip()) 772 | printlock.release() 773 | 774 | def worker_print(com, **kwargs): 775 | 776 | text = "" 777 | for key in kwargs: 778 | text += "%s %s . " % (key, kwargs.get(key)) 779 | with thread_lock(): 780 | printlock.acquire() 781 | print(Fore.WHITE + datetime.now().strftime(Style.DIM + "%H:%M:%S ") 782 | + Fore.WHITE + Style.BRIGHT + Back.MAGENTA + Fore.RESET 783 | + " avr" + port_num(com) + " " + Back.RESET + " " 784 | + "worker capability report -> " 785 | + text) 786 | printlock.release() 787 | 788 | def share_print(id, type, accept, reject, total_hashrate, 789 | computetime, diff, ping, reject_cause=None, iot_data=None): 790 | """ 791 | Produces nicely formatted CLI output for shares: 792 | HH:MM:S |avrN| ⛏ Accepted 0/0 (100%) ∙ 0.0s ∙ 0 kH/s ⚙ diff 0 k ∙ ping 0ms 793 | """ 794 | try: 795 | diff = get_prefix("", int(diff), 0) 796 | except: 797 | diff = "?" 798 | 799 | try: 800 | total_hashrate = get_prefix("H/s", total_hashrate, 2) 801 | except: 802 | total_hashrate = "? H/s" 803 | 804 | if type == "accept": 805 | share_str = get_string("accepted") 806 | fg_color = Fore.GREEN 807 | elif type == "block": 808 | share_str = get_string("block_found") 809 | fg_color = Fore.YELLOW 810 | else: 811 | share_str = get_string("rejected") 812 | if reject_cause: 813 | share_str += f"{Style.NORMAL}({reject_cause}) " 814 | fg_color = Fore.RED 815 | 816 | iot_text = "" 817 | if iot_data: 818 | (temperature, humidity) = iot_data.split("@") 819 | iot_text = f"{temperature}° . " 820 | 821 | with thread_lock(): 822 | printlock.acquire() 823 | print(Fore.WHITE + datetime.now().strftime(Style.DIM + "%H:%M:%S ") 824 | + Fore.WHITE + Style.BRIGHT + Back.MAGENTA + Fore.RESET 825 | + " avr" + str(id) + " " + Back.RESET 826 | + fg_color + Settings.PICK + share_str + Fore.RESET 827 | + str(accept) + "/" + str(accept + reject) + Fore.MAGENTA 828 | + " (" + str(round(accept / (accept + reject) * 100)) + "%)" 829 | + Style.NORMAL + Fore.RESET 830 | + " ∙ " + str("%04.1f" % float(computetime)) + "s" 831 | + Style.NORMAL + " ∙ " + Fore.BLUE + Style.BRIGHT 832 | + str(total_hashrate) + Fore.RESET + Style.NORMAL 833 | + Settings.COG + f" diff {diff} ∙ " 834 | + f"{iot_text}" + Fore.CYAN 835 | + f"ping {(int(ping))}ms") 836 | printlock.release() 837 | 838 | def flush_i2c(i2c_bus,com,period=1): 839 | i2c_flush_start = time() 840 | with thread_lock(): 841 | while True: 842 | i2c_read(i2c_bus, com) 843 | 844 | if (time() - i2c_flush_start) > period: 845 | break 846 | 847 | def i2c_write(i2c_bus, com, i2c_data, wr_rddcy=-1): 848 | 849 | if wr_rddcy == -1: 850 | wr_rddcy = Settings.I2C_WR_RDDCY 851 | debug_output(com + f': i2c_wdata=[{i2c_data}]') 852 | 853 | with thread_lock(): 854 | try: 855 | i2clock.acquire() 856 | for i in range(0, len(i2c_data)): 857 | if wr_rddcy == 1: 858 | # write single byte i2c data 859 | i2c_bus.write_byte(int(com, base=16), 860 | ord(i2c_data[i])) 861 | elif wr_rddcy > 1: 862 | # write repeated i2c data 863 | # help the i2cs to get the msg 864 | i2c_bus.write_i2c_block_data(int(com, base=16), 865 | ord(i2c_data[i]), 866 | [ord(i2c_data[i])]*(wr_rddcy-1)) 867 | sleep(0.0002) 868 | except Exception as e: 869 | debug_output(com + f': {e}') 870 | pass 871 | finally: 872 | i2clock.release() 873 | 874 | def i2c_read(i2c_bus, com): 875 | 876 | i2c_rdata = "" 877 | with thread_lock(): 878 | try: 879 | i2clock.acquire() 880 | i2c_rdata = chr(i2c_bus.read_byte(int(com, base=16))) 881 | except Exception as e: 882 | debug_output(com + f': {e}') 883 | pass 884 | finally: 885 | i2clock.release() 886 | 887 | return i2c_rdata 888 | 889 | def get_temperature(i2c_bus,com): 890 | i2c_cmd = "get,temp$" 891 | i2c_resp = "0.00" 892 | start_time = time() 893 | 894 | try: 895 | i2c_write(i2c_bus, com, i2c_cmd) 896 | 897 | i2c_resp = "" 898 | while True: 899 | i2c_rdata = i2c_read(i2c_bus, com) 900 | 901 | if (i2c_rdata.isalnum() or ('.' in i2c_rdata)): 902 | i2c_resp += i2c_rdata.strip() 903 | 904 | if ('\n' in i2c_rdata) and (len(i2c_resp)>0): 905 | break 906 | 907 | if (time() - start_time) > 1: 908 | i2c_resp = "0.00" 909 | break 910 | except Exception as e: 911 | debug_output(com + f': {e}') 912 | pass 913 | 914 | #debug_output(com + f': i2c_resp:[{i2c_resp}]') 915 | return i2c_resp 916 | 917 | def get_humidity(i2c_bus,com): 918 | # place holder 919 | return "0.00" 920 | 921 | def get_worker_i2cfreq(i2c_bus,com): 922 | i2c_cmd = "get,freq$" 923 | default_answer = "0" 924 | return send_worker_cmd(i2c_bus,com,i2c_cmd,default_answer) 925 | 926 | def send_worker_cmd(i2c_bus,com,cmd,default): 927 | i2c_resp = default 928 | start_time = time() 929 | try: 930 | i2c_write(i2c_bus, com, cmd) 931 | 932 | i2c_resp = "" 933 | while True: 934 | i2c_rdata = i2c_read(i2c_bus, com) 935 | 936 | if (i2c_rdata.isalnum()): 937 | i2c_resp += i2c_rdata.strip() 938 | 939 | if ('\n' in i2c_rdata) and (len(i2c_resp)>0): 940 | break 941 | 942 | # shouldn't take more than 1s to get response 943 | if (time() - start_time) > 1: 944 | i2c_resp = "0" 945 | break 946 | except Exception as e: 947 | debug_output(com + f': {e}') 948 | pass 949 | 950 | #debug_output(com + f': i2c_resp:[{i2c_resp}]') 951 | try: 952 | i2c_resp = int(i2c_resp) 953 | except ValueError: 954 | pass 955 | return i2c_resp 956 | 957 | 958 | def get_worker_crc8_status(i2c_bus,com): 959 | i2c_cmd = "get,crc8$" 960 | default_answer = "1" 961 | 962 | return send_worker_cmd(i2c_bus,com,i2c_cmd,default_answer) 963 | 964 | def get_worker_baton_status(i2c_bus,com): 965 | i2c_cmd = "get,baton$" 966 | default_answer = "1" 967 | 968 | return send_worker_cmd(i2c_bus,com,i2c_cmd,default_answer) 969 | 970 | def get_worker_core_status(i2c_bus,com): 971 | i2c_cmd = "get,singlecore$" 972 | default_answer = "0" 973 | 974 | return send_worker_cmd(i2c_bus,com,i2c_cmd,default_answer) 975 | 976 | def get_worker_name(i2c_bus,com): 977 | i2c_cmd = "get,name$" 978 | default_answer = "unkn" 979 | 980 | return send_worker_cmd(i2c_bus,com,i2c_cmd,default_answer) 981 | 982 | def crc8(data): 983 | crc = 0 984 | for i in range(len(data)): 985 | byte = data[i] 986 | for b in range(8): 987 | fb_bit = (crc ^ byte) & 0x01 988 | if fb_bit == 0x01: 989 | crc = crc ^ 0x18 990 | crc = (crc >> 1) & 0x7f 991 | if fb_bit == 0x01: 992 | crc = crc | 0x80 993 | byte = byte >> 1 994 | return crc 995 | 996 | def is_subscript(c): 997 | if c.isdigit(): 998 | try: 999 | int(c) 1000 | except ValueError: 1001 | return True 1002 | return False 1003 | 1004 | def debouncer(fname, i2c_bus, com): 1005 | count=0 1006 | max_retry=10 1007 | while count < max_retry: 1008 | result = eval(fname+'(i2c_bus,com)') 1009 | sleep(0.2) 1010 | _result = eval(fname+'(i2c_bus,com)') 1011 | if result == _result: 1012 | break 1013 | sleep(0.2) 1014 | count += 1 1015 | return result 1016 | 1017 | def get_worker_cfg_global(i2c_bus, com): 1018 | worker_cfg_global["i2c_freq"] = get_worker_i2cfreq(i2c_bus, com) 1019 | worker_cfg_global["crc8_en"] = debouncer("get_worker_crc8_status", i2c_bus, com) 1020 | sensor_en = get_temperature(i2c_bus, com) 1021 | worker_cfg_global["sensor_en"] = 1 if sensor_en != "0" else 0 1022 | worker_cfg_global["baton_status"] = get_worker_baton_status(i2c_bus, com) 1023 | worker_cfg_global["single_core_only"] = get_worker_core_status(i2c_bus, com) 1024 | worker_cfg_global["worker_name"] = get_worker_name(i2c_bus, com) 1025 | worker_cfg_global["valid"] = True 1026 | 1027 | def mine_avr(com, threadid, fastest_pool): 1028 | global hashrate 1029 | global bad_crc8 1030 | global i2c_retry_count 1031 | global hashrate_mean 1032 | start_time = time() 1033 | report_shares = 0 1034 | last_report_share = 0 1035 | last_bad_crc8 = 0 1036 | last_i2c_retry_count = 0 1037 | wr_rddcy = Settings.I2C_WR_RDDCY 1038 | avr_timeout = Settings.AVR_TIMEOUT 1039 | iot_data = None 1040 | crc8_en = 1 1041 | user_iot = Settings.IoT_EN 1042 | ducoid = "" 1043 | worker_type = "avr" 1044 | worker_cfg_shared = True if Settings.WORKER_CFG_SHARED == "y" else False 1045 | 1046 | flush_i2c(i2c_bus, com) 1047 | 1048 | while worker_cfg_global["valid"] is not True and worker_cfg_shared: 1049 | sleep(1) 1050 | 1051 | if worker_cfg_shared: 1052 | i2c_freq = worker_cfg_global["i2c_freq"] 1053 | crc8_en = worker_cfg_global["crc8_en"] 1054 | sensor_en = worker_cfg_global["sensor_en"] 1055 | baton_status = worker_cfg_global["baton_status"] 1056 | single_core_only = worker_cfg_global["single_core_only"] 1057 | worker_name = worker_cfg_global["worker_name"] 1058 | else: 1059 | i2c_freq = get_worker_i2cfreq(i2c_bus, com) 1060 | crc8_en = debouncer("get_worker_crc8_status", i2c_bus, com) 1061 | sensor_en = get_temperature(i2c_bus, com) 1062 | sensor_en = 1 if sensor_en != "0" else 0 1063 | baton_status = get_worker_baton_status(i2c_bus, com) 1064 | single_core_only = get_worker_core_status(i2c_bus, com) 1065 | worker_name = get_worker_name(i2c_bus, com) 1066 | 1067 | worker_print(com, i2c_clock=i2c_freq, crc8_en=crc8_en, 1068 | sensor_en=sensor_en, baton_status=baton_status, 1069 | single_core_only=single_core_only, worker_name=worker_name, 1070 | shared_worker_cfg=str(worker_cfg_shared)) 1071 | 1072 | if sensor_en == 0 and "y" in user_iot.lower(): 1073 | user_iot = "n" 1074 | pretty_print("sys" + port_num(com), " worker do not have sensor enabled. Disabling IoT reporting", "warning") 1075 | 1076 | while True: 1077 | 1078 | retry_counter = 0 1079 | while True: 1080 | try: 1081 | if retry_counter > 3: 1082 | #jk fastest_pool = Client.fetch_pool() 1083 | fastest_pool = None 1084 | retry_counter = 0 1085 | 1086 | debug_output(f'Connecting to {fastest_pool}') 1087 | #jk s = Client.connect(fastest_pool) 1088 | #jk server_version = Client.recv(s, 6) 1089 | server_version = 1 1090 | 1091 | if threadid == 0: 1092 | if float(server_version) <= float(Settings.VER): 1093 | pretty_print( 1094 | 'net0', get_string('connected') 1095 | + Style.NORMAL + Fore.RESET 1096 | + get_string('connected_server') 1097 | + str(server_version) + ")", 1098 | 'success') 1099 | else: 1100 | pretty_print( 1101 | 'sys0', f' Miner is outdated (v{Settings.VER}) -' 1102 | + get_string('server_is_on_version') 1103 | + server_version + Style.NORMAL 1104 | + Fore.RESET + get_string('update_warning'), 1105 | 'warning') 1106 | sleep(10) 1107 | 1108 | #jk Client.send(s, "MOTD") 1109 | #jk motd = Client.recv(s, 1024) 1110 | motd = "Mocked server\n" 1111 | 1112 | if "\n" in motd: 1113 | motd = motd.replace("\n", "\n\t\t") 1114 | 1115 | pretty_print("net" + str(threadid), 1116 | " MOTD: " + Fore.RESET 1117 | + Style.NORMAL + str(motd), 1118 | "success") 1119 | break 1120 | except Exception as e: 1121 | pretty_print('net0', get_string('connecting_error') 1122 | + Style.NORMAL + f' (connection err: {e})', 1123 | 'error') 1124 | retry_counter += 1 1125 | sleep(10) 1126 | 1127 | pretty_print('sys' + port_num(com), 1128 | get_string('mining_start') + Style.NORMAL + Fore.RESET 1129 | + get_string('mining_algorithm') + str(com) + ')', 1130 | 'success') 1131 | 1132 | flush_i2c(i2c_bus,com) 1133 | 1134 | while True: 1135 | try: 1136 | 1137 | if config["AVR Miner"]["mining_key"] != "None": 1138 | key = b64.b64decode(config["AVR Miner"]["mining_key"]).decode('utf-8') 1139 | else: 1140 | key = config["AVR Miner"]["mining_key"] 1141 | 1142 | debug_output(com + ': Requesting job') 1143 | job_request = 'JOB' 1144 | job_request += Settings.SEPARATOR 1145 | job_request += str(username) 1146 | job_request += Settings.SEPARATOR 1147 | job_request += 'AVR' 1148 | job_request += Settings.SEPARATOR 1149 | job_request += str(key) 1150 | 1151 | if sensor_en and user_iot == "y": 1152 | job_request += Settings.SEPARATOR 1153 | iot_data = get_temperature(i2c_bus,com) 1154 | iot_data += "@" 1155 | iot_data += get_humidity(i2c_bus,com) 1156 | job_request += iot_data 1157 | 1158 | debug_output(com + f": {job_request}") 1159 | 1160 | #jk Client.send(s, job_request) 1161 | #jk job = Client.recv(s, 128).split(Settings.SEPARATOR) 1162 | # Perform a hash test to assign the starting diff 1163 | prev_hash = "ba29a15896fd2d792d5c4b60668bf2b9feebc51d" 1164 | exp_hash = "d0beba883d7e8cd119ea2b0e09b78f60f29e0968" 1165 | idiff = "10" 1166 | exp_result = 50 1167 | job = [prev_hash, exp_hash, idiff] 1168 | job = ["f96a54b0ec35bebc95f4d7a8f4aefecab3a3778c","985ba4d68b8954e8a9d010b7a7a7ca42ba57b24d","8"] 1169 | exp_result = 242 1170 | debug_output(com + f": Received: {job[0]}") 1171 | 1172 | try: 1173 | diff = int(job[2]) 1174 | except: 1175 | pretty_print("sys" + port_num(com), 1176 | f" Node message: {job[1]}", "warning") 1177 | sleep(3) 1178 | except Exception as e: 1179 | pretty_print('net' + port_num(com), 1180 | get_string('connecting_error') 1181 | + Style.NORMAL + Fore.RESET 1182 | + f' (err handling result: {e})', 'error') 1183 | sleep(3) 1184 | break 1185 | 1186 | retry_counter = 0 1187 | while True: 1188 | if retry_counter > 3: 1189 | flush_i2c(i2c_bus,com) 1190 | break 1191 | 1192 | try: 1193 | debug_output(com + ': Sending job to the board') 1194 | i2c_data = str(job[0] 1195 | + Settings.SEPARATOR 1196 | + job[1] 1197 | + Settings.SEPARATOR 1198 | + job[2]) 1199 | 1200 | if crc8_en : 1201 | i2c_data += Settings.SEPARATOR 1202 | i2c_data = str(i2c_data + str(crc8(i2c_data.encode())) + '\n') 1203 | debug_output(com + f': Job+crc8: {i2c_data}') 1204 | else: 1205 | i2c_data = str(i2c_data + '\n') 1206 | debug_output(com + f': Job: {i2c_data}') 1207 | 1208 | i2c_write(i2c_bus, com, i2c_data, wr_rddcy) 1209 | debug_output(com + ': Reading result from the board') 1210 | i2c_responses = '' 1211 | i2c_rdata = '' 1212 | substitute = str.maketrans("⁰¹²³⁴⁵⁶⁷⁸⁹","0123456789") 1213 | result = [] 1214 | i2c_start_time = time() 1215 | sleep_en = True 1216 | while True: 1217 | i2c_rdata = i2c_read(i2c_bus, com) 1218 | 1219 | if is_subscript(i2c_rdata): 1220 | # rare incident where MSB bit flipped 1221 | i2c_rdata = i2c_rdata.translate(substitute) 1222 | 1223 | if ('$' in i2c_rdata): 1224 | # worker cmd overflow into response area. dump it 1225 | i2c_responses = '' 1226 | 1227 | if ((i2c_rdata.isalnum()) or (',' in i2c_rdata)): 1228 | sleep_en = False 1229 | i2c_responses += i2c_rdata.strip() 1230 | 1231 | elif ('#' in i2c_rdata): 1232 | # i2cs received corrupted job 1233 | debug_output(com + f': Received response: {i2c_responses}') 1234 | debug_output(com + f': Retry Job: {job}') 1235 | debug_output(com + f': retransmission requested') 1236 | if wr_rddcy < 32: 1237 | if worker_type == "others": 1238 | wr_rddcy += 1 1239 | debug_output(com + f': increment write redundancy bytes to {wr_rddcy}') 1240 | else: 1241 | debug_output(com + f': write redundancy maxed out at {wr_rddcy}') 1242 | raise Exception("I2C job corrupted") 1243 | 1244 | if sleep_en: 1245 | # pool less when worker is busy 1246 | # feel free to play around this number to find sweet spot for shares/s vs. stability 1247 | sleep(0.05) 1248 | 1249 | result = i2c_responses.split(',') 1250 | if (((len(result)==4 and crc8_en) or 1251 | (len(result)==3 and not crc8_en)) and 1252 | ('\n' in i2c_rdata)): 1253 | debug_output(com + " i2c_responses:" + f'{i2c_responses}') 1254 | break 1255 | 1256 | if (time() - i2c_start_time) > avr_timeout: 1257 | debug_output(com + f' I2C timed out after {avr_timeout}s') 1258 | raise Exception("I2C timed out") 1259 | 1260 | if result[0] and result[1]: 1261 | _ = int(result[0]) 1262 | if not _: 1263 | debug_output(com + ' Invalid result') 1264 | raise Exception("Invalid result") 1265 | _ = int(result[1]) 1266 | if not result[2].isalnum() and len(ducoid) == 0: 1267 | debug_output(com + ' Corrupted DUCOID') 1268 | raise Exception("Corrupted DUCOID") 1269 | if not result[2].isalnum() and len(ducoid) > 0: 1270 | # ducoid corrupted 1271 | # use ducoid from previous response 1272 | result[2] = ducoid 1273 | # reconstruct i2c_responses 1274 | i2c_responses = str(result[0] 1275 | + Settings.SEPARATOR 1276 | + result[1] 1277 | + Settings.SEPARATOR 1278 | + result[2]) 1279 | if int(crc8_en): 1280 | _resp = i2c_responses.rpartition(Settings.SEPARATOR)[0]+Settings.SEPARATOR 1281 | result_crc8 = crc8(_resp.encode()) 1282 | if (int(result[3]) != result_crc8): 1283 | bad_crc8 += 1 1284 | debug_output(com + f': crc8:: expect:{result_crc8} measured:{result[3]}') 1285 | raise Exception("crc8 checksum failed") 1286 | break 1287 | else: 1288 | raise Exception("No data received from AVR") 1289 | except Exception as e: 1290 | debug_output(com + f': Retrying data read: {e}') 1291 | retry_counter += 1 1292 | i2c_retry_count += 1 1293 | flush_i2c(i2c_bus,com,1) 1294 | continue 1295 | 1296 | try: 1297 | computetime = round(int(result[1]) / 1000000, 3) 1298 | num_res = int(result[0]) 1299 | 1300 | 1301 | hashrate_t = round(num_res / computetime, 2) 1302 | 1303 | # experimental: guess worker type. seems like larger wr_rddcy causes more harm than good on avr 1304 | if hashrate_t < 400: 1305 | worker_type = "avr" 1306 | wr_rddcy = 1 1307 | else: 1308 | worker_type = "others" 1309 | 1310 | _avr_timeout = int(((int(diff) * 100) / int(hashrate_t)) * 2) 1311 | if _avr_timeout > avr_timeout: 1312 | debug_output(com + f': changing avr_timeout from {avr_timeout}s to {_avr_timeout}s') 1313 | avr_timeout = _avr_timeout 1314 | 1315 | hashrate_mean.append(hashrate_t) 1316 | hashrate = mean(hashrate_mean[-5:]) 1317 | if len(hashrate_mean) > 5: 1318 | hashrate_mean = hashrate_mean[-5:] 1319 | if (hashrate < 6000) and (hashrate_t >= 6000): 1320 | hashrate_t = 5990 + round(random.random(),2) 1321 | hashrate_list[threadid] = hashrate 1322 | except Exception as e: 1323 | pretty_print('sys' + port_num(com), 1324 | get_string('mining_avr_connection_error') 1325 | + Style.NORMAL + Fore.RESET 1326 | + ' (no response from the board: ' 1327 | + f'{e}, please check the connection, ' 1328 | + 'port setting or reset the AVR)', 'warning') 1329 | debug_output(com + f': Retry count: {retry_counter}') 1330 | debug_output(com + f': Job: {job}') 1331 | debug_output(com + f': Result: {result}') 1332 | flush_i2c(i2c_bus,com) 1333 | break 1334 | ducoid = result[2] 1335 | 1336 | try: 1337 | #jk Client.send(s, str(num_res) 1338 | #jk + Settings.SEPARATOR 1339 | #jk + str(hashrate_t) 1340 | #jk + Settings.SEPARATOR 1341 | #jk + f'RPI I2C AVR Miner {Settings.VER}' 1342 | #jk + Settings.SEPARATOR 1343 | #jk + str(rig_identifier) 1344 | #jk #+ str(port_num(com)) 1345 | #jk + Settings.SEPARATOR 1346 | #jk + str(result[2])) 1347 | 1348 | responsetimetart = now() 1349 | #jk feedback = Client.recv(s, 64).split(",") 1350 | if num_res != exp_result: 1351 | feedback = ['BAD', "Unexpected result"] 1352 | else : 1353 | feedback = ['GOOD'] 1354 | sleep(0.001) 1355 | responsetimestop = now() 1356 | 1357 | time_delta = (responsetimestop - 1358 | responsetimetart).microseconds 1359 | ping_mean.append(round(time_delta / 1000)) 1360 | ping = mean(ping_mean[-10:]) 1361 | diff = get_prefix("", int(diff), 0) 1362 | debug_output(com + f': retrieved feedback: {" ".join(feedback)}') 1363 | except Exception as e: 1364 | pretty_print('net' + port_num(com), 1365 | get_string('connecting_error') 1366 | + Style.NORMAL + Fore.RESET 1367 | + f' (err handling result: {e})', 'error') 1368 | debug_output(com + f': error parsing response: {e}') 1369 | sleep(5) 1370 | break 1371 | 1372 | if feedback[0] == 'GOOD': 1373 | shares[0] += 1 1374 | share_print(port_num(com), "accept", 1375 | shares[0], shares[1], hashrate, 1376 | computetime, diff, ping, None, iot_data) 1377 | elif feedback[0] == 'BLOCK': 1378 | shares[0] += 1 1379 | shares[2] += 1 1380 | share_print(port_num(com), "block", 1381 | shares[0], shares[1], hashrate, 1382 | computetime, diff, ping, None, iot_data) 1383 | elif feedback[0] == 'BAD': 1384 | shares[1] += 1 1385 | reason = feedback[1] if len(feedback) > 1 else None 1386 | share_print(port_num(com), "reject", 1387 | shares[0], shares[1], hashrate_t, 1388 | computetime, diff, ping, reason, iot_data) 1389 | else: 1390 | shares[1] += 1 1391 | share_print(port_num(com), "reject", 1392 | shares[0], shares[1], hashrate_t, 1393 | computetime, diff, ping, feedback, iot_data) 1394 | debug_output(com + f': Job: {job}') 1395 | debug_output(com + f': Result: {result}') 1396 | flush_i2c(i2c_bus,com,5) 1397 | 1398 | title(get_string('duco_avr_miner') + str(Settings.VER) 1399 | + f') - {shares[0]}/{(shares[0] + shares[1])}' 1400 | + get_string('accepted_shares')) 1401 | 1402 | end_time = time() 1403 | elapsed_time = end_time - start_time 1404 | if threadid == 0 and elapsed_time >= Settings.REPORT_TIME: 1405 | report_shares = shares[0] - last_report_share 1406 | report_bad_crc8 = bad_crc8 - last_bad_crc8 1407 | report_i2c_retry_count = i2c_retry_count - last_i2c_retry_count 1408 | uptime = calculate_uptime(mining_start_time) 1409 | pretty_print("net" + str(threadid), 1410 | " POOL_INFO: " + Fore.RESET 1411 | + Style.NORMAL + str(motd), 1412 | "success") 1413 | periodic_report(start_time, end_time, report_shares, 1414 | shares[2], hashrate, uptime, 1415 | report_bad_crc8, report_i2c_retry_count) 1416 | 1417 | start_time = time() 1418 | last_report_share = shares[0] 1419 | last_bad_crc8 = bad_crc8 1420 | last_i2c_retry_count = i2c_retry_count 1421 | 1422 | 1423 | def periodic_report(start_time, end_time, shares, 1424 | block, hashrate, uptime, bad_crc8, i2c_retry_count): 1425 | seconds = round(end_time - start_time) 1426 | pretty_print("sys0", 1427 | " " + get_string('periodic_mining_report') 1428 | + Fore.RESET + Style.NORMAL 1429 | + get_string('report_period') 1430 | + str(seconds) + get_string('report_time') 1431 | + get_string('report_body1') 1432 | + str(shares) + get_string('report_body2') 1433 | + str(round(shares/seconds, 1)) 1434 | + get_string('report_body3') 1435 | + get_string('report_body7') + str(block) 1436 | + get_string('report_body4') 1437 | + str(int(hashrate)) + " H/s" + get_string('report_body5') 1438 | + str(int(hashrate*seconds)) + get_string('report_body6') 1439 | + get_string('total_mining_time') + str(uptime) 1440 | + "\n\t\t‖ CRC8 Error Rate: " + str(round(bad_crc8/seconds, 6)) + " E/s" 1441 | + "\n\t\t‖ I2C Retry Rate: " + str(round(i2c_retry_count/seconds, 6)) + " R/s", "success") 1442 | 1443 | 1444 | def calculate_uptime(start_time): 1445 | uptime = time() - start_time 1446 | if uptime >= 7200: # 2 hours, plural 1447 | return str(uptime // 3600) + get_string('uptime_hours') 1448 | elif uptime >= 3600: # 1 hour, not plural 1449 | return str(uptime // 3600) + get_string('uptime_hour') 1450 | elif uptime >= 120: # 2 minutes, plural 1451 | return str(uptime // 60) + get_string('uptime_minutes') 1452 | elif uptime >= 60: # 1 minute, not plural 1453 | return str(uptime // 60) + get_string('uptime_minute') 1454 | else: # less than 1 minute 1455 | return str(round(uptime)) + get_string('uptime_seconds') 1456 | 1457 | 1458 | if __name__ == '__main__': 1459 | init(autoreset=True) 1460 | title(f"{get_string('duco_avr_miner')}{str(Settings.VER)})") 1461 | 1462 | if sys.platform == "win32": 1463 | os.system('') # Enable VT100 Escape Sequence for WINDOWS 10 Ver. 1607 1464 | 1465 | try: 1466 | load_config() 1467 | debug_output('Config file loaded') 1468 | except Exception as e: 1469 | pretty_print( 1470 | 'sys0', get_string('load_config_error') 1471 | + Settings.DATA_DIR + get_string('load_config_error_warning') 1472 | + Style.NORMAL + Fore.RESET + f' ({e})', 'error') 1473 | debug_output(f'Error reading configfile: {e}') 1474 | sleep(10) 1475 | _exit(1) 1476 | 1477 | try: 1478 | greeting() 1479 | debug_output('Greeting displayed') 1480 | except Exception as e: 1481 | debug_output(f'Error displaying greeting message: {e}') 1482 | 1483 | #jk try: 1484 | #jk check_mining_key(config) 1485 | #jk except Exception as e: 1486 | #jk debug_output(f'Error checking miner key: {e}') 1487 | #jk 1488 | #jk if donation_level > 0: 1489 | #jk try: 1490 | #jk Donate.load(donation_level) 1491 | #jk Donate.start(donation_level) 1492 | #jk except Exception as e: 1493 | #jk debug_output(f'Error launching donation thread: {e}') 1494 | 1495 | try: 1496 | i2c_bus = SMBus(i2c) 1497 | #jk fastest_pool = Client.fetch_pool() 1498 | fastest_pool = None 1499 | threadid = 0 1500 | if Settings.WORKER_CFG_SHARED == "y": 1501 | for port in avrport: 1502 | get_worker_cfg_global(i2c_bus,port) 1503 | if worker_cfg_global["valid"]: break 1504 | for port in avrport: 1505 | Thread(target=mine_avr, 1506 | args=(port, threadid, 1507 | fastest_pool)).start() 1508 | threadid += 1 1509 | if ((len(avrport) > 1) and (threadid != len(avrport))): 1510 | pretty_print('sys' + str(threadid), 1511 | f" Started {threadid}/{len(avrport)} worker(s). Next I2C AVR Miner starts in " 1512 | + str(Settings.DELAY_START) 1513 | + "s", 1514 | "success") 1515 | sleep(Settings.DELAY_START) 1516 | else: 1517 | pretty_print('sys' + str(threadid), 1518 | f" All {threadid}/{len(avrport)} worker(s) started", 1519 | "success") 1520 | except Exception as e: 1521 | debug_output(f'Error launching AVR thread(s): {e}') 1522 | 1523 | #jk if discord_presence == "y": 1524 | #jk try: 1525 | #jk init_rich_presence() 1526 | #jk except Exception as e: 1527 | #jk debug_output(f'Error launching Discord RPC thread: {e}') 1528 | 1529 | --------------------------------------------------------------------------------