Compare commits
8 Commits
21c7f3f990
...
v1.3.7
| Author | SHA1 | Date | |
|---|---|---|---|
| 978919de2a | |||
| 212bd6b2e1 | |||
| cda7c855fe | |||
| ebf61d13d4 | |||
| a43690930b | |||
| 21fa6ef70f | |||
| 1899065ab0 | |||
| 6502904a5a |
126
README.md
126
README.md
@@ -1,30 +1,124 @@
|
||||
# WiFi天气微站
|
||||
# WS2 桌面气象站
|
||||
|
||||
esp8266版天气信息显示设备
|
||||
([English](README_en.md) | 中文)
|
||||
|
||||
基于ESP8266的桌面气象站,能够实时显示天气信息、环境数据和时间。
|
||||
|
||||

|
||||

|
||||
|
||||
## 功能特点
|
||||
|
||||
- 🌤️ **实时天气显示**:获取并显示当前天气状况、温度、湿度和空气质量
|
||||
- 📺 **TFT LCD彩色显示**:1.54寸240x240像素彩色液晶屏
|
||||
- 🕐 **多种显示模式**:支持多种UI界面,包括天气时钟和电子相册模式
|
||||
- 🌐 **Web管理界面**:内置轻量级Web服务器,支持浏览器配置和管理
|
||||
- 📊 **系统监控**:实时显示设备状态、内存使用和网络信息
|
||||
- 📡 **WiFi连接**:支持2.4GHz WiFi网络,自动重连机制
|
||||
|
||||
## 硬件规格
|
||||
|
||||
- **主控芯片**:ESP8266/ESP32
|
||||
- **显示屏**:1.54寸 TFT LCD 240×240像素
|
||||
- **网络**:802.11 b/g/n WiFi
|
||||
|
||||
## 软件架构
|
||||
|
||||
本项目基于MicroPython开发,包含以下核心模块:
|
||||
|
||||
- **app.py** - 主应用程序
|
||||
- **config.py** - 配置管理
|
||||
- **wifi_manager.py** - WiFi连接管理
|
||||
- **display.py** - 显示屏控制
|
||||
- **nanoweb.py** - 轻量级异步Web服务器
|
||||
- **captive_portal.py** - 配置门户
|
||||
|
||||
## 快速开始
|
||||
|
||||
1. 购物网站搜: WiFi天气时钟
|
||||
> 务必与商家确认可以通过usb下载固件
|
||||
|
||||
2. 连接设备usb,下载完整版固件到ESP8266
|
||||
```bash
|
||||
esptool.py --port /dev/ttyUSB0 --baud 460800 write-flash --flash-size=detect 0 firmware.bin
|
||||
```
|
||||
|
||||
### 初次配置
|
||||
|
||||
1. 设备启动后会创建WiFi热点 `WS2-xxxx`
|
||||
2. 连接该热点,浏览器会自动跳转访问 `192.168.4.1`
|
||||
3. 在配置页面输入WiFi信息
|
||||
4. 设备将自动连接并获取天气信息
|
||||
|
||||
## Web管理界面
|
||||
|
||||
设备Web界面提供以下功能:
|
||||
|
||||
### 设备状态
|
||||
- 查看系统运行状态、内存使用情况
|
||||
- 监控设备IP地址和运行时间
|
||||
- 实时显示UUID和固件版本
|
||||
|
||||
### 屏幕显示
|
||||
- 调节屏幕亮度
|
||||
-切换显示模式
|
||||
- LCD内容预览
|
||||
|
||||
### 系统配置
|
||||
- 选择城市和地区
|
||||
- 设置自动熄屏时间
|
||||
- 保存/加载配置
|
||||
|
||||
### 高级设置
|
||||
- 执行系统命令
|
||||
- 查看MAC地址
|
||||
- 重启设备
|
||||
- 清空配置
|
||||
|
||||
## API接口
|
||||
|
||||
1. /ping
|
||||
设备提供RESTful API接口,可通过HTTP请求获取数据:
|
||||
|
||||
2. /status
|
||||
```
|
||||
GET /lcd - 获取LCD设置
|
||||
GET /status - 获取系统状态
|
||||
GET /config - 获取配置信息
|
||||
POST /exec - 执行系统命令 {cmd: "命令内容", token: "认证令牌"}
|
||||
POST /lcd/set - 设置LCD参数 {brightness: 80, ui_type: "default"}
|
||||
POST /config/set - 设置配置 {city: "北京", standby_time: "22:00"}
|
||||
```
|
||||
|
||||
3. /weather
|
||||
/weather/?city=xxx&force=1
|
||||
## 开发指南
|
||||
|
||||
4. /lcd
|
||||
1. 修改/src/rom下相关文件,使用mpremote romfs更新
|
||||
2. 自行编译micropython固件需要集成*st7789py_mpy*(未整理)
|
||||
|
||||
5. /lcd/set, POST
|
||||
## 故障排除
|
||||
|
||||
6. /exec, POST
|
||||
### 常见问题
|
||||
|
||||
```sh
|
||||
# read memory free
|
||||
> *curl -H "Content-Type: application/json" -X POST -d '{"cmd":"import gc;gc.collect();R=gc.mem_free()", "token":"c6b74200"}' http://192.168.99.194/exec*
|
||||
**Q: 设备无法连接WiFi**
|
||||
- A: 检查WiFi密码是否正确,或重启设备重新配置
|
||||
|
||||
# reset
|
||||
> *curl -H "Content-Type: application/json" -X POST -d '{"cmd":"import machine; machine.reset()", "token":"c6b74200"}' http://192.168.99.194/exec*
|
||||
**Q: 天气数据不更新**
|
||||
- A1: 检查网络连接,或尝试强制刷新天气数据
|
||||
- A2: 个人的天气服务器失联~
|
||||
|
||||
## 许可证
|
||||
|
||||
## 参考资料
|
||||
[MicroPython remote control: mpremote](https://docs.micropython.org/en/latest/reference/mpremote.html)
|
||||
本项目采用MIT许可证
|
||||
|
||||
## 致谢
|
||||
|
||||
- [MicroPython](https://micropython.org/) - 高效的Python微控制器实现
|
||||
- [ST7789驱动](https://github.com/devbis/st7789py_mpy) - LCD显示屏驱动
|
||||
- [captive-portal](https://github.com/anson-vandoren/esp8266-captive-portal) - 认证门户
|
||||
- [Nanoweb](https://github.com/hugokernel/micropython-nanoweb) - 轻量级异步Web服务器
|
||||
- [mpremote](https://docs.micropython.org/en/latest/reference/mpremote.html) - mp控制终端
|
||||
|
||||
## 联系方式
|
||||
|
||||
- 项目主页: https://github.com/kicer/ws2
|
||||
- 项目主页: https://iot.foresh.com/git/kicer/ws2
|
||||
- 问题反馈: https://github.com/kicer/ws2/issues
|
||||
- QQ联络群: 697580459
|
||||
|
||||
124
README_en.md
Normal file
124
README_en.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# WS2 Desktop Weather Station
|
||||
|
||||
(English | [中文](README.md))
|
||||
|
||||
An ESP8266-based desktop weather station capable of displaying real-time weather information, environmental data, and time.
|
||||
|
||||

|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- 🌤️ **Real-time Weather Display**: Get and display current weather conditions, temperature, humidity, and air quality
|
||||
- 📺 **TFT LCD Color Display**: 1.54-inch 240x240 pixel color LCD screen
|
||||
- 🕐 **Multiple Display Modes**: Support for various UI interfaces, including weather clock and photo album modes
|
||||
- 🌐 **Web Management Interface**: Built-in lightweight web server supporting browser configuration and management
|
||||
- 📊 **System Monitoring**: Real-time display of device status, memory usage, and network information
|
||||
- 📡 **WiFi Connectivity**: Support for 2.4GHz WiFi networks with automatic reconnection
|
||||
|
||||
## Hardware Specifications
|
||||
|
||||
- **Main Controller**: ESP8266/ESP32
|
||||
- **Display**: 1.54-inch TFT LCD 240×240 pixels
|
||||
- **Network**: 802.11 b/g/n WiFi
|
||||
|
||||
## Software Architecture
|
||||
|
||||
This project is developed based on MicroPython and includes the following core modules:
|
||||
|
||||
- **app.py** - Main application
|
||||
- **config.py** - Configuration management
|
||||
- **wifi_manager.py** - WiFi connection management
|
||||
- **display.py** - Display screen control
|
||||
- **nanoweb.py** - Lightweight asynchronous web server
|
||||
- **captive_portal.py** - Configuration portal
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Search for "WiFi Weather Clock" on shopping websites
|
||||
> Make sure to confirm with the seller that firmware can be downloaded via USB
|
||||
|
||||
2. Connect the device via USB and download the complete firmware to ESP8266
|
||||
```bash
|
||||
esptool.py --port /dev/ttyUSB0 --baud 460800 write-flash --flash_size=detect 0 firmware.bin
|
||||
```
|
||||
|
||||
### Initial Configuration
|
||||
|
||||
1. The device creates a WiFi hotspot named `WS2-xxxx` upon startup
|
||||
2. Connect to this hotspot and the browser will automatically redirect to `192.168.4.1`
|
||||
3. Enter WiFi information on the configuration page
|
||||
4. The device will automatically connect and fetch weather information
|
||||
|
||||
## Web Management Interface
|
||||
|
||||
The device web interface provides the following features:
|
||||
|
||||
### Device Status
|
||||
- View system running status and memory usage
|
||||
- Monitor device IP address and uptime
|
||||
- Real-time display of UUID and firmware version
|
||||
|
||||
### Screen Display
|
||||
- Adjust screen brightness
|
||||
- Switch display modes
|
||||
- LCD content preview
|
||||
|
||||
### System Configuration
|
||||
- Select city and region
|
||||
- Set auto-sleep time
|
||||
- Save/load configuration
|
||||
|
||||
### Advanced Settings
|
||||
- Execute system commands
|
||||
- View MAC address
|
||||
- Restart device
|
||||
- Clear configuration
|
||||
|
||||
## API Interface
|
||||
|
||||
The device provides RESTful API interfaces, accessible via HTTP requests:
|
||||
|
||||
```
|
||||
GET /lcd - Get LCD settings
|
||||
GET /status - Get system status
|
||||
GET /config - Get configuration information
|
||||
POST /exec - Execute system commands {cmd: "Command content", token: "Authentication token"}
|
||||
POST /lcd/set - Set LCD parameters {brightness: 80, ui_type: "default"}
|
||||
POST /config/set - Set configuration {city: "Beijing", standby_time: "22:00"}
|
||||
```
|
||||
|
||||
## Development Guide
|
||||
|
||||
1. Modify relevant files in /src/rom directory and use mpremote romfs to update
|
||||
2. Custom compilation of MicroPython firmware requires integration of *st7789py_mpy* (not organized yet)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Q: Device cannot connect to WiFi**
|
||||
- A: Check if the WiFi password is correct, or restart the device to reconfigure
|
||||
|
||||
**Q: Weather data doesn't update**
|
||||
- A1: Check network connection or try force-refreshing weather data
|
||||
- A2: Personal weather server may be offline~
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
- [MicroPython](https://micropython.org/) - Efficient Python implementation for microcontrollers
|
||||
- [ST7789 Driver](https://github.com/devbis/st7789py_mpy) - LCD display driver
|
||||
- [captive-portal](https://github.com/anson-vandoren/esp8266-captive-portal) - Captive portal
|
||||
- [Nanoweb](https://github.com/hugokernel/micropython-nanoweb) - Lightweight asynchronous web server
|
||||
- [mpremote](https://docs.micropython.org/en/latest/reference/mpremote.html) - MicroPython control terminal
|
||||
|
||||
## Contact
|
||||
|
||||
- Project Homepage: https://github.com/kicer/ws2
|
||||
- Project Homepage: https://iot.foresh.com/git/kicer/ws2
|
||||
- Issue Tracker: https://github.com/kicer/ws2/issues
|
||||
- QQ Group: 697580459
|
||||
BIN
docs/web.jpg
Normal file
BIN
docs/web.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/ws2.jpg
Normal file
BIN
docs/ws2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -188,6 +188,37 @@ async def eval_cmd(request):
|
||||
finally:
|
||||
await json_response(request, json.dumps(ack))
|
||||
|
||||
# 定时熄屏处理功能,熄屏则返回True
|
||||
def standby_control():
|
||||
# 获取当前时间 (格式: HH:MM)
|
||||
now = time.localtime()
|
||||
current_time = "{:02d}:{:02d}".format(now[3], now[4])
|
||||
|
||||
# 从配置中获取熄屏和唤醒时间
|
||||
standby_time = config.get("standby_time")
|
||||
wakeup_time = config.get("wakeup_time")
|
||||
|
||||
_set = config.get('brightness', 10)
|
||||
|
||||
# 如果设置了熄屏和唤醒时间
|
||||
if standby_time and wakeup_time:
|
||||
# 如果当前时间在熄屏时间之后,唤醒时间之前,则关闭屏幕
|
||||
if standby_time <= wakeup_time:
|
||||
if current_time >= standby_time and current_time < wakeup_time:
|
||||
_set = 0
|
||||
else:
|
||||
if current_time >= standby_time or current_time < wakeup_time:
|
||||
_set = 0
|
||||
else:
|
||||
_set = None
|
||||
|
||||
if _set is not None:
|
||||
if display.brightness():
|
||||
if _set == 0:
|
||||
display.brightness(0)
|
||||
else:
|
||||
if _set > 0:
|
||||
display.brightness(config.get("brightness", 10)) # 恢复亮度
|
||||
|
||||
|
||||
# ntp时钟同步
|
||||
@@ -217,7 +248,7 @@ async def fetch_weather_data(city=None):
|
||||
|
||||
print(f"正在获取{city}天气数据...")
|
||||
# 从配置获取API基础URL,默认使用官方API
|
||||
url = config.get("weather_api_url", "http://esp.tangofu.com/api/ws2/")
|
||||
url = config.get("weather_api_url", "http://esp.foresh.com/api/ws2/")
|
||||
params = {"uuid": uuid(), "city": city}
|
||||
|
||||
# 发送GET请求
|
||||
@@ -240,7 +271,9 @@ async def fetch_weather_data(city=None):
|
||||
advice = wdata.get("advice", [])
|
||||
lunar = wdata.get("lunar", None)
|
||||
|
||||
ip = wifi_manager.get_ip()
|
||||
if city == "N/A":
|
||||
advice.append("城市配置错误")
|
||||
advice.append(wifi_manager.get_ip())
|
||||
|
||||
display.update_ui(
|
||||
city,
|
||||
@@ -248,7 +281,6 @@ async def fetch_weather_data(city=None):
|
||||
advice,
|
||||
aqi,
|
||||
lunar,
|
||||
ip,
|
||||
envdat={"t": t, "rh": rh, "co2": co2, "pm": pm, "ap": ap},
|
||||
)
|
||||
else:
|
||||
@@ -327,7 +359,12 @@ async def ui_task():
|
||||
# 每隔100帧,更新一次UI显示
|
||||
F += 1
|
||||
if F % 100 == 0:
|
||||
display.update_ui()
|
||||
standby_control() # 控制开关机
|
||||
# 只在亮屏时更新显示
|
||||
if display.brightness():
|
||||
display.update_ui()
|
||||
else:
|
||||
gc.collect(); print(f'LCD.idle.mem: {gc.mem_free()}')
|
||||
|
||||
# 每轮清理一次内存
|
||||
gc.collect()
|
||||
@@ -361,6 +398,9 @@ def cb_progress(data):
|
||||
|
||||
|
||||
def start():
|
||||
# 禁用repl
|
||||
if config.get('console') == "disable":
|
||||
import os; os.dupterm(None, 1)
|
||||
# 初始化液晶屏
|
||||
display.init_display(config.get("bl_mode") == "pwm", 7000)
|
||||
display.brightness(int(config.get("brightness", 10)))
|
||||
|
||||
@@ -49,7 +49,7 @@ class Display:
|
||||
|
||||
# 初始化显示屏
|
||||
self.tft = ST7789(
|
||||
SPI(1, 40_000_000, polarity=1),
|
||||
SPI(1, 40_000_000, polarity=1, phase=1),
|
||||
240,
|
||||
240,
|
||||
dc=Pin(0, Pin.OUT),
|
||||
@@ -68,7 +68,7 @@ class Display:
|
||||
self.tft.init()
|
||||
self.tft.fill(0)
|
||||
self.show_jpg(self.bootimg, 80, 80)
|
||||
self.message("WS2 v1.3.5 (20260202)")
|
||||
self.message("WS2 v1.3.7 (20260203)")
|
||||
|
||||
_print_mem()
|
||||
return True
|
||||
@@ -146,7 +146,7 @@ class Display:
|
||||
self.window("配置设备网络连接", tips, "portal ip: 192.168.4.1")
|
||||
|
||||
# 更新ui数据
|
||||
def update_ui(self, city=None, weather=None, advice=None, aqi=None, lunar=None, ip=None, envdat=None):
|
||||
def update_ui(self, city=None, weather=None, advice=None, aqi=None, lunar=None, envdat=None):
|
||||
self.ticks += 1
|
||||
if self.ui_type == 'default':
|
||||
# 中文的城市名称
|
||||
@@ -161,7 +161,6 @@ class Display:
|
||||
self.ui_data['weather'] = weather
|
||||
# 建议信息可能有很多条,需要轮换展示
|
||||
if advice is not None and advice != self.ui_data.get('advice'):
|
||||
if ip is not None: advice.append(ip)
|
||||
self.ui_data['advice'] = advice
|
||||
# AQI等级分成0-5级,分别对应优、良、中、差、污、恶
|
||||
if aqi is not None and aqi != self.ui_data.get('aqi'):
|
||||
@@ -186,7 +185,7 @@ class Display:
|
||||
if rh is not None and rh != self.ui_data.get('rh'):
|
||||
self.ui_data['rh'] = rh
|
||||
self.tft.fill_rect(110,179,40,16,0)
|
||||
self.tft.draw(self.vector_font, f' {str(rh)}%', 110,187,0xFFFF,0.5)
|
||||
self.tft.draw(self.vector_font, f' {str(rh)}', 110,187,0xFFFF,0.5)
|
||||
if pm is not None and pm != self.ui_data.get('pm'):
|
||||
self.ui_data['pm'] = pm
|
||||
self.tft.fill_rect(35,213,40,16,0)
|
||||
|
||||
@@ -82,12 +82,17 @@ target="_blank"
|
||||
download
|
||||
>查看城市ID列表</a
|
||||
>)
|
||||
</small></div><div class="form-group"><label class="form-label">自动熄屏时间</label
|
||||
><input
|
||||
type="time"
|
||||
id="standby-time-input"
|
||||
class="form-control"
|
||||
/><small class="text-muted">留空表示不自动熄屏</small></div><button class="btn btn-success" id="save-config-btn">
|
||||
</small></div><div class="form-group"><label class="form-label">自动熄屏时间</label><div style="display: flex;">
|
||||
<input type="time" id="standby-time-input" class="form-control" placeholder="熄屏时间" style="margin-right: 10px; flex: 1;"/>
|
||||
<input type="time" id="wakeup-time-input" class="form-control" placeholder="唤醒时间" style="flex: 1;"/></div>
|
||||
<small class="text-muted">分别设置熄屏和唤醒时间,留空则表示不自动熄屏</small></div>
|
||||
<div class="form-group"><label class="form-label">自定义配置</label>
|
||||
<div id="custom-config-container" class="custom-config-list">
|
||||
<div class="custom-config-row" style="display: flex; margin-bottom: 10px;">
|
||||
<input type="text" placeholder="配置项" class="form-control" id="custom-config-key" style="margin-right: 10px; flex: 1;">
|
||||
<input type="text" placeholder="配置值" class="form-control" id="custom-config-value" style="flex: 1;">
|
||||
</div></div></div>
|
||||
<button class="btn btn-success" id="save-config-btn">
|
||||
保存配置
|
||||
</button><h3 class="mt-3">当前配置</h3><table class="table" id="current-config"><tr><th>配置项</th><th>值</th></tr></table></div><div id="advanced-tab" class="tab-content"><h3>快捷操作</h3><div class="form-group"><button class="btn btn-outline" data-cmd="get_mac">
|
||||
MAC地址
|
||||
@@ -121,10 +126,10 @@ WS2是一款基于ESP8266的桌面气象站,能够实时显示天气信息、
|
||||
温度、湿度、PM2.5、气压、AQI
|
||||
</div></div></div><div class="col"><h3 class="mt-3">软件信息</h3><div class="list"><div class="list-item"><strong>固件:</strong
|
||||
><a
|
||||
href="https://iot.foresh.com/git/kicer/ws2"
|
||||
href="https://iot.foresh.com/git/kicer/ws2/releases"
|
||||
target="_blank"
|
||||
>
|
||||
ws2-firmware-v1.3.5-4M.bin </a
|
||||
ws2-firmware-xxx-4M.bin </a
|
||||
><span class="badge badge-success">开源</span></div><div class="list-item"><strong>协议:</strong> HTTP REST API
|
||||
</div><div class="list-item"><strong>更新频率:</strong> 每小时
|
||||
</div></div></div></div><h3 class="mt-3">开放源码</h3><a
|
||||
@@ -256,6 +261,7 @@ Math.round((memoryValue / maxMemory) * 100),
|
||||
);// 使用micro.js的图表功能创建仪表盘
|
||||
mw.chart.createGauge(mw.$("#memory-gauge"), percentage, 100, {
|
||||
label: "内存使用率",
|
||||
percent: true,
|
||||
color:
|
||||
percentage > 80
|
||||
? "#e74c3c"
|
||||
@@ -306,7 +312,10 @@ mw.val(mw.$("#city-input"), data.city);
|
||||
}
|
||||
if (data.standby_time) {
|
||||
mw.val(mw.$("#standby-time-input"), data.standby_time);
|
||||
}// 更新配置表
|
||||
}
|
||||
if (data.wakeup_time) {
|
||||
mw.val(mw.$("#wakeup-time-input"), data.wakeup_time);
|
||||
}
|
||||
updateConfigTable(data);
|
||||
} catch (error) {
|
||||
showMessage("获取配置失败: " + error.message, "error");
|
||||
@@ -343,16 +352,22 @@ mw.text(cell2, value);
|
||||
}// 保存配置
|
||||
async function saveConfig() {
|
||||
try {
|
||||
const userCfgKey = mw.val(mw.$("#custom-config-key"));
|
||||
const userCfgVal = mw.val(mw.$("#custom-config-value"));
|
||||
const city = mw.val(mw.$("#city-input"));
|
||||
const wakeupTime = mw.val(mw.$("#wakeup-time-input"));
|
||||
const standbyTime = mw.val(mw.$("#standby-time-input"));if (!city) {
|
||||
showMessage("城市名称不能为空", "error");
|
||||
return;
|
||||
}const configData = {
|
||||
city: city,
|
||||
cityid: encodeURIComponent(city),
|
||||
};// 只有当输入了熄屏时间时才添加到配置中
|
||||
if (standbyTime !== "") {
|
||||
};
|
||||
if (userCfgKey !== "") {
|
||||
configData[userCfgKey] = userCfgVal;
|
||||
}if (standbyTime !== "" && wakeupTime !== "") {
|
||||
configData.standby_time = standbyTime;
|
||||
configData.wakeup_time = wakeupTime;
|
||||
}const response = await mw.ajax.post(
|
||||
"/config/set",
|
||||
configData,
|
||||
|
||||
2
src/rom/www/js/micro.min.js
vendored
2
src/rom/www/js/micro.min.js
vendored
@@ -59,7 +59,7 @@ const MicroWeb={$:t=>document.querySelector(t),$$:t=>document.querySelectorAll(t
|
||||
top: ${o/2.5}px;
|
||||
font-size: ${o/7}px;
|
||||
font-weight: bold;">
|
||||
${e}
|
||||
${e}${i.percent?"%":""}
|
||||
<div style="font-size: ${o/10}px; color: #666;">
|
||||
${i.label||""}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user