ATmega16 + DS18B20 + LED + Matlab / Simulink = AR

Circuitry
Circuitry is extremely simple. The heart is the ATmega16. All DS18B20 sensors hang on the same pin (in my case, on the PB0 of the PORTB port). The pin itself is pulled to the supply voltage through a 4.7 kΩ resistor. The scheme is scalable. The picture is clickable.

All LEDs are connected to the PORTA port via terminating resistors. A gray polygon means that this LED is physically connected to the DS18B20. The reset pin is pulled high through a 10 kΩ resistor to avoid accidental reset due to interference. The microcontroller is clocked at 16 MHz quartz. Put as close to conclusions as possible. Load tanks are used internally. Configured via fuse. Separately output ICP connectors (for uploading firmware) and UART for "communication". Capacities C1 (electrolyte 10 μF) and C2 (ceramics 100 nF). Put as close as possible to the power pins of the microcontroller. Used to avoid accidental discharges during load transfer.
Circuitry assembly

What is a gray polygon?

Firmware + algorithm of work
The firmware was written in C in Atmel Studio 7 IDE. Sources are posted on GitHub . The code is maximally documented.
The project is divided into several levels of abstraction:
- Hardware - the lowest level, maximum binding to hardware. Work with the periphery of the microcontroller.
- Middleware is the middle ground between Hardware and Drivers. For example, the implementation of the 1-Wire protocol.
- Drivers - driver level. For example, working with the DS18B20 chip.
- Application - the highest level of abstraction. For example, receiving and transmitting temperature via UART.
A quick glance through the main function. First comes the table of ROM addresses. It is necessary that the address of the sensor physically connected to the zero LED (hanging on the PA0 of the PORTA port) be in the zero position, etc. To get ROM, there is a sendROMToUART function . You just need to remember that the sensor must be on the bus alone, otherwise there will be a conflict of addresses.
main
intmain(void){
constuint8_t ROM[][sizeof(ROM_T)] = /* ROM array */
{
{0x26, 0x00, 0x00, 0x04, 0x4B, 0x15, 0x89, 0x28}, // 0
{0x71, 0x00, 0x00, 0x04, 0x4A, 0xC0, 0x65, 0x28}, // 1
{0xA5, 0x00, 0x00, 0x04, 0x4A, 0xCB, 0xCE, 0x28}, // 2
{0x41, 0x00, 0x00, 0x04, 0x4A, 0xAC, 0x65, 0x28}, // 3
{0x22, 0x00, 0x00, 0x04, 0x4B, 0x06, 0x0D, 0x28}, // 4
{0x86, 0x00, 0x00, 0x04, 0x4A, 0xF6, 0x46, 0x28} // 5
};
uint8_t nDevices = sizeof(ROM) / sizeof(ROM_T); /* Number of DS18B20 devices */
initUART(MYUBRR); /* Initialization of UART with appropriate baudrate */
initTimer0(); /* Initialization of Timer/counter0 */
initLED(nDevices); /* Initialization of LEDs */
{ /* DS18B20s initialization */uint8_t nDevices = sizeof(ROM) / sizeof(ROM_T); /* Number of DS18B20 devices */
ROM_T *pROM = (ROM_T *)&ROM; /* Pointer to ROM array */
initDQ(); /* Initialization of DQ pin */while (nDevices--) /* For all DS18B20 */
initDS18B20(pROM++, RESOLUTION_11BIT); /* Initialization of DS18B20 with appropriate resolution */
}
sei(); /* Global enable interrupts */while (1) /* Infinite loop */
{
sendTemperatureToUART((ROM_T *)&ROM, nDevices); /* Execute function routine */
}
}
Next is the initialization of the peripherals and the DS-ok themselves with the appropriate resolution. The temperature sampling period depends on it. For 11 bits, this is 375 ms. In an infinite loop, the program continuously reads the temperature from each sensor and sends it via UART.
Work with LEDs is based on interrupts. By UART, the LED ID comes 2 times in a row on an even and odd frame. At the first, the LED lights up. The timer extinguishes after a certain time (in my case, 15 ms). The second time is simply ignored. The timer is configured in CTC mode so that an interrupt occurs once every 1 ms.
code
volatileuint8_t ledID = 0; /* Current ledID value */volatileuint8_t ledID_prev = 255; /* Previous ledID value */volatileuint8_t duration = FLASH_DURATION; /* Flash duration value */
ISR(USART_RXC_vect) /* UART interrupt handler */
{
ledID = UDR; /* Assign ledID to receive via UART value */if (ledID != ledID_prev) /* If current ledID equal to previous value */
{
turnOnLED(ledID); /* Turn on the ledID LED */
timer0Start(); /* Start Timer0 */
ledID_prev = ledID; /* Previous ledID assign to current */
duration = FLASH_DURATION; /* Update LED flash duration */
}
}
ISR(TIMER0_COMP_vect) /* Timer0 compare interrupt handler */
{
if (--duration == 0) /* Decrement Duration value each 1ms and if it reach to 0 */
{
timer0Stop(); /* Stop Timer0 */
turnOffAllLED(); /* Turn off all LEDs */
timer0Clear(); /* Clear Timer0 counter register */
}
}
The time sensitive sections of the code, which are 1-Wire signals, are wrapped in an ATOMIC_BLOCK construct. All basic settings are made in global.h . UART operates at a speed of 250,000. Fast and error-free for 16 MHz quartz. The DS18B20 driver has the minimum functionality necessary for this project. The rest - see the code. There will be questions - ask, do not be shy. I would also like to remind you about the settings of the fuses. They need to set the clocking ability from the external quartz, otherwise it will be from the internal generator (and it is at a maximum of 8 MHz and not very stable). Well, program the CKOPT bit, otherwise quartz above 8 MHz will not start. I have High Fuse = 0xD9 , Low Fuse = 0xFF .
Simulink model + operation algorithm
Matlab R2015b version . In addition to the built-in library, Simulink mainly used the Computer Vision System Toolbox and Image Aquisition Toolbox . The entire model and related files are uploaded to GitHub . Below is a detailed description with illustrative examples. All pictures are clickable.
WebCamTemp Module

