Compare commits

...

2 Commits

Author SHA1 Message Date
Villivateur Von 97d17ada7c basic function OK 2021-09-20 22:48:41 +08:00
Villivateur Von 32e1eb8958 init platformio 2021-09-20 20:08:26 +08:00
19 changed files with 558 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

7
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
]
}

24
doc/province_bit_map.md Normal file
View File

@ -0,0 +1,24 @@
# 各省对应的 LED 位
此表为 Mapuino 上实际闪烁的 LED 所在省级行政区与 HTTP 报文中 `data0`、`data1` 字段的对应关系。
| 省 | 字段 | 位 | 省 | 字段 | 位 |
|----|-----|----|----|------|---|
| 新疆 | data0 | 0 | 内蒙古 | data0 | 1 |
| 黑龙江 | data0 | 2 | 吉林 | data0 | 3 |
| 辽宁 | data0 | 4 | 北京 | data0 | 5 |
| 天津 | data0 | 6 | 青海 | data0 | 7 |
| 甘肃 | data0 | 8 | 宁夏 | data0 | 9 |
| 陕西 | data0 | 10 | 山西 | data0 | 11 |
| 河北 | data0 | 12 | 山东 | data0 | 13 |
| 河南 | data0 | 14 | 西藏 | data0 | 15 |
| 四川 | data0 | 16 | 重庆 | data0 | 17 |
| 湖北 | data0 | 18 | 安徽 | data0 | 19 |
| 江苏 | data0 | 20 | 云南 | data0 | 21 |
| 贵州 | data0 | 22 | 湖南 | data0 | 23 |
| 江西 | data0 | 24 | 浙江 | data0 | 25 |
| 上海 | data0 | 26 | 广西 | data0 | 27 |
| 广东 | data0 | 28 | 福建 | data0 | 29 |
| 海南 | data0 | 30 | 澳门 | data0 | 31 |
| 香港 | data1 | 0 | 台湾 | data1 | 1 |
| World | data1 | 2 |

39
include/README Normal file
View File

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

20
include/client_network.h Normal file
View File

@ -0,0 +1,20 @@
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#include <ArduinoJson.h>
class ClientNetwork
{
private:
String url;
StaticJsonDocument<512> receivedData;
HTTPClient* http;
STATUS status;
public:
ClientNetwork();
STATUS FetchNewData();
uint32_t GetData0();
uint32_t GetData1();
};

15
include/config_manager.h Normal file
View File

@ -0,0 +1,15 @@
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
class ConfigManager
{
private:
IPAddress localIp;
IPAddress gateway;
IPAddress subnet;
public:
ESP8266WebServer httpServer;
ConfigManager();
void ProcessConfig();
};

12
include/display.h Normal file
View File

@ -0,0 +1,12 @@
#include <Arduino.h>
#include <TM1638plus.h>
class DisplayPanel
{
private:
TM1638plus* ledMap;
public:
DisplayPanel();
void DisplayLedMap(uint32_t data0, uint32_t data1);
};

11
include/func_button.h Normal file
View File

@ -0,0 +1,11 @@
#include <Arduino.h>
class FuncButton
{
private:
uint8_t pressedTime;
public:
FuncButton();
void Scan();
};

10
include/monitor_items.h Normal file
View File

@ -0,0 +1,10 @@
// Topuino and Topuino_Server must share this header file
#define CPU_PERCENT "CPU_PERCENT"
#define MEM_PERCENT "MEM_PERCENT"
#define DISK0_PERCENT "DISK_PERCENT"
#define DISK1_PERCENT "DISK1_PERCENT"
#define DISK_READ_RATE "DISK_READ_RATE"
#define DISK_WRITE_RATE "DISK_WRITE_RATE"
#define NET_SENT_RATE "NET_SENT_RATE"
#define NET_RECV_RATE "NET_RECV_RATE"

21
include/status_blink.h Normal file
View File

