diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index e93d94ab..924e949c 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -custom: ['https://duinocoin.com/donate', 'https://paypal.me/duinocoin'] +custom: ['https://duinocoin.com/donate', 'https://paypal.me/revoxhere'] diff --git a/.gitignore b/.gitignore index 95c3303f..2e315104 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ Wallet_*/ PCMiner_*/ AVRMiner_*/ +Duino-Coin PC Miner */ +Duino-Coin AVR Miner */ # Byte-compiled and optimized files __pycache__/ diff --git a/AVR_Miner.py b/AVR_Miner.py index 286f583f..824086d2 100644 --- a/AVR_Miner.py +++ b/AVR_Miner.py @@ -1,15 +1,15 @@ #!/usr/bin/env python3 """ -Duino-Coin Official AVR Miner 3.18 © MIT licensed +Duino-Coin Official AVR Miner 4.3 © MIT licensed https://duinocoin.com https://github.com/revoxhere/duino-coin -Duino-Coin Team & Community 2019-2022 +Duino-Coin Team & Community 2019-2025 """ from os import _exit, mkdir from os import name as osname from os import path -from os import system as ossystem +from os import system as ossystem from platform import machine as osprocessor from platform import system import sys @@ -18,7 +18,7 @@ from pathlib import Path from json import load as jsonload -import json +from random import choice from locale import LC_ALL, getdefaultlocale, getlocale, setlocale import zipfile @@ -27,18 +27,16 @@ from datetime import datetime from statistics import mean from signal import SIGINT, signal +from collections import deque from time import ctime, sleep, strptime, time import pip from subprocess import DEVNULL, Popen, check_call, call -from threading import Thread -from threading import Lock as thread_lock -from threading import Semaphore - +from threading import Thread, Lock import base64 as b64 - import os -printlock = Semaphore(value=1) + +printlock = Lock() # Python <3.5 check @@ -109,15 +107,16 @@ def port_num(com): class Settings: - VER = '3.18' - SOC_TIMEOUT = 15 - REPORT_TIME = 120 - AVR_TIMEOUT = 7 # diff 16 * 100 / 258 h/s = 6.2 s + VER = '4.3' + SOC_TIMEOUT = 10 + REPORT_TIME = 300 + AVR_TIMEOUT = 10 BAUDRATE = 115200 DATA_DIR = "Duino-Coin AVR Miner " + str(VER) SEPARATOR = "," ENCODING = "utf-8" TEMP_FOLDER = "Temp" + disable_title = False try: # Raspberry Pi latin users can't display this character @@ -182,7 +181,6 @@ def check_updates(): "avr_timeout": float(config["AVR Miner"]["avr_timeout"]), "discord_presence": config["AVR Miner"]["discord_presence"], "periodic_report": int(config["AVR Miner"]["periodic_report"]), - "shuffle_ports": config["AVR Miner"]["shuffle_ports"], "mining_key": config["AVR Miner"]["mining_key"] } @@ -267,20 +265,31 @@ def check_updates(): print(e) sys.exit() + +def has_mining_key(username): + response = requests.get( + "https://server.duinocoin.com/mining_key" + + "?u=" + username, + timeout=10 + ).json() + return response["has_key"] + + def check_mining_key(user_settings): user_settings = user_settings["AVR Miner"] if user_settings["mining_key"] != "None": - key = b64.b64decode(user_settings["mining_key"]).decode('utf-8') + key = "&k=" + b64.b64decode(user_settings["mining_key"]).decode('utf-8') else: key = '' response = requests.get( "https://server.duinocoin.com/mining_key" + "?u=" + user_settings["username"] - + "&k=" + key, + + key, timeout=10 ).json() + debug_output(response) if response["success"] and not response["has_key"]: # if the user doesn't have a mining key user_settings["mining_key"] = "None" @@ -292,10 +301,13 @@ def check_mining_key(user_settings): print("sys0", Style.RESET_ALL + get_string("config_saved"), "info") - sleep(1.5) return if not response["success"]: + if response["message"] == "Too many requests": + debug_output("Skipping mining key check - getting 429") + return + if user_settings["mining_key"] == "None": pretty_print( "sys0", @@ -312,7 +324,6 @@ def check_mining_key(user_settings): print("sys0", Style.RESET_ALL + get_string("config_saved"), "info") - sleep(1.5) check_mining_key(config) else: pretty_print( @@ -320,7 +331,7 @@ def check_mining_key(user_settings): get_string("invalid_mining_key"), "error") - retry = input("You want to retry? (y/n): ") + retry = input("Do you want to retry? (y/n): ") if retry == "y" or retry == "Y": mining_key = input("Enter your mining key: ") user_settings["mining_key"] = b64.b64encode(mining_key.encode("utf-8")).decode('utf-8') @@ -460,10 +471,9 @@ def start(donation_level): shares = [0, 0, 0] -hashrate_mean = [] -ping_mean = [] +hashrate_mean = deque(maxlen=25) +ping_mean = deque(maxlen=25) diff = 0 -shuffle_ports = "y" donator_running = False job = '' debug = 'n' @@ -511,12 +521,16 @@ def start(donation_level): lang = 'german' elif locale.startswith('fr'): lang = 'french' + elif locale.startswith('jp'): + lang = 'japanese' elif locale.startswith('tr'): lang = 'turkish' elif locale.startswith('it'): lang = 'italian' elif locale.startswith('pt'): lang = 'portuguese' + if locale.startswith("zh_TW"): + lang = "chinese_Traditional" elif locale.startswith('zh'): lang = 'chinese_simplified' elif locale.startswith('th'): @@ -531,6 +545,8 @@ def start(donation_level): lang = "indonesian" elif locale.startswith("cz"): lang = "czech" + elif locale.startswith("fi"): + lang = "finnish" else: lang = 'english' else: @@ -582,24 +598,26 @@ def debug_output(text: str): def title(title: str): - if osname == 'nt': - """ - Changing the title in Windows' cmd - is easy - just use the built-in - title command - """ - ossystem('title ' + title) - else: - """ - Most *nix terminals use - this escape sequence to change - the console window title - """ - try: - print('\33]0;' + title + '\a', end='') - sys.stdout.flush() - except Exception as e: - print(e) + if not Settings.disable_title: + if osname == 'nt': + """ + Changing the title in Windows' cmd + is easy - just use the built-in + title command + """ + ossystem('title ' + title) + else: + """ + Most *nix terminals use + this escape sequence to change + the console window title + """ + try: + print('\33]0;' + title + '\a', end='') + sys.stdout.flush() + except Exception as e: + debug_output("Error setting title: " +str(e)) + Settings.disable_title = True def handler(signal_received, frame): @@ -623,7 +641,6 @@ def load_config(): global debug global rig_identifier global discord_presence - global shuffle_ports global SOC_TIMEOUT if not Path(str(Settings.DATA_DIR) + '/Settings.cfg').is_file(): @@ -637,17 +654,26 @@ def load_config(): + Fore.YELLOW + get_string('wallet') + Fore.RESET + get_string('register_warning')) - username = input( - Style.RESET_ALL + Fore.YELLOW - + get_string('ask_username') - + Fore.RESET + Style.BRIGHT) - - mining_key = input(Style.RESET_ALL + Fore.YELLOW + correct_username = False + while not correct_username: + username = input( + Style.RESET_ALL + Fore.YELLOW + + get_string('ask_username') + + Fore.RESET + Style.BRIGHT) + if not username: + username = choice(["revox", "Bilaboz"]) + + r = requests.get(f"https://server.duinocoin.com/users/{username}", + timeout=Settings.SOC_TIMEOUT).json() + correct_username = r["success"] + if not correct_username: + print(get_string("incorrect_username")) + + mining_key = "None" + if has_mining_key(username): + mining_key = input(Style.RESET_ALL + Fore.YELLOW + get_string("ask_mining_key") + Fore.RESET + Style.BRIGHT) - if not mining_key: - mining_key = "None" - else: mining_key = b64.b64encode(mining_key.encode("utf-8")).decode('utf-8') print(Style.RESET_ALL + Fore.YELLOW @@ -666,6 +692,7 @@ def load_config(): port_names.append(port.device) avrport = '' + rig_identifier = '' while True: current_port = input( Style.RESET_ALL + Fore.YELLOW @@ -673,29 +700,33 @@ def load_config(): + Fore.RESET + Style.BRIGHT) if current_port in port_names: + confirm_identifier = input( + Style.RESET_ALL + Fore.YELLOW + + get_string('ask_rig_identifier') + + Fore.RESET + Style.BRIGHT) + if confirm_identifier == 'y' or confirm_identifier == 'Y': + current_identifier = input( + Style.RESET_ALL + Fore.YELLOW + + get_string('ask_rig_name') + + Fore.RESET + Style.BRIGHT) + rig_identifier += current_identifier + else: + rig_identifier += "None" + avrport += current_port confirmation = input( Style.RESET_ALL + Fore.YELLOW + get_string('ask_anotherport') + Fore.RESET + Style.BRIGHT) - if confirmation == 'y' or confirmation == 'Y': avrport += ',' + rig_identifier += ',' else: break else: print(Style.RESET_ALL + Fore.RED + 'Please enter a valid COM port from the list above') - rig_identifier = input( - Style.RESET_ALL + Fore.YELLOW - + get_string('ask_rig_identifier') - + Fore.RESET + Style.BRIGHT) - if rig_identifier == 'y' or rig_identifier == 'Y': - rig_identifier = input( - Style.RESET_ALL + Fore.YELLOW - + get_string('ask_rig_name') - + Fore.RESET + Style.BRIGHT) else: rig_identifier = 'None' @@ -722,11 +753,10 @@ def load_config(): 'language': lang, 'identifier': rig_identifier, 'debug': 'n', - "soc_timeout": 45, - "avr_timeout": 7, + "soc_timeout": 10, + "avr_timeout": 10, "discord_presence": "y", - "periodic_report": 60, - "shuffle_ports": "y", + "periodic_report": 300, "mining_key": mining_key} with open(str(Settings.DATA_DIR) @@ -734,6 +764,7 @@ def load_config(): config.write(configfile) avrport = avrport.split(',') + rig_identifier = rig_identifier.split(',') print(Style.RESET_ALL + get_string('config_saved')) hashrate_list = [0] * len(avrport) @@ -744,11 +775,10 @@ def load_config(): avrport = avrport.replace(" ", "").split(',') donation_level = int(config["AVR Miner"]['donate']) debug = config["AVR Miner"]['debug'] - rig_identifier = config["AVR Miner"]['identifier'] + rig_identifier = config["AVR Miner"]['identifier'].split(',') Settings.SOC_TIMEOUT = int(config["AVR Miner"]["soc_timeout"]) Settings.AVR_TIMEOUT = float(config["AVR Miner"]["avr_timeout"]) discord_presence = config["AVR Miner"]["discord_presence"] - shuffle_ports = config["AVR Miner"]["shuffle_ports"] Settings.REPORT_TIME = int(config["AVR Miner"]["periodic_report"]) hashrate_list = [0] * len(avrport) @@ -775,7 +805,7 @@ def greeting(): + Style.BRIGHT + get_string('banner') + Style.RESET_ALL + Fore.MAGENTA + f' {Settings.VER}' + Fore.RESET - + ' 2019-2022') + + ' 2019-2025') print( Style.DIM + Fore.MAGENTA @@ -795,7 +825,7 @@ def greeting(): + Settings.BLOCK + Style.NORMAL + Fore.RESET + get_string('avr_on_port') + Style.BRIGHT + Fore.YELLOW - + ' '.join(avrport)) + + ', '.join(avrport)) if osname == 'nt' or osname == 'posix': print( @@ -811,12 +841,19 @@ def greeting(): + Style.BRIGHT + Fore.YELLOW + 'DUCO-S1A ⚙ AVR diff') - if rig_identifier != "None": + if rig_identifier[0] != "None" or len(rig_identifier) > 1: print( Style.DIM + Fore.MAGENTA + Settings.BLOCK + Style.NORMAL + Fore.RESET + get_string('rig_identifier') - + Style.BRIGHT + Fore.YELLOW + rig_identifier) + + Style.BRIGHT + Fore.YELLOW + ", ".join(rig_identifier)) + + print( + Style.DIM + Fore.MAGENTA + + Settings.BLOCK + Style.NORMAL + + Fore.RESET + get_string("using_config") + + Style.BRIGHT + Fore.YELLOW + + str(Settings.DATA_DIR + '/Settings.cfg')) print( Style.DIM + Fore.MAGENTA @@ -885,27 +922,20 @@ def pretty_print(sender: str = "sys0", else: fg_color = Fore.YELLOW - with thread_lock(): - print(Fore.WHITE + datetime.now().strftime(Style.DIM + "%H:%M:%S ") - + bg_color + Style.BRIGHT + " " + sender + " " - + Back.RESET + " " + fg_color + msg.strip()) + + print_queue.append(Fore.RESET + datetime.now().strftime(Style.DIM + "%H:%M:%S ") + + Style.RESET_ALL + Fore.WHITE + bg_color + Style.BRIGHT + f" {sender} " + + Style.RESET_ALL + " " + fg_color + msg.strip()) -def share_print(id, type, accept, reject, total_hashrate, - computetime, diff, ping, reject_cause=None): +def share_print(id, type, accept, reject, thread_hashrate, + total_hashrate, computetime, diff, ping, reject_cause=None): """ Produces nicely formatted CLI output for shares: HH:MM:S |avrN| ⛏ Accepted 0/0 (100%) ∙ 0.0s ∙ 0 kH/s ⚙ diff 0 k ∙ ping 0ms """ - try: - diff = get_prefix("", int(diff), 0) - except: - diff = "?" - - try: - total_hashrate = get_prefix("H/s", total_hashrate, 2) - except: - total_hashrate = "? H/s" + thread_hashrate = get_prefix("H/s", thread_hashrate, 2) + total_hashrate = get_prefix("H/s", total_hashrate, 1) if type == "accept": share_str = get_string("accepted") @@ -919,38 +949,41 @@ def share_print(id, type, accept, reject, total_hashrate, share_str += f"{Style.NORMAL}({reject_cause}) " fg_color = Fore.RED - with thread_lock(): - print(Fore.WHITE + datetime.now().strftime(Style.DIM + "%H:%M:%S ") - + Fore.WHITE + Style.BRIGHT + Back.MAGENTA + Fore.RESET - + " avr" + str(id) + " " + Back.RESET - + fg_color + Settings.PICK + share_str + Fore.RESET - + str(accept) + "/" + str(accept + reject) + Fore.MAGENTA - + " (" + str(round(accept / (accept + reject) * 100)) + "%)" - + Style.NORMAL + Fore.RESET - + " ∙ " + str("%04.1f" % float(computetime)) + "s" - + Style.NORMAL + " ∙ " + Fore.BLUE + Style.BRIGHT - + str(total_hashrate) + Fore.RESET + Style.NORMAL - + Settings.COG + f" diff {diff} ∙ " + Fore.CYAN - + f"ping {(int(ping))}ms") - - -def mine_avr(com, threadid, fastest_pool): - global hashrate + print_queue.append( + Fore.RESET + datetime.now().strftime(Style.DIM + "%H:%M:%S ") + + Style.RESET_ALL + Fore.WHITE + Style.BRIGHT + Back.MAGENTA + + " avr" + str(id) + " " + Style.RESET_ALL + fg_color + + Settings.PICK + share_str + Fore.RESET + + str(accept) + "/" + str(accept + reject) + Fore.MAGENTA + + " (" + str(round(accept / (accept + reject) * 100)) + "%)" + + Style.NORMAL + Fore.RESET + + " ∙ " + str("%04.1f" % float(computetime)) + "s" + + Style.NORMAL + " ∙ " + Fore.BLUE + Style.BRIGHT + + f"{thread_hashrate}" + Style.DIM + + f" ({total_hashrate} {get_string('hashrate_total')})" + Fore.RESET + Style.NORMAL + + Settings.COG + f" {get_string('diff')} {diff} ∙ " + Fore.CYAN + + f"ping {(int(ping))}ms") + + +def mine_avr(com, threadid, fastest_pool, thread_rigid): + global hashrate, shares start_time = time() report_shares = 0 last_report_share = 0 while True: + shares = [0, 0, 0] while True: try: ser.close() pretty_print('sys' + port_num(com), - f"Closed COM port {com}", 'success') + f"No response from the board. Closed port {com}", + 'success') sleep(2) except: pass try: ser = Serial(com, baudrate=int(Settings.BAUDRATE), - timeout=float(Settings.AVR_TIMEOUT)) + timeout=int(Settings.AVR_TIMEOUT)) """ Sleep after opening the port to make sure the board resets properly after @@ -992,7 +1025,7 @@ def mine_avr(com, threadid, fastest_pool): 'success') else: pretty_print( - 'sys0', f' Miner is outdated (v{Settings.VER}) -' + 'sys0', f"{get_string('miner_is_outdated')} (v{Settings.VER}) -" + get_string('server_is_on_version') + server_version + Style.NORMAL + Fore.RESET + get_string('update_warning'), @@ -1006,7 +1039,7 @@ def mine_avr(com, threadid, fastest_pool): motd = motd.replace("\n", "\n\t\t") pretty_print("net" + str(threadid), - " MOTD: " + Fore.RESET + get_string("motd") + Fore.RESET + Style.NORMAL + str(motd), "success") break @@ -1022,9 +1055,66 @@ def mine_avr(com, threadid, fastest_pool): + get_string('mining_algorithm') + str(com) + ')', 'success') - while True: + # Perform a hash test to assign the starting diff + prev_hash = "ba29a15896fd2d792d5c4b60668bf2b9feebc51d" + exp_hash = "d0beba883d7e8cd119ea2b0e09b78f60f29e0968" + exp_result = 50 + retries = 0 + while retries < 3: try: + debug_output(com + ': Sending hash test to the board') + ser.write(bytes(str(prev_hash + + Settings.SEPARATOR + + exp_hash + + Settings.SEPARATOR + + "10" + + Settings.SEPARATOR), + encoding=Settings.ENCODING)) + debug_output(com + ': Reading hash test from the board') + result = ser.read_until(b'\n').decode().strip().split(',') + ser.flush() + + if result[0] and result[1]: + _ = int(result[0], 2) + debug_output(com + f': Result: {result[0]}') + else: + raise Exception("No data received from the board") + if int(result[0], 2) != exp_result: + raise Exception(com + f': Incorrect result received!') + computetime = round(int(result[1], 2) / 1000000, 5) + num_res = int(result[0], 2) + hashrate_test = round(num_res / computetime, 2) + break + except Exception as e: + debug_output(str(e)) + retries += 1 + else: + pretty_print('sys' + port_num(com), + f"Can't start mining on {com}" + Fore.RESET + + f" - board keeps responding improperly. " + + "Check if the code has been uploaded correctly " + + "and your device is supported by Duino-Coin.", + 'error') + break + + start_diff = "AVR" + if hashrate_test > 1000: + start_diff = "DUE" + elif hashrate_test > 550: + start_diff = "ARM" + elif hashrate_test > 380: + start_diff = "MEGA" + + pretty_print('sys' + port_num(com), + get_string('hashrate_test') + + get_prefix("H/s", hashrate_test, 2) + + Fore.RESET + Style.BRIGHT + + get_string('hashrate_test_diff') + + start_diff) + + while True: + try: if config["AVR Miner"]["mining_key"] != "None": key = b64.b64decode(config["AVR Miner"]["mining_key"]).decode() else: @@ -1035,7 +1125,7 @@ def mine_avr(com, threadid, fastest_pool): + Settings.SEPARATOR + str(username) + Settings.SEPARATOR - + 'AVR' + + start_diff + Settings.SEPARATOR + str(key) ) @@ -1072,7 +1162,6 @@ def mine_avr(com, threadid, fastest_pool): encoding=Settings.ENCODING)) debug_output(com + ': Reading result from the board') result = ser.read_until(b'\n').decode().strip().split(',') - ser.flush() if result[0] and result[1]: _ = int(result[0], 2) @@ -1082,6 +1171,7 @@ def mine_avr(com, threadid, fastest_pool): raise Exception("No data received from AVR") except Exception as e: debug_output(com + f': Retrying data read: {e}') + ser.flush() retry_counter += 1 continue @@ -1089,13 +1179,14 @@ def mine_avr(com, threadid, fastest_pool): break try: - computetime = round(int(result[1], 2) / 1000000, 3) + computetime = round(int(result[1], 2) / 1000000, 5) num_res = int(result[0], 2) hashrate_t = round(num_res / computetime, 2) hashrate_mean.append(hashrate_t) - hashrate = mean(hashrate_mean[-5:]) + hashrate = mean(hashrate_mean) hashrate_list[threadid] = hashrate + total_hashrate = sum(hashrate_list) except Exception as e: pretty_print('sys' + port_num(com), get_string('mining_avr_connection_error') @@ -1112,7 +1203,7 @@ def mine_avr(com, threadid, fastest_pool): + Settings.SEPARATOR + f'Official AVR Miner {Settings.VER}' + Settings.SEPARATOR - + str(rig_identifier) + + str(thread_rigid) + Settings.SEPARATOR + str(result[2])) @@ -1123,7 +1214,7 @@ def mine_avr(com, threadid, fastest_pool): time_delta = (responsetimestop - responsetimetart).microseconds ping_mean.append(round(time_delta / 1000)) - ping = mean(ping_mean[-10:]) + ping = mean(ping_mean) diff = get_prefix("", int(diff), 0) debug_output(com + f': retrieved feedback: {" ".join(feedback)}') except Exception as e: @@ -1137,32 +1228,32 @@ def mine_avr(com, threadid, fastest_pool): if feedback[0] == 'GOOD': shares[0] += 1 - printlock.acquire() share_print(port_num(com), "accept", - shares[0], shares[1], hashrate, + shares[0], shares[1], hashrate, total_hashrate, computetime, diff, ping) - printlock.release() + elif feedback[0] == 'BLOCK': shares[0] += 1 shares[2] += 1 - printlock.acquire() share_print(port_num(com), "block", - shares[0], shares[1], hashrate, + shares[0], shares[1], hashrate, total_hashrate, computetime, diff, ping) - printlock.release() + elif feedback[0] == 'BAD': shares[1] += 1 - printlock.acquire() share_print(port_num(com), "reject", - shares[0], shares[1], hashrate, + shares[0], shares[1], hashrate, total_hashrate, computetime, diff, ping, feedback[1]) - printlock.release() + else: - printlock.acquire() share_print(port_num(com), "reject", - shares[0], shares[1], hashrate, + shares[0], shares[1], hashrate, total_hashrate, computetime, diff, ping, feedback) - printlock.release() + + if shares[0] % 100 == 0 and shares[0] > 1: + pretty_print("sys0", + f"{get_string('surpassed')} {shares[0]} {get_string('surpassed_shares')}", + "success") title(get_string('duco_avr_miner') + str(Settings.VER) + f') - {shares[0]}/{(shares[0] + shares[1])}' @@ -1181,22 +1272,28 @@ def mine_avr(com, threadid, fastest_pool): def periodic_report(start_time, end_time, shares, - block, hashrate, uptime): + blocks, hashrate, uptime): + """ + Displays nicely formated uptime stats + """ seconds = round(end_time - start_time) - pretty_print("sys0", - " " + get_string('periodic_mining_report') + pretty_print("sys0", get_string("periodic_mining_report") + Fore.RESET + Style.NORMAL - + get_string('report_period') - + str(seconds) + get_string('report_time') - + get_string('report_body1') - + str(shares) + get_string('report_body2') + + get_string("report_period") + + str(seconds) + get_string("report_time") + + get_string("report_body1") + + str(shares) + get_string("report_body2") + str(round(shares/seconds, 1)) - + get_string('report_body3') - + get_string('report_body7') + str(block) - + get_string('report_body4') - + str(int(hashrate)) + " H/s" + get_string('report_body5') - + str(int(hashrate*seconds)) + get_string('report_body6') - + get_string('total_mining_time') + str(uptime), "success") + + get_string("report_body3") + + get_string("report_body7") + + str(blocks) + + get_string("report_body4") + + str(get_prefix("H/s", hashrate, 2)) + + get_string("report_body5") + + str(int(hashrate*seconds)) + + get_string("report_body6") + + get_string("total_mining_time") + + str(uptime) + "\n", "success") def calculate_uptime(start_time): @@ -1213,8 +1310,23 @@ def calculate_uptime(start_time): return str(round(uptime)) + get_string('uptime_seconds') +print_queue = [] +def print_queue_handler(): + """ + Prevents broken console logs with many threads + """ + while True: + if len(print_queue): + message = print_queue[0] + with printlock: + print(message) + print_queue.pop(0) + sleep(0.01) + + if __name__ == '__main__': init(autoreset=True) + Thread(target=print_queue_handler).start() title(f"{get_string('duco_avr_miner')}{str(Settings.VER)})") if sys.platform == "win32": @@ -1258,7 +1370,7 @@ def calculate_uptime(start_time): for port in avrport: Thread(target=mine_avr, args=(port, threadid, - fastest_pool)).start() + fastest_pool, rig_identifier[threadid])).start() threadid += 1 except Exception as e: debug_output(f'Error launching AVR thread(s): {e}') diff --git a/Arduino_Code/Arduino_Code.ino b/Arduino_Code/Arduino_Code.ino index cfb88f6d..1947373e 100644 --- a/Arduino_Code/Arduino_Code.ino +++ b/Arduino_Code/Arduino_Code.ino @@ -4,9 +4,9 @@ ( _ \( )( )(_ _)( \( )( _ )___ / __)( _ )(_ _)( \( ) )(_) ))(__)( _)(_ ) ( )(_)((___)( (__ )(_)( _)(_ ) ( (____/(______)(____)(_)\_)(_____) \___)(_____)(____)(_)\_) - Official code for Arduino boards version 3.0 + Official code for Arduino boards (and relatives) version 4.3 - Duino-Coin Team & Community 2019-2022 © MIT Licensed + Duino-Coin Team & Community 2019-2024 © MIT Licensed https://duinocoin.com https://github.com/revoxhere/duino-coin If you don't know where to start, visit official website and navigate to @@ -20,27 +20,31 @@ for default settings use -O0. -O may be a good tradeoff between both */ #ifndef LED_BUILTIN #define LED_BUILTIN 13 #endif +#define SEP_TOKEN "," +#define END_TOKEN "\n" /* For 8-bit microcontrollers we should use 16 bit variables since the difficulty is low, for all the other cases should be 32 bits. */ #if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR) -typedef uint16_t uintDiff; +typedef uint32_t uintDiff; #else typedef uint32_t uintDiff; #endif // Arduino identifier library - https://github.com/ricaun #include "uniqueID.h" -#include "sha1.h" -// Create globals -String lastblockhash = ""; -String newblockhash = ""; +#include "duco_hash.h" + +String get_DUCOID() { + String ID = "DUCOID"; + char buff[4]; + for (size_t i = 0; i < 8; i++) { + sprintf(buff, "%02X", (uint8_t)UniqueID8[i]); + ID += buff; + } + return ID; +} + String DUCOID = ""; -uintDiff difficulty = 0; -uintDiff ducos1result = 0; -// 40+40+20+3 is the maximum size of a job -const uint16_t job_maxsize = 104; -uint8_t job[job_maxsize]; -Sha1Wrapper Sha1_base; void setup() { // Prepare built-in led pin as output @@ -54,83 +58,107 @@ void setup() { Serial.flush(); } +void lowercase_hex_to_bytes(char const * hexDigest, uint8_t * rawDigest) { + for (uint8_t i = 0, j = 0; j < SHA1_HASH_LEN; i += 2, j += 1) { + uint8_t x = hexDigest[i]; + uint8_t b = x >> 6; + uint8_t r = ((x & 0xf) | (b << 3)) + b; + + x = hexDigest[i + 1]; + b = x >> 6; + + rawDigest[j] = (r << 4) | (((x & 0xf) | (b << 3)) + b); + } +} + // DUCO-S1A hasher -uintDiff ducos1a(String lastblockhash, String newblockhash, - uintDiff difficulty) { - newblockhash.toUpperCase(); - const char *c = newblockhash.c_str(); - uint8_t final_len = newblockhash.length() >> 1; - for (uint8_t i = 0, j = 0; j < final_len; i += 2, j++) - job[j] = ((((c[i] & 0x1F) + 9) % 25) << 4) + ((c[i + 1] & 0x1F) + 9) % 25; - - // Difficulty loop +uintDiff ducos1a(char const * prevBlockHash, char const * targetBlockHash, uintDiff difficulty) { #if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR) // If the difficulty is too high for AVR architecture then return 0 if (difficulty > 655) return 0; #endif - Sha1_base.init(); - Sha1_base.print(lastblockhash); - for (uintDiff ducos1res = 0; ducos1res < difficulty * 100 + 1; ducos1res++) { - Sha1 = Sha1_base; - Sha1.print(String(ducos1res)); - // Get SHA1 result - uint8_t *hash_bytes = Sha1.result(); - if (memcmp(hash_bytes, job, SHA1_HASH_LEN * sizeof(char)) == 0) { - // If expected hash is equal to the found hash, return the result - return ducos1res; - } - } - return 0; + + uint8_t target[SHA1_HASH_LEN]; + lowercase_hex_to_bytes(targetBlockHash, target); + + uintDiff const maxNonce = difficulty * 100 + 1; + return ducos1a_mine(prevBlockHash, target, maxNonce); } -String get_DUCOID() { - String ID = "DUCOID"; - char buff[4]; - for (size_t i = 0; i < 8; i++) { - sprintf(buff, "%02X", (uint8_t)UniqueID8[i]); - ID += buff; +uintDiff ducos1a_mine(char const * prevBlockHash, uint8_t const * target, uintDiff maxNonce) { + static duco_hash_state_t hash; + duco_hash_init(&hash, prevBlockHash); + + char nonceStr[10 + 1]; + for (uintDiff nonce = 0; nonce < maxNonce; nonce++) { + ultoa(nonce, nonceStr, 10); + + uint8_t const * hash_bytes = duco_hash_try_nonce(&hash, nonceStr); + if (memcmp(hash_bytes, target, SHA1_HASH_LEN) == 0) { + return nonce; + } } - return ID; + + return 0; } void loop() { // Wait for serial data - if (Serial.available() > 0) { - memset(job, 0, job_maxsize); - // Read last block hash - lastblockhash = Serial.readStringUntil(','); - // Read expected hash - newblockhash = Serial.readStringUntil(','); - // Read difficulty - difficulty = strtoul(Serial.readStringUntil(',').c_str(), NULL, 10); - // Clearing the receive buffer reading one job. - while (Serial.available()) Serial.read(); - // Turn on the built-in led - #if defined(ARDUINO_ARCH_AVR) - PORTB = PORTB | B00100000; - #else - digitalWrite(LED_BUILTIN, LOW); - #endif - // Start time measurement - uint32_t startTime = micros(); - // Call DUCO-S1A hasher - ducos1result = ducos1a(lastblockhash, newblockhash, difficulty); - // Calculate elapsed time - uint32_t elapsedTime = micros() - startTime; - // Turn on the built-in led - #if defined(ARDUINO_ARCH_AVR) - PORTB = PORTB & B11011111; - #else - digitalWrite(LED_BUILTIN, HIGH); - #endif - // Clearing the receive buffer before sending the result. - while (Serial.available()) Serial.read(); - // Send result back to the program with share time - Serial.print(String(ducos1result, 2) - + "," - + String(elapsedTime, 2) - + "," - + String(DUCOID) - + "\n"); + if (Serial.available() <= 0) { + return; + } + + // Reserve 1 extra byte for comma separator (and later zero) + char lastBlockHash[40 + 1]; + char newBlockHash[40 + 1]; + + // Read last block hash + if (Serial.readBytesUntil(',', lastBlockHash, 41) != 40) { + return; + } + lastBlockHash[40] = 0; + + // Read expected hash + if (Serial.readBytesUntil(',', newBlockHash, 41) != 40) { + return; } + newBlockHash[40] = 0; + + // Read difficulty + uintDiff difficulty = strtoul(Serial.readStringUntil(',').c_str(), NULL, 10); + // Clearing the receive buffer reading one job. + while (Serial.available()) Serial.read(); + // Turn off the built-in led + #if defined(ARDUINO_ARCH_AVR) + PORTB = PORTB | B00100000; + #else + digitalWrite(LED_BUILTIN, LOW); + #endif + + // Start time measurement + uint32_t startTime = micros(); + + // Call DUCO-S1A hasher + uintDiff ducos1result = ducos1a(lastBlockHash, newBlockHash, difficulty); + + // Calculate elapsed time + uint32_t elapsedTime = micros() - startTime; + + // Turn on the built-in led + #if defined(ARDUINO_ARCH_AVR) + PORTB = PORTB & B11011111; + #else + digitalWrite(LED_BUILTIN, HIGH); + #endif + + // Clearing the receive buffer before sending the result. + while (Serial.available()) Serial.read(); + + // Send result back to the program with share time + Serial.print(String(ducos1result, 2) + + SEP_TOKEN + + String(elapsedTime, 2) + + SEP_TOKEN + + String(DUCOID) + + END_TOKEN); } diff --git a/Arduino_Code/backend.cpp b/Arduino_Code/backend.cpp deleted file mode 100644 index 70c9d201..00000000 --- a/Arduino_Code/backend.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// This file is part of cryptosuite2. // -// // -// cryptosuite2 is free software: you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation, either version 3 of the License, or // -// (at your option) any later version. // -// // -// cryptosuite2 is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with cryptosuite2. If not, see . // -// // - -#pragma GCC optimize ("-Ofast") -#include "config.h" - -#ifndef SHA1_DISABLED -#include "sha1/constants.c" -#include "sha1/hash.c" -#include "sha1/types.c" -#include "sha1/sha1.c" -#endif diff --git a/Arduino_Code/config.h b/Arduino_Code/config.h deleted file mode 100644 index 4406388a..00000000 --- a/Arduino_Code/config.h +++ /dev/null @@ -1,28 +0,0 @@ -// This file is part of cryptosuite2. // -// // -// cryptosuite2 is free software: you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation, either version 3 of the License, or // -// (at your option) any later version. // -// // -// cryptosuite2 is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with cryptosuite2. If not, see . // -// // - -#pragma GCC optimize ("-Ofast") - -// include the module config first, -// overwrite it in the arduino interface config. -#include "sha1/default.h" - -#ifndef SHA_CONFIG_H_ -#define SHA_CONFIG_H_ - -// No changes yet. - -#endif diff --git a/Arduino_Code/duco_hash.cpp b/Arduino_Code/duco_hash.cpp new file mode 100644 index 00000000..48113816 --- /dev/null +++ b/Arduino_Code/duco_hash.cpp @@ -0,0 +1,150 @@ +#include "duco_hash.h" + +#pragma GCC optimize ("-Ofast") + +#define sha1_rotl(bits,word) (((word) << (bits)) | ((word) >> (32 - (bits)))) + +void duco_hash_block(duco_hash_state_t * hasher) { + // NOTE: keeping this static improves performance quite a lot + static uint32_t w[16]; + + for (uint8_t i = 0, i4 = 0; i < 16; i++, i4 += 4) { + w[i] = (uint32_t(hasher->buffer[i4]) << 24) | + (uint32_t(hasher->buffer[i4 + 1]) << 16) | + (uint32_t(hasher->buffer[i4 + 2]) << 8) | + (uint32_t(hasher->buffer[i4 + 3])); + } + + uint32_t a = hasher->tempState[0]; + uint32_t b = hasher->tempState[1]; + uint32_t c = hasher->tempState[2]; + uint32_t d = hasher->tempState[3]; + uint32_t e = hasher->tempState[4]; + + for (uint8_t i = 10; i < 80; i++) { + if (i >= 16) { + w[i & 15] = sha1_rotl(1, w[(i-3) & 15] ^ w[(i-8) & 15] ^ w[(i-14) & 15] ^ w[(i-16) & 15]); + } + + uint32_t temp = sha1_rotl(5, a) + e + w[i & 15]; + if (i < 20) { + temp += (b & c) | ((~b) & d); + temp += 0x5a827999; + } else if(i < 40) { + temp += b ^ c ^ d; + temp += 0x6ed9eba1; + } else if(i < 60) { + temp += (b & c) | (b & d) | (c & d); + temp += 0x8f1bbcdc; + } else { + temp += b ^ c ^ d; + temp += 0xca62c1d6; + } + + e = d; + d = c; + c = sha1_rotl(30, b); + b = a; + a = temp; + } + + a += 0x67452301; + b += 0xefcdab89; + c += 0x98badcfe; + d += 0x10325476; + e += 0xc3d2e1f0; + + hasher->result[0 * 4 + 0] = a >> 24; + hasher->result[0 * 4 + 1] = a >> 16; + hasher->result[0 * 4 + 2] = a >> 8; + hasher->result[0 * 4 + 3] = a; + hasher->result[1 * 4 + 0] = b >> 24; + hasher->result[1 * 4 + 1] = b >> 16; + hasher->result[1 * 4 + 2] = b >> 8; + hasher->result[1 * 4 + 3] = b; + hasher->result[2 * 4 + 0] = c >> 24; + hasher->result[2 * 4 + 1] = c >> 16; + hasher->result[2 * 4 + 2] = c >> 8; + hasher->result[2 * 4 + 3] = c; + hasher->result[3 * 4 + 0] = d >> 24; + hasher->result[3 * 4 + 1] = d >> 16; + hasher->result[3 * 4 + 2] = d >> 8; + hasher->result[3 * 4 + 3] = d; + hasher->result[4 * 4 + 0] = e >> 24; + hasher->result[4 * 4 + 1] = e >> 16; + hasher->result[4 * 4 + 2] = e >> 8; + hasher->result[4 * 4 + 3] = e; +} + +void duco_hash_init(duco_hash_state_t * hasher, char const * prevHash) { + memcpy(hasher->buffer, prevHash, 40); + + if (prevHash == (void*)(0xffffffff)) { + // NOTE: THIS IS NEVER CALLED + // This is here to keep a live reference to hash_block + // Otherwise GCC tries to inline it entirely into the main loop + // Which causes massive perf degradation + duco_hash_block(nullptr); + } + + // Do first 10 rounds as these are going to be the same all time + + uint32_t a = 0x67452301; + uint32_t b = 0xefcdab89; + uint32_t c = 0x98badcfe; + uint32_t d = 0x10325476; + uint32_t e = 0xc3d2e1f0; + + static uint32_t w[10]; + + for (uint8_t i = 0, i4 = 0; i < 10; i++, i4 += 4) { + w[i] = (uint32_t(hasher->buffer[i4]) << 24) | + (uint32_t(hasher->buffer[i4 + 1]) << 16) | + (uint32_t(hasher->buffer[i4 + 2]) << 8) | + (uint32_t(hasher->buffer[i4 + 3])); + } + + for (uint8_t i = 0; i < 10; i++) { + uint32_t temp = sha1_rotl(5, a) + e + w[i & 15]; + temp += (b & c) | ((~b) & d); + temp += 0x5a827999; + + e = d; + d = c; + c = sha1_rotl(30, b); + b = a; + a = temp; + } + + hasher->tempState[0] = a; + hasher->tempState[1] = b; + hasher->tempState[2] = c; + hasher->tempState[3] = d; + hasher->tempState[4] = e; +} + +void duco_hash_set_nonce(duco_hash_state_t * hasher, char const * nonce) { + uint8_t * b = hasher->buffer; + + uint8_t off = SHA1_HASH_LEN * 2; + for (uint8_t i = 0; i < 10 && nonce[i] != 0; i++) { + b[off++] = nonce[i]; + } + + uint8_t total_bytes = off; + + b[off++] = 0x80; + while (off < 62) { + b[off++] = 0; + } + + b[62] = total_bytes >> 5; + b[63] = total_bytes << 3; +} + +uint8_t const * duco_hash_try_nonce(duco_hash_state_t * hasher, char const * nonce) { + duco_hash_set_nonce(hasher, nonce); + duco_hash_block(hasher); + + return hasher->result; +} diff --git a/Arduino_Code/duco_hash.h b/Arduino_Code/duco_hash.h new file mode 100644 index 00000000..462b75a9 --- /dev/null +++ b/Arduino_Code/duco_hash.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#define SHA1_BLOCK_LEN 64 +#define SHA1_HASH_LEN 20 + +struct duco_hash_state_t { + uint8_t buffer[SHA1_BLOCK_LEN]; + uint8_t result[SHA1_HASH_LEN]; + uint32_t tempState[5]; + + uint8_t block_offset; + uint8_t total_bytes; +}; + +void duco_hash_init(duco_hash_state_t * hasher, char const * prevHash); + +uint8_t const * duco_hash_try_nonce(duco_hash_state_t * hasher, char const * nonce); diff --git a/Arduino_Code/sha1.cpp b/Arduino_Code/sha1.cpp deleted file mode 100644 index ed8eebf9..00000000 --- a/Arduino_Code/sha1.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// This file is part of cryptosuite2. // -// // -// cryptosuite2 is free software: you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation, either version 3 of the License, or // -// (at your option) any later version. // -// // -// cryptosuite2 is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with cryptosuite2. If not, see . // -// // - -#pragma GCC optimize ("-Ofast") - -#include "sha1.h" - -#ifndef SHA1_DISABLED -#ifndef SHA1_DISABLE_WRAPPER -void Sha1Wrapper::init(void) -{ - sha1_hasher_init(&_hasher); -} - -#ifdef SHA1_ENABLE_HMAC -void Sha1Wrapper::initHmac(const uint8_t * secret, uint16_t secretLength) -{ - sha1_hasher_init_hmac(&_hasher, secret, secretLength); -} - -uint8_t * Sha1Wrapper::resultHmac(void) -{ - return sha1_hasher_gethmac(&_hasher); -} -#endif - - -size_t Sha1Wrapper::write(uint8_t byte) -{ - if(sha1_hasher_putc(&_hasher, byte) == byte) - { - return 1; - } - return 0; -} - -uint8_t * Sha1Wrapper::result(void) -{ - return sha1_hasher_gethash(&_hasher); -} - -Sha1Wrapper Sha1; - -#endif -#endif diff --git a/Arduino_Code/sha1.h b/Arduino_Code/sha1.h deleted file mode 100644 index 50bfe622..00000000 --- a/Arduino_Code/sha1.h +++ /dev/null @@ -1,48 +0,0 @@ -// This file is part of cryptosuite2. // -// // -// cryptosuite2 is free software: you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation, either version 3 of the License, or // -// (at your option) any later version. // -// // -// cryptosuite2 is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with cryptosuite2. If not, see . // -// // - -#pragma GCC optimize ("-Ofast") - -#include "config.h" - -#ifndef Sha1_h -#define Sha1_h - -#include -#include "Print.h" -#include "sha1/sha1.h" - -#ifndef SHA1_DISABLE_WRAPPER -class Sha1Wrapper : public Print -{ - public: - void init(void); - uint8_t * result(void); -#ifdef SHA1_ENABLE_HMAC - void initHmac(const uint8_t * secret, uint16_t secretLength); - uint8_t * resultHmac(void); -#endif - virtual size_t write(uint8_t); - using Print::write; - private: - struct sha1_hasher_s _hasher; - -}; - -extern Sha1Wrapper Sha1; -#endif - -#endif diff --git a/Arduino_Code/sha1/basic.h b/Arduino_Code/sha1/basic.h deleted file mode 100644 index 1f021c7c..00000000 --- a/Arduino_Code/sha1/basic.h +++ /dev/null @@ -1,26 +0,0 @@ -// This file is part of cryptosuite2. // -// // -// cryptosuite2 is free software: you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation, either version 3 of the License, or // -// (at your option) any later version. // -// // -// cryptosuite2 is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with cryptosuite2. If not, see . // -// // - -#ifndef SHA1_BASIC_H_ -#define SHA1_BASIC_H_ - -#include "default.h" -#include - -#define sha1_rotl(bits,word) (((word) << (bits)) | ((word) >> (32 - (bits)))) - -#endif - diff --git a/Arduino_Code/sha1/constants.c b/Arduino_Code/sha1/constants.c deleted file mode 100644 index 4d044969..00000000 --- a/Arduino_Code/sha1/constants.c +++ /dev/null @@ -1,30 +0,0 @@ -// This file is part of cryptosuite2. // -// // -// cryptosuite2 is free software: you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation, either version 3 of the License, or // -// (at your option) any later version. // -// // -// cryptosuite2 is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with cryptosuite2. If not, see . // -// // - -#include "constants.h" - -const uint32_t sha1_init_state[SHA1_HASH_LEN / 4] PROGMEM = -{ - 0x67452301, 0xefcdab89, 0x98badcfe, - 0x10325476, 0xc3d2e1f0 -}; - -const uint32_t sha1_constants[4] PROGMEM = -{ - 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 -}; - - diff --git a/Arduino_Code/sha1/constants.h b/Arduino_Code/sha1/constants.h deleted file mode 100644 index 294d72d0..00000000 --- a/Arduino_Code/sha1/constants.h +++ /dev/null @@ -1,58 +0,0 @@ -// This file is part of cryptosuite2. // -// // -// cryptosuite2 is free software: you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation, either version 3 of the License, or // -// (at your option) any later version. // -// // -// cryptosuite2 is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with cryptosuite2. If not, see . // -// // - -#ifndef SHA1_CONSTANTS_H_ -#define SHA1_CONSTANTS_H_ - -#include "default.h" -#include - -#define SHA1_BLOCK_LEN 64 -#define SHA1_HASH_LEN 20 - -#include "Arduino.h" - -extern const uint32_t sha1_init_state[SHA1_HASH_LEN / 4] PROGMEM; - -extern const uint32_t sha1_constants[4] PROGMEM; - - -// From RFC3174 (http://www.faqs.org/rfcs/rfc3174.html) -// (Section 5): -// -// A sequence of constant words K(0), K(1), ... , K(79) is used in the -// SHA-1. In hex these are given by -// -// K(t) = 5A827999 ( 0 <= t <= 19) -// -// K(t) = 6ED9EBA1 (20 <= t <= 39) -// -// K(t) = 8F1BBCDC (40 <= t <= 59) -// -// K(t) = CA62C1D6 (60 <= t <= 79). -// -// This can be achieved using an integer division by 20 and only 4 constants. - -#define sha1_k(i) pgm_read_dword(sha1_constants + (i / 20)) - - -#ifdef SHA1_ENABLE_HMAC -#define SHA1_HMAC_IPAD 0x36 -#define SHA1_HMAC_OPAD 0x5c -#endif - -#endif - diff --git a/Arduino_Code/sha1/default.h b/Arduino_Code/sha1/default.h deleted file mode 100644 index 2474e9e8..00000000 --- a/Arduino_Code/sha1/default.h +++ /dev/null @@ -1,31 +0,0 @@ -// This file is part of cryptosuite2. // -// // -// cryptosuite2 is free software: you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation, either version 3 of the License, or // -// (at your option) any later version. // -// // -// cryptosuite2 is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with cryptosuite2. If not, see . // -// // - -// This file is the module config header -// when the arduino interface is NOT used. -// -// If you want to use the library with Arduino, -// edit sha/config.h. If you use a proper build system, -// use this config file for sha1 and sha/sha256/default.h -// for sha256. -#ifndef SHA1_DEFAULT_H_ -#define SHA1_DEFAULT_H_ - -#define SHA1_ENABLE_HMAC - - -#endif - diff --git a/Arduino_Code/sha1/hash.c b/Arduino_Code/sha1/hash.c deleted file mode 100644 index 143ed63e..00000000 --- a/Arduino_Code/sha1/hash.c +++ /dev/null @@ -1,222 +0,0 @@ -// This file is part of cryptosuite2. // -// // -// cryptosuite2 is free software: you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation, either version 3 of the License, or // -// (at your option) any later version. // -// // -// cryptosuite2 is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with cryptosuite2. If not, see . // -// // - -#include "hash.h" -#include -#include - - -void sha1_hash_block(sha1_hasher_t hasher) -{ - uint8_t i; - uint32_t a, b, c, d, e, temp; - - // XXX: Omit initializing the message schedule. - // See how I did this below. - // Allocating the message schedule would eat 2k RAM - // which is a no-go on an AVR. - uint8_t i4; - // On x86 we have to change the byte order, because... - // I actually do not know. - for(i = i4 = 0; i < 16; i++, i4 += 4) - { - hasher->buffer.words[i] = (((uint32_t)hasher->buffer.bytes[i4]) << 24) | - (((uint32_t)hasher->buffer.bytes[i4 + 1]) << 16) | - (((uint32_t)hasher->buffer.bytes[i4 + 2]) << 8) | - (((uint32_t)hasher->buffer.bytes[i4 + 3])); - } - - a = hasher->state.words[0]; - b = hasher->state.words[1]; - c = hasher->state.words[2]; - d = hasher->state.words[3]; - e = hasher->state.words[4]; - - for(i = 0; i < 80; i++) - { - // XXX: - // This part of the computation omits the message schedule - // W as described in https://tools.ietf.org/html/rfc4634 - // The first 16 words of the message schedule is just the block - // anyways and the computation of the message schedule uses only - // the last 16 words, so we can do that. - if( i >= 16 ) - { - hasher->buffer.words[i & 15] = sha1_rotl(1 - , hasher->buffer.words[(i - 3)& 15] - ^ hasher->buffer.words[(i - 8)& 15] - ^ hasher->buffer.words[(i - 14)& 15] - ^ hasher->buffer.words[(i - 16)& 15]); - } - - temp = sha1_rotl(5, a) + e + hasher->buffer.words[i & 15] + sha1_k(i); - if(i < 20) - { - temp += (b & c) | ((~b) & d); - } - else if(i < 40) - { - temp += b ^ c ^ d; - } - else if(i < 60) - { - temp += (b & c) | (b & d) | (c & d); - } - else - { - temp += b ^ c ^ d; - } - - - - - - e = d; - d = c; - c = sha1_rotl(30, b); - b = a; - a = temp; - } - hasher->state.words[0] += a; - hasher->state.words[1] += b; - hasher->state.words[2] += c; - hasher->state.words[3] += d; - hasher->state.words[4] += e; - -} - - - -void sha1_hasher_add_byte(sha1_hasher_t hasher, uint8_t byte) -{ - hasher->buffer.bytes[hasher->block_offset] = byte; - hasher->block_offset++; - if(hasher->block_offset == SHA1_BLOCK_LEN) - { - sha1_hash_block(hasher); - hasher->block_offset = 0; - } -} - -/** - * NOTE: once the block has been pad'ed the hasher will - * produce nonsense data. Therefore putc will return EOF - * once the hasher has been pad'ed (this happens, when - * sha1_hasher_gethash or sha1_hasher_gethmac are invoced). - * */ -uint8_t sha1_hasher_putc(sha1_hasher_t hasher, uint8_t byte) -{ - if(hasher->_lock) - { - return EOF; - } - hasher->total_bytes++; - sha1_hasher_add_byte(hasher, byte); - return byte; - -} - - - -void sha1_hasher_pad(sha1_hasher_t hasher) -{ - hasher->_lock = 1; - sha1_hasher_add_byte(hasher, 0x80); - while(hasher->block_offset != 56) - { - sha1_hasher_add_byte(hasher, 0); - } - - // FIXME: - // Use a loop for this. - sha1_hasher_add_byte(hasher, hasher->total_bytes * 8 >> 56); - sha1_hasher_add_byte(hasher, hasher->total_bytes * 8 >> 48); - sha1_hasher_add_byte(hasher, hasher->total_bytes * 8 >> 40); - sha1_hasher_add_byte(hasher, hasher->total_bytes * 8 >> 32); - sha1_hasher_add_byte(hasher, hasher->total_bytes * 8 >> 24); - sha1_hasher_add_byte(hasher, hasher->total_bytes * 8 >> 16); - sha1_hasher_add_byte(hasher, hasher->total_bytes * 8 >> 8); - sha1_hasher_add_byte(hasher, hasher->total_bytes * 8); - -} - -uint8_t * sha1_hasher_gethash(sha1_hasher_t hasher) -{ - sha1_hasher_pad(hasher); - uint8_t i; - - // switch byte order. - for(i = 0; i < (SHA1_HASH_LEN / 4); i++) - { - uint32_t a, b; - a = hasher->state.words[i]; - b = a << 24; - b |= ( a << 8) & 0x00ff0000; - b |= ( a >> 8) & 0x0000ff00; - b |= a >> 24; - hasher->state.words[i] = b; - } - return hasher->state.bytes; -} - - -#ifdef SHA1_ENABLE_HMAC -void sha1_hasher_init_hmac(sha1_hasher_t hasher, const uint8_t * key, size_t key_len) -{ - uint8_t i; - memset(hasher->hmac_key_buffer, 0, SHA1_BLOCK_LEN); - - if(key_len > SHA1_BLOCK_LEN) - { - sha1_hasher_init(hasher); - while(key_len--) - { - sha1_hasher_putc(hasher, *key++); - } - memcpy(hasher->hmac_key_buffer, - sha1_hasher_gethash(hasher), - SHA1_HASH_LEN); - } - else - { - memcpy(hasher->hmac_key_buffer, key, key_len); - } - sha1_hasher_init(hasher); - for(i = 0; i < SHA1_BLOCK_LEN; i++) - { - sha1_hasher_putc(hasher, hasher->hmac_key_buffer[i] ^ SHA1_HMAC_IPAD); - } - -} -uint8_t * sha1_hasher_gethmac(sha1_hasher_t hasher) -{ - uint8_t i; - memcpy(hasher->hmac_inner_hash, sha1_hasher_gethash(hasher), - SHA1_HASH_LEN); - sha1_hasher_init(hasher); - - for(i = 0; i < SHA1_BLOCK_LEN; i++) - { - sha1_hasher_putc(hasher, hasher->hmac_key_buffer[i] ^ SHA1_HMAC_OPAD); - } - for(i = 0; i < SHA1_HASH_LEN; i++) - { - sha1_hasher_putc(hasher, hasher->hmac_inner_hash[i]); - } - return sha1_hasher_gethash(hasher); -} -#endif - diff --git a/Arduino_Code/sha1/hash.h b/Arduino_Code/sha1/hash.h deleted file mode 100644 index 28dc4850..00000000 --- a/Arduino_Code/sha1/hash.h +++ /dev/null @@ -1,56 +0,0 @@ -// This file is part of cryptosuite2. // -// // -// cryptosuite2 is free software: you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation, either version 3 of the License, or // -// (at your option) any later version. // -// // -// cryptosuite2 is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with cryptosuite2. If not, see . // -// // - -#ifndef SHA1_HASH_H_ -#define SHA1_HASH_H_ - - -#include "default.h" -#include "constants.h" -#include "types.h" -#include "basic.h" - -void sha1_hash_block(sha1_hasher_t hasher); - -void sha1_hasher_add_byte(sha1_hasher_t hasher, uint8_t byte); - -/** - * NOTE: once the block has been pad'ed the hasher will - * produce nonsense data. Therefore putc will return EOF - * once the hasher has been pad'ed (this happens, when - * sha1_hasher_gethash or sha1_hasher_gethmac are invoced). - * */ -uint8_t sha1_hasher_putc(sha1_hasher_t hasher, uint8_t byte); - -void sha1_hasher_pad(sha1_hasher_t hasher); - -/** - * NOTE: this will NOT return a copy of the data but - * a REFERENCE! One MUST NOT free the result. - * - * Also this modifies the state of the hasher. The - * hasher has an internal lock ensuring that writing - * to the hasher fails after this operation. - * */ -uint8_t * sha1_hasher_gethash(sha1_hasher_t hasher); - -#ifdef SHA1_ENABLE_HMAC -void sha1_hasher_init_hmac(sha1_hasher_t hasher, const uint8_t * key, size_t key_len); -uint8_t * sha1_hasher_gethmac(sha1_hasher_t hasher); -#endif - -#endif - diff --git a/Arduino_Code/sha1/sha1.c b/Arduino_Code/sha1/sha1.c deleted file mode 100644 index 925374eb..00000000 --- a/Arduino_Code/sha1/sha1.c +++ /dev/null @@ -1,36 +0,0 @@ -// This file is part of cryptosuite2. // -// // -// cryptosuite2 is free software: you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation, either version 3 of the License, or // -// (at your option) any later version. // -// // -// cryptosuite2 is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with cryptosuite2. If not, see . // -// // - -#include "sha1.h" - -ssize_t sha1_hasher_write(sha1_hasher_t hasher, const void * buf, size_t count) -{ - size_t written = 0; - uint8_t chk_result; - char c; - while(written < count) - { - c = ((char *) buf)[written]; - chk_result = sha1_hasher_putc(hasher, c); - if(chk_result != c) - { - return -1; - } - written++; - } - return written; -} - diff --git a/Arduino_Code/sha1/sha1.h b/Arduino_Code/sha1/sha1.h deleted file mode 100644 index 24544498..00000000 --- a/Arduino_Code/sha1/sha1.h +++ /dev/null @@ -1,33 +0,0 @@ -// This file is part of cryptosuite2. // -// // -// cryptosuite2 is free software: you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation, either version 3 of the License, or // -// (at your option) any later version. // -// // -// cryptosuite2 is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with cryptosuite2. If not, see . // -// // - -#ifndef SHA1_SHA1_H_ -#define SHA1_SHA1_H_ - -#include "default.h" -#include "types.h" -#include "hash.h" -#include -//#include - -#ifndef ssize_t -#define ssize_t long int -#endif - -ssize_t sha1_hasher_write(sha1_hasher_t hasher, const void * buf, size_t count); - -#endif - diff --git a/Arduino_Code/sha1/types.c b/Arduino_Code/sha1/types.c deleted file mode 100644 index 0199bbfe..00000000 --- a/Arduino_Code/sha1/types.c +++ /dev/null @@ -1,52 +0,0 @@ -// This file is part of cryptosuite2. // -// // -// cryptosuite2 is free software: you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation, either version 3 of the License, or // -// (at your option) any later version. // -// // -// cryptosuite2 is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with cryptosuite2. If not, see . // -// // - -#include "types.h" -#include -#include -#include "constants.h" - -sha1_hasher_t sha1_hasher_new(void) -{ - sha1_hasher_t hasher = (sha1_hasher_t) malloc(sizeof(struct sha1_hasher_s)); - if(!hasher) - { - return NULL; - } - sha1_hasher_init(hasher); - return hasher; -} - -void sha1_hasher_init(sha1_hasher_t hasher) -{ - uint8_t i; - for(i = 0; i < SHA1_HASH_LEN / 4; i++) - { - hasher->state.words[i] = pgm_read_dword(sha1_init_state + i); - } - hasher->block_offset = 0; - hasher->total_bytes = 0; - hasher->_lock = 0; - -} - - -void sha1_hasher_del(sha1_hasher_t hasher) -{ - free(hasher); -} - - diff --git a/Arduino_Code/sha1/types.h b/Arduino_Code/sha1/types.h deleted file mode 100644 index d0a681f5..00000000 --- a/Arduino_Code/sha1/types.h +++ /dev/null @@ -1,60 +0,0 @@ -// This file is part of cryptosuite2. // -// // -// cryptosuite2 is free software: you can redistribute it and/or modify // -// it under the terms of the GNU General Public License as published by // -// the Free Software Foundation, either version 3 of the License, or // -// (at your option) any later version. // -// // -// cryptosuite2 is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU General Public License for more details. // -// // -// You should have received a copy of the GNU General Public License // -// along with cryptosuite2. If not, see . // -// // - -#ifndef SHA1_TYPES_H_ -#define SHA1_TYPES_H_ - -#include "default.h" -#include -#include -#include "constants.h" - - -typedef union -{ - uint8_t bytes[SHA1_HASH_LEN]; - uint32_t words[SHA1_HASH_LEN / 4]; - -} sha1_state_t; - -typedef union -{ - uint8_t bytes[SHA1_BLOCK_LEN]; - uint32_t words[SHA1_BLOCK_LEN / 4]; - -} sha1_block_t; - -typedef struct __attribute__((__packed__)) sha1_hasher_s -{ - sha1_state_t state; - sha1_block_t buffer; - - uint8_t block_offset; - uint64_t total_bytes; - uint8_t _lock; -#ifdef SHA1_ENABLE_HMAC - uint8_t hmac_key_buffer[SHA1_BLOCK_LEN]; - uint8_t hmac_inner_hash[SHA1_HASH_LEN]; -#endif -} * sha1_hasher_t; - -sha1_hasher_t sha1_hasher_new(void); -void sha1_hasher_del(sha1_hasher_t hasher); -void sha1_hasher_init(sha1_hasher_t hasher); - - -#endif - diff --git a/Arduino_Code/uniqueID.h b/Arduino_Code/uniqueID.h index a5c171b4..fde6140b 100644 --- a/Arduino_Code/uniqueID.h +++ b/Arduino_Code/uniqueID.h @@ -19,7 +19,7 @@ #elif defined(ARDUINO_ARCH_SAMD) #elif defined(ARDUINO_ARCH_STM32) #elif defined(TEENSYDUINO) -#elif defined(ARDUINO_ARCH_MBED_RP2040) +#elif defined(ARDUINO_ARCH_MBED_RP2040) || defined(ARDUINO_ARCH_RP2040) //#include #elif defined(ARDUINO_ARCH_MEGAAVR) #else @@ -57,7 +57,7 @@ #elif defined(TEENSYDUINO) #define UniqueIDsize 16 #define UniqueIDbuffer 16 -#elif defined(ARDUINO_ARCH_MBED_RP2040) +#elif defined(ARDUINO_ARCH_MBED_RP2040) || defined(ARDUINO_ARCH_RP2040) #define UniqueIDsize 32 #define UniqueIDbuffer 32 #elif defined(ARDUINO_ARCH_MEGAAVR) diff --git a/ESP_Code/Counter.h b/ESP_Code/Counter.h new file mode 100644 index 00000000..58a4a0e6 --- /dev/null +++ b/ESP_Code/Counter.h @@ -0,0 +1,53 @@ +#ifndef _COUNTER_H_ +#define _COUNTER_H_ + +#include +#include + +template +class Counter { + +public: + Counter() { reset(); } + + void reset() { + memset(buffer, '0', max_digits); + buffer[max_digits] = '\0'; + val = 0; + len = 1; + } + + inline Counter &operator++() { + inc_string(buffer + max_digits - 1); + ++val; + return *this; + } + + inline operator unsigned int() const { return val; } + inline const char *c_str() const { return buffer + max_digits - len; } + inline size_t strlen() const { return len; } + +protected: + inline void inc_string(char *c) { + // In theory, the line below should be uncommented to avoid writing outside the buffer. In practice however, + // with max_digits set to 10 or more, we can fit all possible unsigned 32-bit integers in the buffer. + // The check is skipped to gain a small extra speed improvement. + // if (c >= buffer) return; + + if (*c < '9') { + *c += 1; + } + else { + *c = '0'; + inc_string(c - 1); + len = max(max_digits - (c - buffer) + 1, len); + } + } + +protected: + char buffer[max_digits + 1]; + unsigned int val; + size_t len; +}; + +#endif diff --git a/ESP_Code/DSHA1.h b/ESP_Code/DSHA1.h new file mode 100644 index 00000000..58dc41b2 --- /dev/null +++ b/ESP_Code/DSHA1.h @@ -0,0 +1,201 @@ +#ifndef DSHA1_H +#define DSHA1_H + +#include + +class DSHA1 { + +public: + static const size_t OUTPUT_SIZE = 20; + + DSHA1() { + initialize(s); + } + + DSHA1 &write(const unsigned char *data, size_t len) { + size_t bufsize = bytes % 64; + if (bufsize && bufsize + len >= 64) { + memcpy(buf + bufsize, data, 64 - bufsize); + bytes += 64 - bufsize; + data += 64 - bufsize; + transform(s, buf); + bufsize = 0; + } + while (len >= 64) { + transform(s, data); + bytes += 64; + data += 64; + len -= 64; + } + if (len > 0) { + memcpy(buf + bufsize, data, len); + bytes += len; + } + return *this; + } + + void finalize(unsigned char hash[OUTPUT_SIZE]) { + const unsigned char pad[64] = {0x80}; + unsigned char sizedesc[8]; + writeBE64(sizedesc, bytes << 3); + write(pad, 1 + ((119 - (bytes % 64)) % 64)); + write(sizedesc, 8); + writeBE32(hash, s[0]); + writeBE32(hash + 4, s[1]); + writeBE32(hash + 8, s[2]); + writeBE32(hash + 12, s[3]); + writeBE32(hash + 16, s[4]); + } + + DSHA1 &reset() { + bytes = 0; + initialize(s); + return *this; + } + + // Warmup the cache and get a boost in performance + DSHA1 &warmup() { + uint8_t warmup[20]; + this->write((uint8_t *)"warmupwarmupwa", 20).finalize(warmup); + return *this; + } + +private: + uint32_t s[5]; + unsigned char buf[64]; + uint64_t bytes; + + const uint32_t k1 = 0x5A827999ul; + const uint32_t k2 = 0x6ED9EBA1ul; + const uint32_t k3 = 0x8F1BBCDCul; + const uint32_t k4 = 0xCA62C1D6ul; + + uint32_t inline f1(uint32_t b, uint32_t c, uint32_t d) { return d ^ (b & (c ^ d)); } + uint32_t inline f2(uint32_t b, uint32_t c, uint32_t d) { return b ^ c ^ d; } + uint32_t inline f3(uint32_t b, uint32_t c, uint32_t d) { return (b & c) | (d & (b | c)); } + + uint32_t inline left(uint32_t x) { return (x << 1) | (x >> 31); } + + void inline Round(uint32_t a, uint32_t &b, uint32_t c, uint32_t d, uint32_t &e, + uint32_t f, uint32_t k, uint32_t w) { + e += ((a << 5) | (a >> 27)) + f + k + w; + b = (b << 30) | (b >> 2); + } + + void initialize(uint32_t s[5]) { + s[0] = 0x67452301ul; + s[1] = 0xEFCDAB89ul; + s[2] = 0x98BADCFEul; + s[3] = 0x10325476ul; + s[4] = 0xC3D2E1F0ul; + } + + void transform(uint32_t *s, const unsigned char *chunk) { + uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4]; + uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; + + Round(a, b, c, d, e, f1(b, c, d), k1, w0 = readBE32(chunk + 0)); + Round(e, a, b, c, d, f1(a, b, c), k1, w1 = readBE32(chunk + 4)); + Round(d, e, a, b, c, f1(e, a, b), k1, w2 = readBE32(chunk + 8)); + Round(c, d, e, a, b, f1(d, e, a), k1, w3 = readBE32(chunk + 12)); + Round(b, c, d, e, a, f1(c, d, e), k1, w4 = readBE32(chunk + 16)); + Round(a, b, c, d, e, f1(b, c, d), k1, w5 = readBE32(chunk + 20)); + Round(e, a, b, c, d, f1(a, b, c), k1, w6 = readBE32(chunk + 24)); + Round(d, e, a, b, c, f1(e, a, b), k1, w7 = readBE32(chunk + 28)); + Round(c, d, e, a, b, f1(d, e, a), k1, w8 = readBE32(chunk + 32)); + Round(b, c, d, e, a, f1(c, d, e), k1, w9 = readBE32(chunk + 36)); + Round(a, b, c, d, e, f1(b, c, d), k1, w10 = readBE32(chunk + 40)); + Round(e, a, b, c, d, f1(a, b, c), k1, w11 = readBE32(chunk + 44)); + Round(d, e, a, b, c, f1(e, a, b), k1, w12 = readBE32(chunk + 48)); + Round(c, d, e, a, b, f1(d, e, a), k1, w13 = readBE32(chunk + 52)); + Round(b, c, d, e, a, f1(c, d, e), k1, w14 = readBE32(chunk + 56)); + Round(a, b, c, d, e, f1(b, c, d), k1, w15 = readBE32(chunk + 60)); + + Round(e, a, b, c, d, f1(a, b, c), k1, w0 = left(w0 ^ w13 ^ w8 ^ w2)); + Round(d, e, a, b, c, f1(e, a, b), k1, w1 = left(w1 ^ w14 ^ w9 ^ w3)); + Round(c, d, e, a, b, f1(d, e, a), k1, w2 = left(w2 ^ w15 ^ w10 ^ w4)); + Round(b, c, d, e, a, f1(c, d, e), k1, w3 = left(w3 ^ w0 ^ w11 ^ w5)); + Round(a, b, c, d, e, f2(b, c, d), k2, w4 = left(w4 ^ w1 ^ w12 ^ w6)); + Round(e, a, b, c, d, f2(a, b, c), k2, w5 = left(w5 ^ w2 ^ w13 ^ w7)); + Round(d, e, a, b, c, f2(e, a, b), k2, w6 = left(w6 ^ w3 ^ w14 ^ w8)); + Round(c, d, e, a, b, f2(d, e, a), k2, w7 = left(w7 ^ w4 ^ w15 ^ w9)); + Round(b, c, d, e, a, f2(c, d, e), k2, w8 = left(w8 ^ w5 ^ w0 ^ w10)); + Round(a, b, c, d, e, f2(b, c, d), k2, w9 = left(w9 ^ w6 ^ w1 ^ w11)); + Round(e, a, b, c, d, f2(a, b, c), k2, w10 = left(w10 ^ w7 ^ w2 ^ w12)); + Round(d, e, a, b, c, f2(e, a, b), k2, w11 = left(w11 ^ w8 ^ w3 ^ w13)); + Round(c, d, e, a, b, f2(d, e, a), k2, w12 = left(w12 ^ w9 ^ w4 ^ w14)); + Round(b, c, d, e, a, f2(c, d, e), k2, w13 = left(w13 ^ w10 ^ w5 ^ w15)); + Round(a, b, c, d, e, f2(b, c, d), k2, w14 = left(w14 ^ w11 ^ w6 ^ w0)); + Round(e, a, b, c, d, f2(a, b, c), k2, w15 = left(w15 ^ w12 ^ w7 ^ w1)); + + Round(d, e, a, b, c, f2(e, a, b), k2, w0 = left(w0 ^ w13 ^ w8 ^ w2)); + Round(c, d, e, a, b, f2(d, e, a), k2, w1 = left(w1 ^ w14 ^ w9 ^ w3)); + Round(b, c, d, e, a, f2(c, d, e), k2, w2 = left(w2 ^ w15 ^ w10 ^ w4)); + Round(a, b, c, d, e, f2(b, c, d), k2, w3 = left(w3 ^ w0 ^ w11 ^ w5)); + Round(e, a, b, c, d, f2(a, b, c), k2, w4 = left(w4 ^ w1 ^ w12 ^ w6)); + Round(d, e, a, b, c, f2(e, a, b), k2, w5 = left(w5 ^ w2 ^ w13 ^ w7)); + Round(c, d, e, a, b, f2(d, e, a), k2, w6 = left(w6 ^ w3 ^ w14 ^ w8)); + Round(b, c, d, e, a, f2(c, d, e), k2, w7 = left(w7 ^ w4 ^ w15 ^ w9)); + Round(a, b, c, d, e, f3(b, c, d), k3, w8 = left(w8 ^ w5 ^ w0 ^ w10)); + Round(e, a, b, c, d, f3(a, b, c), k3, w9 = left(w9 ^ w6 ^ w1 ^ w11)); + Round(d, e, a, b, c, f3(e, a, b), k3, w10 = left(w10 ^ w7 ^ w2 ^ w12)); + Round(c, d, e, a, b, f3(d, e, a), k3, w11 = left(w11 ^ w8 ^ w3 ^ w13)); + Round(b, c, d, e, a, f3(c, d, e), k3, w12 = left(w12 ^ w9 ^ w4 ^ w14)); + Round(a, b, c, d, e, f3(b, c, d), k3, w13 = left(w13 ^ w10 ^ w5 ^ w15)); + Round(e, a, b, c, d, f3(a, b, c), k3, w14 = left(w14 ^ w11 ^ w6 ^ w0)); + Round(d, e, a, b, c, f3(e, a, b), k3, w15 = left(w15 ^ w12 ^ w7 ^ w1)); + + Round(c, d, e, a, b, f3(d, e, a), k3, w0 = left(w0 ^ w13 ^ w8 ^ w2)); + Round(b, c, d, e, a, f3(c, d, e), k3, w1 = left(w1 ^ w14 ^ w9 ^ w3)); + Round(a, b, c, d, e, f3(b, c, d), k3, w2 = left(w2 ^ w15 ^ w10 ^ w4)); + Round(e, a, b, c, d, f3(a, b, c), k3, w3 = left(w3 ^ w0 ^ w11 ^ w5)); + Round(d, e, a, b, c, f3(e, a, b), k3, w4 = left(w4 ^ w1 ^ w12 ^ w6)); + Round(c, d, e, a, b, f3(d, e, a), k3, w5 = left(w5 ^ w2 ^ w13 ^ w7)); + Round(b, c, d, e, a, f3(c, d, e), k3, w6 = left(w6 ^ w3 ^ w14 ^ w8)); + Round(a, b, c, d, e, f3(b, c, d), k3, w7 = left(w7 ^ w4 ^ w15 ^ w9)); + Round(e, a, b, c, d, f3(a, b, c), k3, w8 = left(w8 ^ w5 ^ w0 ^ w10)); + Round(d, e, a, b, c, f3(e, a, b), k3, w9 = left(w9 ^ w6 ^ w1 ^ w11)); + Round(c, d, e, a, b, f3(d, e, a), k3, w10 = left(w10 ^ w7 ^ w2 ^ w12)); + Round(b, c, d, e, a, f3(c, d, e), k3, w11 = left(w11 ^ w8 ^ w3 ^ w13)); + Round(a, b, c, d, e, f2(b, c, d), k4, w12 = left(w12 ^ w9 ^ w4 ^ w14)); + Round(e, a, b, c, d, f2(a, b, c), k4, w13 = left(w13 ^ w10 ^ w5 ^ w15)); + Round(d, e, a, b, c, f2(e, a, b), k4, w14 = left(w14 ^ w11 ^ w6 ^ w0)); + Round(c, d, e, a, b, f2(d, e, a), k4, w15 = left(w15 ^ w12 ^ w7 ^ w1)); + + Round(b, c, d, e, a, f2(c, d, e), k4, w0 = left(w0 ^ w13 ^ w8 ^ w2)); + Round(a, b, c, d, e, f2(b, c, d), k4, w1 = left(w1 ^ w14 ^ w9 ^ w3)); + Round(e, a, b, c, d, f2(a, b, c), k4, w2 = left(w2 ^ w15 ^ w10 ^ w4)); + Round(d, e, a, b, c, f2(e, a, b), k4, w3 = left(w3 ^ w0 ^ w11 ^ w5)); + Round(c, d, e, a, b, f2(d, e, a), k4, w4 = left(w4 ^ w1 ^ w12 ^ w6)); + Round(b, c, d, e, a, f2(c, d, e), k4, w5 = left(w5 ^ w2 ^ w13 ^ w7)); + Round(a, b, c, d, e, f2(b, c, d), k4, w6 = left(w6 ^ w3 ^ w14 ^ w8)); + Round(e, a, b, c, d, f2(a, b, c), k4, w7 = left(w7 ^ w4 ^ w15 ^ w9)); + Round(d, e, a, b, c, f2(e, a, b), k4, w8 = left(w8 ^ w5 ^ w0 ^ w10)); + Round(c, d, e, a, b, f2(d, e, a), k4, w9 = left(w9 ^ w6 ^ w1 ^ w11)); + Round(b, c, d, e, a, f2(c, d, e), k4, w10 = left(w10 ^ w7 ^ w2 ^ w12)); + Round(a, b, c, d, e, f2(b, c, d), k4, w11 = left(w11 ^ w8 ^ w3 ^ w13)); + Round(e, a, b, c, d, f2(a, b, c), k4, w12 = left(w12 ^ w9 ^ w4 ^ w14)); + Round(d, e, a, b, c, f2(e, a, b), k4, left(w13 ^ w10 ^ w5 ^ w15)); + Round(c, d, e, a, b, f2(d, e, a), k4, left(w14 ^ w11 ^ w6 ^ w0)); + Round(b, c, d, e, a, f2(c, d, e), k4, left(w15 ^ w12 ^ w7 ^ w1)); + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; + } + + uint32_t static inline readBE32(const unsigned char *ptr) { + return __builtin_bswap32(*(uint32_t *)ptr); + } + + void static inline writeBE32(unsigned char *ptr, uint32_t x) { + *(uint32_t *)ptr = __builtin_bswap32(x); + } + + void static inline writeBE64(unsigned char *ptr, uint64_t x) { + *(uint64_t *)ptr = __builtin_bswap64(x); + } +}; +#endif diff --git a/ESP_Code/Dashboard.h b/ESP_Code/Dashboard.h new file mode 100644 index 00000000..30c88924 --- /dev/null +++ b/ESP_Code/Dashboard.h @@ -0,0 +1,151 @@ +#ifndef DASHBOARD_H +#define DASHBOARD_H + +const char WEBSITE[] PROGMEM = R"=====( + + + + + + + Duino-Coin @@DEVICE@@ dashboard + + + + + + + + + + @@DEVICE@@ (@@ID@@) + + + Self-hosted, lightweight, official dashboard for your Duino-Coin miner + + + + + + + + + Mining statistics + + + + + @@HASHRATE@@ kH/s + + + Hashrate + + + + + @@DIFF@@ + + + Difficulty + + + + + @@SHARES@@ + + + Shares + + + + + @@NODE@@ + + + Node + + + + + + + + + Device information + + + + + @@DEVICE@@ + + + Device type + + + + + @@ID@@ + + + Device ID + + + + + @@MEMORY@@ + + + Free memory + + + + + @@VERSION@@ + + + Miner version + + + + + @@SENSOR@@ + + + Sensor reading(s) + + + + + + + + + + Hosted on + + http://@@IP_ADDR@@ + + • + + duinocoin.com + + • + + github.com/revoxhere/duino-coin + + @@RESET_SETTINGS@@ + + + + + + +)====="; + +#endif diff --git a/ESP_Code/DisplayHal.h b/ESP_Code/DisplayHal.h new file mode 100644 index 00000000..dfde2490 --- /dev/null +++ b/ESP_Code/DisplayHal.h @@ -0,0 +1,296 @@ +// Abstraction layer for handling various types of screens +// See Settings.h for enabling the screen of your choice +#ifndef DISPLAY_HAL_H +#define DISPLAY_HAL_H + +// Abstraction layer: custom fonts, images, etc. +#if defined(DISPLAY_SSD1306) + static const unsigned char image_check_contour_bits[] U8X8_PROGMEM = {0x00,0x04,0x00,0x0a,0x04,0x11,0x8a,0x08,0x51,0x04,0x22,0x02,0x04,0x01,0x88,0x00,0x50,0x00,0x20,0x00}; + static const unsigned char image_network_1_bar_bits[] U8X8_PROGMEM = {0x00,0x70,0x00,0x50,0x00,0x50,0x00,0x50,0x00,0x57,0x00,0x55,0x00,0x55,0x00,0x55,0x70,0x55,0x50,0x55,0x50,0x55,0x50,0x55,0x57,0x55,0x57,0x55,0x77,0x77,0x00,0x00}; + static const unsigned char image_network_2_bars_bits[] U8X8_PROGMEM = {0x00,0x70,0x00,0x50,0x00,0x50,0x00,0x50,0x00,0x57,0x00,0x55,0x00,0x55,0x00,0x55,0x70,0x55,0x70,0x55,0x70,0x55,0x70,0x55,0x77,0x55,0x77,0x55,0x77,0x77,0x00,0x00}; + static const unsigned char image_network_3_bars_bits[] U8X8_PROGMEM = {0x00,0x70,0x00,0x50,0x00,0x50,0x00,0x50,0x00,0x57,0x00,0x57,0x00,0x57,0x00,0x57,0x70,0x57,0x70,0x57,0x70,0x57,0x70,0x57,0x77,0x57,0x77,0x57,0x77,0x77,0x00,0x00}; + static const unsigned char image_network_4_bars_bits[] U8X8_PROGMEM = {0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x77,0x00,0x77,0x00,0x77,0x00,0x77,0x70,0x77,0x70,0x77,0x70,0x77,0x70,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x00,0x00}; + static const unsigned char image_duco_logo_bits[] U8X8_PROGMEM = {0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0xff,0x00,0xc0,0x01,0x9f,0x01,0x20,0x01,0x20,0x01,0x20,0x01,0x9f,0x01,0xc0,0x01,0xff,0x00,0x7f,0x00}; + static const unsigned char image_duco_logo_big_bits[] U8X8_PROGMEM = {0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0xff,0xff,0x01,0x00,0x00,0xfc,0xff,0xff,0x0f,0x00,0x00,0xfc,0xff,0xff,0x3f,0x00,0x00,0xfc,0xff,0xff,0x7f,0x00,0x00,0xfc,0xff,0xff,0xff,0x00,0x00,0xfc,0xff,0xff,0xff,0x01,0x00,0xfc,0xff,0xff,0xff,0x03,0x00,0xf8,0xff,0xff,0xff,0x07,0x00,0x00,0x00,0x00,0xfe,0x0f,0x00,0xfc,0xff,0x03,0xf8,0x0f,0x00,0xfc,0xff,0x0f,0xf0,0x1f,0x00,0xfc,0xff,0x1f,0xe0,0x1f,0x00,0xfc,0xff,0x3f,0xe0,0x3f,0x00,0xfc,0xff,0x7f,0xc0,0x3f,0x00,0xfc,0xff,0xff,0xc0,0x7f,0x00,0xf8,0xff,0xff,0x80,0x7f,0x00,0x00,0x00,0xfe,0x80,0x7f,0x00,0x00,0x00,0xfc,0x81,0x7f,0x00,0x00,0x00,0xfc,0x01,0x7f,0x00,0x00,0x00,0xfc,0x01,0xff,0x00,0x00,0x00,0xfc,0x01,0xff,0x00,0x00,0x00,0xfc,0x01,0xff,0x00,0x00,0x00,0xfc,0x01,0xff,0x00,0x00,0x00,0xfc,0x01,0xff,0x00,0x00,0x00,0xfc,0x81,0x7f,0x00,0x00,0x00,0xfe,0x81,0x7f,0x00,0x00,0x80,0xff,0x80,0x7f,0x00,0xfc,0xff,0xff,0xc0,0x7f,0x00,0xfc,0xff,0xff,0xc0,0x7f,0x00,0xfc,0xff,0x7f,0xe0,0x3f,0x00,0xfc,0xff,0x3f,0xf0,0x3f,0x00,0xfc,0xff,0x1f,0xf8,0x1f,0x00,0xfc,0xff,0x0f,0xfc,0x1f,0x00,0xf8,0xff,0x01,0xfe,0x0f,0x00,0x00,0x00,0xc0,0xff,0x0f,0x00,0xfc,0xff,0xff,0xff,0x07,0x00,0xfc,0xff,0xff,0xff,0x03,0x00,0xfc,0xff,0xff,0xff,0x01,0x00,0xfc,0xff,0xff,0xff,0x00,0x00,0xfc,0xff,0xff,0x7f,0x00,0x00,0xfc,0xff,0xff,0x1f,0x00,0x00,0xfc,0xff,0xff,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; +#endif + +#if defined(DISPLAY_16X2) + static byte duco_logo[] = {0x1E, 0x01, 0x1D, 0x05, 0x1D, 0x01, 0x1E, 0x00}; + static byte check_mark[] = {0x00, 0x00, 0x00, 0x01, 0x02,0x14, 0x08, 0x00}; + static byte kh[] = {0x08, 0x0A, 0x0C, 0x0A, 0x00, 0x0A, 0x0E, 0x0A}; + static byte msec[] = {0x0A, 0x15, 0x11, 0x06, 0x08, 0x04, 0x02, 0x0C}; +#endif + + #if defined(DISPLAY_SSD1306) + void drawStrMultiline(const char *msg, int xloc, int yloc) { + //https://github.com/olikraus/u8g2/discussions/1479 + int dspwidth = u8g2.getDisplayWidth(); + int strwidth = 0; + char glyph[2]; glyph[1] = 0; + + for (const char *ptr = msg, *lastblank = NULL; *ptr; ++ptr) { + while (xloc == 0 && *msg == ' ') + if (ptr == msg++) ++ptr; + + glyph[0] = *ptr; + strwidth += u8g2.getStrWidth(glyph); + if (*ptr == ' ') lastblank = ptr; + else ++strwidth; + + if (xloc + strwidth > dspwidth) { + int starting_xloc = xloc; + while (msg < (lastblank ? lastblank : ptr)) { + glyph[0] = *msg++; + xloc += u8g2.drawStr(xloc, yloc, glyph); + } + + strwidth -= xloc - starting_xloc; + yloc += u8g2.getMaxCharHeight(); + xloc = 0; lastblank = NULL; + } + } + while (*msg) { + glyph[0] = *msg++; + xloc += u8g2.drawStr(xloc, yloc, glyph); + } + } +#endif + +#if defined(DISPLAY_SSD1306) || defined(DISPLAY_16X2) + void screen_setup() { + // Ran during setup() + // Abstraction layer: screen initialization + + #if defined(DISPLAY_SSD1306) + u8g2.begin(); + u8g2.clearBuffer(); + u8g2.setFontMode(1); + u8g2.setBitmapMode(1); + u8g2.sendBuffer(); + #endif + + #if defined(DISPLAY_16X2) + lcd.begin(16, 2); + lcd.createChar(0, duco_logo); + lcd.createChar(1, check_mark); + lcd.createChar(2, kh); + lcd.createChar(3, msec); + lcd.home(); + lcd.clear(); + #endif + } + + + void display_boot() { + // Abstraction layer: compilation time, features, etc. + + #if defined(DISPLAY_16X2) + lcd.clear(); + #if defined(ESP8266) + lcd.print("ESP8266 "); + #elif defined(CONFIG_FREERTOS_UNICORE) + lcd.print("ESP32S2 "); + #else + lcd.print("ESP32 "); + #endif + #if defined(ESP8266) + lcd.print(String(ESP.getCpuFreqMHz()).c_str()); + #else + lcd.print(String(getCpuFrequencyMhz()).c_str()); + #endif + lcd.print(" MHz"); + + lcd.setCursor(0, 1); + lcd.print(__DATE__); + #endif + + #if defined(DISPLAY_SSD1306) + u8g2.clearBuffer(); + + u8g2.setFont(u8g2_font_profont15_tr); + u8g2.setCursor(2, 13); + #if defined(ESP8266) + u8g2.print("ESP8266 "); + #elif defined(CONFIG_FREERTOS_UNICORE) + u8g2.print("ESP32S2/C3 "); + #else + u8g2.print("ESP32 "); + #endif + + #if defined(ESP8266) + u8g2.print(String(ESP.getCpuFreqMHz()).c_str()); + #else + u8g2.print(String(getCpuFrequencyMhz()).c_str()); + #endif + u8g2.print(" MHz"); + + u8g2.setFont(u8g2_font_profont10_tr); + u8g2.drawLine(1, 27, 126, 27); + u8g2.setCursor(2, 24); + u8g2.print("Compiled "); + u8g2.print(__DATE__); + + + u8g2.drawStr(2, 37, "Features:"); + u8g2.setCursor(2, 46); + String features_str = "OTA "; + #if defined(USE_LAN) + features_str += "LAN "; + #endif + #if defined(LED_BLINKING) + features_str += "Blink "; + #endif + #if defined(SERIAL_PRINTING) + features_str += "Serial "; + #endif + #if defined(WEB_DASHBOARD) + features_str += "Webserver "; + #endif + #if defined(DISPLAY_16X2) + features_str += "LCD16X2 "; + #endif + #if defined(DISPLAY_SSD1306) + features_str += "SSD1306 "; + #endif + #if defined(USE_INTERNAL_SENSOR) + features_str += "Int. sensor "; + #endif + #if defined(USE_DS18B20) + features_str += "DS18B20 "; + #endif + #if defined(USE_DHT) + features_str += "DHT "; + #endif + #if defined(USE_HSU07M) + features_str += "HSU07M "; + #endif + drawStrMultiline(features_str.c_str(), 2, 46); + u8g2.sendBuffer(); + #endif + } + + void display_info(String message) { + // Abstraction layer: info screens (setups) + + #if defined(DISPLAY_SSD1306) + u8g2.clearBuffer(); + u8g2.drawXBMP(-1, 3, 41, 45, image_duco_logo_big_bits); + u8g2.setFont(u8g2_font_t0_16b_tr); + #if defined(ESP8266) + u8g2.drawStr(42, 27, "ESP8266"); + #elif defined(CONFIG_FREERTOS_UNICORE) + u8g2.drawStr(42, 27, "ESP32S2/C3"); + #else + u8g2.drawStr(42, 27, "ESP32"); + #endif + u8g2.setFont(u8g2_font_t0_13b_tr); + u8g2.drawStr(41, 14, "Duino-Coin"); + u8g2.setFont(u8g2_font_6x10_tr); + u8g2.drawStr(98, 36, "MINER"); + u8g2.setFont(u8g2_font_6x13_tr); + u8g2.drawStr(1, 60, message.c_str()); + u8g2.setFont(u8g2_font_5x8_tr); + u8g2.drawStr(42, 46, "www.duinocoin.com"); + u8g2.setFont(u8g2_font_4x6_tr); + u8g2.drawStr(116, 14, String(SOFTWARE_VERSION).c_str()); + u8g2.sendBuffer(); + #endif + + #if defined(DISPLAY_16X2) + lcd.clear(); + lcd.setCursor(0, 0); + lcd.write(0); + lcd.print(" Duino-Coin "); + lcd.print(SOFTWARE_VERSION); + lcd.setCursor(0, 1); + lcd.print(message); + #endif + } + + + void display_mining_results(String hashrate, String accepted_shares, String total_shares, String uptime, String node, + String difficulty, String sharerate, String ping, String accept_rate) { + // Ran after each found share + // Abstraction layer: displaying mining results + Serial.println("Displaying mining results"); + + #if defined(DISPLAY_SSD1306) + u8g2.clearBuffer(); + u8g2.setFont(u8g2_font_profont10_tr); + u8g2.drawStr(67, 26, "kH"); + if (hashrate.toFloat() < 100.0) { + u8g2.setFont(u8g2_font_profont29_tr); + u8g2.drawStr(2, 36, hashrate.c_str()); + } else { + u8g2.setFont(u8g2_font_profont22_tr); + u8g2.drawStr(3, 35, hashrate.c_str()); + } + + u8g2.setFont(u8g2_font_haxrcorp4089_tr); + u8g2.drawStr(52, 12, node.c_str()); + + u8g2.setFont(u8g2_font_t0_11_tr); + u8g2.drawStr(17, 47, (accepted_shares + "/" + total_shares).c_str()); + u8g2.setFont(u8g2_font_5x7_tr); + u8g2.drawStr(88, 47, ("(" + accept_rate + "%)").c_str()); + + u8g2.setFont(u8g2_font_profont12_tr); + u8g2.drawStr(20, 12, (ping + "ms").c_str()); + u8g2.drawStr(69, 36, "s"); + + u8g2.setFont(u8g2_font_6x13_tr); + u8g2.drawStr(125-u8g2.getStrWidth(uptime.c_str()), 61, uptime.c_str()); + + u8g2.drawStr(85, 38, sharerate.c_str()); + u8g2.drawStr(85, 27, difficulty.c_str()); + u8g2.drawLine(67, 28, 75, 28); + + u8g2.drawXBMP(2, 38, 13, 10, image_check_contour_bits); + + if (WiFi.RSSI() > -40) { + u8g2.drawXBMP(1, 0, 15, 16, image_network_4_bars_bits); + } else if (WiFi.RSSI() > -60) { + u8g2.drawXBMP(1, 0, 15, 16, image_network_3_bars_bits); + } else if (WiFi.RSSI() > -75) { + u8g2.drawXBMP(1, 0, 15, 16, image_network_2_bars_bits); + } else { + u8g2.drawXBMP(1, 0, 15, 16, image_network_1_bar_bits); + } + + u8g2.setFont(u8g2_font_4x6_tr); + u8g2.drawStr(14, 61, String(WiFi.localIP().toString()).c_str()); + u8g2.drawStr(14, 55, ("Duino-Coin " + String(SOFTWARE_VERSION)).c_str()); + u8g2.drawXBMP(2, 11, 9, 50, image_duco_logo_bits); + u8g2.drawStr(111, 27, "diff"); + u8g2.drawStr(107, 38, "shr/s"); + + u8g2.sendBuffer(); + #endif + + #if defined(DISPLAY_16X2) + lcd.clear(); + lcd.setCursor(0,0); + lcd.print(hashrate); + lcd.setCursor(4,0); + lcd.write(2); // kh + + lcd.setCursor(7, 0); + lcd.print(difficulty); + lcd.print(" diff"); + + lcd.setCursor(0, 1); + lcd.write(1); // checkmark + lcd.print(accepted_shares); + + lcd.setCursor(7, 1); + lcd.print(ping); + lcd.write(3); // ms + + lcd.setCursor(12, 1); + lcd.print(sharerate); + lcd.print("s"); + #endif + } +#endif + +#endif diff --git a/ESP_Code/ESP_Code.ino b/ESP_Code/ESP_Code.ino new file mode 100644 index 00000000..c5161a84 --- /dev/null +++ b/ESP_Code/ESP_Code.ino @@ -0,0 +1,818 @@ +/* + ____ __ __ ____ _ _ _____ ___ _____ ____ _ _ + ( _ \( )( )(_ _)( \( )( _ )___ / __)( _ )(_ _)( \( ) + )(_) ))(__)( _)(_ ) ( )(_)((___)( (__ )(_)( _)(_ ) ( + (____/(______)(____)(_)\_)(_____) \___)(_____)(____)(_)\_) + Official code for all ESP8266/32 boards version 4.3 + Main .ino file + + The Duino-Coin Team & Community 2019-2024 © MIT Licensed + https://duinocoin.com + https://github.com/revoxhere/duino-coin + + If you don't know where to start, visit official website and navigate to + the Getting Started page. Have fun mining! + + To edit the variables (username, WiFi settings, etc.) use the Settings.h tab! +*/ + +/* If optimizations cause problems, change them to -O0 (the default) */ +#pragma GCC optimize("-Ofast") + +/* If during compilation the line below causes a + "fatal error: arduinoJson.h: No such file or directory" + message to occur; it means that you do NOT have the + ArduinoJSON library installed. To install it, + go to the below link and follow the instructions: + https://github.com/revoxhere/duino-coin/issues/832 */ +#include + +#if defined(ESP8266) + #include + #include + #include + #include +#else + #include + #include + #include + #include + #include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "MiningJob.h" +#include "Settings.h" + +#ifdef USE_LAN + #include +#endif + +#if defined(WEB_DASHBOARD) + #include "Dashboard.h" +#endif + +#if defined(DISPLAY_SSD1306) || defined(DISPLAY_16X2) + #include "DisplayHal.h" +#endif + +#if !defined(ESP8266) && defined(DISABLE_BROWNOUT) + #include "soc/soc.h" + #include "soc/rtc_cntl_reg.h" +#endif + +// Auto adjust physical core count +// (ESP32-S2/C3 have 1 core, ESP32 has 2 cores, ESP8266 has 1 core) +#if defined(ESP8266) + #define CORE 1 + typedef ESP8266WebServer WebServer; +#elif defined(CONFIG_FREERTOS_UNICORE) + #define CORE 1 +#else + #define CORE 2 + // Install TridentTD_EasyFreeRTOS32 if you get an error + #include + + void Task1Code( void * parameter ); + void Task2Code( void * parameter ); + TaskHandle_t Task1; + TaskHandle_t Task2; +#endif + +#if defined(WEB_DASHBOARD) + WebServer server(80); +#endif + +#if defined(CAPTIVE_PORTAL) + #include // This needs to be first, or it all crashes and burns... + #include + #include + char duco_username[40]; + char duco_password[40]; + char duco_rigid[24]; + WiFiManager wifiManager; + Preferences preferences; + WiFiManagerParameter custom_duco_username("duco_usr", "Duino-Coin username", duco_username, 40); + WiFiManagerParameter custom_duco_password("duco_pwd", "Duino-Coin mining key (if enabled in the wallet)", duco_password, 40); + WiFiManagerParameter custom_duco_rigid("duco_rig", "Custom miner identifier (optional)", duco_rigid, 24); + + void saveConfigCallback() { + preferences.begin("duino_config", false); + preferences.putString("duco_username", custom_duco_username.getValue()); + preferences.putString("duco_password", custom_duco_password.getValue()); + preferences.putString("duco_rigid", custom_duco_rigid.getValue()); + preferences.end(); + RestartESP("Settings saved"); + } + + void reset_settings() { + server.send(200, "text/html", "Settings have been erased. Please redo the configuration by connecting to the WiFi network that will be created"); + delay(500); + wifiManager.resetSettings(); + RestartESP("Manual settings reset"); + } + + void saveParamCallback(){ + Serial.println("[CALLBACK] saveParamCallback fired"); + Serial.println("PARAM customfieldid = " + getParam("customfieldid")); + } + + String getParam(String name){ + //read parameter from server, for customhmtl input + String value; + if(wifiManager.server->hasArg(name)) { + value = wifiManager.server->arg(name); + } + return value; + } +#endif + +void RestartESP(String msg) { + #if defined(SERIAL_PRINTING) + Serial.println(msg); + Serial.println("Restarting ESP..."); + #endif + + #if defined(DISPLAY_SSD1306) || defined(DISPLAY_16X2) + display_info("Restarting ESP..."); + #endif + + #if defined(ESP8266) + ESP.reset(); + #else + ESP.restart(); + abort(); + #endif +} + +#if defined(BLUSHYBOX) + Ticker blinker; + bool lastLedState = false; + void changeState() { + analogWrite(LED_BUILTIN, lastLedState ? 255 : 0); + lastLedState = !lastLedState; + } +#endif + +#if defined(ESP8266) + // WDT Loop + // See lwdtcb() and lwdtFeed() below + Ticker lwdTimer; + + unsigned long lwdCurrentMillis = 0; + unsigned long lwdTimeOutMillis = LWD_TIMEOUT; + + void ICACHE_RAM_ATTR lwdtcb(void) { + if ((millis() - lwdCurrentMillis > LWD_TIMEOUT) || (lwdTimeOutMillis - lwdCurrentMillis != LWD_TIMEOUT)) + RestartESP("Loop WDT Failed!"); + } + + void lwdtFeed(void) { + lwdCurrentMillis = millis(); + lwdTimeOutMillis = lwdCurrentMillis + LWD_TIMEOUT; + } +#else + void lwdtFeed(void) { + Serial.println("lwdtFeed()"); + } +#endif + +namespace { + MiningConfig *configuration = new MiningConfig( + DUCO_USER, + RIG_IDENTIFIER, + MINER_KEY + ); + + #if defined(ESP32) && CORE == 2 + EasyMutex mutexClientData, mutexConnectToServer; + #endif + + #ifdef USE_LAN + static bool eth_connected = false; + #endif + + void UpdateHostPort(String input) { + // Thanks @ricaun for the code + DynamicJsonDocument doc(256); + deserializeJson(doc, input); + const char *name = doc["name"]; + + configuration->host = doc["ip"].as().c_str(); + configuration->port = doc["port"].as(); + node_id = String(name); + + #if defined(SERIAL_PRINTING) + Serial.println("Poolpicker selected the best mining node: " + node_id); + #endif + + #if defined(DISPLAY_SSD1306) || defined(DISPLAY_16X2) + display_info(node_id); + #endif + } + + void VerifyWifi() { + #ifdef USE_LAN + while ((!eth_connected) || (ETH.localIP() == IPAddress(0, 0, 0, 0))) { + #if defined(SERIAL_PRINTING) + Serial.println("Ethernet connection lost. Reconnect..." ); + #endif + SetupWifi(); + } + #else + while (WiFi.status() != WL_CONNECTED + || WiFi.localIP() == IPAddress(0, 0, 0, 0) + || WiFi.localIP() == IPAddress(192, 168, 4, 2) + || WiFi.localIP() == IPAddress(192, 168, 4, 3)) { + #if defined(SERIAL_PRINTING) + Serial.println("WiFi reconnecting..."); + #endif + WiFi.disconnect(); + delay(500); + WiFi.reconnect(); + delay(500); + } + #endif + } + + String httpGetString(String URL) { + String payload = ""; + + WiFiClientSecure client; + HTTPClient https; + client.setInsecure(); + + https.begin(client, URL); + https.addHeader("Accept", "*/*"); + + int httpCode = https.GET(); + #if defined(SERIAL_PRINTING) + Serial.printf("HTTP Response code: %d\n", httpCode); + #endif + + if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { + payload = https.getString(); + } else { + #if defined(SERIAL_PRINTING) + Serial.printf("Error fetching node from poolpicker: %s\n", https.errorToString(httpCode).c_str()); + VerifyWifi(); + #endif + #if defined(DISPLAY_SSD1306) || defined(DISPLAY_16X2) + display_info(https.errorToString(httpCode)); + #endif + } + https.end(); + return payload; + } + + void SelectNode() { + String input = ""; + int waitTime = 1; + int poolIndex = 0; + + while (input == "") { + #if defined(SERIAL_PRINTING) + Serial.println("Fetching mining node from the poolpicker in " + String(waitTime) + "s"); + #endif + delay(waitTime * 1000); + + input = httpGetString("https://server.duinocoin.com/getPool"); + + // Increase wait time till a maximum of 32 seconds + // (addresses: Limit connection requests on failure in ESP boards #1041) + waitTime *= 2; + if (waitTime > 32) + RestartESP("Node fetch unavailable"); + } + + UpdateHostPort(input); + } + + #ifdef USE_LAN + void WiFiEvent(WiFiEvent_t event) { + switch (event) { + case ARDUINO_EVENT_ETH_START: + #if defined(SERIAL_PRINTING) + Serial.println("ETH Started"); + #endif + // The hostname must be set after the interface is started, but needs + // to be set before DHCP, so set it from the event handler thread. + ETH.setHostname("esp32-ethernet"); + break; + case ARDUINO_EVENT_ETH_CONNECTED: + #if defined(SERIAL_PRINTING) + Serial.println("ETH Connected"); + #endif + break; + case ARDUINO_EVENT_ETH_GOT_IP: + #if defined(SERIAL_PRINTING) + Serial.println("ETH Got IP"); + #endif + eth_connected = true; + break; + case ARDUINO_EVENT_ETH_DISCONNECTED: + #if defined(SERIAL_PRINTING) + Serial.println("ETH Disconnected"); + #endif + eth_connected = false; + break; + case ARDUINO_EVENT_ETH_STOP: + #if defined(SERIAL_PRINTING) + Serial.println("ETH Stopped"); + #endif + eth_connected = false; + break; + default: + break; + } + } + #endif + + void SetupWifi() { + #ifdef USE_LAN + #if defined(SERIAL_PRINTING) + Serial.println("Connecting to Ethernet..."); + #endif + WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread. + ETH.begin(); + + while (!eth_connected) { + delay(500); + #if defined(SERIAL_PRINTING) + Serial.print("."); + #endif + } + + #if defined(SERIAL_PRINTING) + Serial.println("\n\nSuccessfully connected to Ethernet"); + Serial.println("Local IP address: " + ETH.localIP().toString()); + Serial.println("Rig name: " + String(RIG_IDENTIFIER)); + Serial.println(); + #endif + + #else + #if defined(SERIAL_PRINTING) + Serial.println("Connecting to: " + String(SSID)); + #endif + + WiFi.begin(SSID, PASSWORD); + while(WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(100); + } + VerifyWifi(); + + #if !defined(ESP8266) + WiFi.config(WiFi.localIP(), WiFi.gatewayIP(), WiFi.subnetMask(), DNS_SERVER); + #endif + + #if defined(SERIAL_PRINTING) + Serial.println("\n\nSuccessfully connected to WiFi"); + Serial.println("Rig name: " + String(RIG_IDENTIFIER)); + Serial.println("Local IP address: " + WiFi.localIP().toString()); + Serial.println("Gateway: " + WiFi.gatewayIP().toString()); + Serial.println("DNS: " + WiFi.dnsIP().toString()); + Serial.println(); + #endif + + #endif + + #if defined(DISPLAY_SSD1306) || defined(DISPLAY_16X2) + display_info("Waiting for node..."); + #endif + SelectNode(); + } + + void SetupOTA() { + // Prepare OTA handler + ArduinoOTA.onStart([]() + { + #if defined(SERIAL_PRINTING) + Serial.println("Start"); + #endif + }); + ArduinoOTA.onEnd([]() + { + #if defined(SERIAL_PRINTING) + Serial.println("\nEnd"); + #endif + }); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) + { + #if defined(SERIAL_PRINTING) + Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + #endif + }); + ArduinoOTA.onError([](ota_error_t error) + { + Serial.printf("Error[%u]: ", error); + #if defined(SERIAL_PRINTING) + if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); + else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); + else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); + else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); + else if (error == OTA_END_ERROR) Serial.println("End Failed"); + #endif + }); + + ArduinoOTA.setHostname(RIG_IDENTIFIER); // Give port a name + ArduinoOTA.begin(); + } + + #if defined(WEB_DASHBOARD) + void dashboard() { + #if defined(SERIAL_PRINTING) + Serial.println("Handling HTTP client"); + #endif + String s = WEBSITE; + #ifdef USE_LAN + s.replace("@@IP_ADDR@@", ETH.localIP().toString()); + #else + s.replace("@@IP_ADDR@@", WiFi.localIP().toString()); + #endif + + s.replace("@@HASHRATE@@", String((hashrate+hashrate_core_two) / 1000)); + s.replace("@@DIFF@@", String(difficulty / 100)); + s.replace("@@SHARES@@", String(share_count)); + s.replace("@@NODE@@", String(node_id)); + + #if defined(ESP8266) + s.replace("@@DEVICE@@", "ESP8266"); + #elif defined(CONFIG_FREERTOS_UNICORE) + s.replace("@@DEVICE@@", "ESP32-S2/C3"); + #else + s.replace("@@DEVICE@@", "ESP32"); + #endif + + s.replace("@@ID@@", String(RIG_IDENTIFIER)); + s.replace("@@MEMORY@@", String(ESP.getFreeHeap())); + s.replace("@@VERSION@@", String(SOFTWARE_VERSION)); + + #if defined(CAPTIVE_PORTAL) + s.replace("@@RESET_SETTINGS@@", "• Reset settings"); + #else + s.replace("@@RESET_SETTINGS@@", ""); + #endif + + #if defined(USE_DS18B20) + sensors.requestTemperatures(); + float temp = sensors.getTempCByIndex(0); + s.replace("@@SENSOR@@", "DS18B20: " + String(temp) + "*C"); + #elif defined(USE_DHT) + float temp = dht.readTemperature(); + float hum = dht.readHumidity(); + s.replace("@@SENSOR@@", "DHT11/22: " + String(temp) + "*C, " + String(hum) + "rh%"); + #elif defined(USE_HSU07M) + float temp = read_hsu07m(); + s.replace("@@SENSOR@@", "HSU07M: " + String(temp) + "*C"); + #elif defined(USE_INTERNAL_SENSOR) + float temp = 0; + temp_sensor_read_celsius(&temp); + s.replace("@@SENSOR@@", "CPU: " + String(temp) + "*C"); + #else + s.replace("@@SENSOR@@", "None"); + #endif + + server.send(200, "text/html", s); + } + #endif + +} // End of namespace + +MiningJob *job[CORE]; + +#if CORE == 2 + EasyFreeRTOS32 task1, task2; +#endif + +void task1_func(void *) { + #if defined(ESP32) && CORE == 2 + VOID SETUP() { } + + VOID LOOP() { + job[0]->mine(); + + #if defined(DISPLAY_SSD1306) || defined(DISPLAY_16X2) + float hashrate_float = (hashrate+hashrate_core_two) / 1000.0; + float accept_rate = (accepted_share_count / 0.01 / share_count); + + long millisecs = millis(); + int uptime_secs = int((millisecs / 1000) % 60); + int uptime_mins = int((millisecs / (1000 * 60)) % 60); + int uptime_hours = int((millisecs / (1000 * 60 * 60)) % 24); + String uptime = String(uptime_hours) + "h" + String(uptime_mins) + "m" + String(uptime_secs) + "s"; + + float sharerate = share_count / (millisecs / 1000.0); + + display_mining_results(String(hashrate_float, 1), String(accepted_share_count), String(share_count), String(uptime), + String(node_id), String(difficulty / 100), String(sharerate, 1), + String(ping), String(accept_rate, 1)); + #endif + } + #endif +} + +void task2_func(void *) { + #if defined(ESP32) && CORE == 2 + VOID SETUP() { + job[1] = new MiningJob(1, configuration); + } + + VOID LOOP() { + job[1]->mine(); + + #if defined(DISPLAY_SSD1306) || defined(DISPLAY_16X2) + float hashrate_float = (hashrate+hashrate_core_two) / 1000.0; + float accept_rate = (accepted_share_count / 0.01 / share_count); + + long millisecs = millis(); + int uptime_secs = int((millisecs / 1000) % 60); + int uptime_mins = int((millisecs / (1000 * 60)) % 60); + int uptime_hours = int((millisecs / (1000 * 60 * 60)) % 24); + String uptime = String(uptime_hours) + "h" + String(uptime_mins) + "m" + String(uptime_secs) + "s"; + + float sharerate = share_count / (millisecs / 1000.0); + + display_mining_results(String(hashrate_float, 1), String(accepted_share_count), String(share_count), String(uptime), + String(node_id), String(difficulty / 100), String(sharerate, 1), + String(ping), String(accept_rate, 1)); + #endif + } + #endif +} + +void setup() { + #if !defined(ESP8266) && defined(DISABLE_BROWNOUT) + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); + #endif + + #if defined(SERIAL_PRINTING) + Serial.begin(SERIAL_BAUDRATE); + Serial.println("\n\nDuino-Coin " + String(configuration->MINER_VER)); + #endif + pinMode(LED_BUILTIN, OUTPUT); + + #if defined(BLUSHYBOX) + analogWrite(LED_BUILTIN, 255); + for (int i = 255; i > 0; i--) { + analogWrite(LED_BUILTIN, i); + delay(1); + } + pinMode(GAUGE_PIN, OUTPUT); + + // Gauge up and down effect on startup + for (int i = GAUGE_MIN; i < GAUGE_MAX; i++) { + analogWrite(GAUGE_PIN, i); + delay(10); + } + for (int i = GAUGE_MAX; i > GAUGE_MIN; i--) { + analogWrite(GAUGE_PIN, i); + delay(10); + } + #endif + + #if defined(DISPLAY_SSD1306) || defined(DISPLAY_16X2) + screen_setup(); + display_boot(); + delay(2500); + #endif + + assert(CORE == 1 || CORE == 2); + WALLET_ID = String(random(0, 2811)); // Needed for miner grouping in the wallet + job[0] = new MiningJob(0, configuration); + + #if defined(USE_DHT) + #if defined(SERIAL_PRINTING) + Serial.println("Initializing DHT sensor (Duino IoT)"); + #endif + dht.begin(); + #if defined(SERIAL_PRINTING) + Serial.println("Test reading: " + String(dht.readHumidity()) + "% humidity"); + Serial.println("Test reading: temperature " + String(dht.readTemperature()) + "°C"); + #endif + #endif + + #if defined(USE_DS18B20) + #if defined(SERIAL_PRINTING) + Serial.println("Initializing DS18B20 sensor (Duino IoT)"); + #endif + sensors.begin(); + sensors.requestTemperatures(); + #if defined(SERIAL_PRINTING) + Serial.println("Test reading: " + String(sensors.getTempCByIndex(0)) + "°C"); + #endif + #endif + + #if defined(USE_HSU07M) + #if defined(SERIAL_PRINTING) + Serial.println("Initializing HSU07M sensor (Duino IoT)"); + Serial.println("Test reading: " + String(read_hsu07m()) + "°C"); + #endif + #endif + + #if defined(USE_INTERNAL_SENSOR) + #if defined(SERIAL_PRINTING) + Serial.println("Initializing internal ESP32 temperature sensor (Duino IoT)"); + #endif + temp_sensor_config_t temp_sensor = TSENS_CONFIG_DEFAULT(); + temp_sensor.dac_offset = TSENS_DAC_L2; + temp_sensor_set_config(temp_sensor); + temp_sensor_start(); + float result = 0; + temp_sensor_read_celsius(&result); + #if defined(SERIAL_PRINTING) + Serial.println("Test reading: " + String(result) + "°C"); + #endif + #endif + + WiFi.mode(WIFI_STA); // Setup ESP in client mode + //WiFi.disconnect(true); + #if defined(ESP8266) + WiFi.setSleepMode(WIFI_NONE_SLEEP); + #else + WiFi.setSleep(false); + #endif + + #if defined(CAPTIVE_PORTAL) + preferences.begin("duino_config", false); + strcpy(duco_username, preferences.getString("duco_username", "username").c_str()); + strcpy(duco_password, preferences.getString("duco_password", "None").c_str()); + strcpy(duco_rigid, preferences.getString("duco_rigid", "None").c_str()); + preferences.end(); + configuration->DUCO_USER = duco_username; + configuration->RIG_IDENTIFIER = duco_rigid; + configuration->MINER_KEY = duco_password; + RIG_IDENTIFIER = duco_rigid; + + String captivePortalHTML = R"( + Duino BlushyBox + + )"; + + wifiManager.setCustomHeadElement(captivePortalHTML.c_str()); + + wifiManager.setSaveConfigCallback(saveConfigCallback); + wifiManager.addParameter(&custom_duco_username); + wifiManager.addParameter(&custom_duco_password); + wifiManager.addParameter(&custom_duco_rigid); + + #if defined(BLUSHYBOX) + blinker.attach_ms(200, changeState); + #endif + wifiManager.autoConnect("Duino-Coin"); + delay(1000); + VerifyWifi(); + #if defined(BLUSHYBOX) + blinker.detach(); + #endif + + #if defined(DISPLAY_SSD1306) || defined(DISPLAY_16X2) + display_info("Waiting for node..."); + #endif + #if defined(BLUSHYBOX) + blinker.attach_ms(500, changeState); + #endif + SelectNode(); + #if defined(BLUSHYBOX) + blinker.detach(); + #endif + #else + #if defined(DISPLAY_SSD1306) || defined(DISPLAY_16X2) + display_info("Waiting for WiFi..."); + #endif + SetupWifi(); + #endif + SetupOTA(); + + #if defined(WEB_DASHBOARD) + if (!MDNS.begin(RIG_IDENTIFIER)) { + #if defined(SERIAL_PRINTING) + Serial.println("mDNS unavailable"); + #endif + } + MDNS.addService("http", "tcp", 80); + #if defined(SERIAL_PRINTING) + #ifdef USE_LAN + Serial.println("Configured mDNS for dashboard on http://" + String(RIG_IDENTIFIER) + + ".local (or http://" + ETH.localIP().toString() + ")"); + #else + Serial.println("Configured mDNS for dashboard on http://" + String(RIG_IDENTIFIER) + + ".local (or http://" + WiFi.localIP().toString() + ")"); + #endif + #endif + + server.on("/", dashboard); + #if defined(CAPTIVE_PORTAL) + server.on("/reset", reset_settings); + #endif + server.begin(); + #endif + + #if defined(ESP8266) + // Start the WDT watchdog + lwdtFeed(); + lwdTimer.attach_ms(LWD_TIMEOUT, lwdtcb); + #endif + + #if defined(ESP8266) + // Fastest clock mode for 8266s + system_update_cpu_freq(160); + os_update_cpu_frequency(160); + // Feed the watchdog + lwdtFeed(); + #else + // Fastest clock mode for 32s + setCpuFrequencyMhz(240); + #endif + + job[0]->blink(BLINK_SETUP_COMPLETE); + + #if defined(ESP32) && CORE == 2 + mutexClientData = xSemaphoreCreateMutex(); + mutexConnectToServer = xSemaphoreCreateMutex(); + + xTaskCreatePinnedToCore(system_events_func, "system_events_func", 10000, NULL, 1, NULL, 0); + xTaskCreatePinnedToCore(task1_func, "task1_func", 10000, NULL, 1, &Task1, 0); + xTaskCreatePinnedToCore(task2_func, "task2_func", 10000, NULL, 1, &Task2, 1); + #endif +} + +void system_events_func(void* parameter) { + while (true) { + delay(10); + #if defined(WEB_DASHBOARD) + server.handleClient(); + #endif + ArduinoOTA.handle(); + } +} + +void single_core_loop() { + job[0]->mine(); + + lwdtFeed(); + + #if defined(DISPLAY_SSD1306) || defined(DISPLAY_16X2) + float hashrate_float = (hashrate+hashrate_core_two) / 1000.0; + float accept_rate = (accepted_share_count / 0.01 / share_count); + + long millisecs = millis(); + int uptime_secs = int((millisecs / 1000) % 60); + int uptime_mins = int((millisecs / (1000 * 60)) % 60); + int uptime_hours = int((millisecs / (1000 * 60 * 60)) % 24); + String uptime = String(uptime_hours) + "h" + String(uptime_mins) + "m" + String(uptime_secs) + "s"; + + float sharerate = share_count / (millisecs / 1000.0); + + display_mining_results(String(hashrate_float, 1), String(accepted_share_count), String(share_count), String(uptime), + String(node_id), String(difficulty / 100), String(sharerate, 1), + String(ping), String(accept_rate, 1)); + #endif + + VerifyWifi(); + ArduinoOTA.handle(); + #if defined(WEB_DASHBOARD) + server.handleClient(); + #endif +} + +void loop() { + #if defined(ESP8266) || defined(CONFIG_FREERTOS_UNICORE) + single_core_loop(); + #endif + delay(10); +} diff --git a/ESP_Code/MiningJob.h b/ESP_Code/MiningJob.h new file mode 100644 index 00000000..be205211 --- /dev/null +++ b/ESP_Code/MiningJob.h @@ -0,0 +1,432 @@ +#pragma GCC optimize("-Ofast") + +#ifndef MINING_JOB_H +#define MINING_JOB_H + +#include +#include +#include +#include +#include + +#include "DSHA1.h" +#include "Counter.h" +#include "Settings.h" + +// https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TypeConversion.cpp +const char base36Chars[36] PROGMEM = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' +}; + +const uint8_t base36CharValues[75] PROGMEM{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, // 0 to 9 + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0, 0, // Upper case letters + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 // Lower case letters +}; + +#define SPC_TOKEN ' ' +#define END_TOKEN '\n' +#define SEP_TOKEN ',' +#define IOT_TOKEN '@' + +struct MiningConfig { + String host = ""; + int port = 0; + String DUCO_USER = ""; + String RIG_IDENTIFIER = ""; + String MINER_KEY = ""; + String MINER_VER = SOFTWARE_VERSION; + #if defined(ESP8266) + // "High-band" 8266 diff + String START_DIFF = "ESP8266H"; + #elif defined(CONFIG_FREERTOS_UNICORE) + // Single core 32 diff + String START_DIFF = "ESP32S"; + #else + // Normal 32 diff + String START_DIFF = "ESP32"; + #endif + + MiningConfig(String DUCO_USER, String RIG_IDENTIFIER, String MINER_KEY) + : DUCO_USER(DUCO_USER), RIG_IDENTIFIER(RIG_IDENTIFIER), MINER_KEY(MINER_KEY) {} +}; + +class MiningJob { + +public: + MiningConfig *config; + int core = 0; + + MiningJob(int core, MiningConfig *config) { + this->core = core; + this->config = config; + this->client_buffer = ""; + dsha1 = new DSHA1(); + dsha1->warmup(); + generateRigIdentifier(); + } + + void blink(uint8_t count, uint8_t pin = LED_BUILTIN) { + #if defined(LED_BLINKING) + uint8_t state = HIGH; + + for (int x = 0; x < (count << 1); ++x) { + digitalWrite(pin, state ^= HIGH); + delay(50); + } + #else + digitalWrite(LED_BUILTIN, HIGH); + #endif + } + + bool max_micros_elapsed(unsigned long current, unsigned long max_elapsed) { + static unsigned long _start = 0; + + if ((current - _start) > max_elapsed) { + _start = current; + return true; + } + return false; + } + + void handleSystemEvents(void) { + #if defined(ESP32) && CORE == 2 + esp_task_wdt_reset(); + #endif + delay(10); // Required vTaskDelay by ESP-IDF + yield(); + ArduinoOTA.handle(); + } + + void mine() { + connectToNode(); + askForJob(); + + dsha1->reset().write((const unsigned char *)getLastBlockHash().c_str(), getLastBlockHash().length()); + + int start_time = micros(); + max_micros_elapsed(start_time, 0); + #if defined(LED_BLINKING) + #if defined(BLUSHYBOX) + for (int i = 0; i < 72; i++) { + analogWrite(LED_BUILTIN, i); + delay(1); + } + #else + digitalWrite(LED_BUILTIN, LOW); + #endif + #endif + for (Counter<10> counter; counter < difficulty; ++counter) { + DSHA1 ctx = *dsha1; + ctx.write((const unsigned char *)counter.c_str(), counter.strlen()).finalize(hashArray); + + #ifndef CONFIG_FREERTOS_UNICORE + #if defined(ESP32) + #define SYSTEM_TIMEOUT 100000 // 10ms for esp32 looks like the lowest value without false watchdog triggers + #else + #define SYSTEM_TIMEOUT 500000 // 50ms for 8266 for same reason as above + #endif + if (max_micros_elapsed(micros(), SYSTEM_TIMEOUT)) { + handleSystemEvents(); + } + #endif + + if (memcmp(getExpectedHash(), hashArray, 20) == 0) { + unsigned long elapsed_time = micros() - start_time; + float elapsed_time_s = elapsed_time * .000001f; + share_count++; + + #if defined(LED_BLINKING) + #if defined(BLUSHYBOX) + for (int i = 72; i > 0; i--) { + analogWrite(LED_BUILTIN, i); + delay(1); + } + #else + digitalWrite(LED_BUILTIN, HIGH); + #endif + #endif + + if (String(core) == "0") { + hashrate = counter / elapsed_time_s; + submit(counter, hashrate, elapsed_time_s); + } else { + hashrate_core_two = counter / elapsed_time_s; + submit(counter, hashrate_core_two, elapsed_time_s); + } + + #if defined(BLUSHYBOX) + gauge_set(hashrate + hashrate_core_two); + #endif + + break; + } + } + } + +private: + String client_buffer; + uint8_t hashArray[20]; + String last_block_hash; + String expected_hash_str; + uint8_t expected_hash[20]; + DSHA1 *dsha1; + WiFiClient client; + String chipID = ""; + + #if defined(ESP8266) + #if defined(BLUSHYBOX) + String MINER_BANNER = "Official BlushyBox Miner (ESP8266)"; + #else + String MINER_BANNER = "Official ESP8266 Miner"; + #endif + #elif defined(CONFIG_FREERTOS_UNICORE) + String MINER_BANNER = "Official ESP32-S2 Miner"; + #else + #if defined(BLUSHYBOX) + String MINER_BANNER = "Official BlushyBox Miner (ESP32)"; + #else + String MINER_BANNER = "Official ESP32 Miner"; + #endif + #endif + + uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, const uint32_t arrayLength) { + assert(hexString.length() >= arrayLength * 2); + const char *hexChars = hexString.c_str(); + for (uint32_t i = 0; i < arrayLength; ++i) { + uint8Array[i] = (pgm_read_byte(base36CharValues + hexChars[i * 2] - '0') << 4) + pgm_read_byte(base36CharValues + hexChars[i * 2 + 1] - '0'); + } + return uint8Array; + } + + void generateRigIdentifier() { + String AutoRigName = ""; + + #if defined(ESP8266) + chipID = String(ESP.getChipId(), HEX); + + if (strcmp(config->RIG_IDENTIFIER.c_str(), "Auto") != 0) + return; + + AutoRigName = "ESP8266-" + chipID; + AutoRigName.toUpperCase(); + config->RIG_IDENTIFIER = AutoRigName.c_str(); + #else + uint64_t chip_id = ESP.getEfuseMac(); + uint16_t chip = (uint16_t)(chip_id >> 32); // Prepare to print a 64 bit value into a char array + char fullChip[23]; + snprintf(fullChip, 23, "%04X%08X", chip, + (uint32_t)chip_id); // Store the (actually) 48 bit chip_id into a char array + + chipID = String(fullChip); + + if (strcmp(config->RIG_IDENTIFIER.c_str(), "Auto") != 0) + return; + // Autogenerate ID if required + AutoRigName = "ESP32-" + String(fullChip); + AutoRigName.toUpperCase(); + config->RIG_IDENTIFIER = AutoRigName.c_str(); + #endif + #if defined(SERIAL_PRINTING) + Serial.println("Core [" + String(core) + "] - Rig identifier: " + + config->RIG_IDENTIFIER); + #endif + } + + void connectToNode() { + if (client.connected()) return; + + unsigned int stopWatch = millis(); + #if defined(SERIAL_PRINTING) + Serial.println("Core [" + String(core) + "] - Connecting to a Duino-Coin node..."); + #endif + while (!client.connect(config->host.c_str(), config->port)) { + if (max_micros_elapsed(micros(), 100000)) { + handleSystemEvents(); + } + if (millis()-stopWatch>100000) ESP.restart(); + } + + waitForClientData(); + #if defined(SERIAL_PRINTING) + Serial.println("Core [" + String(core) + "] - Connected. Node reported version: " + + client_buffer); + #endif + + blink(BLINK_CLIENT_CONNECT); + + /* client.print("MOTD" + END_TOKEN); + waitForClientData(); + #if defined(SERIAL_PRINTING) + Serial.println("Core [" + String(core) + "] - MOTD: " + + client_buffer); + #endif */ + } + + void waitForClientData() { + client_buffer = ""; + unsigned int stopWatch = millis(); + while (client.connected()) { + if (client.available()) { + client_buffer = client.readStringUntil(END_TOKEN); + if (client_buffer.length() == 1 && client_buffer[0] == END_TOKEN) + client_buffer = "???\n"; // NOTE: Should never happen + break; + } + if (max_micros_elapsed(micros(), 100000)) { + handleSystemEvents(); + } + if (millis()-stopWatch>120000) { + Serial.println("Timeout after 120s. Forced restart.."); + ESP.restart(); + } + } + } + + void submit(unsigned long counter, float hashrate, float elapsed_time_s) { + client.print(String(counter) + + SEP_TOKEN + String(hashrate) + + SEP_TOKEN + MINER_BANNER + + SPC_TOKEN + config->MINER_VER + + SEP_TOKEN + config->RIG_IDENTIFIER + + SEP_TOKEN + "DUCOID" + String(chipID) + + SEP_TOKEN + String(WALLET_ID) + + END_TOKEN); + + unsigned long ping_start = millis(); + waitForClientData(); + ping = millis() - ping_start; + + if (client_buffer == "GOOD") { + accepted_share_count++; + } + + #if defined(SERIAL_PRINTING) + Serial.println("Core [" + String(core) + "] - " + + client_buffer + + " share #" + String(share_count) + + " (" + String(counter) + ")" + + " hashrate: " + String(hashrate / 1000, 2) + " kH/s (" + + String(elapsed_time_s) + "s) " + + "Ping: " + String(ping) + "ms " + + "(" + node_id + ")\n"); + #endif + } + + bool parse() { + // Create a non-constant copy of the input string + char *job_str_copy = strdup(client_buffer.c_str()); + + if (job_str_copy) { + String tokens[3]; + char *token = strtok(job_str_copy, ","); + for (int i = 0; token != NULL && i < 3; i++) { + tokens[i] = token; + token = strtok(NULL, ","); + } + + last_block_hash = tokens[0]; + expected_hash_str = tokens[1]; + hexStringToUint8Array(expected_hash_str, expected_hash, 20); + difficulty = tokens[2].toInt() * 100 + 1; + + // Free the memory allocated by strdup + free(job_str_copy); + + return true; + } + else { + // Handle memory allocation failure + return false; + } + } + + void askForJob() { + Serial.println("Core [" + String(core) + "] - Asking for a new job for user: " + + String(config->DUCO_USER)); + + #if defined(USE_DS18B20) + sensors.requestTemperatures(); + float temp = sensors.getTempCByIndex(0); + #if defined(SERIAL_PRINTING) + Serial.println("DS18B20 reading: " + String(temp) + "°C"); + #endif + + client.print("JOB," + + String(config->DUCO_USER) + + SEP_TOKEN + config->START_DIFF + + SEP_TOKEN + String(config->MINER_KEY) + + SEP_TOKEN + "Temp:" + String(temp) + "*C" + + END_TOKEN); + #elif defined(USE_DHT) + float temp = dht.readTemperature(); + float hum = dht.readHumidity(); + #if defined(SERIAL_PRINTING) + Serial.println("DHT reading: " + String(temp) + "°C"); + Serial.println("DHT reading: " + String(hum) + "%"); + #endif + + client.print("JOB," + + String(config->DUCO_USER) + + SEP_TOKEN + config->START_DIFF + + SEP_TOKEN + String(config->MINER_KEY) + + SEP_TOKEN + "Temp:" + String(temp) + "*C" + + IOT_TOKEN + "Hum:" + String(hum) + "%" + + END_TOKEN); + #elif defined(USE_HSU07M) + float temp = read_hsu07m(); + #if defined(SERIAL_PRINTING) + Serial.println("HSU reading: " + String(temp) + "°C"); + #endif + + client.print("JOB," + + String(config->DUCO_USER) + + SEP_TOKEN + config->START_DIFF + + SEP_TOKEN + String(config->MINER_KEY) + + SEP_TOKEN + "Temp:" + String(temp) + "*C" + + END_TOKEN); + #elif defined(USE_INTERNAL_SENSOR) + float temp = 0; + temp_sensor_read_celsius(&temp); + #if defined(SERIAL_PRINTING) + Serial.println("Internal temp sensor reading: " + String(temp) + "°C"); + #endif + + client.print("JOB," + + String(config->DUCO_USER) + + SEP_TOKEN + config->START_DIFF + + SEP_TOKEN + String(config->MINER_KEY) + + SEP_TOKEN + "CPU Temp:" + String(temp) + "*C" + + END_TOKEN); + #else + client.print("JOB," + + String(config->DUCO_USER) + + SEP_TOKEN + config->START_DIFF + + SEP_TOKEN + String(config->MINER_KEY) + + END_TOKEN); + #endif + + waitForClientData(); + #if defined(SERIAL_PRINTING) + Serial.println("Core [" + String(core) + "] - Received job with size of " + + String(client_buffer.length()) + + " bytes " + client_buffer); + #endif + + parse(); + #if defined(SERIAL_PRINTING) + Serial.println("Core [" + String(core) + "] - Parsed job: " + + getLastBlockHash() + " " + + getExpectedHashStr() + " " + + String(getDifficulty())); + #endif + } + + const String &getLastBlockHash() const { return last_block_hash; } + const String &getExpectedHashStr() const { return expected_hash_str; } + const uint8_t *getExpectedHash() const { return expected_hash; } + unsigned int getDifficulty() const { return difficulty; } +}; + +#endif diff --git a/ESP_Code/Settings.h b/ESP_Code/Settings.h new file mode 100644 index 00000000..41d0948a --- /dev/null +++ b/ESP_Code/Settings.h @@ -0,0 +1,226 @@ +// Settings.h +#ifndef SETTINGS_H +#define SETTINGS_H + +// ---------------------- General settings ---------------------- // +// Change the part in brackets to your Duino-Coin username +extern char *DUCO_USER = "my_cool_username"; +// Change the part in brackets to your mining key (if you have set it in the wallet) +extern char *MINER_KEY = "mySecretPass"; +// Change the part in brackets if you want to set a custom miner name +// Use Auto to autogenerate, None for no custom identifier +extern char *RIG_IDENTIFIER = "None"; +// Change the part in brackets to your WiFi name +extern const char SSID[] = "SSID"; +// Change the part in brackets to your WiFi password +extern const char PASSWORD[] = "PASSW0RD"; +// -------------------------------------------------------------- // + +// -------------------- Advanced options ------------------------ // +// Uncomment if you want to host the dashboard page (available on ESPs IP address and mDNS) +// #define WEB_DASHBOARD + +// Comment out the line below if you wish to disable LED blinking +#define LED_BLINKING + +// Uncomment if you want to use LAN8720. WLAN-credentials will be ignored using LAN +// Select correct Board in ArduinoIDE!!! Really! +// #define USE_LAN + +// Comment out the line below if you wish to disable Serial printing +#define SERIAL_PRINTING + +// Edit the line below if you wish to change the serial speed (low values may reduce performance but are less prone to interference) +#define SERIAL_BAUDRATE 500000 + +// ESP8266 WDT loop watchdog. Do not edit this value, but if you must - do not set it too low or it will falsely trigger during mining! +#define LWD_TIMEOUT 30000 + +// Uncomment to disable ESP32 brownout detector if you're suffering from faulty insufficient power detection +// #define DISABLE_BROWNOUT + +// Uncomment to enable WiFiManager captive portal in AP mode +// The board will create its own network you connect to and change the settings +// REQUIRES WiFiManager library by tzapu (https://github.com/tzapu/WiFiManager) +// #define CAPTIVE_PORTAL +// -------------------------------------------------------------- // + +// ------------------------ Displays ---------------------------- // + +// Uncomment to enable a SSD1306 OLED screen on the I2C bus to display mining info in real time +// Default connections (can be overriden by using a different u8g2 initializer, see line 140): +// GND - GND +// VCC - 5V or 3.3V depending on display +// SCL - GPIO22 (ESP32) or GPIO5 (D2 on ESP8266) or GPIO35 (ESP32-S2) +// SDA - GPIO21 (ESP32) or GPIO4 (D1 on ESP8266) or GPIO33 (ESP32-S2) +// #define DISPLAY_SSD1306 + +// Uncomment to enable a 16x2 LCD screen on a direct bus to display mining info in real time +// See line 150 for connections and initializer +// #define DISPLAY_16X2 + +// Uncomment if your device is a Duino BlushyBox device +// #define BLUSHYBOX +// -------------------------------------------------------------- // + +// ---------------------- IoT examples -------------------------- // +// https://github.com/revoxhere/duino-coin/wiki/Duino's-take-on-the-Internet-of-Things + +// Uncomment the line below if you wish to use the internal temperature sensor (Duino IoT example) +// Only ESP32-S2, -S3, -H2, -C2, -C3, -C6 and some old models have one! +// More info: https://www.espboards.dev/blog/esp32-inbuilt-temperature-sensor/ +// NOTE: Mining performance will decrease by about 20 kH/s! +// #define USE_INTERNAL_SENSOR + +// Uncomment the line below if you wish to use a DS18B20 temperature sensor (Duino IoT example) +// NOTE: Mining performance should stay the same +// #define USE_DS18B20 + +// Uncomment the line below if you wish to use a DHT11/22 temperature and humidity sensor (Duino IoT example) +// NOTE: Mining performance should stay the same +// #define USE_DHT + +// Uncomment the line below if you wish to use a HSU07M sensor (Duino IoT Example) +// NOTE: Untested as of right now +// #define USE_HSU07M +// -------------------------------------------------------------- // + +// ---------------- Variables and definitions ------------------- // +// You generally do not need to edit stuff below this line +// unless you're know what you're doing. + +#if defined(ESP8266) + // ESP8266 + #define LED_BUILTIN 2 +#elif defined(CONFIG_FREERTOS_UNICORE) + #if defined(CONFIG_IDF_TARGET_ESP32C3) + // ESP32-C3 + #define LED_BUILTIN 8 + #else + // ESP32-S2 + #define LED_BUILTIN 15 + #endif +#else + // ESP32 + #ifndef LED_BUILTIN + #define LED_BUILTIN 2 + #endif + #if defined(BLUSHYBOX) + #define LED_BUILTIN 4 + #endif +#endif + +#define BLINK_SETUP_COMPLETE 2 +#define BLINK_CLIENT_CONNECT 5 + +#define SOFTWARE_VERSION "4.3" +extern unsigned int hashrate = 0; +extern unsigned int hashrate_core_two = 0; +extern unsigned int difficulty = 0; +extern unsigned long share_count = 0; +extern unsigned long accepted_share_count = 0; +extern String node_id = ""; +extern String WALLET_ID = ""; +extern unsigned int ping = 0; + +#if defined(USE_INTERNAL_SENSOR) + #include "driver/temp_sensor.h" +#endif + +#if defined(USE_DS18B20) + // Install OneWire and DallasTemperature libraries if you get an error + #include + #include + // Change 12 to the pin you've connected your sensor to + const int DSPIN = 12; + + OneWire oneWire(DSPIN); + DallasTemperature extern sensors(&oneWire); +#endif + +#if defined(USE_DHT) + // Install "DHT sensor library" if you get an error + #include + // Change 12 to the pin you've connected your sensor to + #define DHTPIN 12 + // Set DHT11 or DHT22 type accordingly + #define DHTTYPE DHT11 + + DHT extern dht(DHTPIN, DHTTYPE); +#endif + +#if defined(DISPLAY_SSD1306) + // Install "u8g2" if you get an error + #include + #include + // Display definition from the U8G2 library. Edit if you use a different display + // For software I2C, use ..._F_SW_I2C and define the pins in the initializer + U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); +#endif + +#if defined(DISPLAY_16X2) + #include "Wire.h" + // Install "Adafruit_LiquidCrystal" if you get an error + #include "Adafruit_LiquidCrystal.h" + + // initialize the library with the numbers of the interface pins + // RS E D4 D5 D6 D7 + Adafruit_LiquidCrystal lcd(1, 2, 3, 4, 5, 6); +#endif + +#if defined(USE_HSU07M) + #include "Wire.h" + #define HSU07M_ADDRESS 0x4B // Change this if your sensor has a different address + + float read_hsu07m() { + Wire.beginTransmission(HSU07M_ADDRESS); + Wire.write(0x00); + Wire.endTransmission(); + delay(100); + Wire.requestFrom(HSU07M_ADDRESS, 2); + if(Wire.available() >= 2) { + byte tempMSB = Wire.read(); + byte tempLSB = Wire.read(); + int tempRaw = (tempMSB << 8) | tempLSB; + float tempC = (tempRaw / 16.0) - 40.0; + return tempC; + } + return -1.0; + } +#endif + +#if defined(BLUSHYBOX) + #define GAUGE_PIN 5 + #define GAUGE_MAX 190 + #define GAUGE_MIN 0 + #if defined(ESP8266) + #define GAUGE_MAX_HR 80000 + #else + #define GAUGE_MAX_HR 200000 + #endif + extern float hashrate_old = 0.0; + + void gauge_set(float hashrate) { + float old = hashrate_old; + float new_val = hashrate; + + if (hashrate_old == 0) { + float delta = (new_val - old) / 50; + for (int x=0; x < 50; x++) { + analogWrite(5, map(old + x*delta, 0, GAUGE_MAX_HR, GAUGE_MIN, GAUGE_MAX) + random(0, 10)); + delay(20); + } + } else { + float delta = (new_val - old) / 10; + for (int x=0; x < 10; x++) { + analogWrite(5, map(old + x*delta, 0, GAUGE_MAX_HR, GAUGE_MIN, GAUGE_MAX) + random(0, 10)); + delay(10); + } + } + hashrate_old = hashrate; + } +#endif + +IPAddress DNS_SERVER(1, 1, 1, 1); // Cloudflare DNS server + +#endif // End of SETTINGS_H diff --git a/LICENSE b/LICENSE index fd6ef7b9..b2bc599c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-2022 Robert Piotrowski +Copyright (c) 2019-present Robert Piotrowski Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/CLI_Wallet.py b/Legacy codes/CLI_Wallet.py similarity index 100% rename from CLI_Wallet.py rename to Legacy codes/CLI_Wallet.py diff --git a/ESP32_Code/ESP32_Code.ino b/Legacy codes/ESP32_Code/ESP32_Code_3.5.ino similarity index 85% rename from ESP32_Code/ESP32_Code.ino rename to Legacy codes/ESP32_Code/ESP32_Code_3.5.ino index c03684ef..3c17b18b 100644 --- a/ESP32_Code/ESP32_Code.ino +++ b/Legacy codes/ESP32_Code/ESP32_Code_3.5.ino @@ -3,7 +3,7 @@ ( _ \( )( )(_ _)( \( )( _ )___ / __)( _ )(_ _)( \( ) )(_) ))(__)( _)(_ ) ( )(_)((___)( (__ )(_)( _)(_ ) ( (____/(______)(____)(_)\_)(_____) \___)(_____)(____)(_)\_) - Official code for ESP32 boards version 3.18 + Official code for ESP32 boards version 3.5 Duino-Coin Team & Community 2019-2022 © MIT Licensed https://duinocoin.com @@ -14,18 +14,28 @@ */ /***************** START OF MINER CONFIGURATION SECTION *****************/ +// Change the part in brackets to your Duino-Coin username +const char *DUCO_USER = "USERNAME"; +// Change the part in brackets to your mining key (if you enabled it in the wallet) +const char* MINER_KEY = "MINING_KEY"; // Change the part in brackets to your WiFi name -const char *SSID = "My cool Wi-Fi"; +const char *SSID = "WIFI_NAME"; // Change the part in brackets to your WiFi password -const char *WIFI_PASS = "My secret pass"; -// Change the part in brackets to your Duino-Coin username -const char *DUCO_USER = "my_cool_username"; +const char *WIFI_PASS = "WIFI_PASSWORD"; // Change the part in brackets if you want to set a custom miner name (use Auto to autogenerate, None for no name) const char *RIG_IDENTIFIER = "None"; -// Change the part in brackets to your mining key (if you enabled it in the wallet) -const char* MINER_KEY = "None"; // Change this if your board has built-in led on non-standard pin #define LED_BUILTIN 2 + +template +class DuinoIoT { + T sensor; +}; + +typedef struct { + String val; +} SensorData_t; + // Uncomment the line below if you wish to use a DHT sensor (Duino IoT beta) // #define USE_DHT #ifdef USE_DHT @@ -35,19 +45,116 @@ const char* MINER_KEY = "None"; #define DHTPIN 2 // Set DHT11 or DHT22 accordingly #define DHTTYPE DHT11 - DHT dht(DHTPIN, DHTTYPE); + + template<> + class DuinoIoT { + + DHT sensor; + + public: + + DuinoIoT() : sensor(DHTPIN, DHTTYPE) {} + + void begin() { + Serial.println("Initializing DHT sensor"); + sensor.begin(); + Serial.println("Test reading: " + String(sensor.readTemperature()) + "*C " + String(sensor.readHumidity()) + "% humidity"); + } + + void getSensorData(SensorData_t *data) { + String temp = String(sensor.readTemperature()); + String hum = String(sensor.readHumidity()); + Serial.println("DHT readings: " + temp + "*C, " + hum + "%"); + data->val = temp + "@" + hum; + } + }; + + DuinoIoT duinoIoT; #endif // Uncomment the line below if you wish to use an AHT10 or AHT20 sensor (Duino IoT beta) -// #define USE_AHT +//#define USE_AHT #ifdef USE_AHT // Install "Adafruit AHTX0 Library" if you get an error #include // AHT10/AHT20 should be connected to ESP32 default I2C pins // i.e. (I2C_SDA: GPIO_21 and I2C_SCL: GPIO_22) - Adafruit_AHTX0 aht; + + template<> + class DuinoIoT { + + Adafruit_AHTX0 sensor; + + public: + + DuinoIoT() : sensor() {} + + void begin() { + Serial.println("Initializing AHT sensor"); + if (! sensor.begin()) { + Serial.println("Could not find AHT Sensor. Check wiring?"); + while (1) delay(10); + } + sensors_event_t hum, temp; + sensor.getEvent(&hum, &temp); + Serial.println("Test reading: " + String(temp.temperature) + "*C, " + String(hum.relative_humidity) + "% rH"); + } + + void getSensorData(SensorData_t *data) { + sensors_event_t hum, temp; + sensor.getEvent(&hum, &temp); + Serial.println("AHT readings: " + String(temp.temperature) + "*C, " + String(hum.relative_humidity) + "% rH"); + data->val = String(temp.temperature) + "@" + String(hum.relative_humidity); + } + }; + + DuinoIoT duinoIoT; +#endif + +// Uncomment the line below if you wish to use an BMP280 sensor (Duino IoT beta) +//#define USE_BMP280 +#ifdef USE_BMP280 +// Install "Adafruit BMP280 Library" if you get an error +# include +# include +# define I2C_SDA_PIN 22 +# define I2C_SCL_PIN 23 +# define BMP280_I2C_ADDR 0x76 + template<> + class DuinoIoT { + + Adafruit_BMP280 sensor; + + public: + + DuinoIoT() : sensor() {} + + void begin() { + Serial.println("Initializing BMP280 sensor"); + Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN); + if (!sensor.begin(BMP280_I2C_ADDR)) { + Serial.println("Could not find BMP280 Sensor. Check wiring?"); + while (1); delay(10); + } + sensor.setSampling(Adafruit_BMP280::MODE_NORMAL, + Adafruit_BMP280::SAMPLING_X2, + Adafruit_BMP280::SAMPLING_X16, + Adafruit_BMP280::FILTER_X16, + Adafruit_BMP280::STANDBY_MS_500); + + Serial.print(F("Test reading: "));Serial.print(sensor.readTemperature());Serial.print(sensor.readPressure()); + } + + void getSensorData(SensorData_t *data) { + String temp = String(sensor.readTemperature()); + String pressure = String(sensor.readPressure() / 100.0F); + Serial.println("BMP280 readings: " + temp + "*C, " + pressure + " hPa"); + data->val = "temperature:" + temp + "@pressure:" + pressure; + } + }; + + DuinoIoT duinoIoT; #endif -#define BLINK_SHARE_FOUND 1 #define BLINK_SETUP_COMPLETE 2 #define BLINK_CLIENT_CONNECT 3 #define BLINK_RESET_DEVICE 5 @@ -76,9 +183,12 @@ const int mqtt_port = 1883; /* If you're using the ESP32-CAM board or other board that doesn't support OTA (Over-The-Air programming) comment the ENABLE_OTA definition line (#define ENABLE_OTA) - NOTE: enabling OTA support could decrease hashrate (up to 40%) */ + NOTE: enabling OTA support may decrease the hashrate */ // #define ENABLE_OTA +/* If you want to use the web dashboard uncomment the line below */ +// #define WEB_DASHBOARD + /* If you don't want to use the Serial interface comment the ENABLE_SERIAL definition line (#define ENABLE_SERIAL)*/ #define ENABLE_SERIAL @@ -155,7 +265,7 @@ SemaphoreHandle_t xMutex; const char * DEVICE = "ESP32"; const char * POOLPICKER_URL[] = {"https://server.duinocoin.com/getPool"}; const char * MINER_BANNER = "Official ESP32 Miner"; -const char * MINER_VER = "3.18"; +const char * MINER_VER = "3.5"; String pool_name = ""; String host = ""; String node_id = ""; @@ -469,11 +579,13 @@ void WiFireconnect(void *pvParameters) { for (;;) { wifi_state = WiFi.status(); -#ifdef ENABLE_OTA - ArduinoOTA.handle(); -#endif - - server.handleClient(); + #ifdef ENABLE_OTA + ArduinoOTA.handle(); + #endif + + #ifdef WEB_DASHBOARD + server.handleClient(); + #endif if (ota_state) // If OTA is working, reset the watchdog esp_task_wdt_reset(); @@ -491,17 +603,19 @@ void WiFireconnect(void *pvParameters) { Serial.println("Rig name: " + String(RIG_IDENTIFIER)); Serial.println(); - if (!MDNS.begin(RIG_IDENTIFIER)) { - Serial.println("mDNS unavailable"); - } - MDNS.addService("http", "tcp", 80); - Serial.print("Configured mDNS for dashboard on http://" - + String(RIG_IDENTIFIER) - + ".local (or http://" - + WiFi.localIP().toString() - + ")"); - server.on("/", dashboard); - server.begin(); + #ifdef WEB_DASHBOARD + if (!MDNS.begin(RIG_IDENTIFIER)) { + Serial.println("mDNS unavailable"); + } + MDNS.addService("http", "tcp", 80); + Serial.print("Configured mDNS for dashboard on http://" + + String(RIG_IDENTIFIER) + + ".local (or http://" + + WiFi.localIP().toString() + + ")"); + server.on("/", dashboard); + server.begin(); + #endif // Notify Setup Complete blink(BLINK_SETUP_COMPLETE);// Sucessfull connection with wifi network @@ -640,20 +754,13 @@ void TaskMining(void *pvParameters) { Serial.println(String(taskCoreName + " asking for a new job for user: " + DUCO_USER)); jobClient.flush(); - #if !(defined(USE_DHT) || defined(USE_AHT)) + #if !(defined(USE_DHT) || defined(USE_AHT) || defined(USE_BMP280)) jobClient.print("JOB," + String(DUCO_USER) + ",ESP32," + String(MINER_KEY) + MSGNEWLINE); - #elif defined(USE_DHT) - int temp = dht.readTemperature(); - int hum = dht.readHumidity(); - Serial.println("DHT readings: " + String(temp) + "*C, " + String(hum) + "%"); - jobClient.print("JOB," + String(DUCO_USER) + ",ESP32," + String(MINER_KEY) + "," + - String(temp) + "@" + String(hum) + MSGNEWLINE); - #elif defined(USE_AHT) - sensors_event_t hum, temp; - aht.getEvent(&hum, &temp); - Serial.println("AHT readings: " + String(temp.temperature) + "*C, " + String(hum.relative_humidity) + "% rH"); + #else + SensorData_t data = {""}; + duinoIoT.getSensorData(&data); jobClient.print("JOB," + String(DUCO_USER) + ",ESP32," + String(MINER_KEY) + "," + - String(temp.temperature) + "@" + String(hum.relative_humidity) + MSGNEWLINE); + data.val + MSGNEWLINE); #endif while (!jobClient.available()) { @@ -700,6 +807,7 @@ void TaskMining(void *pvParameters) { bool ignoreHashrate = false; // Try to find the nonce which creates the expected hash + digitalWrite(LED_BUILTIN, HIGH); for (unsigned long nonceCalc = 0; nonceCalc <= TaskThreadData[taskId].difficulty; nonceCalc++) { // Define hash under Test hashUnderTest = previousHash + String(nonceCalc); @@ -716,12 +824,13 @@ void TaskMining(void *pvParameters) { // Check if we have found the nonce for the expected hash if ( memcmp( shaResult, expectedHashBytes, sizeof(shaResult) ) == 0 ) { - // Found the nonce submit it to the server - Serial.println(String(taskCoreName + " found a correct hash using nonce: " + nonceCalc )); - + // Found the nonce - submit it to the server + digitalWrite(LED_BUILTIN, LOW); + // Calculate mining time float elapsedTime = (micros() - startTime) / 1000.0 / 1000.0; // Total elapsed time in seconds TaskThreadData[taskId].hashrate = nonceCalc / elapsedTime; + Serial.println(String(taskCoreName + " found a correct hash (" + elapsedTime + "s)")); // Validate connection if (!jobClient.connected()) { @@ -757,20 +866,9 @@ void TaskMining(void *pvParameters) { TaskThreadData[taskId].shares++; if (LED_BLINKING) digitalWrite(LED_BUILTIN, HIGH); - // Validate Hashrate - if ( TaskThreadData[taskId].hashrate < 4000 && !ignoreHashrate) { - // Hashrate is low so restart esp - Serial.println(String(taskCoreName + " has low hashrate: " + (TaskThreadData[taskId].hashrate / 1000) + "kH/s, job feedback: " + feedback + " - restarting...")); - jobClient.flush(); - jobClient.stop(); - blink(BLINK_RESET_DEVICE); - esp_restart(); - } - else { - // Print statistics - Serial.println(String(taskCoreName + " retrieved job feedback: " + feedback + ", hashrate: " + (TaskThreadData[taskId].hashrate / 1000) + "kH/s, share #" + TaskThreadData[taskId].shares)); - } - + // Print statistics + Serial.println(String(taskCoreName + " retrieved job feedback: " + feedback + ", hashrate: " + (TaskThreadData[taskId].hashrate / 1000) + "kH/s, share #" + TaskThreadData[taskId].shares)); + // Stop current loop and ask for a new job break; } @@ -786,24 +884,10 @@ void TaskMining(void *pvParameters) { void setup() { Serial.begin(500000); // Start serial connection Serial.println("\n\nDuino-Coin " + String(MINER_BANNER)); - - #ifdef USE_DHT - Serial.println("Initializing DHT sensor"); - dht.begin(); - Serial.println("Test reading: " + String(dht.readHumidity()) + "% humidity"); - Serial.println("Test reading: temperature " + String(dht.readTemperature()) + "*C"); - #elif defined(USE_AHT) - Serial.println("Initializing AHT sensor"); - if (! aht.begin()) { - Serial.println("Could not find AHT Sensor. Check wiring?"); - while (1) delay(10); - } - sensors_event_t hum, temp; - aht.getEvent(&hum, &temp); - Serial.println("Test reading: " + String(hum.relative_humidity) + "% humidity"); - Serial.println("Test reading: temperature " + String(temp.temperature) + "*C"); - #endif - +#if (defined(USE_DHT) || defined(USE_AHT) || defined(USE_BMP280)) + duinoIoT.begin(); +#endif + WiFi.setSleep(false); // Better network responsiveness WiFi.mode(WIFI_STA); // Setup ESP in client mode btStop(); WiFi.begin(SSID, WIFI_PASS); // Connect to wifi @@ -879,7 +963,7 @@ void setup() { xMutex = xSemaphoreCreateMutex(); xTaskCreatePinnedToCore( WiFireconnect, "WiFirec", 10000, NULL, NUMBEROFCORES + 2, &WiFirec, - mqttCore); // create a task with highest priority and executed on core 0 + wifiCore); // create a task with highest priority and executed on core 0 delay(250); // If MQTT is enabled create a sending thread diff --git a/ESP8266_Code/ESP8266_Code.ino b/Legacy codes/ESP8266_Code/ESP8266_Code_3.5.ino similarity index 76% rename from ESP8266_Code/ESP8266_Code.ino rename to Legacy codes/ESP8266_Code/ESP8266_Code_3.5.ino index ab056a15..ffccd876 100644 --- a/ESP8266_Code/ESP8266_Code.ino +++ b/Legacy codes/ESP8266_Code/ESP8266_Code_3.5.ino @@ -1,9 +1,9 @@ /* - ____ __ __ ____ _ _ _____ ___ _____ ____ _ _ + ____ __ __ ____ _ _ _____ ___ _____ ____ _ _ ( _ \( )( )(_ _)( \( )( _ )___ / __)( _ )(_ _)( \( ) - )(_) ))(__)( _)(_ ) ( )(_)((___)( (__ )(_)( _)(_ ) ( + )(_) ))(__)( _)(_ ) ( )(_)((___)( (__ )(_)( _)(_ ) ( (____/(______)(____)(_)\_)(_____) \___)(_____)(____)(_)\_) - Official code for ESP8266 boards version 3.18 + Official code for ESP8266 boards version 3.5 Duino-Coin Team & Community 2019-2022 © MIT Licensed https://duinocoin.com @@ -22,8 +22,8 @@ /* If during compilation the line below causes a "fatal error: arduinoJson.h: No such file or directory" message to occur; it means that you do NOT have the - ArduinoJSON library installed. To install it, - go to the below link and follow the instructions: + ArduinoJSON library installed. To install it, + go to the below link and follow the instructions: https://github.com/revoxhere/duino-coin/issues/832 */ #include @@ -35,7 +35,7 @@ follow the instructions of the readme file: https://github.com/esp8266/Arduino */ #include -#include +//#include #include #include @@ -46,94 +46,111 @@ #include #include +// Uncomment the line below if you wish to use a DHT sensor (Duino IoT beta) +// #define USE_DHT + // Uncomment the line below if you wish to register for IOT updates with an MQTT broker // #define USE_MQTT -// Uncomment the line below if you wish to use a DHT sensor (Duino IoT beta) -// #define USE_DHT +// If you don't know what MQTT means check this link: +// https://www.techtarget.com/iotagenda/definition/MQTT-MQ-Telemetry-Transport -#ifdef USE_MQTT - #include - // update below mqtt broker parameters - #define mqtt_server "your_mqtt_server" - #define mqtt_port 1883 - #define mqtt_user "your_mqtt_username" - #define mqtt_password "your_super_secret_mqtt_password" - // update humidity_topic to your mqtt humidity topic - #define humidity_topic "sensor/humidity" - // update temperature_topic to your mqtt temperature topic - #define temperature_topic "sensor/temperature" - - WiFiClient espClient; - PubSubClient mqttClient(espClient); - - void mqttReconnect() { - // Loop until we're reconnected - while (!mqttClient.connected()) { - Serial.print("Attempting MQTT connection..."); - // Attempt to connect - // If you do not want to use a username and password, change next line to - // if (mqttClient.connect("ESP8266Client")) { - if (mqttClient.connect("ESP8266Client", mqtt_user, mqtt_password)) { - Serial.println("connected"); - } else { - Serial.print("failed, rc="); - Serial.print(mqttClient.state()); - Serial.println(" try again in 5 seconds"); - // Wait 5 seconds before retrying - delay(5000); - } - } - } - - bool checkBound(float newValue, float prevValue, float maxDiff) { - return !isnan(newValue) && - (newValue < prevValue - maxDiff || newValue > prevValue + maxDiff); - } - - long lastMsg = 0; - float diff = 0.01; // change this to the minimum difference considered for update +#ifdef USE_DHT +float temp = 0.0; +float hum = 0.0; + +// Install "DHT sensor library" if you get an error +#include +// Change D3 to the pin you've connected your sensor to +#define DHTPIN D3 +// Set DHT11 or DHT22 accordingly +#define DHTTYPE DHT11 +DHT dht(DHTPIN, DHTTYPE); #endif -#ifdef USE_DHT - - float temp = 0.0; - float hum = 0.0; - float temp_weight = 0.9; // 1 for absolute new value, 0-1 for smoothing the new reading with previous value - float temp_min_value = -20.0; - float temp_max_value = 70.0; - float hum_weight = 0.9; // 1 for absolute new value, 0-1 for smoothing the new reading with previous value - float hum_min_value = 0.1; - float hum_max_value = 100.0; - - // Install "DHT sensor library" if you get an error - #include - // Change D3 to the pin you've connected your sensor to - #define DHTPIN D3 - // Set DHT11 or DHT22 accordingly - #define DHTTYPE DHT11 - DHT dht(DHTPIN, DHTTYPE); +#ifdef USE_MQTT +// Install "PubSubClient" if you get an error +#include + +long lastMsg = 0; + +// Change the part in brackets to your MQTT broker address +#define mqtt_server "broker.hivemq.com" +// broker.hivemq.com is for testing purposes, change it to your broker address + +// Change this to your MQTT broker port +#define mqtt_port 1883 +// If you want to use user and password for your MQTT broker, uncomment the line below +// #define mqtt_use_credentials + +// Change the part in brackets to your MQTT broker username +#define mqtt_user "My cool mqtt username" +// Change the part in brackets to your MQTT broker password +#define mqtt_password "My secret mqtt pass" + +// Change this if you want to send data to the topic every X milliseconds +#define mqtt_update_time 5000 + +// Change the part in brackets to your MQTT humidity topic +#define humidity_topic "sensor/humidity" +// Change the part in brackets to your MQTT temperature topic +#define temperature_topic "sensor/temperature" + +WiFiClient espClient; +PubSubClient mqttClient(espClient); + +void mqttReconnect() +{ + // Loop until we're reconnected + while (!mqttClient.connected()) + { + Serial.print("Attempting MQTT connection..."); + + // Create a random client ID + String clientId = "ESP8266Client-"; + clientId += String(random(0xffff), HEX); + + // Attempt to connect +#ifdef mqtt_use_credentials + if (mqttClient.connect("ESP8266Client", mqtt_user, mqtt_password)) +#else + if (mqttClient.connect(clientId.c_str())) +#endif + { + Serial.println("connected"); + } + else + { + Serial.print("failed, rc="); + Serial.print(mqttClient.state()); + Serial.println(" try again in 5 seconds"); + // Wait 5 seconds before retrying + delay(5000); + } + } +} #endif -namespace { +namespace +{ +// Change the part in brackets to your Duino-Coin username +const char *DUCO_USER = "USERNAME"; +// Change the part in brackets to your mining key (if you have enabled it in the wallet) +const char *MINER_KEY = "MINING_KEY"; // Change the part in brackets to your WiFi name -const char* SSID = "My cool wifi name"; +const char *SSID = "WIFI_NAME"; // Change the part in brackets to your WiFi password -const char* PASSWORD = "My secret wifi pass"; -// Change the part in brackets to your Duino-Coin username -const char* USERNAME = "my_cool_username"; +const char *PASSWORD = "WIFI_PASSWORD"; // Change the part in brackets if you want to set a custom miner name (use Auto to autogenerate, None for no name) -const char* RIG_IDENTIFIER = "None"; -// Change the part in brackets to your mining key (if you enabled it in the wallet) -const char* MINER_KEY = "None"; -// Change false to true if using 160 MHz clock mode to not get the first share rejected -const bool USE_HIGHER_DIFF = false; -// Change true to false if you don't want to host the dashboard page -const bool WEB_DASHBOARD = true; -// Change false to true if you want to update hashrate in browser without reloading page +const char *RIG_IDENTIFIER = "None"; +// Set to true to use the 160 MHz overclock mode (and not get the first share rejected) +const bool USE_HIGHER_DIFF = true; +// Set to true if you want to host the dashboard page (available on ESPs IP address) +const bool WEB_DASHBOARD = false; +// Set to true if you want to update hashrate in browser without reloading the page const bool WEB_HASH_UPDATER = false; -// Change true to false if you want to disable led blinking(But the LED will work in the beginning until esp connects to the pool) +// Set to false if you want to disable the onboard led blinking when finding shares const bool LED_BLINKING = true; /* Do not change the lines below. These lines are static and dynamic variables @@ -141,7 +158,7 @@ const bool LED_BLINKING = true; const char * DEVICE = "ESP8266"; const char * POOLPICKER_URL[] = {"https://server.duinocoin.com/getPool"}; const char * MINER_BANNER = "Official ESP8266 Miner"; -const char * MINER_VER = "3.18"; +const char * MINER_VER = "3.5"; unsigned int share_count = 0; unsigned int port = 0; unsigned int difficulty = 0; @@ -160,7 +177,6 @@ const char WEBSITE[] PROGMEM = R"=====( https://github.com/revoxhere/duino-coin https://duinocoin.com --> - @@ -169,7 +185,6 @@ const char WEBSITE[] PROGMEM = R"=====( - @@ -323,7 +338,6 @@ const char WEBSITE[] PROGMEM = R"=====( -
+ Self-hosted, lightweight, official dashboard for your Duino-Coin miner +
+ Mining statistics +
+ Device information +