Img diff
Слева оригинал, справа — разностная картинка. По ней в дальнейшем ищутся координаты светодиодов (соответственно датчиков тоже). Лучше смотреть по кадрам, но видимо в ютубе такой возможности нет. Можно поставить скорость 0.25.
Img gray
Слева оригинал, справа — картинка, на которую впоследствии накладывается карта. После выхода в стабильный режим видно, что светодиоды не моргают. Сделано чтобы не напрягало.
Frame Rate Display displays the current FPS. All processing is in the block LEDs . We will consider it as follows.
LEDs module

Work example
Сверху слева — входящее разностное изображение. Снизу большое — статическое изображение с разрешением в два раза больше требуемого. Бегающий прямоугольник — область которая вырезается с нужным разрешением. Вверху справа — то что имеем на выходе. Лучше смотреть на скорости 0.25.
Blocks of memory are circled in green. Their purpose is to store the coordinates and Gaussians for each LED. We consider in more detail below. The To color block is intended for building a temperature distribution and a color map. Also will be discussed below. Signal composition unit Compositing blends the two images Image1 and Image2 in the following manner:

The Insert Text block imposes formatted text (in this case, temperature) on the image. Accepts n variables and coordinates in the [XY] format . You can select the font and its size. The blocks inside the red rectangle implement the moving average algorithm . Transitions become less twitchy, which preserves nerves and pleases the eye.
Example
Слева оригинал, справа — скользящее среднее для 8-ми выборок. Когда температуры всех датчиков отличаются между собой на десятые доли градусов (парочку разрешений датчика) такие рывки имеют место быть.
Memory modules
Memory interference and Memory opacity store 2D Gaussian sets, Memory Pts - coordinates for each LED.
Memory interference and memory opacity
Эти два модуля идентичны. На вход Address подается номер ячейки куда запишется входной гауссиан. Приходит от счетчика. Совпадает с номером горящего светодиода. Блок Delay модуля LEDs служит для дополнительной синхронизации (пока приходит картинка, счетчик уже успевает оттикать). Так что все у нас синхронизировано. Сигнал Enable разрешает запись. Имеет истинное значение, если значение максимума выше порога (смотрите блок Maximum и Threshold модуля LEDs). Если значение ложно — содержание ячейки не меняется. На выходе все склеивается по третьему измерению. Получается такой себе бутерброд размера [H W n], где HxW — разрешение веб-камеры, ну а n — количество датчиков/светодиодов.