@ -0,0 +1,21 @@
#include <Arduino.h>
#include <Ticker.h>
#define STATUS_LED_PIN 2
class StatusBlink
{
private:
Ticker flipper;
public:
enum BlinkRate {
RateAlwaysOn,
Rate5Hz,
Rate2Hz,
Rate0_5Hz,
RateAlwaysOff,
};
StatusBlink();
void SetBlinkRate(BlinkRate rate);
};

19
include/user_data.h Normal file
View File

@ -0,0 +1,19 @@
#include <Arduino.h>
class UserData
{
private:
String ReadEepromString(uint32_t offset);
void WriteEepromString(String data, uint32_t offset);
public:
UserData();
bool UserDataValid();
void ConfirmData();
void EraseData();
String GetWifiSsid();
void SetWifiSsid(String ssid);
String GetWifiPasswd();
void SetWifiPasswd(String password);
String GetDeviceUuid();
void SetDeviceUuid(String uuid);
};

17
platformio.ini Normal file
View File

@ -0,0 +1,17 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp12e]
platform = espressif8266
board = esp12e
framework = arduino
lib_deps =
gavinlyonsrepo/TM1638plus@^1.7.0
bblanchon/ArduinoJson@^6.18.3

61
src/client_network.cpp Normal file
View File

@ -0,0 +1,61 @@
#include "client_network.h"
#include "status_blink.h"
#include "user_data.h"
#include "func_button.h"
extern StatusBlink* statusLed;
extern UserData* userdataManager;
extern FuncButton* funcButton;
ClientNetwork::ClientNetwork()
{
WiFi.begin(userdataManager->GetWifiSsid(), userdataManager->GetWifiPasswd());
statusLed->SetBlinkRate(StatusBlink::BlinkRate::Rate2Hz);
while (WiFi.status() != WL_CONNECTED)
{
delay(1000);
funcButton->Scan();
}
statusLed->SetBlinkRate(StatusBlink::BlinkRate::RateAlwaysOff);
url = "http://iot.vvzero.com/mapuino/hardwareAPI?UUID=";
url += userdataManager->GetDeviceUuid();
status = FAIL;
}
STATUS ClientNetwork::FetchNewData()
{
status = FAIL;
if (WiFi.status() == WL_CONNECTED) {
WiFiClient client;
http = new HTTPClient();
if (http->begin(client, url)) {
if (http->GET() == HTTP_CODE_OK) {
if (deserializeJson(receivedData, http->getString().c_str()) == DeserializationError::Code::Ok) {
status = OK;
}
}
http->end();
}
delete http;
}
return status;
}
uint32_t ClientNetwork::GetData0()
{
if (status != OK) {
return 0;
} else {
return (uint32_t)receivedData["DATA0"];
}
}
uint32_t ClientNetwork::GetData1()
{
if (status != OK) {
return 0;
} else {
return (uint32_t)receivedData["DATA1"];
}
}

88
src/config_manager.cpp Normal file
View File

