前几天给大家简单介绍了一下如何使用树莓派搭建一个RTMP直播服务,并可以通过vlc等工具观看。这几天,我又改进了一下整个系统,现在可以通过网页观看直播,并通过MQTT服务控制一个Arduino发送红外信号,控制机顶盒。
现在,整套系统有这么几个东西。
- 机顶盒
- 通过网络接受直播信号
- 采集卡
- 采集机顶盒信号,并推送给PC
- PC
- 接受来自采集卡的信号,并推送给RTMP直播服务
- 树莓派
- RTMP服务,接收并转发视频信号
- MMQT服务,通过网络发送信息给Arduino
- PHP服务,通过网页收看直播,发送MMQT信息
- Ardunio
- 接收MMQT的信息,并根据信息发送红外信号控制机顶盒
机顶盒,采集卡,PC方面的事情就不详细说了,今天主要说一下树莓派和Ardunio的东西。
树莓派方面,还是给大家写了一个script,大家修改后可以直接运行。我也加了一些comment在里面。注意替换<your ip>, <your port>, <your service>之类的位置,运行之前记得搜一下有没有“<your”开头的地方,还有没有没有替换的
#!/bin/bash
# Raspberry Pi 5 RTMP Streaming Server Setup Script
# Installs NGINX with RTMP, configures HLS, and sets up systemd service
# Installs PHP and MMQT service
set -e
echo "🚀 Updating system..."
apt update && apt upgrade -y
echo "📦 Installing dependencies..."
apt install -y build-essential libpcre3 libpcre3-dev libssl-dev zlib1g-dev git wget
echo "📦 Installing mosquitto and mosquitto-clients..."
apt install -y mosquitto mosquitto-clients
echo "🔁 Enabling and starting mosquitto service..."
systemctl enable mosquitto
systemctl start mosquitto
echo "⚙️ Configuring Mosquitto for WebSockets..."
MOSQ_CONF="/etc/mosquitto/mosquitto.conf"
# Backup original config
cp $MOSQ_CONF ${MOSQ_CONF}.bak
cat <<EOF > $MOSQ_CONF
# Place your local configuration in /etc/mosquitto/conf.d/
#
# A full description of the configuration file is at
# /usr/share/doc/mosquitto/examples/mosquitto.conf.example
pid_file /run/mosquitto/mosquitto.pid
persistence true
persistence_location /var/lib/mosquitto/
log_dest file /var/log/mosquitto/mosquitto.log
include_dir /etc/mosquitto/conf.d
# MQTT listener
listener 1883
protocol mqtt
# WebSocket listener
listener 9001
protocol websockets
allow_anonymous true
# Optional authentication (uncomment below to enable)
# allow_anonymous false
# password_file /etc/mosquitto/passwd
EOF
echo "🔁 Restarting Mosquitto..."
systemctl restart mosquitto
echo "📦 Installing PHP and FPM..."
apt install -y php php-fpm php-cli
echo "✅ Ensuring PHP-FPM is running..."
# Double check the version of php
systemctl enable php8.2-fpm
systemctl start php8.2-fpm
echo "🌐 Downloading NGINX and RTMP module..."
cd /opt
git clone https://github.com/arut/nginx-rtmp-module.git
wget http://nginx.org/download/nginx-1.27.5.tar.gz
tar -zxvf nginx-1.27.5.tar.gz
cd nginx-1.27.5
echo "⚙️ Building NGINX with RTMP module..."
./configure --with-http_ssl_module --add-module=../nginx-rtmp-module
make -j$(nproc)
make install
echo "📝 Writing NGINX config with RTMP + HLS..."
cat > /usr/local/nginx/conf/nginx.conf <<'EOF'
user www-data;
worker_processes 1;
events {
worker_connections 1024;
}
http {
sendfile off;
tcp_nopush on;
directio 512;
server {
listen <your port>;
root html;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.2-fpm.sock; # Update based on your PHP version
}
location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /tmp;
add_header Cache-Control no-cache;
add_header 'Access-Control-Allow-Origin' '*';
}
}
}
rtmp {
server {
listen 1935;
chunk_size 4096;
application <your service> {
live on;
record off;
hls on;
hls_path /tmp/hls;
hls_fragment 3s;
hls_playlist_length 60s;
}
}
}
EOF
echo "📁 Creating HLS output directory..."
mkdir -p /tmp/hls
echo "🔧 Creating systemd service for NGINX RTMP..."
cat > /etc/systemd/system/nginx-rtmp.service <<EOF
[Unit]
Description=NGINX RTMP Streaming Server
After=network.target
[Service]
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s stop
PIDFile=/usr/local/nginx/logs/nginx.pid
Type=forking
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
echo "🔁 Enabling and starting NGINX service..."
systemctl daemon-reexec
systemctl daemon-reload
systemctl enable nginx-rtmp
systemctl start nginx-rtmp
echo "📄 Creating MQTT PHP script..."
cat > /usr/local/nginx/html/send.php <<'EOF'
<?php
// Simple security token (optional)
$secret = $_POST['key'] ?? '';
if ($secret !== '<your key2>') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized']);
exit;
}
$msg = $_POST['msg'] ?? '';
// Publish to Mosquitto
$cmd = escapeshellcmd("mosquitto_pub -h localhost -t 'tv/remote' -m '$msg'");
exec($cmd, $output, $retval);
echo json_encode([
'status' => $retval === 0 ? 'sent' : 'error',
'msg' => $msg
]);
?>
EOF
cat > /usr/local/nginx/html/index.php <<'EOF'
<?php
$key = $_GET['key'] ?? '';
if ($key !== '<your key1>') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized']);
exit;
}
$channels = [
'体育频道' => [
['msg' => 's00720', 'name' => '纬来体育'],
['msg' => 's00721', 'name' => '纬来体育 备用'],
['msg' => 's01420', 'name' => 'ELTA体育1'],
['msg' => 's01421', 'name' => 'ELTA体育1 备用'],
['msg' => 's01430', 'name' => 'ELTA体育2'],
['msg' => 's01431', 'name' => 'ELTA体育2 备用'],
['msg' => 's01440', 'name' => 'ELTA体育3'],
['msg' => 's01441', 'name' => 'ELTA体育3 备用1'],
['msg' => 's01442', 'name' => 'ELTA体育3 备用2'],
['msg' => 's01450', 'name' => 'ELTA体育4'],
['msg' => 's01451', 'name' => 'ELTA体育4 备用'],
],
'Reboot' => [
['msg' => 'r', 'name' => 'Reboot TV Box'],
['msg' => 'a', 'name' => 'Reboot TV App']
]
];
$secretKey = '<your key2>';
$streamKey = '<your stream key>';
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TV Streaming Panel</title>
<style>
body {
margin: 0;
font-family: "Segoe UI", sans-serif;
background: #1c1c1c;
color: white;
display: flex;
flex-direction: row;
height: 100vh;
}
#player-container {
flex: 1;
background: black;
display: flex;
align-items: center;
justify-content: center;
}
video {
width: 100%;
height: 100%;
background: black;
}
#sidebar {
width: 300px;
background: #2b2b2b;
border-left: 2px solid #444;
overflow-y: auto;
padding: 20px;
box-shadow: -2px 0 10px rgba(0,0,0,0.5);
transition: width 0.3s ease;
}
h2 {
font-size: 20px;
color: #00bfff;
margin: 0 0 10px;
}
.category {
margin-bottom: 10px;
}
.category-header {
font-size: 16px;
font-weight: bold;
cursor: pointer;
padding: 8px;
background-color: #333;
border-radius: 4px;
user-select: none;
}
.category-header .arrow {
display: inline-block;
width: 1.2em;
}
.channel-list {
display: none;
margin-top: 5px;
margin-left: 10px;
}
.channel-btn {
display: block;
width: 100%;
margin: 6px 0;
padding: 10px;
background: #00bfff;
color: #fff;
border: none;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
text-align: left;
}
.channel-btn:hover {
background: #0099cc;
}
.status {
margin-top: 20px;
font-size: 14px;
color: #ccc;
}
/* 📱 Responsive styles */
@media screen and (max-width: 768px) {
body {
flex-direction: column;
}
#sidebar {
width: 100%;
border-left: none;
border-top: 2px solid #444;
padding: 10px;
}
h2 {
font-size: 18px;
}
.channel-btn {
font-size: 16px;
padding: 12px;
}
.category-header {
font-size: 18px;
padding: 10px;
}
}
</style>
</head>
<body>
<div id="player-container">
<video id="video" controls autoplay muted></video>
</div>
<div id="sidebar">
<h2>Channel List</h2>
<?php foreach ($channels as $category => $items): ?>
<div class="category">
<div class="category-header" onclick="toggleCategory(this)">
<span class="arrow">▶</span> <?php echo htmlspecialchars($category); ?>
</div>
<div class="channel-list">
<?php foreach ($items as $channel): ?>
<button class="channel-btn" onclick="changeChannel('<?php echo $channel['msg']; ?>')">
<?php echo htmlspecialchars($channel['name']); ?>
</button>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
<div class="status" id="status">Status: Ready</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
const streamKey = "<?php echo $streamKey; ?>";
const secretKey = "<?php echo $secretKey; ?>";
const video = document.getElementById('video');
const statusBox = document.getElementById('status');
const src = 'http://<your ip>:<your port>/hls/' + streamKey + '.m3u8';
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(src);
hls.attachMedia(video);
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = src;
}
function changeChannel(msg) {
statusBox.textContent = "Sending msg: " + msg + "...";
fetch('send.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
msg: msg,
key: secretKey
})
})
.then(res => res.json())
.then(data => {
if (data.status === 'sent') {
statusBox.textContent = "✅ Msg sent: " + msg;
} else {
statusBox.textContent = "❌ Failed: " + (data.error || 'Unknown error');
}
})
.catch(err => {
statusBox.textContent = "❌ Network error: " + err.message;
});
}
function toggleCategory(header) {
const list = header.nextElementSibling;
const arrow = header.querySelector('.arrow');
const open = list.style.display === 'block';
list.style.display = open ? 'none' : 'block';
arrow.textContent = open ? '▶' : '▼';
}
// Auto-expand the first category on load
document.addEventListener("DOMContentLoaded", () => {
const firstHeader = document.querySelector(".category-header");
if (firstHeader) toggleCategory(firstHeader);
});
</script>
</body>
</html>
EOF
echo ""
echo "✅ All done!"
echo ""
echo "🎥 Set OBS to stream to: rtmp://<your ip>/<your service>/<your stream key>"
echo "📺 Watch via VLC or player: rtmp://<your ip>/<your service>/<your stream key>"
echo "🌐 Or in browser (HLS): http://<your ip>:<your port>/hls/<your stream key>.m3u8"
echo "✅ Setup complete. Visit http://<your ip>:<your port>/index.php?key=<your key1> to test."
总体来讲,这个script会安装nginx,并且设置php,rtmp,mmqt服务。并通过一个简单的网页实现从网页上观看直播信号,并通过mmqt发送一些信息出去。不过网页的安全性不是很高,基本授权是通过get发送一个密码,并检查这个强密码和script里hard code的密码一样不一样。另外一个事就是,因为网页端使用的HLS服务,会有大概30秒的延迟。如果用VLC直接观看rtmp信号的话,延迟会小很多。
Ardunio方面,要做两件事,第一件事是通过IR receiver来学习遥控器的信号。
#include <IRremote.h>
void setup() {
Serial.begin(9600);
IrReceiver.begin(8, ENABLE_LED_FEEDBACK); // Receiver connected to pin 8
Serial.println("Ready to receive IR signal...");
}
void loop() {
if (IrReceiver.decode()) {
// Print full raw dump for replay later
IrReceiver.printIRResultShort(&Serial);
IrReceiver.printIRSendUsage(&Serial);
Serial.println();
IrReceiver.resume(); // Ready to receive the next
}
}
首先,我是把接收器安装在了Ardunio的8号接口,这时,就可以用这个接收器看遥控器发出来的信号是什么了。比如,当我按确认键,就会收到一下信息
Protocol=NEC Address=0xFF40 Command=0xD Raw-Data=0xF20DFF40 32 bits LSB first Gap=1940000us Duration=68650us
Send with: IrSender.sendNEC(0xFF40, 0xD, <numberOfRepeats>);
这时我们就知道,可以发送什么样的信号了。当学习完所有的需要按的信号后,就可以进入下一步。简单来说,就是连接WIFI(我用的Arduino 4 withi wifi),然后连接MMQT的服务(搭设在树莓派上的)。通过MMQT接收到什么信息,就解析出来,并执行相应的操作。比如我这里,s开头的信息,表示要选台,他会输入台号,并执行一些其他的操作。如果收到r,表示重启机顶盒,他会模拟遥控器的几次操作。这边可能就不能直接抄了,需要你自己修改一下。另外,当发送什么按键的时候,Arduino的灯就会亮起一个相应的数字或者按键,方便我知道发送的啥信号。
#include "Arduino_LED_Matrix.h"
#include <IRremote.h>
#include <PubSubClient.h>
#include "WiFiS3.h"
#include "secrets.h"
ArduinoLEDMatrix matrix;
char ssid[] = <your wifi ssid>; // your network SSID (name)
char pass[] = <your wifi pwd>; // your network password (use for WPA)
int status = WL_IDLE_STATUS;
// MQTT broker settings
const char* mqtt_server = "<your ip>"; // your Raspberry Pi IP address
const int mqtt_port = 1883;
const char* mqtt_topic = "tv/remote";
WiFiClient wifiClient;
PubSubClient client(wifiClient);
void setup() {
Serial.begin(9600); // initialize serial communication
// check for the WiFi module:
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Communication with WiFi module failed!");
// don't continue
while (true);
}
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
Serial.println("Please upgrade the firmware");
}
// attempt to connect to WiFi network:
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to Network named: ");
Serial.println(ssid); // print the network name (SSID);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
printWifiStatus(); // you're connected now, so print out the status
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
matrix.begin();
IrSender.begin(7); // Transmitter LED connected to pin 7
}
void loop() {
// Reconnect Wi-Fi if disconnected
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi disconnected. Attempting reconnection...");
while (WiFi.status() != WL_CONNECTED) {
status = WiFi.begin(ssid, pass);
delay(10000); // Wait for connection
}
printWifiStatus(); // Print new status once connected
}
// Reconnect MQTT if needed
if (!client.connected()) {
reconnect();
}
client.loop();
}
void callback(char* topic, byte* message, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("]: ");
for (int i = 0; i < length; i++) {
Serial.print((char)message[i]);
}
Serial.println();
execute(String((char*)message));
}
void reconnect() {
unsigned long startAttemptTime = millis();
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect("arduinoClient")) {
Serial.println("connected");
client.subscribe(mqtt_topic);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
delay(2000);
// Timeout after 30 seconds
if (millis() - startAttemptTime > 30000) {
Serial.println("MQTT reconnect timeout");
break;
}
}
}
}
void execute(String message) {
char c = message[0];
int i;
int j;
int num;
switch(c) {
case 's': // channel selection
for (i = 1; i < message.length(); i++) {
c = message[i];
if (i < 5) { // channel number, 4 digits
sendSignal(c);
delay(500);
}
if (i == 5) { // process the last digit
sendSignal('o'); // press ok
num = c - '0'; // convert char to int
delay(3000);
for (j = 0; j < num; j++) {
sendSignal('d'); // press 'down' based on the last digit
delay(500);
}
if (num > 0) { // if 'down' is pressed, press 'ok'
sendSignal('o');
} else {
sendSignal('b'); // close menu
}
}
}
break;
case 'r': // restart tv box
sendSignal('p'); // open power menu
delay(1000);
sendSignal('d'); // press down
delay(1000);
sendSignal('d'); // select reboot
delay(1000);
sendSignal('o'); // reboot
delay(45000);
sendSignal('o');
delay(2000);
sendSignal('r');
delay(1000);
sendSignal('r');
delay(1000);
sendSignal('o'); // select tv app
delay(1000);
sendSignal('o'); // enter tv app
delay(10000);
sendSignal('b'); // enter tv
break;
case 'a': // restart tv app
sendSignal('b'); // back
delay(1000);
sendSignal('u'); // select quit
delay(1000);
sendSignal('o'); // confirm quit
delay(3000);
sendSignal('o'); // select tv
delay(1000);
sendSignal('o'); // enter tv
delay(10000);
sendSignal('b'); // enter tv
break;
}
delay(1000);
clearScreen();
}
void sendSignal(char c) {
switch(c) {
case '0':
show0();
IrSender.sendNEC(0xFF40, 0x0, 1);
break;
case '1':
show1();
IrSender.sendNEC(0xFF40, 0x1, 1);
break;
case '2':
show2();
IrSender.sendNEC(0xFF40, 0x2, 1);
break;
case '3':
show3();
IrSender.sendNEC(0xFF40, 0x3, 1);
break;
case '4':
show4();
IrSender.sendNEC(0xFF40, 0x4, 1);
break;
case '5':
show5();
IrSender.sendNEC(0xFF40, 0x5, 1);
break;
case '6':
show6();
IrSender.sendNEC(0xFF40, 0x6, 1);
break;
case '7':
show7();
IrSender.sendNEC(0xFF40, 0x7, 1);
break;
case '8':
show8();
IrSender.sendNEC(0xFF40, 0x8, 1);
break;
case '9':
show9();
IrSender.sendNEC(0xFF40, 0x9, 1);
break;
case 'o':
showOK();
IrSender.sendNEC(0xFF40, 0xD, 1);
break;
case 'b':
showBack();
IrSender.sendNEC(0xFF40, 0x42, 1);
break;
case 'p':
showPower();
IrSender.sendNEC(0xFF40, 0x4D, 1);
break;
case 'u':
showUp();
IrSender.sendNEC(0xFF40, 0xB, 1);
break;
case 'd':
showDown();
IrSender.sendNEC(0xFF40, 0xE, 1);
break;
case 'l':
showLeft();
IrSender.sendNEC(0xFF40, 0x10, 1);
break;
case 'r':
showRight();
IrSender.sendNEC(0xFF40, 0x11, 1);
break;
}
}
void show0() {
byte frame[8][12] = {
{ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void show1() {
byte frame[8][12] = {
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void show2() {
byte frame[8][12] = {
{ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void show3() {
byte frame[8][12] = {
{ 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void show4() {
byte frame[8][12] = {
{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void show5() {
byte frame[8][12] = {
{ 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void show6() {
byte frame[8][12] = {
{ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void show7() {
byte frame[8][12] = {
{ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void show8() {
byte frame[8][12] = {
{ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void show9() {
byte frame[8][12] = {
{ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void showOK() {
byte frame[8][12] = {
{ 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void showBack() {
byte frame[8][12] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void showPower() {
byte frame[8][12] = {
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 },
{ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void showUp() {
byte frame[8][12] = {
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void showDown() {
byte frame[8][12] = {
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void showLeft() {
byte frame[8][12] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void showRight() {
byte frame[8][12] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void clearScreen() {
byte frame[8][12] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
matrix.renderBitmap(frame, 8, 12);
}
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your board's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
然后就没然后……不过下一步可能看看如何让网页的声音可以在后台播放。我现在用iOS和chrome,如果把直播画面放到画中画,就可以在后台继续听直播。但是如果不用画中画,当我把屏幕关闭的时候,声音就会中断了,体验挺不好的,反正……