1 //
2 // Copyright (c) 2010-2023 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Exceptions;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.I2C;
13 using Antmicro.Renode.Peripherals.Sensor;
14 using Antmicro.Renode.Utilities;
15 
16 namespace Antmicro.Renode.Peripherals.Sensors
17 {
18     public class SI70xx : II2CPeripheral, ITemperatureSensor, IHumiditySensor
19     {
SI70xx(Model model)20         public SI70xx(Model model)
21         {
22             this.model = model;
23             commands = new I2CCommandManager<Action<byte[]>>();
24             outputBuffer = new Queue<byte>();
25 
26             commands.RegisterCommand(MeasureHumidity, 0xE5);
27             commands.RegisterCommand(MeasureHumidity, 0xF5);
28             commands.RegisterCommand(MeasureTemperature, 0xE0);
29             commands.RegisterCommand(MeasureTemperature, 0xE3);
30             commands.RegisterCommand(MeasureTemperature, 0xF3);
31             commands.RegisterCommand(ReadElectronicId1stByte, 0xFA, 0xF);
32             commands.RegisterCommand(ResetOutputBuffer, 0xFE);
33             commands.RegisterCommand(ReadElectronicId2ndByte, 0xFC, 0xC9);
34 
35             Reset();
36         }
37 
Read(int count = 1)38         public byte[] Read(int count = 1)
39         {
40             var result = outputBuffer.ToArray();
41             this.Log(LogLevel.Noisy, "Reading {0} bytes from the device (asked for {1} bytes).", result.Length, count);
42             outputBuffer.Clear();
43             return result;
44         }
45 
Write(byte[] data)46         public void Write(byte[] data)
47         {
48             this.Log(LogLevel.Noisy, "Received {0} bytes: [{1}]", data.Length, string.Join(", ", data.Select(x => x.ToString())));
49             if(!commands.TryGetCommand(data, out var command))
50             {
51                 this.Log(LogLevel.Warning, "Unknown command: [{0}]. Ignoring the data.", string.Join(", ", data.Select(x => string.Format("0x{0:X}", x))));
52                 return;
53             }
54             command(data);
55         }
56 
FinishTransmission()57         public void FinishTransmission()
58         {
59         }
60 
Reset()61         public void Reset()
62         {
63             Temperature = 0;
64             Humidity = 0;
65             outputBuffer.Clear();
66         }
67 
68         public decimal Humidity
69         {
70             get
71             {
72                 return (humidity * 125) / 65536 - 6;
73             }
74             set
75             {
76                 if(MinHumidity > value || value > MaxHumidity)
77                 {
78                     throw new RecoverableException("The humidity value must be between {0} and {1}.".FormatWith(MinHumidity, MaxHumidity));
79                 }
80                 humidity = (value + 6) * 65536 / 125;
81             }
82         }
83 
84         public decimal Temperature
85         {
86             get
87             {
88                 return (temperature * 175.72m) / 65536 - 46.85m;
89             }
90             set
91             {
92                 if(MinTemperature > value || value > MaxTemperature)
93                 {
94                     throw new RecoverableException("The temperature value must be between {0} and {1}.".FormatWith(MinTemperature, MaxTemperature));
95                 }
96                 temperature = (value + 46.85m) * 65536 / 175.72m;
97             }
98         }
99 
MeasureHumidity(byte[] command)100         private void MeasureHumidity(byte[] command)
101         {
102             outputBuffer.Enqueue((byte)((uint)humidity >> 8));
103             outputBuffer.Enqueue((byte)((uint)humidity & 0xFF));
104         }
105 
MeasureTemperature(byte[] command)106         private void MeasureTemperature(byte[] command)
107         {
108             outputBuffer.Enqueue((byte)((uint)temperature >> 8));
109             outputBuffer.Enqueue((byte)((uint)temperature & 0xFF));
110         }
111 
ReadElectronicId1stByte(byte[] command)112         private void ReadElectronicId1stByte(byte[] command)
113         {
114             // 1st: SNA_3
115             // 2nd: CRC
116             // 3rd: SNA_2
117             // 4th: CRC
118             // ...
119             for(var i = 0; i < 8; i++)
120             {
121                 outputBuffer.Enqueue(0x0);
122             }
123         }
124 
ReadElectronicId2ndByte(byte[] command)125         private void ReadElectronicId2ndByte(byte[] command)
126         {
127             // 1st: SNB_3
128             // 2nd: SNB_2
129             // 3rd: CRC
130             // 4th: SNB_1
131             // 5th: SNB_0
132             // 6th: CRC
133             outputBuffer.Enqueue((byte)model);
134             for(var i = 0; i < 5; i++)
135             {
136                 outputBuffer.Enqueue(0x0);
137             }
138         }
139 
ResetOutputBuffer(byte[] command)140         private void ResetOutputBuffer(byte[] command)
141         {
142             outputBuffer.Clear();
143         }
144 
145         private decimal humidity;
146         private decimal temperature;
147 
148         private readonly Model model;
149         private readonly I2CCommandManager<Action<byte[]>> commands;
150         private readonly Queue<byte> outputBuffer;
151 
152         private const decimal MaxHumidity = 100;
153         private const decimal MinHumidity = 0;
154         private const decimal MaxTemperature = 85;
155         private const decimal MinTemperature = -40;
156 
157         public enum Model
158         {
159             SI7021 = 0x15,
160             SI7006 = 0x06
161         }
162     }
163 }
164