@ -0,0 +1,88 @@
#include "config_manager.h"
#include "status_blink.h"
#include "user_data.h"
#define SSID "Topuino"
#define PASSWORD "vvzero.com"
extern StatusBlink* statusLed;
extern ConfigManager* configManager;
extern UserData* userdataManager;
static String configPage("\
<!DOCTYPE html>\
<html>\
<head>\
<title>Topuino</title>\
<meta name='viewport' content='width=device-width, initial-scale=1, user-scalable=no' />\
<link rel='shortcut icon' href='data:,'>\
</head>\
<body>\
<h1 style='text-align: center; font-size: 40px'>Topuino Config Page</h1>\
<form action='/setup' method='POST'>\
<div style='text-align: center'>\
<div style='font-size: 20px; margin-bottom: 15px'>\
<label for='ssid'><b>Wi-Fi SSID</b></label>\
<input type='text' name='ssid' required>\
</div>\
<div style='font-size: 20px; margin-bottom: 15px'>\
<label for='psw'><b>Wi-Fi Password</b></label>\
<input type='password' name='psw' required>\
</div>\
<div style='font-size: 20px; margin-bottom: 15px'>\
<label for='uuid'><b>Device ID</b></label>\
<input type='text' name='uuid' required>\
</div>\
<div style='font-size: 20px'>\
<button type='submit'>Commit</button>\
</div>\
</div>\
</form>\
</body>\
</html>\
");
static void handleOnConnect()
{
configManager->httpServer.send(200, "text/html", configPage);
}
static void handleOnCommit()
{
if (configManager->httpServer.hasArg("ssid") && configManager->httpServer.hasArg("psw") && configManager->httpServer.hasArg("uuid")) {
userdataManager->SetWifiSsid(configManager->httpServer.arg("ssid"));
userdataManager->SetWifiPasswd(configManager->httpServer.arg("psw"));
userdataManager->SetDeviceUuid(configManager->httpServer.arg("uuid"));
userdataManager->ConfirmData();
configManager->httpServer.send(200, "text/html", "OK");
delay(200);
ESP.restart();
} else {
configManager->httpServer.send(200, "text/html", "ERROR");
}
}
ConfigManager::ConfigManager() :
localIp(192,168,1,1),
gateway(192,168,1,1),
subnet(255,255,255,0),
httpServer(80)
{
statusLed->SetBlinkRate(StatusBlink::BlinkRate::Rate0_5Hz);
WiFi.softAPConfig(localIp, gateway, subnet);
WiFi.softAP(SSID, PASSWORD);
delay(100);
httpServer.on("/", handleOnConnect);
httpServer.on("/setup", handleOnCommit);
httpServer.begin();
}
void ConfigManager::ProcessConfig()
{
while (1) {
httpServer.handleClient();
}
}

22
src/display.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "display.h"
#define STROBE_TM1 13
#define CLOCK_TM 4 // clock = GPIO connected to clock line of module
#define DIO_TM 5 // data = GPIO connected to data line of module
#define HIGH_FREQ false //default false, If using a high freq CPU > ~100 MHZ set to true.
DisplayPanel::DisplayPanel()
{
// Constructor object (GPIO STB , GPIO CLOCK , GPIO DIO, use high freq MCU)
ledMap = new TM1638plus(STROBE_TM1, CLOCK_TM, DIO_TM, HIGH_FREQ);
ledMap->displayBegin();
}
void DisplayPanel::DisplayLedMap(uint32_t data0, uint32_t data1)
{
ledMap->display7Seg(0, data0 % 0x100);
ledMap->display7Seg(1, data0 / 0x100 % 0x100);
ledMap->display7Seg(2, data0 / 0x10000 % 0x100);
ledMap->display7Seg(3, data0 / 0x1000000);
ledMap->display7Seg(4, data1 % 0x100);
}

26
src/func_button.cpp Normal file
View File

@ -0,0 +1,26 @@
#include "func_button.h"
#include "user_data.h"
#define FUNC_BTN 0
#define PRESSED_STATUS LOW
extern UserData* userdataManager;
FuncButton::FuncButton()
{
pinMode(FUNC_BTN, INPUT);
pressedTime = 0;
}
void FuncButton::Scan()
{
if (digitalRead(FUNC_BTN) == PRESSED_STATUS) {
pressedTime++;
} else {
pressedTime = 0;
}
if (pressedTime > 5) {
userdataManager->EraseData();
ESP.restart();
}
}

40
src/main.cpp Normal file
View File

