mirror of
https://github.com/skot/ESP-Miner.git
synced 2025-03-17 13:22:53 +01:00
Http-server (#17)
* rename miner to main * serving out of storage * axe os * http server work * basic stats showing * update sdkconfig * SDKCONFIG * sdk config * edit page init * edit pool config * pool config edit working * OTA Success * remove compiled output * toggle AP mode with boot button * favicon * ota website update * add sdkconfig.ci back * update readme * change website build to warning * Update github workflow to build web dist * Allow AP mode before STA connection complete * spacing for johnny :) * formatting * Improve connecting to wifi with AP mode * added working indicator for UI * formatting * formatting * remove redundant sdkconfig in CMakeLists * vs code format on save workspace settings --------- Co-authored-by: johnny9 <985648+johnny9@users.noreply.github.com>
This commit is contained in:
parent
6d76741af8
commit
199c151c0f
9
.github/workflows/build.yml
vendored
9
.github/workflows/build.yml
vendored
@ -8,6 +8,15 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
- name: Build web dist
|
||||
working-directory: ./main/http_server/axe-os
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
- name: esp-idf build
|
||||
uses: espressif/esp-idf-ci-action@v1
|
||||
with:
|
||||
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -3,8 +3,13 @@ sdkconfig
|
||||
sdkconfig.old
|
||||
dependencies.lock
|
||||
|
||||
# User specific VSCode
|
||||
**/.vscode/*
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Production folder
|
||||
build/
|
||||
|
17
.vscode/settings.json
vendored
Normal file
17
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"idf.flashType": "UART",
|
||||
"idf.portWin": "COM6",
|
||||
"idf.adapterTargetName": "esp32s3",
|
||||
"idf.openOcdConfigs": [
|
||||
"interface/ftdi/esp32_devkitj_v1.cfg",
|
||||
"target/esp32s3.cfg"
|
||||
],
|
||||
"files.associations": {
|
||||
"freertos.h": "c",
|
||||
"esp_netif.h": "c",
|
||||
"esp_http_server.h": "c",
|
||||
"esp_chip_info.h": "c",
|
||||
"xtensa_context.h": "c"
|
||||
},
|
||||
"editor.formatOnSave": true
|
||||
}
|
@ -7,4 +7,7 @@ cmake_minimum_required(VERSION 3.5)
|
||||
# set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
|
||||
project(esp-miner)
|
||||
|
||||
|
@ -8,12 +8,12 @@
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#include "lwip/lwip_napt.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#include "connect.h"
|
||||
#include "miner.h"
|
||||
#include "main.h"
|
||||
|
||||
|
||||
#if CONFIG_ESP_WPA3_SAE_PWE_HUNT_AND_PECK
|
||||
@ -59,6 +59,10 @@ static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
esp_wifi_connect();
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
|
||||
// Wait a little
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
|
||||
if (s_retry_num < WIFI_MAXIMUM_RETRY) {
|
||||
esp_wifi_connect();
|
||||
s_retry_num++;
|
||||
@ -77,23 +81,53 @@ static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_
|
||||
}
|
||||
}
|
||||
|
||||
EventBits_t wifi_init_sta(const char * wifi_ssid, const char * wifi_pass) {
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
esp_netif_t *wifi_init_softap(void)
|
||||
{
|
||||
esp_netif_t *esp_netif_ap = esp_netif_create_default_wifi_ap();
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_netif_create_default_wifi_sta();
|
||||
wifi_config_t wifi_ap_config = {
|
||||
.ap = {
|
||||
.ssid = "Bitaxe",
|
||||
.ssid_len = strlen("Bitaxe"),
|
||||
.channel = 1,
|
||||
.max_connection = 30,
|
||||
.authmode = WIFI_AUTH_OPEN,
|
||||
.pmf_cfg = {
|
||||
.required = false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_ap_config));
|
||||
|
||||
esp_event_handler_instance_t instance_any_id;
|
||||
esp_event_handler_instance_t instance_got_ip;
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip));
|
||||
return esp_netif_ap;
|
||||
}
|
||||
|
||||
wifi_config_t wifi_config = {
|
||||
void toggle_wifi_softap(void){
|
||||
wifi_mode_t mode = WIFI_MODE_NULL;
|
||||
ESP_ERROR_CHECK(esp_wifi_get_mode(&mode));
|
||||
|
||||
if (mode == WIFI_MODE_APSTA) {
|
||||
ESP_LOGI(TAG, "ESP_WIFI Access Point Off");
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
} else {
|
||||
ESP_LOGI(TAG, "ESP_WIFI Access Point On");
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
|
||||
}
|
||||
}
|
||||
|
||||
void wifi_softap_off(void){
|
||||
ESP_LOGI(TAG, "ESP_WIFI Access Point Off");
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
}
|
||||
|
||||
/* Initialize wifi station */
|
||||
esp_netif_t *wifi_init_sta(const char * wifi_ssid, const char * wifi_pass)
|
||||
{
|
||||
esp_netif_t *esp_netif_sta = esp_netif_create_default_wifi_sta();
|
||||
|
||||
wifi_config_t wifi_sta_config = {
|
||||
.sta = {
|
||||
/* Authmode threshold resets to WPA2 as default if password matches WPA2 standards (pasword len => 8).
|
||||
* If you want to connect the device to deprecated WEP/WPA networks, Please set the threshold value
|
||||
@ -105,18 +139,56 @@ EventBits_t wifi_init_sta(const char * wifi_ssid, const char * wifi_pass) {
|
||||
// .sae_h2e_identifier = EXAMPLE_H2E_IDENTIFIER,
|
||||
},
|
||||
};
|
||||
strncpy((char *) wifi_config.sta.ssid, wifi_ssid, 31);
|
||||
wifi_config.sta.ssid[31] = '\0';
|
||||
strncpy((char *) wifi_config.sta.password, wifi_pass, 63);
|
||||
wifi_config.sta.password[63] = '\0';
|
||||
strncpy((char *) wifi_sta_config.sta.ssid, wifi_ssid, 31);
|
||||
wifi_sta_config.sta.ssid[31] = '\0';
|
||||
strncpy((char *) wifi_sta_config.sta.password, wifi_pass, 63);
|
||||
wifi_sta_config.sta.password[63] = '\0';
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_sta_config) );
|
||||
|
||||
ESP_LOGI(TAG, "wifi_init_sta finished.");
|
||||
|
||||
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
|
||||
return esp_netif_sta;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void wifi_init(const char * wifi_ssid, const char * wifi_pass) {
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
esp_event_handler_instance_t instance_any_id;
|
||||
esp_event_handler_instance_t instance_got_ip;
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip));
|
||||
|
||||
/*Initialize WiFi */
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
|
||||
|
||||
/* Initialize AP */
|
||||
ESP_LOGI(TAG, "ESP_WIFI Access Point On");
|
||||
esp_netif_t *esp_netif_ap = wifi_init_softap();
|
||||
|
||||
/* Initialize STA */
|
||||
ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
|
||||
esp_netif_t *esp_netif_sta = wifi_init_sta(wifi_ssid, wifi_pass);
|
||||
|
||||
/* Start WiFi */
|
||||
ESP_ERROR_CHECK(esp_wifi_start() );
|
||||
|
||||
ESP_LOGI(TAG, "wifi_init_sta finished.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
EventBits_t wifi_connect(void){
|
||||
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
|
||||
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
|
||||
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
||||
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||
|
@ -27,4 +27,7 @@ typedef enum {
|
||||
WIFI_RETRYING,
|
||||
} wifi_status_t;
|
||||
|
||||
EventBits_t wifi_init_sta(const char * ssid, const char * pass);
|
||||
void toggle_wifi_softap(void);
|
||||
void wifi_softap_off(void);
|
||||
void wifi_init(const char * wifi_ssid, const char * wifi_pass);
|
||||
EventBits_t wifi_connect(void);
|
||||
|
@ -6,7 +6,7 @@ SRCS
|
||||
"fonts.c"
|
||||
"INA260.c"
|
||||
"led_controller.c"
|
||||
"miner.c"
|
||||
"main.c"
|
||||
"nvs_config.c"
|
||||
"oled.c"
|
||||
"system.c"
|
||||
@ -15,9 +15,20 @@ SRCS
|
||||
"./tasks/create_jobs_task.c"
|
||||
"./tasks/asic_task.c"
|
||||
"./tasks/asic_result_task.c"
|
||||
"./tasks/user_input_task.c"
|
||||
"./tasks/power_management_task.c"
|
||||
"./http_server/http_server.c"
|
||||
|
||||
INCLUDE_DIRS
|
||||
"."
|
||||
"tasks"
|
||||
"http_server"
|
||||
"../components/connect/include"
|
||||
)
|
||||
|
||||
set(WEB_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/http_server/axe-os")
|
||||
if(EXISTS ${WEB_SRC_DIR}/dist/axe-os)
|
||||
spiffs_create_partition_image(www ${WEB_SRC_DIR}/dist/axe-os FLASH_IN_PROJECT)
|
||||
else()
|
||||
message(WARNING "${WEB_SRC_DIR}/dist doesn't exit. Please run 'npm run build' in ${WEB_SRC_DIR}")
|
||||
endif()
|
@ -53,4 +53,5 @@ typedef struct {
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* GLOBAL_STATE_H_ */
|
16
main/http_server/axe-os/.editorconfig
Normal file
16
main/http_server/axe-os/.editorconfig
Normal file
@ -0,0 +1,16 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
42
main/http_server/axe-os/.gitignore
vendored
Normal file
42
main/http_server/axe-os/.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
4
main/http_server/axe-os/.vscode/extensions.json
vendored
Normal file
4
main/http_server/axe-os/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||
"recommendations": ["angular.ng-template"]
|
||||
}
|
20
main/http_server/axe-os/.vscode/launch.json
vendored
Normal file
20
main/http_server/axe-os/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ng serve",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"url": "http://localhost:4200/",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "ng test",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: test",
|
||||
"url": "http://localhost:9876/debug.html"
|
||||
}
|
||||
]
|
||||
}
|
5
main/http_server/axe-os/.vscode/settings.json
vendored
Normal file
5
main/http_server/axe-os/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"toastr"
|
||||
]
|
||||
}
|
42
main/http_server/axe-os/.vscode/tasks.json
vendored
Normal file
42
main/http_server/axe-os/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
27
main/http_server/axe-os/README.md
Normal file
27
main/http_server/axe-os/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
# AxeOs
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.1.3.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
111
main/http_server/axe-os/angular.json
Normal file
111
main/http_server/axe-os/angular.json
Normal file
@ -0,0 +1,111 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"axe-os": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/axe-os",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": [
|
||||
"zone.js"
|
||||
],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss",
|
||||
"node_modules/ngx-toastr/toastr.css"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "750kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"optimization": false,
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true,
|
||||
"namedChunks": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "axe-os:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "axe-os:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "axe-os:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"polyfills": [
|
||||
"zone.js",
|
||||
"zone.js/testing"
|
||||
],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13229
main/http_server/axe-os/package-lock.json
generated
Normal file
13229
main/http_server/axe-os/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
42
main/http_server/axe-os/package.json
Normal file
42
main/http_server/axe-os/package.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "axe-os",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build --configuration=production",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test",
|
||||
"bundle-report": "ng build --configuration=production --stats-json && webpack-bundle-analyzer dist/axe-os/stats.json"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^16.1.0",
|
||||
"@angular/common": "^16.1.0",
|
||||
"@angular/compiler": "^16.1.0",
|
||||
"@angular/core": "^16.1.0",
|
||||
"@angular/forms": "^16.1.0",
|
||||
"@angular/platform-browser": "^16.1.0",
|
||||
"@angular/platform-browser-dynamic": "^16.1.0",
|
||||
"@angular/router": "^16.1.0",
|
||||
"ngx-toastr": "^17.0.2",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^16.1.3",
|
||||
"@angular/cli": "~16.1.3",
|
||||
"@angular/compiler-cli": "^16.1.0",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
"gzipper": "^7.2.0",
|
||||
"jasmine-core": "~4.6.0",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.2.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"typescript": "~5.1.3",
|
||||
"webpack-bundle-analyzer": "^4.9.0"
|
||||
}
|
||||
}
|
22
main/http_server/axe-os/src/app/app-routing.module.ts
Normal file
22
main/http_server/axe-os/src/app/app-routing.module.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { EditComponent } from './components/edit/edit.component';
|
||||
import { HomeComponent } from './components/home/home.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: HomeComponent
|
||||
},
|
||||
{
|
||||
path: 'edit',
|
||||
component: EditComponent
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
5
main/http_server/axe-os/src/app/app.component.html
Normal file
5
main/http_server/axe-os/src/app/app.component.html
Normal file
@ -0,0 +1,5 @@
|
||||
<app-loading></app-loading>
|
||||
<app-header></app-header>
|
||||
<div class="content">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
14
main/http_server/axe-os/src/app/app.component.scss
Normal file
14
main/http_server/axe-os/src/app/app.component.scss
Normal file
@ -0,0 +1,14 @@
|
||||
.content {
|
||||
margin-top: 150px;
|
||||
|
||||
padding-left: 10vw;
|
||||
padding-right: 10vw;
|
||||
|
||||
}
|
||||
|
||||
@media only screen and (max-width:900px) {
|
||||
.content {
|
||||
padding-left: 25px;
|
||||
padding-right: 25px;
|
||||
}
|
||||
}
|
29
main/http_server/axe-os/src/app/app.component.spec.ts
Normal file
29
main/http_server/axe-os/src/app/app.component.spec.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({
|
||||
imports: [RouterTestingModule],
|
||||
declarations: [AppComponent]
|
||||
}));
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'axe-os'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title).toEqual('axe-os');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('.content span')?.textContent).toContain('axe-os app is running!');
|
||||
});
|
||||
});
|
15
main/http_server/axe-os/src/app/app.component.ts
Normal file
15
main/http_server/axe-os/src/app/app.component.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
44
main/http_server/axe-os/src/app/app.module.ts
Normal file
44
main/http_server/axe-os/src/app/app.module.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { CommonModule, HashLocationStrategy, LocationStrategy } from '@angular/common';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ToastrModule } from 'ngx-toastr';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { EditComponent } from './components/edit/edit.component';
|
||||
import { HeaderComponent } from './components/header/header.component';
|
||||
import { HomeComponent } from './components/home/home.component';
|
||||
import { LoadingComponent } from './components/loading/loading.component';
|
||||
|
||||
const components = [
|
||||
AppComponent,
|
||||
HeaderComponent
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
...components,
|
||||
EditComponent,
|
||||
HomeComponent,
|
||||
LoadingComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
HttpClientModule,
|
||||
ReactiveFormsModule,
|
||||
ToastrModule.forRoot({
|
||||
positionClass: 'toast-bottom-right'
|
||||
}),
|
||||
BrowserAnimationsModule,
|
||||
CommonModule
|
||||
],
|
||||
providers: [
|
||||
{ provide: LocationStrategy, useClass: HashLocationStrategy },
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
@ -0,0 +1,48 @@
|
||||
<div class="card">
|
||||
<h2>Edit</h2>
|
||||
<ng-container *ngIf="form != null">
|
||||
<form [formGroup]="form">
|
||||
<div class="form-group">
|
||||
<label>WiFi SSID: </label>
|
||||
<input formControlName="ssid" type="text">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>WiFi Password: </label>
|
||||
<input formControlName="wifiPass" type="password">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Stratum URL:</label>
|
||||
<input formControlName="stratumURL" type="text">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Stratum Port:</label>
|
||||
<input formControlName="stratumPort" type="number">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Stratum User: </label>
|
||||
<input formControlName="stratumUser" type="text">
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</ng-container>
|
||||
<div class="mt-2">
|
||||
<button (click)="updateSystem()" class="btn btn-primary mr-2">Save</button>
|
||||
<button [routerLink]="['/']" class="btn btn-secondary">Cancel</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Update Firmware <span *ngIf="firmwareUpdateProgress != null">{{firmwareUpdateProgress}}%</span></h2>
|
||||
<input type="file" id="file" (change)="otaUpdate($event)" accept=".bin">
|
||||
<br>
|
||||
<small>(esp-miner.bin)</small>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Update Website <span *ngIf="websiteUpdateProgress != null">{{websiteUpdateProgress}}%</span></h2>
|
||||
<input type="file" id="file" (change)="otaWWWUpdate($event)" accept=".bin">
|
||||
<br>
|
||||
<small>(www.bin)</small>
|
||||
</div>
|
@ -0,0 +1,3 @@
|
||||
input {
|
||||
min-width: 500px
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EditComponent } from './edit.component';
|
||||
|
||||
describe('EditComponent', () => {
|
||||
let component: EditComponent;
|
||||
let fixture: ComponentFixture<EditComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [EditComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(EditComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,114 @@
|
||||
import { HttpErrorResponse, HttpEventType } from '@angular/common/http';
|
||||
import { Component } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { LoadingService } from 'src/app/services/loading.service';
|
||||
import { SystemService } from 'src/app/services/system.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-edit',
|
||||
templateUrl: './edit.component.html',
|
||||
styleUrls: ['./edit.component.scss']
|
||||
})
|
||||
export class EditComponent {
|
||||
|
||||
public form!: FormGroup;
|
||||
|
||||
public firmwareUpdateProgress: number | null = null;
|
||||
public websiteUpdateProgress: number | null = null;
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private systemService: SystemService,
|
||||
private toastr: ToastrService,
|
||||
private toastrService: ToastrService,
|
||||
private loadingService: LoadingService
|
||||
) {
|
||||
this.systemService.getInfo()
|
||||
.pipe(this.loadingService.lockUIUntilComplete())
|
||||
.subscribe(info => {
|
||||
this.form = this.fb.group({
|
||||
stratumURL: [info.stratumURL, [Validators.required]],
|
||||
stratumPort: [info.stratumPort, [Validators.required]],
|
||||
stratumUser: [info.stratumUser, [Validators.required]],
|
||||
ssid: [info.ssid, [Validators.required]],
|
||||
wifiPass: [info.wifiPass, [Validators.required]],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public updateSystem() {
|
||||
this.systemService.updateSystem(this.form.value)
|
||||
.pipe(this.loadingService.lockUIUntilComplete())
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this.toastr.success('Success!', 'Saved.');
|
||||
},
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this.toastr.error('Error.', `Could not save. ${err.message}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
otaUpdate(event: any) {
|
||||
const file = event.target?.files.item(0) as File;
|
||||
|
||||
if (file.name != 'esp-miner.bin') {
|
||||
this.toastrService.error('Incorrect file, looking for esp-miner.bin.', 'Error');
|
||||
return;
|
||||
}
|
||||
|
||||
this.systemService.performOTAUpdate(file)
|
||||
.pipe(this.loadingService.lockUIUntilComplete())
|
||||
.subscribe({
|
||||
next: (event) => {
|
||||
if (event.type === HttpEventType.UploadProgress) {
|
||||
this.firmwareUpdateProgress = Math.round((event.loaded / (event.total as number)) * 100);
|
||||
} else if (event.type === HttpEventType.Response) {
|
||||
if (event.ok) {
|
||||
this.toastrService.success('Firmware updated', 'Success!');
|
||||
|
||||
} else {
|
||||
this.toastrService.error(event.statusText, 'Error');
|
||||
}
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
this.toastrService.error(event.statusText, 'Error');
|
||||
},
|
||||
complete: () => {
|
||||
this.firmwareUpdateProgress = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
otaWWWUpdate(event: any) {
|
||||
const file = event.target?.files.item(0) as File;
|
||||
if (file.name != 'www.bin') {
|
||||
this.toastrService.error('Incorrect file, looking for www.bin.', 'Error');
|
||||
return;
|
||||
}
|
||||
|
||||
this.systemService.performWWWOTAUpdate(file)
|
||||
.pipe(this.loadingService.lockUIUntilComplete())
|
||||
.subscribe({
|
||||
next: (event) => {
|
||||
if (event.type === HttpEventType.UploadProgress) {
|
||||
this.websiteUpdateProgress = Math.round((event.loaded / (event.total as number)) * 100);
|
||||
} else if (event.type === HttpEventType.Response) {
|
||||
if (event.ok) {
|
||||
this.toastrService.success('Website updated', 'Success!');
|
||||
window.location.reload();
|
||||
} else {
|
||||
this.toastrService.error(event.statusText, 'Error');
|
||||
}
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
this.toastrService.error(event.statusText, 'Error');
|
||||
},
|
||||
complete: () => {
|
||||
this.websiteUpdateProgress = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
<div [routerLink]="['/']" class="header-text">AxeOS</div>
|
@ -0,0 +1,16 @@
|
||||
:host {
|
||||
height: 70px;
|
||||
background-color: #1f2d40;
|
||||
border-bottom: 1px solid #304562;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.header-text {
|
||||
font-size: 30pt;
|
||||
padding: 6px;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HeaderComponent } from './header.component';
|
||||
|
||||
describe('HeaderComponent', () => {
|
||||
let component: HeaderComponent;
|
||||
let fixture: ComponentFixture<HeaderComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [HeaderComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(HeaderComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
templateUrl: './header.component.html',
|
||||
styleUrls: ['./header.component.scss']
|
||||
})
|
||||
export class HeaderComponent {
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
<ng-container *ngIf="info$ | async as info">
|
||||
<div class="button-row">
|
||||
<button [routerLink]="['edit']" class="btn btn-primary edit">Edit</button>
|
||||
<button (click)="restart()" class="btn btn-danger restart">Restart</button>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Pool Information</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td>URL:</td>
|
||||
<td>{{info.stratumURL}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Port:</td>
|
||||
<td>{{info.stratumPort}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>User:</td>
|
||||
<td>{{info.stratumUser}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Shares Accepted:</td>
|
||||
<td>{{info.sharesAccepted}}</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Shares Rejected:</td>
|
||||
<td>{{info.sharesRejected}}</td>
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div>
|
||||
<div class="card">
|
||||
<h2>Overview</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Model:</td>
|
||||
<td>{{info.ASICModel}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Uptime (seconds):</td>
|
||||
<td>{{info.uptimeSeconds}}</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>WiFi Status:</td>
|
||||
<td>{{info.wifiStatus}}</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Free Heap Memory:</td>
|
||||
<td>{{info.freeHeap}}</td>
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="card">
|
||||
<h2>Power</h2>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>Power Consumption:</td>
|
||||
<td>{{info.power | number: '1.2-2'}} <small>W</small></td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Input Voltage:</td>
|
||||
<td>{{info.voltage | number: '0.0-0'}} <small>mV</small></td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Input Current:</td>
|
||||
<td>{{info.current | number: '0.0-0'}} <small>mA</small></td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Core Voltage:</td>
|
||||
<td>{{info.coreVoltage}} <small>mV</small></td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fan Speed:</td>
|
||||
<td>{{info.fanSpeed}} <small>RPM</small></td>
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Results</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td> Hash Rate:</td>
|
||||
<td>{{info.hashRate | number: '1.2-2'}}Gh/s</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Best Difficulty:</td>
|
||||
<td>{{info.bestDiff}}</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</ng-container>
|
@ -0,0 +1,39 @@
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 2fr;
|
||||
grid-gap: 1.5rem;
|
||||
}
|
||||
|
||||
table>tr>td {
|
||||
|
||||
&:first-child {
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media only screen and (max-width:900px) {
|
||||
.container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.edit {
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
}
|
||||
|
||||
.restart {
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HomeComponent } from './home.component';
|
||||
|
||||
describe('HomeComponent', () => {
|
||||
let component: HomeComponent;
|
||||
let fixture: ComponentFixture<HomeComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [HomeComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(HomeComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,33 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { Observable } from 'rxjs';
|
||||
import { LoadingService } from 'src/app/services/loading.service';
|
||||
import { SystemService } from 'src/app/services/system.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: './home.component.html',
|
||||
styleUrls: ['./home.component.scss']
|
||||
})
|
||||
export class HomeComponent {
|
||||
|
||||
public info$: Observable<any>;
|
||||
|
||||
constructor(
|
||||
private systemService: SystemService,
|
||||
private toastr: ToastrService,
|
||||
private loadingService: LoadingService
|
||||
) {
|
||||
this.info$ = this.systemService.getInfo().pipe(
|
||||
this.loadingService.lockUIUntilComplete()
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
public restart() {
|
||||
this.systemService.restart().subscribe(res => {
|
||||
|
||||
});
|
||||
this.toastr.success('Success!', 'Bitaxe restarted');
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
<div id="loading" *ngIf="loading$ | async">
|
||||
<div id="loading-text">Working...</div>
|
||||
</div>
|
@ -0,0 +1,16 @@
|
||||
#loading {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
pointer-events: none;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
position: fixed;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
#loading-text {
|
||||
text-align: center;
|
||||
margin-top: 40vh;
|
||||
font-size: 20pt;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoadingComponent } from './loading.component';
|
||||
|
||||
describe('LoadingComponent', () => {
|
||||
let component: LoadingComponent;
|
||||
let fixture: ComponentFixture<LoadingComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [LoadingComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(LoadingComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,17 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { LoadingService } from 'src/app/services/loading.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-loading',
|
||||
templateUrl: './loading.component.html',
|
||||
styleUrls: ['./loading.component.scss']
|
||||
})
|
||||
export class LoadingComponent {
|
||||
|
||||
public loading$: Observable<boolean>;
|
||||
|
||||
constructor(private loadingService: LoadingService) {
|
||||
this.loading$ = this.loadingService.loading$.asObservable();
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoadingService } from './loading.service';
|
||||
|
||||
describe('LoadingService', () => {
|
||||
let service: LoadingService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(LoadingService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
33
main/http_server/axe-os/src/app/services/loading.service.ts
Normal file
33
main/http_server/axe-os/src/app/services/loading.service.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LoadingService {
|
||||
|
||||
public loading$: BehaviorSubject<boolean> = new BehaviorSubject(false);
|
||||
|
||||
constructor() { }
|
||||
|
||||
public lockUIUntilComplete() {
|
||||
return <T>(source: Observable<T>): Observable<T> => {
|
||||
return new Observable(subscriber => {
|
||||
this.loading$.next(true);
|
||||
source.subscribe({
|
||||
next: (value) => {
|
||||
subscriber.next(value);
|
||||
},
|
||||
error: (err) => {
|
||||
subscriber.next(err);
|
||||
},
|
||||
complete: () => {
|
||||
this.loading$.next(false);
|
||||
subscriber.complete();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SystemService } from './system.service';
|
||||
|
||||
describe('SystemService', () => {
|
||||
let service: SystemService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(SystemService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
93
main/http_server/axe-os/src/app/services/system.service.ts
Normal file
93
main/http_server/axe-os/src/app/services/system.service.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import { HttpClient, HttpEvent } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { delay, Observable, of } from 'rxjs';
|
||||
import { ISystemInfo } from 'src/models/ISystemInfo';
|
||||
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SystemService {
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient
|
||||
) { }
|
||||
|
||||
public getInfo(): Observable<ISystemInfo> {
|
||||
if (environment.production) {
|
||||
return this.httpClient.get(`/api/system/info`) as Observable<ISystemInfo>;
|
||||
} else {
|
||||
return of(
|
||||
{
|
||||
"power": 11.670000076293945,
|
||||
"voltage": 5208.75,
|
||||
"current": 2237.5,
|
||||
"fanSpeed": 82,
|
||||
"hashRate": 0,
|
||||
"bestDiff": "0",
|
||||
"freeHeap": 200504,
|
||||
"coreVoltage": 1188,
|
||||
"ssid": "skimadtrees-secure",
|
||||
"wifiPass": "password",
|
||||
"wifiStatus": "Connected!",
|
||||
"sharesAccepted": 1,
|
||||
"sharesRejected": 0,
|
||||
"uptimeSeconds": 38,
|
||||
"ASICModel": "BM1366",
|
||||
"stratumURL": "192.168.1.242",
|
||||
"stratumPort": 3333,
|
||||
"stratumUser": "bc1q99n3pu025yyu0jlywpmwzalyhm36tg5u37w20d.bitaxe-U1"
|
||||
}
|
||||
).pipe(delay(1000));
|
||||
}
|
||||
}
|
||||
|
||||
public restart() {
|
||||
return this.httpClient.post(`/api/system/restart`, {});
|
||||
}
|
||||
|
||||
public updateSystem(update: any) {
|
||||
return this.httpClient.patch(`/api/system`, update);
|
||||
}
|
||||
|
||||
|
||||
private otaUpdate(file: File, url: string) {
|
||||
return new Observable<HttpEvent<string>>((subscriber) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = (event: any) => {
|
||||
const fileContent = event.target.result;
|
||||
|
||||
return this.httpClient.post(url, fileContent, {
|
||||
reportProgress: true,
|
||||
observe: 'events',
|
||||
responseType: 'text', // Specify the response type
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream', // Set the content type
|
||||
},
|
||||
}).subscribe({
|
||||
next: (e) => {
|
||||
subscriber.next(e)
|
||||
},
|
||||
error: (err) => {
|
||||
subscriber.error(err)
|
||||
},
|
||||
complete: () => {
|
||||
subscriber.complete();
|
||||
}
|
||||
});
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
}
|
||||
|
||||
public performOTAUpdate(file: File) {
|
||||
return this.otaUpdate(file, `/api/system/OTA`);
|
||||
}
|
||||
public performWWWOTAUpdate(file: File) {
|
||||
return this.otaUpdate(file, `/api/system/OTAWWW`);
|
||||
}
|
||||
|
||||
|
||||
}
|
0
main/http_server/axe-os/src/assets/.gitkeep
Normal file
0
main/http_server/axe-os/src/assets/.gitkeep
Normal file
@ -0,0 +1,3 @@
|
||||
export const environment = {
|
||||
production: true
|
||||
};
|
3
main/http_server/axe-os/src/environments/environment.ts
Normal file
3
main/http_server/axe-os/src/environments/environment.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const environment = {
|
||||
production: false
|
||||
};
|
BIN
main/http_server/axe-os/src/favicon.ico
Normal file
BIN
main/http_server/axe-os/src/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
13
main/http_server/axe-os/src/index.html
Normal file
13
main/http_server/axe-os/src/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>AxeOs</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
7
main/http_server/axe-os/src/main.ts
Normal file
7
main/http_server/axe-os/src/main.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
21
main/http_server/axe-os/src/models/ISystemInfo.ts
Normal file
21
main/http_server/axe-os/src/models/ISystemInfo.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export interface ISystemInfo {
|
||||
|
||||
power: number,
|
||||
voltage: number,
|
||||
current: number,
|
||||
fanSpeed: number,
|
||||
hashRate: number,
|
||||
bestDiff: string,
|
||||
freeHeap: number,
|
||||
coreVoltage: number,
|
||||
ssid: string,
|
||||
wifiPass: string,
|
||||
wifiStatus: string,
|
||||
sharesAccepted: number,
|
||||
sharesRejected: number,
|
||||
uptimeSeconds: number,
|
||||
ASICModel: string,
|
||||
stratumURL: string,
|
||||
stratumPort: number,
|
||||
stratumUser: string
|
||||
}
|
161
main/http_server/axe-os/src/styles.scss
Normal file
161
main/http_server/axe-os/src/styles.scss
Normal file
@ -0,0 +1,161 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
body {
|
||||
background-color: #17212f;
|
||||
margin: 0;
|
||||
color: white;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #1f2d40;
|
||||
border: 1px solid #304562;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
line-break: anywhere;
|
||||
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 0;
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
vertical-align: bottom;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 1rem;
|
||||
transition: background-color 0.2s, color 0.2s, border-color 0.2s, box-shadow 0.2s;
|
||||
border-radius: 3px;
|
||||
|
||||
&.btn-primary {
|
||||
color: #212529;
|
||||
background: #64B5F6;
|
||||
border: 1px solid #64B5F6;
|
||||
|
||||
&:hover {
|
||||
background: #43a5f4;
|
||||
color: #212529;
|
||||
border-color: #43a5f4;
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-danger {
|
||||
color: #121212;
|
||||
background: #F48FB1;
|
||||
border: 1px solid #F48FB1;
|
||||
|
||||
&:hover {
|
||||
background: #f16c98;
|
||||
color: #121212;
|
||||
border-color: #f16c98;
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-secondary {
|
||||
color: #ffffff;
|
||||
background: #78909C;
|
||||
border: 1px solid #78909C;
|
||||
|
||||
&:hover {
|
||||
background: #69838f;
|
||||
color: #ffffff;
|
||||
border-color: #69838f;
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-warning {
|
||||
color: #121212;
|
||||
background: #FFE082;
|
||||
border: 1px solid #FFE082;
|
||||
|
||||
&:hover {
|
||||
background: #ffd65c;
|
||||
color: #121212;
|
||||
border-color: #ffd65c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
input {
|
||||
|
||||
font-size: 1rem;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background: #17212f;
|
||||
padding: 0.5rem 0.5rem;
|
||||
border: 1px solid #304562;
|
||||
transition: background-color 0.2s, color 0.2s, border-color 0.2s, box-shadow 0.2s;
|
||||
appearance: none;
|
||||
border-radius: 3px;
|
||||
|
||||
}
|
||||
|
||||
label {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
label {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
label,
|
||||
input {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.mr-2 {
|
||||
margin-right: 2rem;
|
||||
}
|
||||
|
||||
.ml-2 {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.p-fileupload-choose {
|
||||
margin: 0;
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
vertical-align: bottom;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 1rem;
|
||||
transition: background-color 0.2s, color 0.2s, border-color 0.2s, box-shadow 0.2s;
|
||||
border-radius: 3px;
|
||||
color: #212529;
|
||||
background: #64B5F6;
|
||||
border: 1px solid #64B5F6;
|
||||
}
|
14
main/http_server/axe-os/tsconfig.app.json
Normal file
14
main/http_server/axe-os/tsconfig.app.json
Normal file
@ -0,0 +1,14 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
33
main/http_server/axe-os/tsconfig.json
Normal file
33
main/http_server/axe-os/tsconfig.json
Normal file
@ -0,0 +1,33 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"downlevelIteration": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"useDefineForClassFields": false,
|
||||
"lib": [
|
||||
"ES2022",
|
||||
"dom"
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
14
main/http_server/axe-os/tsconfig.spec.json
Normal file
14
main/http_server/axe-os/tsconfig.spec.json
Normal file
@ -0,0 +1,14 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
419
main/http_server/http_server.c
Normal file
419
main/http_server/http_server.c
Normal file
@ -0,0 +1,419 @@
|
||||
#include "http_server.h"
|
||||
#include "global_state.h"
|
||||
#include <sys/param.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include "esp_http_server.h"
|
||||
#include "esp_chip_info.h"
|
||||
#include "esp_random.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "cJSON.h"
|
||||
#include "esp_spiffs.h"
|
||||
#include "esp_log.h"
|
||||
#include "adc.h"
|
||||
#include "esp_timer.h"
|
||||
#include "nvs_config.h"
|
||||
|
||||
#include "esp_netif.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/lwip_napt.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "esp_ota_ops.h"
|
||||
|
||||
static const char *TAG = "http_server";
|
||||
|
||||
static GlobalState *GLOBAL_STATE;
|
||||
|
||||
|
||||
#define REST_CHECK(a, str, goto_tag, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (!(a)) \
|
||||
{ \
|
||||
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||
goto goto_tag; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128)
|
||||
#define SCRATCH_BUFSIZE (10240)
|
||||
|
||||
typedef struct rest_server_context {
|
||||
char base_path[ESP_VFS_PATH_MAX + 1];
|
||||
char scratch[SCRATCH_BUFSIZE];
|
||||
} rest_server_context_t;
|
||||
|
||||
#define CHECK_FILE_EXTENSION(filename, ext) (strcasecmp(&filename[strlen(filename) - strlen(ext)], ext) == 0)
|
||||
|
||||
|
||||
esp_err_t init_fs(void)
|
||||
{
|
||||
esp_vfs_spiffs_conf_t conf = {
|
||||
.base_path = "",
|
||||
.partition_label = NULL,
|
||||
.max_files = 5,
|
||||
.format_if_mount_failed = false
|
||||
};
|
||||
esp_err_t ret = esp_vfs_spiffs_register(&conf);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
if (ret == ESP_FAIL) {
|
||||
ESP_LOGE(TAG, "Failed to mount or format filesystem");
|
||||
} else if (ret == ESP_ERR_NOT_FOUND) {
|
||||
ESP_LOGE(TAG, "Failed to find SPIFFS partition");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
size_t total = 0, used = 0;
|
||||
ret = esp_spiffs_info(NULL, &total, &used);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Function for stopping the webserver */
|
||||
void stop_webserver(httpd_handle_t server)
|
||||
{
|
||||
if (server) {
|
||||
/* Stop the httpd server */
|
||||
httpd_stop(server);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set HTTP response content type according to file extension */
|
||||
static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filepath)
|
||||
{
|
||||
const char *type = "text/plain";
|
||||
if (CHECK_FILE_EXTENSION(filepath, ".html")) {
|
||||
type = "text/html";
|
||||
} else if (CHECK_FILE_EXTENSION(filepath, ".js")) {
|
||||
type = "application/javascript";
|
||||
} else if (CHECK_FILE_EXTENSION(filepath, ".css")) {
|
||||
type = "text/css";
|
||||
} else if (CHECK_FILE_EXTENSION(filepath, ".png")) {
|
||||
type = "image/png";
|
||||
} else if (CHECK_FILE_EXTENSION(filepath, ".ico")) {
|
||||
type = "image/x-icon";
|
||||
} else if (CHECK_FILE_EXTENSION(filepath, ".svg")) {
|
||||
type = "text/xml";
|
||||
}
|
||||
return httpd_resp_set_type(req, type);
|
||||
}
|
||||
|
||||
/* Send HTTP response with the contents of the requested file */
|
||||
static esp_err_t rest_common_get_handler(httpd_req_t *req)
|
||||
{
|
||||
char filepath[FILE_PATH_MAX];
|
||||
|
||||
rest_server_context_t *rest_context = (rest_server_context_t *)req->user_ctx;
|
||||
strlcpy(filepath, rest_context->base_path, sizeof(filepath));
|
||||
if (req->uri[strlen(req->uri) - 1] == '/') {
|
||||
strlcat(filepath, "/index.html", sizeof(filepath));
|
||||
} else {
|
||||
strlcat(filepath, req->uri, sizeof(filepath));
|
||||
}
|
||||
int fd = open(filepath, O_RDONLY, 0);
|
||||
if (fd == -1) {
|
||||
ESP_LOGE(TAG, "Failed to open file : %s", filepath);
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
set_content_type_from_file(req, filepath);
|
||||
|
||||
char *chunk = rest_context->scratch;
|
||||
ssize_t read_bytes;
|
||||
do {
|
||||
/* Read file in chunks into the scratch buffer */
|
||||
read_bytes = read(fd, chunk, SCRATCH_BUFSIZE);
|
||||
if (read_bytes == -1) {
|
||||
ESP_LOGE(TAG, "Failed to read file : %s", filepath);
|
||||
} else if (read_bytes > 0) {
|
||||
/* Send the buffer contents as HTTP response chunk */
|
||||
if (httpd_resp_send_chunk(req, chunk, read_bytes) != ESP_OK) {
|
||||
close(fd);
|
||||
ESP_LOGE(TAG, "File sending failed!");
|
||||
/* Abort sending file */
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
} while (read_bytes > 0);
|
||||
/* Close file after sending complete */
|
||||
close(fd);
|
||||
ESP_LOGI(TAG, "File sending complete");
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t PATCH_update_settings(httpd_req_t *req)
|
||||
{
|
||||
int total_len = req->content_len;
|
||||
int cur_len = 0;
|
||||
char *buf = ((rest_server_context_t *)(req->user_ctx))->scratch;
|
||||
int received = 0;
|
||||
if (total_len >= SCRATCH_BUFSIZE) {
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "content too long");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
while (cur_len < total_len) {
|
||||
received = httpd_req_recv(req, buf + cur_len, total_len);
|
||||
if (received <= 0) {
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to post control value");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
cur_len += received;
|
||||
}
|
||||
buf[total_len] = '\0';
|
||||
|
||||
cJSON *root = cJSON_Parse(buf);
|
||||
char *stratumURL = cJSON_GetObjectItem(root, "stratumURL")->valuestring;
|
||||
char *stratumUser = cJSON_GetObjectItem(root, "stratumUser")->valuestring;
|
||||
uint16_t stratumPort = cJSON_GetObjectItem(root, "stratumPort")->valueint;
|
||||
char *ssid = cJSON_GetObjectItem(root, "ssid")->valuestring;
|
||||
char *wifiPass = cJSON_GetObjectItem(root, "wifiPass")->valuestring;
|
||||
|
||||
|
||||
nvs_config_set_string(NVS_CONFIG_STRATUM_URL, stratumURL);
|
||||
nvs_config_set_string(NVS_CONFIG_STRATUM_USER, stratumUser);
|
||||
nvs_config_set_u16(NVS_CONFIG_STRATUM_PORT, stratumPort);
|
||||
nvs_config_set_string(NVS_CONFIG_WIFI_SSID, ssid);
|
||||
nvs_config_set_string(NVS_CONFIG_WIFI_PASS, wifiPass);
|
||||
|
||||
cJSON_Delete(root);
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t POST_restart(httpd_req_t *req)
|
||||
{
|
||||
esp_restart();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Simple handler for getting system handler */
|
||||
static esp_err_t GET_system_info(httpd_req_t *req)
|
||||
{
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
|
||||
// Add CORS headers
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Headers", "Content-Type");
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Credentials", "true");
|
||||
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(root, "power", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.power);
|
||||
cJSON_AddNumberToObject(root, "voltage", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.voltage);
|
||||
cJSON_AddNumberToObject(root, "current", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.current);
|
||||
cJSON_AddNumberToObject(root, "fanSpeed", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.fan_speed);
|
||||
cJSON_AddNumberToObject(root, "hashRate", GLOBAL_STATE->SYSTEM_MODULE.current_hashrate);
|
||||
cJSON_AddStringToObject(root, "bestDiff", GLOBAL_STATE->SYSTEM_MODULE.best_diff_string);
|
||||
cJSON_AddNumberToObject(root, "freeHeap", esp_get_free_heap_size());
|
||||
cJSON_AddNumberToObject(root, "coreVoltage", ADC_get_vcore());
|
||||
cJSON_AddStringToObject(root, "ssid", nvs_config_get_string(NVS_CONFIG_WIFI_SSID, CONFIG_ESP_WIFI_SSID));
|
||||
cJSON_AddStringToObject(root, "wifiPass", nvs_config_get_string(NVS_CONFIG_WIFI_PASS, CONFIG_ESP_WIFI_PASSWORD));
|
||||
cJSON_AddStringToObject(root, "wifiStatus", GLOBAL_STATE->SYSTEM_MODULE.wifi_status);
|
||||
cJSON_AddNumberToObject(root, "sharesAccepted", GLOBAL_STATE->SYSTEM_MODULE.shares_accepted);
|
||||
cJSON_AddNumberToObject(root, "sharesRejected", GLOBAL_STATE->SYSTEM_MODULE.shares_rejected);
|
||||
cJSON_AddNumberToObject(root, "uptimeSeconds", (esp_timer_get_time() - GLOBAL_STATE->SYSTEM_MODULE.start_time)/1000000);
|
||||
cJSON_AddStringToObject(root, "ASICModel", CONFIG_ASIC_MODEL);
|
||||
cJSON_AddStringToObject(root, "stratumURL", nvs_config_get_string(NVS_CONFIG_STRATUM_URL, CONFIG_STRATUM_URL));
|
||||
cJSON_AddNumberToObject(root, "stratumPort", nvs_config_get_u16(NVS_CONFIG_STRATUM_PORT, CONFIG_STRATUM_PORT));
|
||||
cJSON_AddStringToObject(root, "stratumUser", nvs_config_get_string(NVS_CONFIG_STRATUM_USER, CONFIG_STRATUM_USER));
|
||||
|
||||
const char *sys_info = cJSON_Print(root);
|
||||
httpd_resp_sendstr(req, sys_info);
|
||||
free((void *)sys_info);
|
||||
cJSON_Delete(root);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t POST_WWW_update(httpd_req_t *req)
|
||||
{
|
||||
char buf[1000];
|
||||
int remaining = req->content_len;
|
||||
|
||||
const esp_partition_t *www_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, "www");
|
||||
if (www_partition == NULL) {
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "WWW partition not found");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Erase the entire www partition before writing
|
||||
ESP_ERROR_CHECK(esp_partition_erase_range(www_partition, 0, www_partition->size));
|
||||
|
||||
while (remaining > 0) {
|
||||
int recv_len = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)));
|
||||
|
||||
if (recv_len == HTTPD_SOCK_ERR_TIMEOUT) {
|
||||
continue;
|
||||
} else if (recv_len <= 0) {
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Protocol Error");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (esp_partition_write(www_partition, www_partition->size - remaining, (const void *)buf, recv_len) != ESP_OK) {
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Write Error");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
remaining -= recv_len;
|
||||
}
|
||||
|
||||
httpd_resp_sendstr(req, "WWW update complete\n");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle OTA file upload
|
||||
*/
|
||||
esp_err_t POST_OTA_update(httpd_req_t *req)
|
||||
{
|
||||
char buf[1000];
|
||||
esp_ota_handle_t ota_handle;
|
||||
int remaining = req->content_len;
|
||||
|
||||
const esp_partition_t *ota_partition = esp_ota_get_next_update_partition(NULL);
|
||||
ESP_ERROR_CHECK(esp_ota_begin(ota_partition, OTA_SIZE_UNKNOWN, &ota_handle));
|
||||
|
||||
while (remaining > 0) {
|
||||
int recv_len = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)));
|
||||
|
||||
// Timeout Error: Just retry
|
||||
if (recv_len == HTTPD_SOCK_ERR_TIMEOUT) {
|
||||
continue;
|
||||
|
||||
// Serious Error: Abort OTA
|
||||
} else if (recv_len <= 0) {
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Protocol Error");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Successful Upload: Flash firmware chunk
|
||||
if (esp_ota_write(ota_handle, (const void *)buf, recv_len) != ESP_OK) {
|
||||
esp_ota_abort(ota_handle);
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Flash Error");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
remaining -= recv_len;
|
||||
}
|
||||
|
||||
// Validate and switch to new OTA image and reboot
|
||||
if (esp_ota_end(ota_handle) != ESP_OK || esp_ota_set_boot_partition(ota_partition) != ESP_OK) {
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Validation / Activation Error");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
httpd_resp_sendstr(req, "Firmware update complete, rebooting now!\n");
|
||||
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t start_rest_server(void *pvParameters)
|
||||
{
|
||||
GLOBAL_STATE = (GlobalState*)pvParameters;
|
||||
const char *base_path = "";
|
||||
|
||||
ESP_ERROR_CHECK(init_fs());
|
||||
|
||||
REST_CHECK(base_path, "wrong base path", err);
|
||||
rest_server_context_t *rest_context = calloc(1, sizeof(rest_server_context_t));
|
||||
REST_CHECK(rest_context, "No memory for rest context", err);
|
||||
strlcpy(rest_context->base_path, base_path, sizeof(rest_context->base_path));
|
||||
|
||||
httpd_handle_t server = NULL;
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.uri_match_fn = httpd_uri_match_wildcard;
|
||||
|
||||
ESP_LOGI(TAG, "Starting HTTP Server");
|
||||
REST_CHECK(httpd_start(&server, &config) == ESP_OK, "Start server failed", err_start);
|
||||
|
||||
/* URI handler for fetching system info */
|
||||
httpd_uri_t system_info_get_uri = {
|
||||
.uri = "/api/system/info",
|
||||
.method = HTTP_GET,
|
||||
.handler = GET_system_info,
|
||||
.user_ctx = rest_context
|
||||
};
|
||||
httpd_register_uri_handler(server, &system_info_get_uri);
|
||||
|
||||
|
||||
httpd_uri_t system_restart_uri = {
|
||||
.uri = "/api/system/restart",
|
||||
.method = HTTP_POST,
|
||||
.handler = POST_restart,
|
||||
.user_ctx = rest_context
|
||||
};
|
||||
httpd_register_uri_handler(server, &system_restart_uri);
|
||||
|
||||
httpd_uri_t update_system_settings_uri = {
|
||||
.uri = "/api/system",
|
||||
.method = HTTP_PATCH,
|
||||
.handler = PATCH_update_settings,
|
||||
.user_ctx = rest_context
|
||||
};
|
||||
httpd_register_uri_handler(server, &update_system_settings_uri);
|
||||
|
||||
|
||||
httpd_uri_t update_post_ota_firmware = {
|
||||
.uri = "/api/system/OTA",
|
||||
.method = HTTP_POST,
|
||||
.handler = POST_OTA_update,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
httpd_register_uri_handler(server, &update_post_ota_firmware);
|
||||
|
||||
httpd_uri_t update_post_ota_www = {
|
||||
.uri = "/api/system/OTAWWW",
|
||||
.method = HTTP_POST,
|
||||
.handler = POST_WWW_update,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
httpd_register_uri_handler(server, &update_post_ota_www);
|
||||
|
||||
|
||||
/* URI handler for getting web server files */
|
||||
httpd_uri_t common_get_uri = {
|
||||
.uri = "/*",
|
||||
.method = HTTP_GET,
|
||||
.handler = rest_common_get_handler,
|
||||
.user_ctx = rest_context
|
||||
};
|
||||
httpd_register_uri_handler(server, &common_get_uri);
|
||||
|
||||
return ESP_OK;
|
||||
err_start:
|
||||
free(rest_context);
|
||||
err:
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
7
main/http_server/http_server.h
Normal file
7
main/http_server/http_server.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef HTTP_SERVER_H_
|
||||
#define HTTP_SERVER_H_
|
||||
#include <esp_http_server.h>
|
||||
|
||||
esp_err_t start_rest_server(void *pvParameters);
|
||||
|
||||
#endif
|
6
main/http_server/package-lock.json
generated
Normal file
6
main/http_server/package-lock.json
generated
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "http_server",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
35
main/miner.c → main/main.c
Executable file → Normal file
35
main/miner.c → main/main.c
Executable file → Normal file
@ -4,7 +4,7 @@
|
||||
#include "nvs_flash.h"
|
||||
|
||||
//#include "protocol_examples_common.h"
|
||||
#include "miner.h"
|
||||
#include "main.h"
|
||||
|
||||
|
||||
#include "stratum_task.h"
|
||||
@ -14,11 +14,15 @@
|
||||
#include "serial.h"
|
||||
#include "asic_result_task.h"
|
||||
#include "nvs_config.h"
|
||||
#include "http_server.h"
|
||||
#include "esp_netif.h"
|
||||
#include "user_input_task.h"
|
||||
|
||||
|
||||
|
||||
#define ASIC_MODEL CONFIG_ASIC_MODEL
|
||||
|
||||
|
||||
|
||||
static GlobalState GLOBAL_STATE = {
|
||||
.extranonce_str = NULL,
|
||||
.extranonce_2_len = 0,
|
||||
@ -64,9 +68,6 @@ void app_main(void)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ESP_LOGI(TAG, "Welcome to the bitaxe!");
|
||||
//wait between 0 and 5 seconds for multiple units
|
||||
vTaskDelay(rand() % 5001 / portTICK_PERIOD_MS);
|
||||
@ -74,8 +75,6 @@ void app_main(void)
|
||||
|
||||
xTaskCreate(SYSTEM_task, "SYSTEM_task", 4096, (void*)&GLOBAL_STATE, 3, NULL);
|
||||
|
||||
ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
|
||||
|
||||
//pull the wifi credentials out of NVS
|
||||
char * wifi_ssid = nvs_config_get_string(NVS_CONFIG_WIFI_SSID, WIFI_SSID);
|
||||
char * wifi_pass = nvs_config_get_string(NVS_CONFIG_WIFI_PASS, WIFI_PASS);
|
||||
@ -84,7 +83,9 @@ void app_main(void)
|
||||
strncpy(GLOBAL_STATE.SYSTEM_MODULE.ssid, wifi_ssid, 20);
|
||||
|
||||
//init and connect to wifi
|
||||
EventBits_t result_bits = wifi_init_sta(wifi_ssid, wifi_pass);
|
||||
wifi_init(wifi_ssid, wifi_pass);
|
||||
start_rest_server((void*)&GLOBAL_STATE);
|
||||
EventBits_t result_bits = wifi_connect();
|
||||
|
||||
if (result_bits & WIFI_CONNECTED_BIT) {
|
||||
ESP_LOGI(TAG, "Connected to SSID: %s", wifi_ssid);
|
||||
@ -92,13 +93,23 @@ void app_main(void)
|
||||
} else if (result_bits & WIFI_FAIL_BIT) {
|
||||
ESP_LOGE(TAG, "Failed to connect to SSID: %s", wifi_ssid);
|
||||
strncpy(GLOBAL_STATE.SYSTEM_MODULE.wifi_status, "Failed to connect", 20);
|
||||
esp_restart(); //this is pretty much fatal, so just restart
|
||||
// User might be trying to configure with AP, just chill here
|
||||
ESP_LOGI(TAG,"Finished, waiting for user input.");
|
||||
while(1){
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "UNEXPECTED EVENT");
|
||||
strncpy(GLOBAL_STATE.SYSTEM_MODULE.wifi_status, "unexpected error", 20);
|
||||
esp_restart(); //this is pretty much fatal, so just restart
|
||||
// User might be trying to configure with AP, just chill here
|
||||
ESP_LOGI(TAG,"Finished, waiting for user input.");
|
||||
while(1){
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
wifi_softap_off();
|
||||
|
||||
free(wifi_ssid);
|
||||
free(wifi_pass);
|
||||
|
||||
@ -118,6 +129,9 @@ void app_main(void)
|
||||
xTaskCreate(ASIC_task, "asic", 8192, (void*)&GLOBAL_STATE, 10, NULL);
|
||||
xTaskCreate(ASIC_result_task, "asic result", 8192, (void*)&GLOBAL_STATE, 15, NULL);
|
||||
|
||||
xTaskCreate(USER_INPUT_task, "user input", 8192, (void*)&GLOBAL_STATE, 5, NULL);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void MINER_set_wifi_status(wifi_status_t status, uint16_t retry_count) {
|
||||
@ -129,4 +143,3 @@ void MINER_set_wifi_status(wifi_status_t status, uint16_t retry_count) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
#ifndef MINER_H_
|
||||
#define MINER_H_
|
||||
#ifndef MAIN_H_
|
||||
#define MAIN_H_
|
||||
|
||||
#include "connect.h"
|
||||
|
||||
void MINER_set_wifi_status(wifi_status_t status, uint16_t retry_count);
|
||||
|
||||
#endif /* MINER_H_ */
|
||||
#endif /* MAIN_H_ */
|
@ -40,6 +40,29 @@ char * nvs_config_get_string(const char * key, const char * default_value)
|
||||
return out;
|
||||
}
|
||||
|
||||
void nvs_config_set_string(const char * key, const char * value)
|
||||
{
|
||||
|
||||
nvs_handle handle;
|
||||
esp_err_t err;
|
||||
err = nvs_open(NVS_CONFIG_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGW(TAG, "Could not open nvs");
|
||||
return;
|
||||
}
|
||||
|
||||
err = nvs_set_str(handle, key, value);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGW(TAG, "Could not write nvs key: %s, value: %s", key, value);
|
||||
return;
|
||||
}
|
||||
|
||||
nvs_close(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t nvs_config_get_u16(const char * key, const uint16_t default_value)
|
||||
{
|
||||
nvs_handle handle;
|
||||
@ -63,3 +86,26 @@ uint16_t nvs_config_get_u16(const char * key, const uint16_t default_value)
|
||||
nvs_close(handle);
|
||||
return out;
|
||||
}
|
||||
|
||||
void nvs_config_set_u16(const char * key, const uint16_t value)
|
||||
{
|
||||
|
||||
nvs_handle handle;
|
||||
esp_err_t err;
|
||||
err = nvs_open(NVS_CONFIG_NAMESPACE, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGW(TAG, "Could not open nvs");
|
||||
return;
|
||||
}
|
||||
|
||||
err = nvs_set_u16(handle, key, value);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGW(TAG, "Could not write nvs key: %s, value: %u", key, value);
|
||||
return;
|
||||
}
|
||||
|
||||
nvs_close(handle);
|
||||
return;
|
||||
}
|
||||
|
@ -14,6 +14,8 @@
|
||||
#define NVS_CONFIG_ASIC_MODEL "asicModel"
|
||||
|
||||
char * nvs_config_get_string(const char * key, const char * default_value);
|
||||
void nvs_config_set_string(const char * key, const char * default_value);
|
||||
uint16_t nvs_config_get_u16(const char * key, const uint16_t default_value);
|
||||
void nvs_config_set_u16(const char * key, const uint16_t value);
|
||||
|
||||
#endif // MAIN_NVS_CONFIG_H
|
||||
|
@ -20,104 +20,105 @@
|
||||
#define VOLTAGE_RANGE (VOLTAGE_START_THROTTLE - VOLTAGE_MIN_THROTTLE)
|
||||
#define ASIC_MODEL CONFIG_ASIC_MODEL
|
||||
|
||||
static const char * TAG = "power_management";
|
||||
static const char *TAG = "power_management";
|
||||
|
||||
static float _fbound(float value, float lower_bound, float upper_bound)
|
||||
{
|
||||
if (value < lower_bound)
|
||||
return lower_bound;
|
||||
if (value > upper_bound)
|
||||
return upper_bound;
|
||||
if (value < lower_bound)
|
||||
return lower_bound;
|
||||
if (value > upper_bound)
|
||||
return upper_bound;
|
||||
|
||||
return value;
|
||||
return value;
|
||||
}
|
||||
|
||||
// void _power_init(PowerManagementModule * power_management){
|
||||
// power_management->frequency_multiplier = 1;
|
||||
// power_management->frequency_value = ASIC_FREQUENCY;
|
||||
void POWER_MANAGEMENT_task(void *pvParameters)
|
||||
{
|
||||
|
||||
// }
|
||||
GlobalState *GLOBAL_STATE = (GlobalState *)pvParameters;
|
||||
|
||||
void POWER_MANAGEMENT_task(void * pvParameters){
|
||||
|
||||
GlobalState *GLOBAL_STATE = (GlobalState*)pvParameters;
|
||||
//bm1397Module * bm1397 = &GLOBAL_STATE->BM1397_MODULE;
|
||||
PowerManagementModule * power_management = &GLOBAL_STATE->POWER_MANAGEMENT_MODULE;
|
||||
// _power_init(power_management);
|
||||
PowerManagementModule *power_management = &GLOBAL_STATE->POWER_MANAGEMENT_MODULE;
|
||||
|
||||
int last_frequency_increase = 0;
|
||||
while(1){
|
||||
while (1)
|
||||
{
|
||||
|
||||
power_management->voltage = INA260_read_voltage();
|
||||
power_management->power = INA260_read_power() / 1000;
|
||||
power_management->current = INA260_read_current();
|
||||
power_management->fan_speed = EMC2101_get_fan_speed();
|
||||
|
||||
if(strcmp(ASIC_MODEL, "BM1397") == 0){
|
||||
if (strcmp(ASIC_MODEL, "BM1397") == 0)
|
||||
{
|
||||
|
||||
power_management->chip_temp = EMC2101_get_chip_temp();
|
||||
|
||||
// Voltage
|
||||
// We'll throttle between 4.9v and 3.5v
|
||||
float voltage_multiplier = _fbound((power_management->voltage - VOLTAGE_MIN_THROTTLE) * (1/(float)VOLTAGE_RANGE), 0, 1);
|
||||
|
||||
float voltage_multiplier = _fbound((power_management->voltage - VOLTAGE_MIN_THROTTLE) * (1 / (float)VOLTAGE_RANGE), 0, 1);
|
||||
|
||||
// Temperature
|
||||
float temperature_multiplier = 1;
|
||||
float over_temp = -(THROTTLE_TEMP - power_management->chip_temp);
|
||||
if(over_temp > 0){
|
||||
temperature_multiplier = (THROTTLE_TEMP_RANGE - over_temp)/THROTTLE_TEMP_RANGE;
|
||||
if (over_temp > 0)
|
||||
{
|
||||
temperature_multiplier = (THROTTLE_TEMP_RANGE - over_temp) / THROTTLE_TEMP_RANGE;
|
||||
}
|
||||
|
||||
float lowest_multiplier = 1;
|
||||
float multipliers[2] = {voltage_multiplier, temperature_multiplier};
|
||||
|
||||
for(int i = 0; i < 2; i++){
|
||||
if(multipliers[i] < lowest_multiplier){
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (multipliers[i] < lowest_multiplier)
|
||||
{
|
||||
lowest_multiplier = multipliers[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
power_management->frequency_multiplier = lowest_multiplier;
|
||||
|
||||
|
||||
float target_frequency = _fbound(power_management->frequency_multiplier * ASIC_FREQUENCY, 0, ASIC_FREQUENCY);
|
||||
|
||||
if(target_frequency < 50){
|
||||
if (target_frequency < 50)
|
||||
{
|
||||
// TODO: Turn the chip off
|
||||
}
|
||||
|
||||
// chip is coming back from a low/no voltage event
|
||||
if(power_management->frequency_value < 50 && target_frequency > 50){
|
||||
if (power_management->frequency_value < 50 && target_frequency > 50)
|
||||
{
|
||||
// TODO recover gracefully?
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
|
||||
if(power_management->frequency_value > target_frequency){
|
||||
if (power_management->frequency_value > target_frequency)
|
||||
{
|
||||
power_management->frequency_value = target_frequency;
|
||||
last_frequency_increase = 0;
|
||||
BM1397_send_hash_frequency(power_management->frequency_value);
|
||||
ESP_LOGI(TAG, "target %f, Freq %f, Temp %f, Power %f", target_frequency, power_management->frequency_value, power_management->chip_temp, power_management->power);
|
||||
}else{
|
||||
if(
|
||||
}
|
||||
else
|
||||
{
|
||||
if (
|
||||
last_frequency_increase > 120 &&
|
||||
power_management->frequency_value != ASIC_FREQUENCY
|
||||
){
|
||||
power_management->frequency_value != ASIC_FREQUENCY)
|
||||
{
|
||||
float add = (target_frequency + power_management->frequency_value) / 2;
|
||||
power_management->frequency_value += _fbound(add, 2 , 20);
|
||||
power_management->frequency_value += _fbound(add, 2, 20);
|
||||
BM1397_send_hash_frequency(power_management->frequency_value);
|
||||
ESP_LOGI(TAG, "target %f, Freq %f, Temp %f, Power %f", target_frequency, power_management->frequency_value, power_management->chip_temp, power_management->power);
|
||||
last_frequency_increase = 60;
|
||||
}else{
|
||||
}
|
||||
else
|
||||
{
|
||||
last_frequency_increase++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//ESP_LOGI(TAG, "target %f, Freq %f, Volt %f, Power %f", target_frequency, power_management->frequency_value, power_management->voltage, power_management->power);
|
||||
// ESP_LOGI(TAG, "target %f, Freq %f, Volt %f, Power %f", target_frequency, power_management->frequency_value, power_management->voltage, power_management->power);
|
||||
vTaskDelay(POLL_RATE / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
27
main/tasks/user_input_task.c
Normal file
27
main/tasks/user_input_task.c
Normal file
@ -0,0 +1,27 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "connect.h"
|
||||
|
||||
#define BUTTON_BOOT GPIO_NUM_0
|
||||
|
||||
static const char * TAG = "user_input";
|
||||
static bool button_being_pressed = false;
|
||||
|
||||
void USER_INPUT_task(void * pvParameters){
|
||||
|
||||
gpio_set_direction(BUTTON_BOOT, GPIO_MODE_INPUT);
|
||||
|
||||
while(1) {
|
||||
if (gpio_get_level(BUTTON_BOOT) == 0 && button_being_pressed == false) { // If button is pressed
|
||||
ESP_LOGI(TAG, "BUTTON PRESSED");
|
||||
button_being_pressed = true;
|
||||
toggle_wifi_softap();
|
||||
} else if (gpio_get_level(BUTTON_BOOT) == 1 && button_being_pressed == true){
|
||||
button_being_pressed = false;
|
||||
}
|
||||
|
||||
vTaskDelay(30/portTICK_PERIOD_MS); // Add delay so that current task does not starve idle task and trigger watchdog timer
|
||||
}
|
||||
}
|
7
main/tasks/user_input_task.h
Normal file
7
main/tasks/user_input_task.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef USER_INPUT_TASK_H_
|
||||
#define USER_INPUT_TASK_H_
|
||||
|
||||
|
||||
void USER_INPUT_task(void * pvParameters);
|
||||
|
||||
#endif
|
9
partitions.csv
Normal file
9
partitions.csv
Normal file
@ -0,0 +1,9 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000
|
||||
phy_init, data, phy, 0xf000, 0x1000
|
||||
factory, app, factory, 0x10000, 1M
|
||||
www, data, spiffs, 0x110000, 2M
|
||||
ota_0, app, ota_0, 0x310000, 1M
|
||||
ota_1, app, ota_1, 0x410000, 1M
|
||||
otadata, data, ota, 0x510000, 8k
|
|
18
readme.md
18
readme.md
@ -51,6 +51,24 @@ Set following parameters under Example Connection Configuration Options:
|
||||
|
||||
For more information about the example_connect() method used here, check out <https://github.com/espressif/esp-idf/blob/master/examples/protocols/README.md>.
|
||||
|
||||
## Build website
|
||||
|
||||
To build the website for viewing and OTA updates open the Angular project found in
|
||||
```
|
||||
ESP-Miner\main\http_server\axe-os
|
||||
```
|
||||
|
||||
Then install dependencies and build.
|
||||
|
||||
```
|
||||
npm i
|
||||
npm run build
|
||||
```
|
||||
|
||||
|
||||
When the esp-idf project is built it will bundle the website in www.bin
|
||||
|
||||
|
||||
## Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
0
sdkconfig.ci
Executable file → Normal file
0
sdkconfig.ci
Executable file → Normal file
8
sdkconfig.defaults
Normal file
8
sdkconfig.defaults
Normal file
@ -0,0 +1,8 @@
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0x8000
|
||||
CONFIG_PARTITION_TABLE_MD5=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE="8MB"
|
||||
CONFIG_ESP_MAXIMUM_RETRY=3
|
Loading…
x
Reference in New Issue
Block a user