Memory pts
To color module



m file
It is executed at the beginning, before the simulation, introducing variables into the Workspace.
code
H = 480; % Height of image
W = 640; % Width of image
minT = 20; % Min temperature
maxT = 25; % Max temperature
sigmaInt = 40; % Sigma interference
sigmaOp = 80; % Sigma opacity
gain = 1.0; % Gain value
T = 0.3; % Threshold value
nAvr = 8; % number of means% ------------------------------------------------------
[M,N] = meshgrid(-W:W, -H:H); % Meshgrid function
R = sqrt(M.^2 + N.^2); % Distance from the center
Int = normpdf(R, 0, sigmaInt); % 2D gaussian for interference
Op = normpdf(R, 0, sigmaOp); % 2D gaussian for opacity
Int = Int/max(max(Int)); % Normalization of interference gaussian
Op = Op/max(max(Op)); % Normalization of opacity gaussian
clear M N R sigmaInt sigmaOp % Delete unused variables from memory
load('MyColormaps','mycmap'); % Load colormap
It contains the main control variables among which:
- H - video resolution in height.
- W - video resolution in width.
- minT is the minimum absolute temperature.
- maxT is the maximum absolute temperature.
- sigmaInt - Sigma Gaussian Interference.
- sigmaOp - Sigma Gaussian Opacity.
- gain - the maximum value of Factor.
- T is the threshold for avoiding errors.
- nAvr is the number of averages for the moving average.
H and W must match the current in the WebCamera block. minT and maxT affect the color map in absolute temperature mode. T is set from 0 to 1. Sometimes the COM port and webcam are out of sync. The phase of the differential image can change by 180 °. And where there should be a maximum - there is a minimum. And the system can choose an arbitrary coordinate that does not correspond to reality. For this, there is a threshold system. nAvr is the number of averages in the moving average. The larger it is, the smoother the transitions, but relevance is lost (a time shift appears). To understand the effect of the remaining variables, one cannot understand without illustrative examples.
SigmaInt effect
При
картинка вырождается в некое подобие разбиения Вороного. При
вся карта будет одного цвета соответствующего средней, от всех датчиков, температуре. При увеличении — границы как бы размываются.


SigmaOp effect
Устанавливает скорость спада прозрачности от расстояния.
Gain effect
По-сути устанавливает прозрачность карты. Само значение соответствует «прозрачности» самого «не прозрачного» пикселя карты.
Experiments
The following are some experiments.
Open window
Sensors are scattered on the bed by the window. The window opens and closes after a while. A good example of the difference between the relative (left) and absolute (right) modes. Using the first, it is convenient to consider the distribution, and the second, how the cold is distributed or heat is restored.
Windowsill
Sensors are located along the windowsill. The window opens - the profile changes. The coldest and warmest zones are clearly visible.
Is it warmer on top?
They say that the top is warmer. This experiment is a complete confirmation of this. At the 10th second, the window opens, at the 30th - it closes.
Conclusion
Such a scheme will not replace a full-fledged thermal imager. But he is not able to see the spread of air masses. And at a price this design is incomparably lower. You can use a different color map in it. You can take other functions instead of Gaussians. You can even change the laws of construction. Or rewrite to OpenCV + QT for speed and convenience. But that is what I intended - I have achieved. Just Just For Fun .