Как стать автором
Обновить

Комментарии 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 холиварная, но если вы с ардуины на Си съехали то разберётесь.

Atmega вполне себе низкопотребляющий, если правильно пользоваться. Если хочется выжать максимум то atmel picopower. Но в данном случае бессмысленно экономить пикоамперы, контроллер тут далеко не на первом месте в потреблении энергии.

Сударь, на счёт потребления атмеги и СТМ вы не правы, СТМ потребляет куда больше, но правда и вычислительные возможности лучше. Все зависит от применения и прямоты рук программиста и схемотехника)

Экран E-ink 2.13 + Контроллер NRF52 < 2мкА потребления в режиме сна
Либо взять E-ink + Zigbee как на Xiaomi термометрах
С Zigbee придется же ещё полноценный шлюз организовывать? Это всё проект сильно удорожает.
Ну да. Как и с Mysensors
Готовый USB брелок или модуль к малинке стоит порядка 2-х тысяч
Можно взять SOC на алишке за 500р и спаять самому
Зато сяоминые, акваровские и др. устройства работаю готовые
С брелком-то ещё хуже всё будет — он же просто функцию координатора играет. Это надо будет разворачивать какую-то систему автоматизации и его туда интегрировать. У автора, кажется, задачи сильно проще)
Экран E-ink 2.13 + Контроллер NRF52 < 2мкА потребления в режиме сна
Либо взять E-ink + Zigbee как на Xiaomi термометрах

Дисплей 2.13 будет маловат для комнатного термостата и метеостанции в одном. В списке желаний на Али уже несколько месяцев e-paper дисплей 3,7 дюйма.
NRF52832 уже в пути.
Снизить энергопотребление в режиме сна можно, если питание внешних модулей коммутировать p-канальным полевиком или даже просто запитать их от одного или нескольких пинов GPIO микроконтроллера. Максимальный ток 20мА на пин.

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, а дальше — дело техники.
При ресетах с интервалом минута ресурса флеш-памяти хватит на несколько суток.

При чем ресурс флеш-памяти к ресету? Ее ресурс "расходуется" только при перепрошивке. Или у вас бутлоадер при каждом сбросе перепрошивает контроллер?

Для максимальной экономии лучше отключать потребителей наподобие этого примера:
image
habr.com/ru/post/386735

А почему нет кода на ассемблер, откуда эти "оценочные" оценки? Очень интересно посмотреть, как Вы уместили в 200 байт шрифты для экранчика и преобразование данных с датчиков.

А почему нет кода на ассемблер, откуда эти «оценочные» оценки? Очень интересно посмотреть, как Вы уместили в 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

Аргументы:
— Датчик другой
— Экран другой
Приведённый код не имеет смысла применительно к описанному проекту.
Вы свой код собрать пробовали?

ATtiny13 memory use summary [bytes]:
Segment Begin End Code Data Used Size Use%
---------------------------------------------------------------
[.cseg] 0x000000 0x0002f4 738 18 756 1024 73.8%
[.dseg] 0x000060 0x000089 0 41 41 64 64.1%
[.eseg] 0x000000 0x000000 0 0 0 64 0.0%

Assembly complete, 0 errors. 0 warnings


Видимо, разницы между 40 байтами и 738 байтами совсем нет. И это мы еще никак знакогенератор на несколько шрифтов для экранчика не трогали.
На уровне таких «оценок» и фраз
Код на Ассемблере уменьшает размер памяти на порядки. Соответственно пиковое потребление падает в сотни раз.
адекватность всего остального в статье вызывает крайне высокие сомнения. Не очень понимаю, как размер кода влияет на энергоэффективность. Настоящий специалист может и в пустом цикле крутить на полную мощность ядра.
Ну, если Вы не понимаете как размер кода влияет на энергопотребление, тогда о чем говорить. Посмотрите, пожалуйста, техописание комнатного термостата стр 11 — это оттуда: Напряжение питания 2 x 1.5V алкальных батарейки (LR6 AA), Потребляемая мощность 1.3 mW, Срок действия батареек ~ 1 год.
Вряд ли прибор прошит в коде Ардуино или у Вас есть сомнения? )

Далее, для примера, один из тестовых скетчей и короткий фрагмент таблицы с результатами теста. Таких таблиц с записями на 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:29.744 -> Tin=21.90 Hin=41 BatIn=3.31 Tout=00.00 Hout=00 BatOut=BGood
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


Если уж сильно захочется посмотреть видео на тему потребления, тогда сюда. Тут подобных гифок хватает.

Я к тому, что отношусь к проблеме серьезно, а Вы делаете обобщающие выводы по одной неточности. Успехов!
То есть, вопрос с размером кода уже замылился? Думаю, что смысла доказывать и про энергопотребление тогда нет. Вам удачи и новых чудесных открытий!
если Вы не понимаете как размер кода влияет на энергопотребление