@ -0,0 +1,40 @@
#include <Arduino.h>
#include "display.h"
#include "client_network.h"
#include "monitor_items.h"
#include "status_blink.h"
#include "config_manager.h"
#include "user_data.h"
#include "func_button.h"
DisplayPanel* displayPanel;
ClientNetwork* netClient;
StatusBlink* statusLed;
UserData* userdataManager;
ConfigManager* configManager;
FuncButton* funcButton;
void setup()
{
statusLed = new StatusBlink();
userdataManager = new UserData();
displayPanel = new DisplayPanel();
funcButton = new FuncButton();
if (!userdataManager->UserDataValid()) {
configManager = new ConfigManager();
configManager->ProcessConfig();
} else {
netClient = new ClientNetwork();
}
}
void loop()
{
delay(1000);
funcButton->Scan();
if (netClient->FetchNewData() != OK) {
return;
}
displayPanel->DisplayLedMap(netClient->GetData0(), netClient->GetData1());
}

36
src/status_blink.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "status_blink.h"
StatusBlink::StatusBlink()
{
pinMode(STATUS_LED_PIN, OUTPUT);
digitalWrite(STATUS_LED_PIN, HIGH);
}
static void BlinkTask()
{
digitalWrite(STATUS_LED_PIN, !digitalRead(STATUS_LED_PIN)); // set pin to the opposite state
}
void StatusBlink::SetBlinkRate(BlinkRate rate)
{
switch (rate)
{
case BlinkRate::RateAlwaysOn:
flipper.detach();
digitalWrite(STATUS_LED_PIN, LOW);
break;
case BlinkRate::RateAlwaysOff:
flipper.detach();
digitalWrite(STATUS_LED_PIN, HIGH);
break;
case BlinkRate::Rate5Hz:
flipper.attach(0.1, BlinkTask);
break;
case BlinkRate::Rate2Hz:
flipper.attach(0.25, BlinkTask);
break;
case BlinkRate::Rate0_5Hz:
flipper.attach(1.0, BlinkTask);
break;
}
}

85
src/user_data.cpp Normal file
View File

@ -0,0 +1,85 @@
#include "user_data.h"
#include <EEPROM.h>
#define MAX_USER_DATA_SIZE 0x200
#define VALIDATION_MAGIC 0xaa
#define GENERAL_FEILD_SIZE 0x20
#define VALIDATION_OFFSET 0x00
#define SSID_OFFSET 0x20
#define PASSWORD_OFFSET 0x40
#define UUID_OFFSET 0x60
UserData::UserData()
{
EEPROM.begin(MAX_USER_DATA_SIZE);
}
String UserData::ReadEepromString(uint32_t offset)
{
String result;
char readByte;
for (uint32_t addr = offset; (readByte = EEPROM.read(addr)) != '\0'; addr++) {
result += readByte;
}
return result;
}
void UserData::WriteEepromString(String data, uint32_t offset)
{
for (uint32_t i = 0; i < data.length(); i++) {
EEPROM.write(i + offset, data[i]);
}
EEPROM.write(data.length() + offset, 0x00);
}
bool UserData::UserDataValid()
{
byte validation = EEPROM.read(VALIDATION_OFFSET);
return validation == VALIDATION_MAGIC;
}
void UserData::ConfirmData()
{
EEPROM.write(VALIDATION_OFFSET, VALIDATION_MAGIC);
EEPROM.commit();
}
void UserData::EraseData()
{
EEPROM.write(VALIDATION_OFFSET, 0x00);
EEPROM.commit();
}
String UserData::GetWifiSsid()
{
return ReadEepromString(SSID_OFFSET);
}
void UserData::SetWifiSsid(String ssid)
{
return WriteEepromString(ssid, SSID_OFFSET);
}
String UserData::GetWifiPasswd()
{
return ReadEepromString(PASSWORD_OFFSET);
}
void UserData::SetWifiPasswd(String password)
{
return WriteEepromString(password, PASSWORD_OFFSET);
}
String UserData::GetDeviceUuid()
{
return ReadEepromString(UUID_OFFSET);
}
void UserData::SetDeviceUuid(String uuid)
{
return WriteEepromString(uuid, UUID_OFFSET);
}