深度优先

这个家伙好懒,除了文章什么都没留下

0%

【.Net Core】树莓派通过Signalr上传传感器数据

树莓派4B 采集传感器的温湿度 通过.Net Core 3.1的控制台程序将数据上传到服务器端,程序部署在docker中

客户端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
using Iot.Device.DHTxx;
using Microsoft.AspNetCore.SignalR.Client;
using NLog;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace IotClient
{
class Program
{
private static HubConnection _connection;
private static readonly Logger logger = LogManager.GetLogger("IotClient");
private static Guid DeviceGuid = Guid.Parse("886dae3f-1e08-41ce-9531-e2d326ee693b");
static async Task Main(string[] args)
{
logger.Debug("启动客户端");

_connection = new HubConnectionBuilder()
.WithUrl("http://ip:port/iotHub")
.WithAutomaticReconnect()
.Build();

_connection.On<string>("IotClientRsult", message => IotClientRsult(message));

try
{
await _connection.StartAsync();
logger.Info($"SignalR连接成功!");
}
catch (Exception ex)
{
logger.Fatal($"SignalR连接失败:{ex.Message}");
}
using Dht11 dht = new Dht11(26);

List<Attribute> attributes = new List<Attribute>()
{
new Attribute(){Identifier ="humidity", Data =new List<Data>()},
new Attribute(){Identifier ="temperature", Data =new List<Data>()},
};
UploadData uploadData = new UploadData()
{
DeviceGuid = DeviceGuid,
Attributes = attributes,
Timestamp = DateTimeHelper.GetTimeStamp()
};
while (true)
{
try
{
var humidityOrNan = dht.Humidity.ToString("0.0");
var temperatureOrNan = dht.Temperature.Celsius.ToString("0.0");

if (dht.IsLastReadSuccessful)
{
var temperature = decimal.Parse(temperatureOrNan);
var humidity = decimal.Parse(humidityOrNan);

Console.WriteLine($"Temperature: {temperature} °C, Humidity: { humidity} % ");
if (_connection.State == HubConnectionState.Connected)
{
try
{
uploadData.Timestamp = DateTimeHelper.GetTimeStamp();
uploadData.Attributes[0].Data.Clear();
uploadData.Attributes[0].Data.Add(new Data
{
Value = humidity,
Timestamp = DateTimeHelper.GetTimeStamp(),
});
uploadData.Attributes[1].Data.Clear();
uploadData.Attributes[1].Data.Add(new Data
{
Value = temperature,
Timestamp = DateTimeHelper.GetTimeStamp(),
});
await _connection.InvokeAsync("IotClientUpload", uploadData);
}
catch (Exception ex)
{
logger.Fatal($"SignalR发送数据失败:{ex.Message}");
}
}
}
}
catch (Exception ex)
{
logger.Fatal($"出现错误:{ex.Message} {ex.StackTrace}");
}
await Task.Delay(1000 * 5);
}
}

private static void IotClientRsult(string message)
{
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}{message}");
}
}
}

数据对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class UploadData
{
public List<Attribute> Attributes { get; set; }
public int Timestamp { get; set; }
public Guid DeviceGuid { get; set; }
}

public class Attribute
{
public string Identifier { get; set; }
public List<Data> Data { get; set; }
}

public class Data
{
public decimal Value { get; set; }
public int Timestamp { get; set; }
}

服务器端,SignalR:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
using demo.Database;
using demo.Entities;
using demo.SignalR.IotClient;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;

namespace demo.SignalR
{
public class IotHub: Hub
{
private readonly MyContext _myContext;
private readonly ILoggerFactory _loggerFactory;
public IotHub(MyContext myContext,
ILoggerFactory loggerFactory)
{
_myContext = myContext;
_loggerFactory = loggerFactory;
}
public async Task IotClientUpload(UploadData uploadData)
{
var logger = _loggerFactory.CreateLogger(typeof(IotHub));
logger.LogError(Newtonsoft.Json.JsonConvert.SerializeObject(uploadData));
var connectionId = Context.ConnectionId;
if (uploadData is null)
{
await IotClientRsult(connectionId, $"上传内容为空!");
return;
}
if (uploadData.Attributes != null)
{
foreach (var attribute in uploadData.Attributes)
{
foreach (var data in attribute.Data)
{
_myContext.IotData.Add(new IotData()
{
Guid = Guid.NewGuid(),
Timestamp = data.Timestamp,
Value = data.Value,
Identifier = attribute.Identifier,
DeviceGuid = uploadData.DeviceGuid
});
}
}
}
int count = await _myContext.SaveChangesAsync();

await IotClientRsult(connectionId, $"成功上传 {count} 条数据!");
}
public async Task IotClientRsult(string connectionId, string message)
{
await Clients.Client(connectionId).SendAsync("IotClientRsult", message);
}
}
}

Dockerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim-arm32v7 AS base
WORKDIR /app

ENV LANG C.UTF-8

# 时区设置
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone

ENTRYPOINT ["dotnet", "IotClient.dll"]

Build:

1
2
3
docker build -t iot-client-image .

docker run -d --name iot-client -v /opt/netcore/IotClient:/app --privileged iot-client-image

Docker访问Raspberry Pi GPIO引脚,需要加 –privileged 参数

效果图:


后续再根据数据做异常预警

树莓派 安装 .Net Core 3.1:

1
2
3
4
5
6
7
8
9
wget https://download.visualstudio.microsoft.com/download/pr/8c839c0e-a5ae-4254-8d8b-c012528fe601/c147e26bad68f97eacc287a71e01331d/aspnetcore-runtime-3.1.0-linux-arm.tar.gz

wget https://download.visualstudio.microsoft.com/download/pr/67766a96-eb8c-4cd2-bca4-ea63d2cc115c/7bf13840aa2ed88793b7315d5e0d74e6/dotnet-sdk-3.1.100-linux-arm.tar.gz

mkdir -p $HOME/pi/dotnet && tar zxf dotnet-sdk-3.1.100-linux-arm.tar.gz -C $HOME/pi/dotnet
tar zxf aspnetcore-runtime-3.1.0-linux-arm.tar.gz -C $HOME/pi/dotnet

export DOTNET_ROOT=$HOME/pi/dotnet
export PATH=$PATH:$HOME/pi/dotnet

临时变量永久生效:

1
vi /etc/profile

添加:

1
2
export DOTNET_ROOT=$HOME/pi/dotnet 
export PATH=$PATH:$HOME/pi/dotnet