Если и влияет, то очень косвенно. Практически — никак, ни на пиковое, ни на среднее.
Готов поспорить: размер кода > количество операций > энергопотребление. Ведь очевидно, что на две операции затраты меньше, чем, допустим, на 10 таких же. Что легче: вытащить 2 ведра воды из колодца или 10?
2 операции, выполняющиеся непрерывно в цикле, или 100 операций, выполняющиеся однократно раз в 10 секунд. По Вашей же аналогии — вытащить 2 ведра из колодца одним махом или по 1 литру в день за 20 дней.
Если же говорить о более продвинутых контроллерах с настраиваемой частотой, DMA и т.д., то примеров можно привести множество.
Читаем еще раз :
… Ведь очевидно, что на две операции затраты меньше, чем, допустим, на 10 таких же.

И неважно, как они выполняются — одним махом или не одним.
Вы не признаете режимы энергосбережения микроконтроллеров? :) Ведь совершенно неочевидно, что код с меньшим объемом будет более энергоэффективным. Можете проверить сами — напишите прошивку, в которой функция main() будет содержать только строку:
while (1);
и сравните объем кода и энергопотребление этой прошивки и Вашей прошивки метеостанции из статьи. А если хотите еще большего контраста, то напишите не пустой бесконечный цикл, а бесконечный цикл с отправкой пары байт через радиоканал без всяких powerdown и sleep :)
Вот код:

Как-то совсем не похоже на 40 байт кода. Одна только таблица прерываний и таблица преобразования в 7-сегментное отображение уже тянут на 30 байт. И это только самое начало всей портянки кода.
Да, вы правы! Дикая описка и выводы соответственно.
Не всегда понимаю чем руководствуются когда ставят тип переменной int если в эту переменную максимум что прилетает или записывается в самом начале в виде константы нечто в пределах 0-255.
Учту на будущее. Как-то не было проблем до сих пор. Спасибо!
Первый раз слышу про «спячку» BM*280 от дёрганья питания. Мануал вполне однозначен: при сбросе/подаче питания датчик в режиме «sleep» — 0.1uA потребление, дальше — командуем по I2C.
Ардуиновские библиотеки, ЕМНИП, переключают датчик в «normal» режим и обратно есс-но не отключают. А это — измерения раз в секунду. «Правильный» ход — поправить используемую библиотеку (точнее запись в регистр ctrl_meas) для работы в «forced» режиме или написать свою обёртку. Я писал свою, чтобы читать сразу все данные за один запрос по I2C.

Javian, Крутовато для поделок :) Использовал похожий приём — супервизор на примерно 2.1В в качестве источника прерываний и полевик на отключение себя от батарейки. Дальше спим без watchdog-ов, пока не сработает IRQ0, на остатках конденсатора подключаем батарейку обратно. Идея долго бродила, но оформилась после чтения комментов сайта hallard.me про Ultra Low Power Node.

ЗЫ: прошелся по ссылке и нашел себя же во втором комменте. Идём на следующий круг )))

Ардуиновские библиотеки (конкретно от Adafruit) умеют выставлять режим датчика

немного не понял зачем Лора? выносной блок на км удалён? хотя Лора интересно, я не рискнул работать на 433- очень много помех, решил делать на 868 на 1276. интересно, что мне не удалось добиться потребления в режиме передачи, как в даташитах, спит, да 2мка или что то на грани чувствительности мультиметра. Что с реальным потреблением у Ра01?

А почему бы и не Лора? Ra-01 это SX1278. В sleep там наноамперы какие-то. И с помехоусточивостью всё отлично.
А почему бы и не Лора?

Поддерживаю. Да и мощности в пределах квартиры достаточно 4 дБ.
Ассемблер даст выигрыш в размере кода ну может процентов 10 по сравнению с Си. Это если проект не большой, сотня-две байт. Если проект больше, то выигрыш в размере кода стремится к нулю, а в больших проектах может и Си скомпилировать меньший код за счёт лучшей оптимизации. В общем не тот это случай чтобы париться с асмом. Не пользуйтесь printf(), float, string и что там ещё у вас многожрущее. И самый главный выигрыш — нафиг ардуину, пишите на обычном си.
Ассемблер даст выигрыш в размере кода ну может процентов 10 по сравнению с Си. Это если проект не большой, сотня-две байт. Если проект больше, то выигрыш в размере кода стремится к нулю, а в больших проектах может и Си скомпилировать меньший код за счёт лучшей оптимизации. В общем не тот это случай чтобы париться с асмом. Не пользуйтесь printf(), float, string и что там ещё у вас многожрущее. И самый главный выигрыш — нафиг ардуину, пишите на обычном си.

