Merge branch 'master' into abo_stats_pages_auto_swap
[inav.git] / src / main / sensors / temperature.c
blob5cb146cf16495af48042143e67031c59bb09b434
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
18 #include "stdbool.h"
19 #include "stdint.h"
20 #include <string.h>
22 #include "platform.h"
24 #include "common/maths.h"
25 #include "common/printf.h"
27 #include "config/parameter_group.h"
28 #include "config/parameter_group_ids.h"
30 #include "drivers/1-wire.h"
31 #include "drivers/temperature/temperature.h"
32 #include "drivers/temperature/lm75.h"
33 #include "drivers/temperature/ds18b20.h"
35 #include "fc/runtime_config.h"
37 #include "sensors/sensors.h"
38 #include "sensors/temperature.h"
39 #include "sensors/gyro.h"
40 #include "sensors/barometer.h"
42 #include "scheduler/protothreads.h"
45 PG_REGISTER_ARRAY(tempSensorConfig_t, MAX_TEMP_SENSORS, tempSensorConfig, PG_TEMP_SENSOR_CONFIG, 2);
47 #define MPU_TEMP_VALID_BIT 0
48 #define BARO_TEMP_VALID_BIT 1
49 #define MPU_TEMP_VALID (mpuBaroTempValid & (1 << MPU_TEMP_VALID_BIT))
50 #define BARO_TEMP_VALID (mpuBaroTempValid & (1 << BARO_TEMP_VALID_BIT))
52 static uint16_t mpuTemperature, baroTemperature;
53 static uint8_t mpuBaroTempValid = 0;
55 #ifdef USE_TEMPERATURE_SENSOR
56 static int16_t tempSensorValue[MAX_TEMP_SENSORS];
58 // Each bit corresponds to a sensor LSB = first sensor. 1 = valid value
59 static uint8_t sensorStatus[MAX_TEMP_SENSORS / 8 + (MAX_TEMP_SENSORS % 8 ? 1 : 0)];
61 #ifdef USE_TEMPERATURE_LM75
62 static temperatureDev_t lm75Dev[8];
63 #endif
65 #ifdef DS18B20_DRIVER_AVAILABLE
66 static owDev_t *owDev;
67 #endif
69 static void newSensorCheckAndEnter(uint8_t type, uint64_t addr)
71 int8_t foundConfigIndex = -1, firstFreeConfigSlot = -1;
73 // try to find sensor or free config slot
74 for (uint8_t configIndex = 0; configIndex < MAX_TEMP_SENSORS; ++configIndex) {
76 const tempSensorConfig_t *configSlot = tempSensorConfig(configIndex);
78 if ((configSlot->type == type) && (configSlot->address == addr)) {
79 foundConfigIndex = configIndex;
80 break;
83 if ((firstFreeConfigSlot == -1) && !configSlot->type) firstFreeConfigSlot = configIndex;
87 // if new sensor and have free config slot enter new sensor
88 if ((foundConfigIndex == -1) && (firstFreeConfigSlot != -1)) {
89 tempSensorConfig_t *configSlot = tempSensorConfigMutable(firstFreeConfigSlot);
90 configSlot->type = type;
91 configSlot->address = addr;
92 configSlot->osdSymbol = 0;
93 configSlot->label[0] = '\0';
94 configSlot->alarm_min = -200;
95 configSlot->alarm_max = 600;
99 void temperatureInit(void)
101 memset(sensorStatus, 0, sizeof(sensorStatus) * sizeof(*sensorStatus));
103 sensorsSet(SENSOR_TEMP);
105 #ifdef USE_TEMPERATURE_LM75
106 memset(lm75Dev, 0, sizeof(lm75Dev));
107 for (uint8_t lm75Addr = 0; lm75Addr < 8; ++lm75Addr) {
108 if (lm75Detect(lm75Dev + lm75Addr, lm75Addr))
109 newSensorCheckAndEnter(TEMP_SENSOR_LM75, lm75Addr);
111 #endif
113 #ifdef DS18B20_DRIVER_AVAILABLE
114 owDev = getOwDev();
115 if (owDev) {
116 uint64_t ds18b20_rom_table[MAX_TEMP_SENSORS];
117 uint8_t ds18b20_rom_count = MAX_TEMP_SENSORS;
118 ds18b20Enumerate(owDev, ds18b20_rom_table, &ds18b20_rom_count);
120 for (uint8_t rom_index = 0; rom_index < ds18b20_rom_count; ++rom_index)
121 newSensorCheckAndEnter(TEMP_SENSOR_DS18B20, ds18b20_rom_table[rom_index]);
123 #endif
125 // configure sensors
126 for (uint8_t configIndex = 0; configIndex < MAX_TEMP_SENSORS; ++configIndex) {
127 const tempSensorConfig_t *configSlot = tempSensorConfig(configIndex);
129 switch (configSlot->type) {
130 #ifdef DS18B20_DRIVER_AVAILABLE
131 case TEMP_SENSOR_DS18B20:
132 if (owDev) {
133 tempSensorValue[configIndex] = TEMPERATURE_INVALID_VALUE;
134 ds18b20Configure(owDev, configSlot->address, DS18B20_CONFIG_9BIT);
136 break;
137 #endif
139 default:;
144 static bool temperatureSensorValueIsValid(uint8_t temperatureUpdateSensorIndex)
146 uint8_t mask = 1 << (temperatureUpdateSensorIndex % 8);
147 uint8_t byteIndex = temperatureUpdateSensorIndex / 8;
148 return sensorStatus[byteIndex] & mask;
151 // returns decidegrees centigrade
152 bool getSensorTemperature(uint8_t temperatureUpdateSensorIndex, int16_t *temperature)
154 *temperature = tempSensorValue[temperatureUpdateSensorIndex];
155 return temperatureSensorValueIsValid(temperatureUpdateSensorIndex);
158 // Converts 64bit integer address to hex format
159 // hex_address must be at least 17 bytes long (16 chars + NULL)
160 void tempSensorAddressToString(uint64_t address, char *hex_address)
162 if (address < 8)
163 tfp_sprintf(hex_address, "%d", (int)address);
164 else {
165 uint32_t *address32 = (uint32_t *)&address;
166 tfp_sprintf(hex_address, "%08lx%08lx", address32[1], address32[0]);
170 // Converts address string in hex format to unsigned integer
171 // the hex_address parameter must be NULL or space terminated
172 bool tempSensorStringToAddress(const char *hex_address, uint64_t *address)
174 uint16_t char_count = 0;
175 *address = 0;
176 while (*hex_address && (*hex_address != ' ')) {
177 if (++char_count > 16) return false;
178 char byte = *hex_address++;
179 if (byte >= '0' && byte <= '9') byte = byte - '0';
180 else if (byte >= 'a' && byte <='f') byte = byte - 'a' + 10;
181 else if (byte >= 'A' && byte <='F') byte = byte - 'A' + 10;
182 else return false;
183 *address = (*address << 4) | (byte & 0xF);
185 return true;
187 #endif /* USE_TEMPERATURE_SENSOR */
189 #ifdef USE_TEMPERATURE_SENSOR
191 static uint8_t temperatureUpdateSensorIndex;
192 static bool temperatureUpdateValueValid;
194 #ifdef DS18B20_DRIVER_AVAILABLE
195 static uint8_t temperatureUpdateIndex;
196 static uint8_t temperatureUpdateBuf[9];
197 #endif
199 #endif /* defined(USE_TEMPERATURE_SENSOR) */
201 PROTOTHREAD(temperatureUpdate)
203 ptBegin(temperatureUpdate);
205 while (1) {
207 if (gyroReadTemperature()) {
208 mpuTemperature = gyroGetTemperature();
209 mpuBaroTempValid |= (1 << MPU_TEMP_VALID_BIT);
210 } else
211 mpuBaroTempValid &= ~(1 << MPU_TEMP_VALID_BIT);
213 #ifdef USE_BARO
214 if (sensors(SENSOR_BARO)) {
215 baroTemperature = baroGetTemperature();
216 mpuBaroTempValid |= (1 << BARO_TEMP_VALID_BIT);
217 } else
218 mpuBaroTempValid &= ~(1 << BARO_TEMP_VALID_BIT);
219 #endif
221 #ifdef USE_TEMPERATURE_SENSOR
223 temperatureUpdateSensorIndex = 0;
224 do {
225 const tempSensorConfig_t *configSlot = tempSensorConfig(temperatureUpdateSensorIndex);
226 temperatureUpdateValueValid = false;
228 #ifdef USE_TEMPERATURE_LM75
229 if (configSlot->type == TEMP_SENSOR_LM75) {
230 if (configSlot->address < 8) {
231 temperatureDev_t *dev = lm75Dev + configSlot->address;
232 if (dev->read && dev->read(dev, &tempSensorValue[temperatureUpdateSensorIndex])) temperatureUpdateValueValid = true;
235 #endif
237 #ifdef DS18B20_DRIVER_AVAILABLE
238 if ((configSlot->type == TEMP_SENSOR_DS18B20) && owDev) {
239 bool ack = owDev->owResetCommand(owDev);
240 if (!ack) goto temperatureUpdateError;
241 ptWait(owDev->owBusReady(owDev));
243 ack = owDev->owMatchRomCommand(owDev);
244 if (!ack) goto temperatureUpdateError;
245 ptWait(owDev->owBusReady(owDev));
247 temperatureUpdateIndex = 0;
248 do {
249 ack = owDev->owWriteByteCommand(owDev, ((uint8_t *)&tempSensorConfig(temperatureUpdateSensorIndex)->address)[temperatureUpdateIndex]);
250 if (!ack) goto temperatureUpdateError;
251 ptWait(owDev->owBusReady(owDev));
252 } while (++temperatureUpdateIndex < 8);
254 ack = ds18b20ReadScratchpadCommand(owDev);
255 if (!ack) goto temperatureUpdateError;
256 ptWait(owDev->owBusReady(owDev));
258 temperatureUpdateIndex = 0;
259 do {
260 ack = owDev->owReadByteCommand(owDev);
261 if (!ack) goto temperatureUpdateError;
262 ptWait(owDev->owBusReady(owDev));
263 ack = owDev->owReadByteResult(owDev, temperatureUpdateBuf + temperatureUpdateIndex);
264 if (!ack) goto temperatureUpdateError;
265 } while (++temperatureUpdateIndex < 9);
267 int16_t temperature;
268 if (ds18b20ReadTemperatureFromScratchPadBuf(temperatureUpdateBuf, &temperature)) {
269 if (temperatureSensorValueIsValid(temperatureUpdateSensorIndex) || (tempSensorValue[temperatureUpdateSensorIndex] == -1240)) {
270 tempSensorValue[temperatureUpdateSensorIndex] = temperature;
271 temperatureUpdateValueValid = true;
272 } else
273 tempSensorValue[temperatureUpdateSensorIndex] = -1240;
274 } else
275 tempSensorValue[temperatureUpdateSensorIndex] = TEMPERATURE_INVALID_VALUE;
278 temperatureUpdateError:;
279 #endif
281 uint8_t statusMask = 1 << (temperatureUpdateSensorIndex % 8);
282 uint8_t byteIndex = temperatureUpdateSensorIndex / 8;
283 if (temperatureUpdateValueValid)
284 sensorStatus[byteIndex] |= statusMask;
285 else
286 sensorStatus[byteIndex] &= ~statusMask;
288 ptYield();
290 } while (++temperatureUpdateSensorIndex < MAX_TEMP_SENSORS);
292 #ifdef DS18B20_DRIVER_AVAILABLE
293 if (owDev) {
294 bool ack = owDev->owResetCommand(owDev);
295 if (!ack) goto ds18b20StartConversionError;
296 ptWait(owDev->owBusReady(owDev));
298 ack = owDev->owSkipRomCommand(owDev);
299 if (!ack) goto ds18b20StartConversionError;
300 ptWait(owDev->owBusReady(owDev));
302 ds18b20StartConversionCommand(owDev);
305 ds18b20StartConversionError:;
306 #endif
308 #endif /* defined(USE_TEMPERATURE_SENSOR) */
310 ptDelayMs(100); // DS18B20 sensors take 94ms for a temperature conversion with 9bit resolution
314 ptEnd(0);
317 // returns decidegrees centigrade
318 bool getIMUTemperature(int16_t *temperature)
320 *temperature = mpuTemperature;
321 return MPU_TEMP_VALID;
324 // returns decidegrees centigrade
325 bool getBaroTemperature(int16_t *temperature)
327 *temperature = baroTemperature;
328 return BARO_TEMP_VALID;
331 void resetTempSensorConfig(void)
333 memset(tempSensorConfigMutable(0), 0, sizeof(tempSensorConfig_t) * MAX_TEMP_SENSORS);