Комментарии 44
Коль уж с Arduino уходим на C/asm, возникает резонный вопрос: может уже "гулять на все" и с самой atmega уйти на что-нибудь специально созданное для низкого потребления? STM8L или STM32L например.
Коль уж с Arduino уходим на C/asm, возникает резонный вопрос: может уже «гулять на все» и с самой atmega уйти на что-нибудь специально созданное для низкого потребления? STM8L или STM32L например.
Спасибо, но Вы переоцениваете мои возможности.
Для меня программисты, работающие на низкоуровневых языках типа Ассемблера — небожители и, как оказалось, на Ардуино тоже можно решать не тривиальные задачи.
А причем тут асм. STM32 также програмируются на Си как и большинство мк в мире. Более того ARM Asm куда более сложен чем AVR, и имхо лучше без сильной необходимости в него не лезть.
Также в сети полно страшилок про то, что stm32 мегасложные.
Но это уже пару лет как не так, благодаря кодогенератору CubeMX все настройки можно за 10 минут выставить и контроллер заведётся. А потом к этому всему обращаться через HAL функции (почти как в ардуине).
Ну и на крайняк stm32 blue pill eсть в ардуине.
P. S. HAL при всей моще имеет тот же минус что и ардуина (хоть и гибче) — много универсального кода == много больше места. С другой стороны при 100-200К памяти нет так и страшно.
P. P. S. Тема использования Cuba и HAL холиварная, но если вы с ардуины на Си съехали то разберётесь.
Сударь, на счёт потребления атмеги и СТМ вы не правы, СТМ потребляет куда больше, но правда и вычислительные возможности лучше. Все зависит от применения и прямоты рук программиста и схемотехника)
Либо взять E-ink + Zigbee как на Xiaomi термометрах
Готовый USB брелок или модуль к малинке стоит порядка 2-х тысяч
Можно взять SOC на алишке за 500р и спаять самому
Зато сяоминые, акваровские и др. устройства работаю готовые
Экран E-ink 2.13 + Контроллер NRF52 < 2мкА потребления в режиме сна
Либо взять E-ink + Zigbee как на Xiaomi термометрах
Дисплей 2.13 будет маловат для комнатного термостата и метеостанции в одном. В списке желаний на Али уже несколько месяцев e-paper дисплей 3,7 дюйма.
NRF52832 уже в пути.
VelocidadAbsurda
Коль уж с Arduino уходим на C/asm, возникает резонный вопрос: может уже «гулять на все» и с самой atmega уйти на что-нибудь специально созданное для низкого потребления? STM8L или STM32L например.
В ATMega328PA нет ничего плохого. Это уже pico-power контроллер. У неё достаточно возможностей отключать периферию и уходить в глубокий сон с потреблением около 1мкА.
В данном случае, все равно большую часть потребления уходит на прием и передачу данных. Если датчик находится недалеко, метров 30 до базы, то я бы поставил лучше NRF24L01. У него низкое потребление и высокая скорость передачи. А это значит, что в активном состоянии его нужно держать очень короткое время.
Объем флеш-памяти, занимаемой в коде в Ардуино — 12968 байт, на С — 5954 байта и оценочно на Ассемблере не больше 200 байт.
Думаю, что все дело тут в функции println, которая притягивает библиотечную функцию printf. А в ней, в зависимости от настроек, может быть либо только самая базовая функциональность, либо дополнительные параметры форматирования, вещественные числа и т.п.
В любом случае, большой объем занятой флеш памяти не обязательно означает, что она вся будет исполняться. Чаще всего это просто мертвый груз.
Также переписывание с С на ассемблер не имеет почти никакого смысла. Зато имеет смысл порой избавиться от стандартной функции printf() и написать свою, если вы выводите, например, только целые числа.
Снизить энергопотребление в режиме сна можно, если питание внешних модулей коммутировать p-канальным полевиком или даже просто запитать их от одного или нескольких пинов GPIO микроконтроллера. Максимальный ток 20мА на пин.
Питание периферии с коммутацией транзистором или от пинов контроллера у меня практически во всех проектах.
В nRF24L01+ требуется повторная инициализация контроллера радиомодуля. Инициализация несовместима с командами библиотеки LowPower.h, которые задают у меня время сна.
Короче, чтобы восстановить настройки радиомодуля после сна надо сделать Reset контроллера, который ним управляет. У меня — это atmega328p. При ресетах с интервалом минута ресурса флеш-памяти хватит на несколько суток.
Возможно я заблуждаюсь, еще поиграюсь с nRF24L01+ уж очень низкое у него энергопотребление.
Также переписывание с С на ассемблер не имеет почти никакого смысла. Зато имеет смысл порой избавиться от стандартной функции printf() и написать свою, если вы выводите, например, только целые числа
Перед отправкой данных с десятыми-сотыми умножаю на 100, а дальше — дело техники.
habr.com/ru/post/386735
А почему нет кода на ассемблер, откуда эти «оценочные» оценки? Очень интересно посмотреть, как Вы уместили в 200 байт шрифты для экранчика и преобразование данных с датчиков.
Работал с кодом термометра-гидрометра на ATtiny13. Получил объем занимаемой флеш-памяти 40 байт. Навскидку увеличил это число в 5 раз. Подредактирую эту цифру, если будут аргументы.
Вот код:
// *********************************************
// *** Simple digital thermometer/hygrometer ***
// *********************************************
// *** (c) SD, 14.03.2016 ***
// *********************************************
// Based on ATtiny13, AM2303 and MAX7219
// **************
// *** Clocks ***
// **************
// MCU clock frequency is 9.6MHz (internal oscillator)
// Timer frequency is 75KHz = 9.6MHz/128
// (13.3 us between interrupts)
#define SKIPNEXT1W (PC + 2)
#define DS(var) Y + var - _dataStart
// ************
// *** Pins ***
// ************
// MAX7219 output pins
.equ MAX_DIN = 0
.equ MAX_CS = 1
.equ MAX_CLK = 4
// AM2302 input pin
.equ AM2302_PIN = 3
// MAX7219 registers
.equ MAX_DECODE = 0x09
.equ MAX_INTENSITY = 0x0A
.equ MAX_SCANLIMIT = 0x0B
.equ MAX_SHUTDOWN = 0x0C
.equ MAX_DISPTEST = 0x0F
// Temperature measurement state register
// Bits 0 - 2 define the byte number being received
// Bit 3 is set when there are valid data received
// Bits 4 - 7 define the current receiver state
.def R_TS = R0
// Temperature measurement tick
.def R_TT = R1
// Temperature data register
.def R_TD = R2
// Temperature measurement states
.equ TMS_NONE = 0x00 // TMS_NONE - do nothing an wait until
// somebody changes the state
.equ TMS_START = 0x10 // Start of the measurement cycle
.equ TMS_ST_LOW = 0x20 // Initial low signal is being sent
// (1 ms = 75 timer ticks)
.equ TMS_WRSP_LOW = 0x30 // Initial low signal has been sent,
// waiting for the response low signal
.equ TMS_WRSP_HIGH = 0x40 // Response low signal has been received,
// waiting for the response high signal
.equ TMS_W1ST_BIT_LOW = 0x50 // Waiting for the first bit low signal
.equ TMS_WBIT_HIGH = 0x60 // Waiting for the bit high signal
.equ TMS_WBIT_LOW = 0x70 // Waiting for the bit low signal
.equ TMS_WHIGH = 0x80 // Waiting for the final high signal
// Timer 100Hz tick counter
// (counts upwards from 0 to 255)
.def R_TICK100 = R3
// Timer 16bit 75KHz tick counter
// (counts downwords from 749 to 0)
.def R_TICKL = R4
.def R_TICKH = R5
// ************
// *** Data ***
// ************
.dseg
_dataStart: // Data start label
tempData: .byte 5 // Data, received from the AM2302 sensor
displayData: .byte 4 // Decimal printing result
.equ DATA_BUF_SIZE = 8 // AM2302 data buffer size in samples
// (each sample is 4 bytes)
dataBuffer: .byte DATA_BUF_SIZE*4
.cseg
.org 0
// *** Interrupts ***
// Reset Handler
rjmp start
// IRQ0 Handler
reti
// PCINT0 Handler
reti
// Timer0 Overflow Handler
rjmp timerOvfl
// EEPROM Ready Handler
reti
// Analog Comparator Handler
reti
// Timer0 CompareA Handler
rjmp timerCompA
// Timer0 CompareB Handler
reti
// Watchdog Interrupt Handler
reti
// ADC Conversion Handler
reti
// Table to convert decimal digit into 7-segment code
hexTable:
.db 0b01111110, 0b00110000, 0b01101101, 0b01111001
.db 0b00110011, 0b01011011, 0b01011111, 0b01110010
.db 0b01111111, 0b01111011
start:
cli
ldi R16, RAMEND
out (SPL), R16
// Init watchdog (4s interval)
wdr
ldi R16, (1 << WDCE) | (1 << WDE)
out (WDTCR), R16
ldi R16, (1 << WDE) | (1 << WDP3)
out (WDTCR), R16
// Init registers
ldi YL, low (_dataStart)
ldi YH, high (_dataStart)
clr R_TS
clr R_TT
clr R_TICKL
clr R_TICKH
clr R_TICK100
// Init ports
out (PORTB), R_TS
ldi R16, (1 << MAX_DIN) | (1 << MAX_CS) | (1 << MAX_CLK)
out (DDRB), R16
// Init LED driver
// Set all digits to "-"
ldi XL, 0b00000001
ldi XH, 1
init1:
rcall maxWriteWord
cpi XH, 9
brne init1
// Set control registers
ldi XL, 0 // Decode
rcall maxWriteWord
ldi XL, 4 // Intensity
rcall maxWriteWord
ldi XL, 7 // Scan limit
rcall maxWriteWord
ldi XL, 1 // Shutdown
rcall maxWriteWord
ldi XH, 0x0F
ldi XL, 0 // Display test
rcall maxWriteWord
// Init timer for 1 interrupt each 128 CPU cycles
ldi R16, 127
out (OCR0A), R16
ldi R16, 0b00000110
out (TIMSK0), R16
ldi R16, 0b00000001
out (TCCR0B), R16
// First part of the initialization is done.
// Enable interrupts
sei
// Wait 2 sec (while AM2302 initialize itself)
// with little animation
ldi XH, 1
ldi XL, 0
init2:
ldi R16, 25
rcall wait100Hz
rcall maxWriteWord
cpi XH, 9
brne init2
// R6 will contain the number of
// measurement values received
clr R6
// R7 will contain the number of
// continious errors
clr R7
loop:
// Reset watchdog timer
wdr
// Initiate measurement
ldi R16, TMS_START
mov R_TS, R16
loop1:
// Wait for the TMS_NONE state
// which indicates that the measurement
// is done
sleep
mov R16, R_TS
andi R16, 0xF0
brne loop1
// Do we have the valid data?
sbrs R_TS, 3
loop_error1:
rjmp loop_error
// Check control sum of the received data
ldd R16, DS (tempData)
ldd ZL, DS (tempData + 1)
add R16, ZL
ldd ZL, DS (tempData + 2)
add R16, ZL
ldd ZL, DS (tempData + 3)
add R16, ZL
ldd ZL, DS (tempData + 4)
cp R16, ZL
brne loop_error1
// We have valid new measurement data,
// reset error count
clr R7
// Move up data in the buffer
// and count the sum at the same time.
// R12:R13 will contain the humidity value and
// R14:R15 the temperature value
clr R12
clr R13
clr R14
clr R15
ldi ZL, low (dataBuffer + (DATA_BUF_SIZE - 2)*4)
ldi ZH, 0
buf1:
ldd R16, Z + 0
ldd R17, Z + 1
std Z + 4, R16
std Z + 5, R17
add R12, R16
adc R13, R17
ldd R16, Z + 2
ldd R17, Z + 3
std Z + 6, R16
std Z + 7, R17
add R14, R16
adc R15, R17
subi ZL, 4
cpi ZL, low (dataBuffer - 4)
brne buf1
// Add new humidity value to the buffer
// and to the sum
ldd R16, DS (tempData + 1)
ldd R17, DS (tempData)
std DS (dataBuffer + 0), R16
std DS (dataBuffer + 1), R17
add R12, R16
adc R13, R17
// Add new temperature value to the buffer
// and to the sum
ldd R16, DS (tempData + 3)
ldd R17, DS (tempData + 2)
// Check for a negative value
and R17, R17
brpl buf2
// Convert negative temperature to the 2's
// complement form
clr ZL
andi R17, 0x7F
neg R16
sbc ZL, R17
mov R17, ZL
buf2:
std DS (dataBuffer + 2), R16
std DS (dataBuffer + 3), R17
add R14, R16
adc R15, R17
// Divide the humidity and temperature
// sum values by 8 (by shifting them right
// three times)
ldi R16, 3
buf3:
asr R15
ror R14
asr R13
ror R12
dec R16
brne buf3
// Do we have 8 full measurements?
mov R16, R6
cpi R16, 7
// If so, use the average values from
// the buffer
breq buf4
// Otherwise use the latest measurement
ldd R12, DS (dataBuffer + 0)
ldd R13, DS (dataBuffer + 1)
ldd R14, DS (dataBuffer + 2)
ldd R15, DS (dataBuffer + 3)
inc R6
buf4:
// Print out values
// *** Humidity ***
movw X, R12
rcall printDecX
ldi XH, 1
ldd XL, DS (displayData + 3)
rcall maxWriteWord
ldd XL, DS (displayData + 2)
ori XL, 0x80
rcall maxWriteWord
ldd XL, DS (displayData + 1)
rcall maxWriteWord
ldd XL, DS (displayData)
rcall maxWriteWord
// *** Temperature ***
movw X, R14
// Check for a negative value
and XH, XH
brpl buf5
// Calculate the absolute value
clr ZL
neg XL
sbc ZL, XH
mov XH, ZL
buf5:
rcall printDecX
ldi XH, 5
ldd XL, DS (displayData + 3)
rcall maxWriteWord
ldd XL, DS (displayData + 2)
ori XL, 0x80
rcall maxWriteWord
ldd XL, DS (displayData + 1)
rcall maxWriteWord
// If temperature is negative
// write the minus sign to the first digit
// (temperatures of -100.0 and below
// are not supported anyway)
ldd XL, DS (displayData)
and R15, R15
brpl SKIPNEXT1W
ldi XL, 1
rcall maxWriteWord
loop2:
// Wait for 1 sec
ldi R16, 100
rcall wait100Hz
// And repeat
rjmp loop
loop_error:
// An error had occured.
// Increment error count
inc R7
// Do we have 3 or more errors in a row?
mov R16, R7
cpi R16, 3
// No? Just do nothing
brne loop2
// Prevent error count from growing
dec R7
// Display error
ldi ZL, low (errText*2)
ldi ZH, high (errText*2)
rcall maxWrite8Bytes
rjmp loop2
errText:
// "Sn Error"
.db 0b00000101, 0b00011101, 0b00000101, 0b00000101
.db 0b01001111, 0b00000000, 0b00010101, 0b01011011
// **********
// Waits given number (R16) of 100Hz ticks
// Uses: Z
wait100Hz:
// Enable sleep
ldi ZL, 0b00100000
out (MCUCR), ZL
mov ZL, R_TICK100
w100:
sleep
mov ZH, R_TICK100
sub ZH, ZL
cp ZH, R16
brcs w100
ret
// Timer interrupt
timerOvfl:
timerCompA:
push R16
in R16, (SREG)
push R16
push ZL
push ZH
// Receive AM2303 data
rcall am2302proc
// Decrement current 75KHz tick
ldi R16, 1
sub R_TICKL, R16
brcc timerRet
sub R_TICKH, R16
brcc timerRet
// Initialize 75KHz tick value
ldi ZL, low (750 - 1)
ldi ZH, high (750 - 1)
movw R_TICKL, Z
// Increment current 100Hz tick
inc R_TICK100
timerRet:
pop ZH
pop ZL
pop R16
out (SREG), R16
pop R16
reti
// **************
// *** AM2302 ***
// **************
amStart:
// Send the start low signal.
// Switch corresponding PORTB pin to output
// (there is already 0 in the PORTB register)
sbi (DDRB), AM2302_PIN
ldi R16, TMS_ST_LOW
rjmp amSetState
amStartLow:
// Initial start low signal is being sent.
// Wait for 75 ticks
cpi R16, 75
brne amNone
// Switch PORTB pin back to input
cbi (DDRB), AM2302_PIN
ldi R16, TMS_WRSP_LOW
// Do not check AM2303 input pin at this tick
// since it's possible that it has not recovered
// from the low state yet.
rjmp amSetState
amWRespLow:
// Waiting for the response low signal
sbrc ZH, AM2302_PIN
ret
ldi R16, TMS_WRSP_HIGH
rjmp amSetState
amWRespHigh:
// Waiting for the response high signal
sbrs ZH, AM2302_PIN
ret
ldi R16, TMS_W1ST_BIT_LOW
rjmp amSetState
amW1StBitLow:
// Waiting for the first bit low signal
sbrc ZH, AM2302_PIN
ret
// Get ready to receive the first bit
ldi R16, 1
mov R_TD, R16
// Set new state and reset the byte counter
ldi ZL, TMS_WBIT_HIGH
rjmp amSetState2
amBitHigh:
sbrs ZH, AM2302_PIN
ret
// If the bit low signal was there too long
// (longer than 5 ticks (5*13.3 = 66.5us)
// something went wrong)
cpi R16, 6
brcc amResetState
ldi R16, TMS_WBIT_LOW
rjmp amSetState
am2302proc:
// First, check for the TMS_NONE state.
// In this case just do nothing to
// not waste MCU cycles.
mov ZL, R_TS
andi ZL, 0xF0
cpi ZL, TMS_NONE
breq amNone
// Increment receiver tick
inc R_TT
// If we are waiting for too long,
// something went wrong, reset the state
breq amResetState
// Save the current tick into a more
// convenient register
mov R16, R_TT
// Get input signal
in ZH, (PINB)
// Branch depending on the current state.
// Check for TMS_WBIT_LOW first since it
// has the longest service routine
cpi ZL, TMS_WBIT_LOW
breq amBitLow
cpi ZL, TMS_START
breq amStart
cpi ZL, TMS_ST_LOW
breq amStartLow
cpi ZL, TMS_WRSP_LOW
breq amWRespLow
cpi ZL, TMS_WRSP_HIGH
breq amWRespHigh
cpi ZL, TMS_W1ST_BIT_LOW
breq amW1StBitLow
cpi ZL, TMS_WBIT_HIGH
breq amBitHigh
cpi ZL, TMS_WHIGH
breq amWHigh
amResetState:
// In case of an error, reset state to
// the default TMS_NONE
ldi R16, TMS_NONE
amSetState:
// Preserve the current byte number
mov ZL, R_TS
andi ZL, 0x07
or ZL, R16
amSetState2:
mov R_TS, ZL
// Clear receiver tick counter
clr R_TT
amNone:
ret
amBitLow:
sbrc ZH, AM2302_PIN
ret
// The high bit signal was too long?
cpi R16, 8
brcc amResetState
// Store input bit (inverted, since cpi produces
// inverted result in the carry flag)
cpi R16, 4
rol R_TD
// Initally we set R_TD to 1, so when all 8
// bits are received, the carry flag will be set
// indicating that a full byte has been received.
// Otherwise, receive the next bit
ldi R16, TMS_WBIT_HIGH
brcc amSetState
// We have the full byte. Invert it
com R_TD
// Save it
mov ZL, R_TS
andi ZL, 0x07
subi ZL, low (-tempData)
ldi ZH, high (tempData)
st Z+, R_TD
// Did we receive all 5 bytes?
cpi ZL, low (tempData + 5)
ldi R16, TMS_WHIGH
breq amSetState
// OK, receive the next byte.
// Increment the byte counter
inc R_TS
// Initialize R_TD
ldi R16, 1
mov R_TD, R16
ldi R16, TMS_WBIT_HIGH
rjmp amSetState
amWHigh:
sbrs ZH, AM2302_PIN
ret
cpi R16, 6
brcc amResetState
// We received everything. Set
// the state to TMS_NONE and set
// the data validity bit
ldi R16, 0x08
mov R_TS, R16
ret
// *********
/*
// Write data from Z
// Uses R16 - R19, X, Z
maxWriteData:
lpm XH, Z+
tst XH
brne SKIPNEXT1W
ret
lpm XL, Z+
rcall maxWriteWord
rjmp maxWriteData
maxInit:
.db MAX_DECODE, 0
.db MAX_INTENSITY, 4
.db MAX_SCANLIMIT, 7
.db MAX_SHUTDOWN, 1
.db MAX_DISPTEST, 0
.db 0, 0
maxTest:
.db 0, 0b00011101, 0b00010101, 0b00010000, 0b00011100, 0b00111101, 0b00000101, 0b01110111
*/
// Writes 8 bytes from (Z) (program memory)
// to MAX7219
// Uses R16 - R19, X, Z
maxWrite8Bytes:
ldi XH, 0x01
mw8b1:
lpm XL, Z+
rcall maxWriteWord
cpi XH, 9
brne mw8b1
ret
// Write word X (XL = data, XH = address) to MAX2719
// Uses R16 - R19, X
maxWriteWord:
// Set all pins to zero
in R17, (PORTB)
andi R17, ~((1 << MAX_DIN) | (1 << MAX_CS) | (1 << MAX_CLK))
out (PORTB), R17
ldi R19, (1 << MAX_CLK)
mov R16, XH
rcall mww1
mov R16, XL
rcall mww1
// Set LOAD(CS) to high thus writing all 16 bits into
// MAX register
sbi (PORTB), MAX_CS
// Increment MAX register number
inc XH
ret
mww1:
ldi R18, 8
mww2:
bst R16, 7
bld R17, MAX_DIN
out (PORTB), R17
lsl R16
dec R18
// Create clock impulse by toggling clock output twice
out (PINB), R19
out (PINB), R19
brne mww2
ret
// *********
printDecX:
ldi ZH, low (1000)
ldi R16, high (1000)
rcall pdx
// Change zero digit to empty space
cpi ZL, 0b01111110
brne SKIPNEXT1W
ldi ZL, 0
std DS (displayData), ZL
ldi ZH, 100
ldi R16, 0
rcall pdx
// If this digit is zero and the first
// digit is empty (i.e. it was zero too)
// change this digit to empty space
ldi R16, 0b01111110
eor R16, ZL
ldd ZH, DS (displayData)
or R16, ZH
brne SKIPNEXT1W
ldi ZL, 0
std DS (displayData + 1), ZL
ldi ZH, 10
ldi R16, 0
rcall pdx
std DS (displayData + 2), ZL
mov ZL, XL
rcall pdx3
std DS (displayData + 3), ZL
// Clear carry flag to indicate that
// no error occurred
clc
ret
pdx:
ldi ZL, 0
pdx1:
sub XL, ZH
sbc XH, R16
brcs pdx2
cpi ZL, 9
breq pdxOverflow
inc ZL
rjmp pdx1
pdx2:
add XL, ZH
adc XH, R16
pdx3:
subi ZL, -low (hexTable << 1)
ldi ZH, high (hexTable << 1)
lpm ZL, Z
ret
pdxOverflow:
// Set carry flag to indicate error
sec
// Pop return address out of the stack
// so we can return to the caller of printDecX
pop R16
pop R16
ret
— Датчик другой
— Экран другой
Приведённый код не имеет смысла применительно к описанному проекту.
Вряд ли прибор прошит в коде Ардуино или у Вас есть сомнения? )
Далее, для примера, один из тестовых скетчей и короткий фрагмент таблицы с результатами теста. Таких таблиц с записями на 1300-1500 строк и тестовых скетчей у меня десяток. В ближайшее время размещу их в каком-нибудь облаке, чтобы рассеять сомнения. Моя статья и так перегружена, поэтому не стал не размещать тестовые скетчи, таблицы, видео (гифки) с измерениями тока и т.п.
/*скетч для измерения операционного времени приемником LoRa и макc. операц. тока
операционное время 0,083сек (83 мсек)
операционный ток 11.5 mA (Vbat=3V, 8 MHz)
*/
#include <avr/io.h>
#include <util/delay.h>
#include <SPI.h>
#include <LoRa.h>
#include <LowPower.h>
#include "HTU21D.h"
#include <LCD5110_Graph.h>
String LoRaData;
void setup() {
Serial.begin(38400);
Serial.println("Power ON!");
int counter = 0;
while (!LoRa.begin(433E6) && counter < 10) {
Serial.println("Не удалось найти LoRa-передатчик!");
counter++;
_delay_ms(500);
}
// Диапазон для синхрослова – между "0-0xFF".
LoRa.setSyncWord(0xF3);
Serial.println("Прослушивание эфира. Ожидание пакета с в.датчика ...");
_delay_ms(2000);
}
void loop() {
int packetSize = LoRa.parsePacket();
Serial.println("Прослушивание эфира.");
if (packetSize) {
while (LoRa.available()) {
LoRaData = LoRa.readString();
}
int pos1 = LoRaData.indexOf('#');
if ((LoRaData).substring(pos1, pos1 + 1) == "#")
{
Serial.println("Принято, декодировано!");
}
}
}
int main() {
init();
setup();
for (;;) {
loop();
}
}
10:53:30.144 -> Tin=21.90 Hin=41 BatIn=3.30 Tout=00.00 Hout=00 BatOut=BGood
10:53:30.544 -> Tin=21.90 Hin=41 BatIn=3.27 Tout=00.00 Hout=00 BatOut=BGood
10:53:30.944 -> Tin=21.90 Hin=41 BatIn=3.30 Tout=00.00 Hout=00 BatOut=BGood
10:53:31.304 -> Tin=21.90 Hin=41 BatIn=3.31 Tout=00.00 Hout=00 BatOut=BGood
10:53:31.704 -> Tin=21.90 Hin=41 BatIn=3.30 Tout=00.00 Hout=00 BatOut=BGood
10:53:32.104 -> Tin=21.80 Hin=41 BatIn=3.30 Tout=00.00 Hout=00 BatOut=BGood
10:53:32.504 -> Tin=21.90 Hin=41 BatIn=3.31 Tout=00.00 Hout=00 BatOut=BGood
10:53:32.904 -> Tin=21.90 Hin=41 BatIn=3.30 Tout=00.00 Hout=00 BatOut=BGood
10:53:33.304 -> Tin=21.90 Hin=41 BatIn=3.30 Tout=00.00 Hout=00 BatOut=BGood
10:53:33.704 -> Tin=21.90 Hin=41 BatIn=3.30 Tout=00.00 Hout=00 BatOut=BGood
10:53:34.064 -> Tin=21.90 Hin=41 BatIn=3.31 Tout=00.00 Hout=00 BatOut=BGood
10:53:34.464 -> Tin=21.80 Hin=41 BatIn=3.30 Tout=00.00 Hout=00 BatOut=BGood
10:53:34.866 -> Tin=21.90 Hin=41 BatIn=3.31 Tout=00.00 Hout=00 BatOut=BGood
10:53:35.266 -> Tin=21.90 Hin=41 BatIn=3.31 Tout=00.00 Hout=00 BatOut=BGood
10:53:35.666 -> Tin=21.90 Hin=41 BatIn=3.31 Tout=00.00 Hout=00 BatOut=BGood
10:53:36.066 -> Tin=21.90 Hin=41 BatIn=3.30 Tout=00.00 Hout=00 BatOut=BGood
10:53:36.466 -> Tin=21.90 Hin=41 BatIn=3.27 Tout=00.00 Hout=00 BatOut=BGood
10:53:36.866 -> Tin=21.90 Hin=41 BatIn=3.32 Tout=00.00 Hout=00 BatOut=BGood
10:53:37.226 -> Tin=21.80 Hin=41 BatIn=3.31 Tout=00.00 Hout=00 BatOut=BGood
10:53:37.626 -> Tin=21.90 Hin=41 BatIn=3.29 Tout=00.00 Hout=00 BatOut=BGood
10:53:38.026 -> Tin=21.90 Hin=41 BatIn=3.29 Tout=00.00 Hout=00 BatOut=BGood
10:53:38.426 -> Tin=21.90 Hin=41 BatIn=3.30 Tout=00.00 Hout=00 BatOut=BGood
Если уж сильно захочется посмотреть видео на тему потребления, тогда сюда. Тут подобных гифок хватает.
Я к тому, что отношусь к проблеме серьезно, а Вы делаете обобщающие выводы по одной неточности. Успехов!
если Вы не понимаете как размер кода влияет на энергопотребление
Если и влияет, то очень косвенно. Практически — никак, ни на пиковое, ни на среднее.
Если же говорить о более продвинутых контроллерах с настраиваемой частотой, DMA и т.д., то примеров можно привести множество.
… Ведь очевидно, что на две операции затраты меньше, чем, допустим, на 10 таких же.
И неважно, как они выполняются — одним махом или не одним.
while (1);
и сравните объем кода и энергопотребление этой прошивки и Вашей прошивки метеостанции из статьи. А если хотите еще большего контраста, то напишите не пустой бесконечный цикл, а бесконечный цикл с отправкой пары байт через радиоканал без всяких powerdown и sleep :)
Вот код:
Как-то совсем не похоже на 40 байт кода. Одна только таблица прерываний и таблица преобразования в 7-сегментное отображение уже тянут на 30 байт. И это только самое начало всей портянки кода.
Ардуиновские библиотеки, ЕМНИП, переключают датчик в «normal» режим и обратно есс-но не отключают. А это — измерения раз в секунду. «Правильный» ход — поправить используемую библиотеку (точнее запись в регистр ctrl_meas) для работы в «forced» режиме или написать свою обёртку. Я писал свою, чтобы читать сразу все данные за один запрос по I2C.
Javian, Крутовато для поделок :) Использовал похожий приём — супервизор на примерно 2.1В в качестве источника прерываний и полевик на отключение себя от батарейки. Дальше спим без watchdog-ов, пока не сработает IRQ0, на остатках конденсатора подключаем батарейку обратно. Идея долго бродила, но оформилась после чтения комментов сайта hallard.me про Ultra Low Power Node.
ЗЫ: прошелся по ссылке и нашел себя же во втором комменте. Идём на следующий круг )))
немного не понял зачем Лора? выносной блок на км удалён? хотя Лора интересно, я не рискнул работать на 433- очень много помех, решил делать на 868 на 1276. интересно, что мне не удалось добиться потребления в режиме передачи, как в даташитах, спит, да 2мка или что то на грани чувствительности мультиметра. Что с реальным потреблением у Ра01?
Ассемблер даст выигрыш в размере кода ну может процентов 10 по сравнению с Си. Это если проект не большой, сотня-две байт. Если проект больше, то выигрыш в размере кода стремится к нулю, а в больших проектах может и Си скомпилировать меньший код за счёт лучшей оптимизации. В общем не тот это случай чтобы париться с асмом. Не пользуйтесь printf(), float, string и что там ещё у вас многожрущее. И самый главный выигрыш — нафиг ардуину, пишите на обычном си.
Спасибо! Коротко, внятно и однозначно.
Результаты измерений, как и раньше, — в таблице статьи.
Еще заметка с опытом выяснилось что сам модуль nRF24L01+ надо подбирать. Есть модули которые спят с током 6мА а есть которые с 0.05мА (ниже я уже не измеряю). BM*280 и правда надо усыплять програмно. Вешал его питание на ногу микроконтроллера. Так вот даже опустив ногу в ноль датчик умудрялся есть аккумулятор. Так что его нужно принудительно усыплять. Ну а точнее перевести в режим единичного измерения.
При этом датчики живут полгода — год на одном аккумуляторе легко. Все кроме уличного. Уличный кроме того что морозится так еще находится дальше всех от базы. С ним постоянные танцы. Если уличный питать батарейками — то мороз по ним бьет прямо сразу.
А почему бы не вывести за пределы помещения тонким кабелем только датчик ds18b20, а остальное оставить в помещении. В промышленных образцах реализовано именно такое решение.
… к тому же не красивый проводок и где то установленная коробка с батарейками. жена не оценит такую красоту
Тоже есть варианты:
- Красивый многожильный проводок. Плюс красивая коробочка для жены. Замечу, красивая коробочка — это работа посложнее. Эстетику разделения проводка на два — оставить на суд голубей.
- По результатам своих наблюдений внести корректировку температуры в скетч и оставить только am2320.
- Побороть недоверие, разобравшись в его причинах.
- Продолжать гробить не дешевые аккумуляторы, оставив все как есть.
Сейчас повесил за окно датчик уже на здоровом аккумуляторе. Работает стабильно )
даже на narodmon вытащил свои данные.
esp8266.ru/forum/threads/tlsr8251-lcd-termometr-lywsd03mmc-xiaomi-bluetooth-termometr.5263
Снова о автономной Arduino-метеостанции на батарейках