Спасибо! Коротко, внятно и однозначно.
Добавлю немного своего экспириенса. Использую датчики построенные на довольно близкой базе: Mega328P@12MHz, Ra-01, HTU21D, TPS61291, 2xAAA (щелочные «мизинчики»). Связь информационным пакетом в одну сторону на хост, пакет 11 байт, мощность 14дБм, диапазон 433МГц. Дальше режим приёма длительностью 10 символов для получения возможного управляющего пакета от хоста. Везде где можно сон в моде PowerDown. Не нужную периферию отключать. Никаких повторных пересылок я не делаю, наоборот, используется адаптивный интервал, чтобы не флудить избыточными данными. Прошивка объёмом 9412 байт собрана с использованием выпотрошенных и отученных либ от ардуины. Самые старые элементы питания живут уже 15 месяцев, умирать пока не спешат. Больше времени с последнего апгрейда HW+FW просто не прошло. Потерь и повреждений данных я не фиксировал ни разу (отдельные случаи потерь за весь период, конечно, возможны, на общей картине не видно). Радиоканал работает на 25+ метров с несколькими деревянными перекрытиями, стенами, стальной крышей на пути, сигнал LoRa хороший и стабильный. Узкополосный сигнал в диапазоне 2.4ГГц (всевозможные nRF24L01+) ничего подобного и близко не показал. Но да, хост у меня не на батарейках, там режим приёма постоянный.
Несколько лет как сделал «умную квартиру». Сейчас уже вторая квартира. Для климатических датчиков использую связку Arduino Mini Pro 8Мгц nRF24L01+ ds18b20 am2320 и все это на аккумуляторе 16340. Библиотеку для связи использую mysensors. Никаких хитростей типа отключения питания датчикам или радиомодулю не использую. Так же не хочу заменять на что то ds18b20 по тому что ему верю больше всех (проводил испытания разных). Хотя часто читаю что он якобы не пригоден для батарейного питания. Код примитивен. Передаю данные раз в 5 минут. При этом датчики живут полгода — год на одном аккумуляторе легко. Все кроме уличного. Уличный кроме того что морозится так еще находится дальше всех от базы. С ним постоянные танцы. Если уличный питать батарейками — то мороз по ним бьет прямо сразу. Я к чему это все — достаочно поставить один аккумулятор 18650 в базу и перехать на 8 мгц и больше никаких танцев по моему не надо. Ну это если надо устройство работающее от аккумулятора а не стремится повторить энергопотребление промышленных устройств.
Еще заметка с опытом выяснилось что сам модуль nRF24L01+ надо подбирать. Есть модули которые спят с током 6мА а есть которые с 0.05мА (ниже я уже не измеряю). BM*280 и правда надо усыплять програмно. Вешал его питание на ногу микроконтроллера. Так вот даже опустив ногу в ноль датчик умудрялся есть аккумулятор. Так что его нужно принудительно усыплять. Ну а точнее перевести в режим единичного измерения.
При этом датчики живут полгода — год на одном аккумуляторе легко. Все кроме уличного. Уличный кроме того что морозится так еще находится дальше всех от базы. С ним постоянные танцы. Если уличный питать батарейками — то мороз по ним бьет прямо сразу.

А почему бы не вывести за пределы помещения тонким кабелем только датчик ds18b20, а остальное оставить в помещении. В промышленных образцах реализовано именно такое решение.
ну во первых на улице не только ds18b20 но еще датчик влажности и давления. т.е получится что почти все надо будет оставить на улице а в квартиру тащить только батарейки и может быть радиомодуль. странное решение выдет. к тому же не красивый проводок и где то установленная коробка с батарейками. жена не оценит такую красоту
… к тому же не красивый проводок и где то установленная коробка с батарейками. жена не оценит такую красоту

Тоже есть варианты:
  • Красивый многожильный проводок. Плюс красивая коробочка для жены. Замечу, красивая коробочка — это работа посложнее. Эстетику разделения проводка на два — оставить на суд голубей.
  • По результатам своих наблюдений внести корректировку температуры в скетч и оставить только am2320.
  • Побороть недоверие, разобравшись в его причинах.
  • Продолжать гробить не дешевые аккумуляторы, оставив все как есть.

Причины недоверия — я тестировал разные датчики. Кстати на самом деле am2320 весьма нормально показывает. Но веры все равно больше ds18b20. Когда я устал от того что десяток термометров стоящих радом (и иногда и в одном корпусе) показывает совершенное разное я купил поверенный термометр ВИТ и уже всех сравнивал с ним. Влажность кстати как оказалось am2320 тоже показывает весьма хорошо. Но у них опять же есть такое что конкретные экземпляры немного отличаются.
Сейчас повесил за окно датчик уже на здоровом аккумуляторе. Работает стабильно )
даже на narodmon вытащил свои данные.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.