ATtiny85 + ADXL335 = контроллер положений


По многочисленным просьбам читателей рассказываю, что именно и каким образом сделал из акселерометра ADXL335.

Суть в том, что дам, которые ждут ребенка, подстерегает потенциальная опасность в виде сна на спине на более-менее поздних сроках беременности. Проблема в том, что в этом случае под весом плода пережимается кровеносная артерия, что грозит очень серьезными, если не сказать больше, последствиями.

Поэтому я и подумал, что мне, наверное, будет не очень сложно сделать какую-то штуку, которая сможет предотвратить попадание в эту потенциально опасную ситуацию. Конечно, спустя довольно короткое время мне открыли глаза на тот факт, что мудрые женщины избегают риска совершенно естественным путем — им просто неудобно спать на спине на этих самых поздних сроках.

Но не выкидывать же уже купленные микроконтроллеры, акселерометры и поработившие разум гениальные идеи, правда?



Были и другие демотиваторы. Начиная с хардкорного варианта:



И заканчивая вполне гламурными корректорами осанки, о которых я, честное слово, узнал уже позже собственного озарения.

В общем, сначала я хотел просто и без излишеств, как у нормальных Оккамов, с нормальными бритвами. То есть, прибор должен был просто фиксировать позицию и, в случае, если она не меняется в течение заданного времени, доносить эту бесценную информацию до своего владельца путем вибрации. Выбор именно такого уведомления для меня был органичен, поскольку уже второй год ношу браслет-шагомер с вибробудильником (сейчас это Vidonn X6), который (будильник) чудо как хорош.

Разумеется, мы все понимаем, что вибросигнал подходит не всем. Но у меня и не было претензий на универсальный прибор — везде, понимаете, должен соблюдаться разумный баланс. Кстати, именно поэтому в финальной конструкции есть и так нами любимая среда Arduino, и множество варварских решений, и даже легендарная синяя (реально — синяя, сантехник принес) изолента.

В общем, бритва Оккама сломалась, когда я приступил к обтачиванию контуров будущего будильника. У которого внезапно обнаружились и другие потенциальные таланты, именно: контроль осанки и предупреждение [офисного планктона] о слишком длительной неподвижности. Причина расширения функционала банальна — жаль было видеть, как пропадают ресурсы пусть и не самого мощного, но все же довольно серьезного микроконтроллера.

Об одном из компонентов — акселерометре ADXL335 — я не слишком удачно рассказал раньше, поэтому ссылку на него вы уже имеете. Поэтому в заголовке этого текста ссылка на второй компонент — контроллер ATtiny85, который я купил в корпусе SOIC8.

Сразу хочу сказать, что это довольно компактный корпус и он, конечно, хорош для компактных устройств, но не слишком удобен для макетирования. Точнее — вообще неудобен.

. Вот, к примеру, как приходится ухищряться, чтобы поставить его на макетную плату:


Поэтому рассчитывайте свои силы, а если не уверены, то можете пожертвовать габаритами и купить тот же контроллер в привычном DIP.

Как это работает

Как вы уже знаете, у Позиционера три режима работы:

1) Предотвращение сна на спине (ну или в любом другом положении, ему без разницы)
2) Контроль осанки
3) Предупреждение о малой подвижности

И, как еще не знаете, всего одна кнопка, которая выполняет функцию сброса микроконтроллера.

Поэтому для выбора нужного режима достаточно выполнить два действия: нажать кнопку и подержать Позиционер в одном из трех положений, если образно — на спине, на боку или стоя. А чтобы перевести штуку просто в режим ожидания (аналог выключения), вообще ничего делать не нужно. То есть, нажали кнопку, положили — и позиционер засыпает.

В чем фишка? После выбора режима или при усыплении Позиционер нужно держать довольно неподвижно, поскольку в первом случае ему нужно время на запоминание позиции, а во втором — на понимание того, что пора спать. При этом существует два главных интервала: первые пять секунд после включения служат для выбора режима, а последующие десять — для получения позиции.

При этом, кстати, десять секунд является условным понятием, поскольку Позиционер запоминает «координаты» только в том случае, если в течение указанного времени позиция относительно неизменна. Это для того, чтобы можно было выбрать режим работы и спокойно цеплять на себя устройство, не беспокоясь о жестком таймауте на эту операцию. А вот как только прицепил — замри на десять секунд, чтобы Позиционер понял, что пора приступать к делу.

Иными словами, внимательный читатель уже догадался, что для выбора режима используются показания акселерометра, а я все еще растекаюсь по древу.

Вот, можете посмотреть, как это работает на примере прототипа на макетной плате:



Как видите, все довольно просто.

В более-менее финальном варианте сигнализация работает следующим образом:

1) В режиме предотвращения сна на спине Позиционер срабатывает примерно через минуту и будит серией из 8 сигналов, которая не прерывается ни при каких обстоятельствах, кроме разряда батареи. А чтобы он прекратил это делать, следует лечь как-нибудь иначе и дождаться окончания серии сигналов. Иначе серия сигналов повторится.

2) В режиме контроля осанки Позиционер следит, чтобы верная осанка сохранялась на протяжении примерно 60% времени из 5-минутного интервала. Иначе начинает вибрировать до тех пор, пока не вернешься к правильной осанке.

3) В режиме предупреждения о малой подвижности Позиционер следит, чтобы по крайней мере 8% времени из 30-минутного интервала были посвящены движению. Если это не так — однократно выдает серию из 10 сигналов, которая не прерывается и начинает отсчет нового интервала.

Разумеется, сигнализацию в любом режиме можно прервать нажатием кнопки сброса.

А что касается могущих показаться странными интервалов и уведомлений, то я исходил из следующего. Минута на спине в моем представлении — достаточно долго, а 8 сигналов без перерыва (и перспектива получить столько же повторно) кажутся достаточным основанием, чтобы проснуться.

С другой стороны, контроль осанки не столь критичная вещь, поэтому, во-первых, не требуется стопроцентный контроль и, во-вторых, вряд ли контроль осанки происходит во сне, поэтому сигналов, которые прекращаются, когда вернешься в нужное положение, вполне достаточно. Ну и все время сидеть, словно аршин проглотил — немного глупо и не очень-то возможно. Поэтому контрольный интервал настраивается.

Наконец, когда я гуглил про необходимый минимум двигательной активности, то наткнулся на упоминание того, что 15-20 минут раз в три часа представляется более-менее адекватным. Настройки Позиционера учитывают эту информацию, но при желании модифицируются на этапе «прошивки» микроконтроллера. Опять же, здесь не нужно трясти человека до потери сознания, поскольку идти или не идти — дело добровольное. Отсюда и лишь деликатное напоминание в виде десяти вибросигналов.

Разумеется, все эти интервалы — не догма и легко меняются. Было бы желание.

Питается Позиционер от литиевой батарейки типа CR2032. Потребление тока в режиме сна (некий четвертый псевдорежим Позиционера, а не контроллера) 4 мкА, что я посчитал достаточным основанием для упрощения конструкции в части отказа от выделенного выключателя питания. На старте и при непосредственном определении позиции потребление может на доли секунды превышать 2 мА, но преимущественно находится в пределах 1 мА за исключением моментов срабатывания сигнала.

В интервалах между определением позиции контроллер находится в режиме экономии энергии (Power down), где потребление аналогично режиму сна Позиционера — 4 мкА. Просыпается контроллер по таймеру примерно раз в 8 секунд — это одновременно и максимальная экономия батарейки и приемлемо для более-менее корректной работы всех режимов.



Полную автономность я посчитать, к сожалению, не могу, потому что не умею.

Как это сделано

Во-первых, все из головы, во-вторых, экспромтом, в третьих по-дилетантски, поскольку мне интереснее было закрыть этот гештальт, чем возводить его в степень абсолюта.

Поэтому в дальнейшем будем иметь в виду, что речь идет как бы о весьма конкретной и одной единственной реализации. Для которой нужно:

1) Микроконтроллер ATtiny85 (например, такой)
2) Акселерометр ADXL335 в сборе (например, такой или сразу с питанием 3.3В)
3) Вибромотор от мобильного телефона (я путем многочисленных самоуничижений получил в ремонте)
4) Кнопка (взял от разобранного радиобрелка)
5) Резистор 2,2КОм
6) Диод типа 1N4007 (у меня что-то вроде КД522)
7) Транзистор NPN, например 2N2222 (я взял что-то из сломанного DVD)
8) Батарейка (CR2032)
9) Подходящий корпус

Схема выглядит следующим образом:



Часть, касающуюся подключения мотора я честно украл в очень интересном проекте ELSE.

Кстати, еще очень рекомендую настолько прекрасную картинку от Sparkfun, что ее просто невозможно не процитировать:



Есть и еще одна хитрость. Суть в том, что существует как минимум две версии плат с акселерометром ADXL335: на 3,3В и до 5В. Собственно, рабочий диапазон напряжений акселерометра от 1,8В до 3,6В, поэтому вторая версия отличается от первой наличием стабилизатора напряжения.

. вот такая со стабилизатором (трехногий таракан слева от акселерометра). Фото Tinydeal


. а эта — без стабилизатора. Фото Hobby Electronics


. есть и гибридный вариант — где можно питать отдельно и от 3,3В (мимо стабилизатора, и от 5В через стабилизатор). Фото Adafruit


У меня вот оказалась плата со стабилизатором, которая работает и от 5В, что неудивительно, и от 3,3В (я проверял). Но так как я не вижу никакого смысла греть стабилизатор и, соответственно, зря тратить батарейку, то в финальном варианте устройства подключил питание акселерометра прямо к нему самому, минуя стабилизатор.

. для обхода стабилизатора можно ориентироваться и по плате, и по даташиту ADXL335


Наверное, еще имеет смысл рассказать о том, что микроконтроллеры ATtiny85 тоже разные бывают. В магазинах чаще и дешевле всего встречаются чипы с маркировкой вида ATtiny85-20, реже и дороже — ATtiny85-10. С точки зрения изготовления Позиционера (и по информации даташита) наиболее важное отличие в том, что ATtiny85-20 работает в диапазоне напряжений 2,7В — 5,5В, а ATtiny85-10 — в диапазоне 1,8В — 5,5В. Т.е. второй гораздо более предпочтителен с точки зрения наиболее полного использования емкости батарейки.

Второе отличие — тактовая частота 20 МГц против 10 МГц. Хотя для Позиционера это, в общем-то, неважно: он работает на 1 МГц и на жизнь не жалуется. Но так как я просто схватил первое, что попалось и подешевле, то у меня как раз ATtiny85-20.

Наконец, если вдруг для кого-то это загадка, то питание акселерометра я подключил к контроллеру по той причине, что так проще всего выключать его (акселерометр) в режиме сна Позиционера. Иначе будем постоянно иметь утечку до 350 мкА (если верить даташиту ADXL335).

Собирал все навесным монтажом, соединяя элементы эмалированным проводом 0,1 мм (с маркировкой Jumper wire), пару катушек которого добрые китайцы подарили мне вместе с паяльной станцией. И который я уже год не знал, куда девать. Оказалось идеально: провод прекрасно лудится паяльником на 300С с обычной канифолью, и достаточно удобен для пайки как ATtiny85, так и транзистора в корпусе SOT23.

В итоге получился довольно утилитарный ландшафт:

. компоненты какие были — такие и ставил. Очень уж накладно по одной штуке в бутике Ч-и-Д закупаться


Алгоритм и код


Базовый алгоритм работы Позиционера выглядит следующим образом:



В качестве среды программирования я совершенно безальтернативно выбрал Arduino, поскольку сейчас пусть худо-бедно, но знаком только с ней. Ну и гештальт я в начале упоминал — это тоже объясняет тот факт, почему я не стал изучать C++, чтобы сделать Позиционер.

Что касается кода, то должен дать некоторые комментарии.

Исходя из модели использования прибора (включил и забыл, пока он не напомнит), крайне желательно было бы забыть и про ежедневную замену батареек. А для этого нужно серьезно экономить энергию, что очень даже возможно, поскольку в даташите ATtiny85 упоминается, что минимальное потребление — десятые доли микроамера. Сразу скажу — до таких высот я не поднялся (или не опустился?).

Здесь я опять выбрал самый простой путь. Именно — нагуглил то, что было нужно. Первое — код для укладывания ATtiny85 в режим максимально глубокого сна с пробуждением по watchdog-таймеру.

При этом потребление тока с единиц миллиампер упало до сотен микроампер. Это уже было прекрасно, но все же недостаточно прекрасно.

Пришлось искать дальше. И тут выяснилось, что сон сном, а периферию нужно отключать вручную. При этом львиную долю энергии даже в режиме сна кушает АЦП. Вот здесь так и написано.

После отключения АЦП на время сна энергетика Позиционера стала гармоничной настолько, насколько я вообще мог это нагуглить. Т.е., как и писал выше, минимальное потребление — 4 мкА, во время пробуждений на время измерений — десятки или сотни микроампер, при работающем вибромоторе может превышать 2 мА.

Жизнеспособность этой смеси кода можно оценить по тому, что далеко не первой свежести CR2032 уже вторую неделю трудится в Позиционере в режиме тестирования. Большую часть времени, конечно, в режиме ожидания, но и активного времени прилично. Одним словом, близко к реальной модели использования.

Относительно работы с акселерометром. Дело в том, что я, насколько мог, абстрагировался от абсолютных величин. Поводов для этого несколько. Во-первых, так можно менять акселерометры, не задумываясь о том, как та или иная железка работает. Во-вторых, у меня не было, простите за тавтологию, железных гарантий, что при неизбежном в процессе разряда батареи падении напряжения не «поплывут» и показания акселерометра, которые связаны с их обработкой АЦП в микроконтроллере.

Наконец, настройки вынесены в раздел объявления переменных. Это я так себя хвалю — потому что для подстройки Позиционера не придется перекапывать весь код и вспоминать меня нехорошими словами во время поисков всех фрагментов, где нужно заменить значения.

Код заливал в ATtiny85 через Arduino Mega согласно распространенной инструкции. Т.е. сначала скачал поддержку ATtiny для своей версии среды Arduino вот здесь.

И, как водится, распаковал содержимое архива (каталог tiny со всем содержимым) в папочку hardware своего каталога Arduino:



Потом зашел в этот каталог и, согласно инструкции, сделал там файлик boards.txt, в который из уже имеющегося файла Prospective Boards.txt скопировал то, что мне было нужно — т.е. описания ATtiny 1 МГц и на всякий случай — 8 МГц.

Вот такой получился boards.txt
attiny85at8.name=ATtiny85 @ 8 MHz (internal oscillator; BOD disabled)

# The following do NOT work…
# attiny85at8.upload.using=avrispv2
# attiny85at8.upload.using=Pololu USB AVR Programmer

# The following DO work (pick one)…
attiny85at8.upload.using=arduino:arduinoisp
# attiny85at8.upload.protocol=avrispv2
# attiny85at8.upload.using=pololu

attiny85at8.upload.maximum_size=8192

# Default clock (slowly rising power; long delay to clock; 8 MHz internal)
# Int. RC Osc. 8 MHz; Start-up time PWRDWN/RESET: 6 CK/14 CK + 64 ms; [CKSEL=0010 SUT=10]; default value
# Brown-out detection disabled; [BODLEVEL=111]
# Preserve EEPROM memory through the Chip Erase cycle; [EESAVE=0]

attiny85at8.bootloader.low_fuses=0xE2
attiny85at8.bootloader.high_fuses=0xD7
attiny85at8.bootloader.extended_fuses=0xFF
attiny85at8.bootloader.path=empty
attiny85at8.bootloader.file=empty85at8.hex

attiny85at8.build.mcu=attiny85
attiny85at8.build.f_cpu=8000000L
attiny85at8.build.core=tiny
###########################################################################
###########################################################################

attiny85at1.name=ATtiny85 @ 1 MHz (internal oscillator; BOD disabled)

# The following do NOT work…
# attiny85at1.upload.using=avrispv2
# attiny85at1.upload.using=Pololu USB AVR Programmer

# The following DO work (pick one)…
attiny85at1.upload.using=arduino:arduinoisp
# attiny85at1.upload.protocol=avrispv2
# attiny85at1.upload.using=pololu

attiny85at1.upload.maximum_size=8192

# Default clock (slowly rising power; long delay to clock; 8 MHz internal; divide clock by 8)
# Int. RC Osc. 8 MHz; Start-up time PWRDWN/RESET: 6 CK/14 CK + 64 ms; [CKSEL=0010 SUT=10]; default value
# Divide clock by 8 internally; [CKDIV8=0]
# Brown-out detection disabled; [BODLEVEL=111]
# Preserve EEPROM memory through the Chip Erase cycle; [EESAVE=0]

attiny85at1.bootloader.low_fuses=0x62
attiny85at1.bootloader.high_fuses=0xD7
attiny85at1.bootloader.extended_fuses=0xFF
attiny85at1.bootloader.path=empty
attiny85at1.bootloader.file=empty85at1.hex

attiny85at1.build.mcu=attiny85
attiny85at1.build.f_cpu=1000000L
attiny85at1.build.core=tiny
###########################################################################
###########################################################################



После этого запустил Arduino, убедился, что в меню Сервис — Плата появились нужные ATtiny:



Подключил (и, разумеется, выбрал нужную плату) Arduino Mega 2560, записал в нее скетч Arduino ISP из примеров:



Потом подключил ATtiny к Mega, руководствуясь распиновкой Mega 2560 и инструкцией из скетча Arduino ISP:

// pin name: not-mega: mega(1280 and 2560)
// slave reset: 10: 53
// MOSI: 11: 51
// MISO: 12: 50
// SCK: 13: 52


Конечно же подключил ATtiny к питанию и земле, в моем случае обе линии были общими с программатором, т.е. Mega 2560, хотя все мы понимаем, что необходимый минимум из общего — это линии SPI, reset и земля.

А когда все было готово, то сначала (не забыв выбрать плату ATtiny85) записал загрузчик:



И вслед за ним - собственно скетч
// энергосбережение
// donalmorrissey.blogspot.ru/2010/04/sleeping-arduino-part-5-wake-up-via.html
// www.technoblogy.com/show?KX0

/* СИГНАЛЫ
. включение
— . будильник
— … движение
— … осанка
— изменение позиции при ее фиксации
… позиция зафиксирована
— ошибка, выключение
… предупреждение
*/

#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>

volatile int f_wdt=1;

#define xPin A1 // пин оси X
#define yPin A2 // пин оси Y
#define zPin A3 // пин оси Z
#define alPin 0 // пин устройства уведомления
#define posPowPin 1 // пин питания акселерометра
#define timeLimit 10000 // таймаут для фиксации позиции
#define posCountSleep 9 // таймаут во время режима сна (*8 сек) 8
#define posCountMove 231 // таймаут во время режима движения (*8 сек) 230
#define posCountBack 41 // таймаут во время контроля осанки 40
#define tresholdSleep 25 // порог чувствительности в режиме сна
#define tresholdMove 25 // порог чувствительности в движения
#define tresholdBack 20 // порог чувствительности в режиме осанки
#define movPercentMove 92 // количество покоя (в %) в режиме движения
#define movPercentBack 40 // количество неправильных поз (в %) в режиме осанки
#define startTimeOut 5000// таймаут для перехода в состояние сна или выбора режима
#define motionTimeOut 1500// таймаут для определения движений в режиме контроля подвижности

unsigned long timeOut; // счетчик для фиксации позиции
byte treshold = 15; // чувствительность к изменению позиции
int posCounter = 1; // счетчик интервалов вычисления позиции
byte posMode = 0; // рабочий режим (по умолчанию — выкл = 0; сон на спине — 1; движение — 2; осанка — 3)
int posTolerance = 0; // счетчик для активации сигнала в зависимости от режима (алгоритмы 009b).
int x, y, z, x1, y1, z1; // координаты
int relX, relY, relZ; // относительные координаты для сравнений
int posCount; // счетчик для включения сигнала тревоги
boolean alarmRaised = false; // признак тревоги
boolean standBy = false; // флаг готовности к работе

// www.technoblogy.com/show?KX0
#define adc_disable() (ADCSRA &= ~(1<<ADEN)) // disable ADC (before power-off)
#define adc_enable() (ADCSRA |= (1<<ADEN)) // re-enable ADC

ISR(WDT_vect)
{
if(f_wdt == 0)
{
f_wdt=1;
}
}

void enterSleep(void)
{
pinMode(alPin, INPUT);
pinMode(posPowPin, INPUT);

adc_disable();
set_sleep_mode(SLEEP_MODE_PWR_DOWN);

sleep_enable();

sleep_mode();

sleep_disable();

power_all_enable();
adc_enable();

pinMode(alPin, OUTPUT);
digitalWrite(alPin, LOW);

pinMode(posPowPin, OUTPUT);
digitalWrite(posPowPin, LOW);
}

void setup() {

/*** Setup the WDT ***/

/* Clear the reset flag. */
MCUSR &= ~(1<<WDRF);

/* In order to change WDE or the prescaler, we need to
* set WDCE (This will allow updates for 4 clock cycles).
*/
WDTCR |= (1<<WDCE) | (1<<WDE);

/* set new watchdog timeout prescaler value */
WDTCR = 1<<WDP0 | 1<<WDP3; /* 8.0 seconds */

/* Enable the WD interrupt (note no reset). */
WDTCR |= _BV(WDIE);

// пин устройства уведомления
pinMode(alPin, OUTPUT);
digitalWrite(alPin, LOW);

// пин питания акселерометра
pinMode(posPowPin, OUTPUT);
digitalWrite(posPowPin, LOW);

pinMode(xPin, INPUT);
pinMode(yPin, INPUT);
pinMode(zPin, INPUT);

delay(1000); // включились-устаканились
blinker(500, 1); // вибро при включении

if (motionDetect(startTimeOut) == true) {

getPos(); // получение позиции

if (abs(x1-y1) > abs(x1-z1)) {
if (abs(x1-z1) > abs(y1-z1)) {
selectX(); // x
}
else {
posMode = 2; // y
posCount = posCountMove; // таймаут без движения
treshold = tresholdMove;
blinker(1000, 1);
blinker(500, 2);
}
}
else {
if (abs(x1-y1) > abs(z1-y1)) {
selectX(); //x
}
else {
posMode = 1; // z
posCount = posCountSleep; // таймаут сна на спине
treshold = tresholdSleep;
blinker(1000, 1);
blinker(500, 1);
}
}
}

if (posMode > 0) {

getPos(); // получение позиции
x = x1; // запоминаем позицию
y = y1;
z = z1;
timeOut = millis();

while ((millis() — timeOut) < timeLimit) {
getPos();
if (comparePos(x, y, z, x1, y1, z1) == false) { // если позиция изменилась — сбрасываем счетчик
blinker(1000, 1); // сигнал об изменении позиции
x = x1;
y = y1;
z = z1;
timeOut = millis();
}
}

// выход из цикла только при фиксации позиции
standBy = true;
blinker(500, 3); // сигнал фиксации позиции
}
else {
blinker(1500, 2);
}

}

void loop() {

if(f_wdt == 1) {

if (posMode == 0) {
/* Don't forget to clear the flag. */
f_wdt = 0;

/* Re-enter sleep mode. */
enterSleep();
}
else {

getPos();

if (posCounter <= posCount && alarmRaised == false) { // пока в пределах таймаута по текущему режиму и нет уведомлений
if (posMode == 2) { // если режим контроля подвижности
if (motionDetect(motionTimeOut) == true) { // если за отведенное время были движения
posTolerance = posTolerance++; // увеличиваем счетчик движений
}

if ((posCounter — posTolerance) > (posCount*movPercentMove)/100) { // если количество движений менее нужного % от интервала измерений
alarmRaised = true; // нужно включить сигнал
}

} else { // если другие режимы
if (comparePos(x, y, z, x1, y1, z1) == true) { // если позиция неизменна
posTolerance = posTolerance++; // подсчет количества «совпавших» позиций (подходит для сна на спине и контроля осанки)
}
}
if (posMode == 1) { // если режим сна
if (posTolerance >= (posCount — 1)) { // если слишком долго на спине
alarmRaised = true; // нужно включить сигнал
}
}

if (posMode == 3) { // если режим контроля осанки
if ((posCounter — posTolerance) > (posCount*movPercentBack)/100) { // если количество правильных позиций менее необходимого % от интервала измерений
alarmRaised = true; // нужно включить сигнал
}
}

posCounter++;

}
else { // Результаты по итогам таймаута

posCounter = 1; // сброс всех счетчиков
posTolerance = 0;
}

if (alarmRaised == true) { // если нужны уведомления

if (posMode == 1) { // если режим сна
blinker(500, 8); // 8 сигналов и…
getPos();
if (comparePos(x, y, z, x1, y1, z1) == false) { //… если повернулись
alarmRaised = false; // отключаем тревогу
posCounter = 0;
}
}

if (posMode == 2) { // если режим движений
blinker(500, 10); // 10 сигналов
alarmRaised = false; // и отключаем тревогу
posCounter = 0;
}

if (posMode == 3) { // если режим осанки
blinker(500, 1); // по одному сигналу пока…
getPos();
if (comparePos(x, y, z, x1, y1, z1) == true) { //… осанка не исправится
alarmRaised = false; // и отключаем тревогу
posCounter = 0;
}
}
posTolerance = 0; // сброс счетчика для уведомлений
}
else {
/* Don't forget to clear the flag. */
f_wdt = 0;

/* Re-enter sleep mode. */
enterSleep();
}
} // posMode == 0
} // wdt
} // loop

// Мигалка

void blinker(unsigned int impulse, byte times) {

for (byte ttimes = 0; ttimes < times; ttimes++) {

digitalWrite(alPin, HIGH);
delay(impulse);
digitalWrite(alPin, LOW);
delay(impulse);

}

}

// Сверка текущего положения с изначальным
boolean comparePos(int xOne, int yOne, int zOne, int xTwo, int yTwo, int zTwo) {

boolean compareRes = false;

relX = xOne — xTwo;
relY = yOne — yTwo;
relZ = zOne — zTwo;

if (abs(relX) < treshold && abs(relY) < treshold && abs(relZ) < treshold) {
compareRes = true; // совпадение значений позиции с учетом погрешности
}
return compareRes;

}

boolean motionDetect(int detectTimeOut) {
boolean motionDetected = false;
getPos();
x = x1; y = y1; z = z1; // запоминаем новое положение, чтобы в следующий раз сравнивать с ним, а не со статикой
timeOut = millis();
while (((millis() — timeOut) < detectTimeOut)) { // после включения есть detectTimeOut секунд, чтобы повернуть брелок в положение выбора режима
if (motionDetected == false) { // если положение не менялось
if (comparePos(x, y, z, x1, y1, z1) == false) { // если положение после включения изменилось
motionDetected = true; // разрешен выбор режима
}
else {
getPos();
}
}
}
return motionDetected;
}

// Вычисление текущей позиции
void getPos(){

digitalWrite(posPowPin, HIGH);
delay(10);

byte i;
unsigned int posX = 0;
unsigned int posY = 0;
unsigned int posZ = 0;

for (i = 0; i < 100; i++) {
posX = posX + analogRead(xPin);
posY = posY + analogRead(yPin);
posZ = posZ + analogRead(zPin);
}

x1 = posX/100;
y1 = posY/100;
z1 = posZ/100;

digitalWrite(posPowPin, LOW);
}

void selectX() {
posMode = 3;
posCount = posCountBack; // таймаут неправильной осанки
treshold = tresholdBack;
blinker(1000, 1);
blinker(500, 3);
}




Корпус

С самого начала у меня не было иллюзий относительно того, что корпус придется делать с нуля из того, что под руками. Первая идея, от которой я довольно быстро отказался, состояла в том, чтобы получить параллелепипед из нескольких склеенных пластиночек полистирола. Было бы пафосно — ну как наборный корпус швейцарских часов — и довольно надежно за счет достаточно высокой прочности и жесткости материала.

Однако пыл мой несколько угас, после того, как я из указанного полистирола (всего-то 1,5 мм толщиной) вырезал по необходимости другой элемент. И, знаете, сложно это оказалось — вырезать из полистирола одним лишь ножом для ковролина. А я ведь делал довольно крупную деталь — около 10х10 см. Понятно, что на более ювелирной работе просто бы умер.

Поэтому я был очень рад, когда случилось очередное озарение — нужно взять полимерную глину, которой легко придать требуемую форму, а потом запечь до твердого состояния. Точнее, это мне так показалось, поскольку я не учел важной детали: у меня что эпоксидка не застывает, что полимерная глина не особенно-то твердеет. Но это лирика.

Разложил компоненты наиболее компактным образом, прикинул размеры корпуса и сделал картонную опалубку соответствующей формы, набил ее полимерной глиной. Затем прорезал в еще мягкой массе необходимые отверстия и поставил запекаться согласно инструкции. А потом еще на столько же времени, поскольку застывать глина никак не желала.

К слову, для батарейки логично было делать отверстие диаметром несколько больше, поэтому изучив сайт Банка России выяснил, что на эту роль подходит монетка 10 рублей, которая чеканится с 2010 года, поскольку ее диаметр — 22 мм. Как раз запас для того, чтобы CR2032 нормально падала в батарейный отсек, который я подпружинил контактом, давным-давно извлеченным из какого-то другого батарейного отсека.

Конструкцию батарейного отсека, конечно, тоже позаимствовал, поскольку вряд ли могу придумать что-то лучше, что избавляет от головной боли при переполюсовке.

. примерно так это выглядит в процессе


. и так после того, как пришлось разобрать уже склеенный корпус


То есть, все довольно просто: приложил компонент, чуть придавил, снял — вырезал по контуру. Ну и плюс прорези и каналы для проводочков. Что хорошо: на Позиционер ушел совсем мизер глины. Натурально, я купил самую маленькую пачку 57 г., и у меня еще осталось 50 граммов этого ценного сырья. Это первое. И второе: чтобы не нарушить геометрию еще пластичного макета, запекать можно не вынимая из картона, поскольку требуемая температура — 100С — 110С, т.е. далеко от возгорания бумаги.

После того, как каркас корпуса кое-как запекся немного подравнял края ножом. А после примерки компонентов выяснил, что эта #$%&*# глина мало того, что не твердеет, как нужно, так еще и «ужарилась» в процессе запекания. Поэтому пришлось аккуратно расширить вырезы. По счастью, итоговый материал оказался не камнем.

Проблему верхней и нижней стенки решил с помощью фрагментов пластиковой карточки, которая, в отличие от полуторамиллиметрового полистирола, отлично режется простыми ножницами. Нижнюю стенку сразу приклеил (и верхнюю тоже, а зря), а с верхней в итоге поступил по-другому. Именно: положил на каркас и аккуратно запаковал полученную конструкцию в термоусадочную трубку. Плюс такого подхода в том, что при необходимости все можно легко разобрать для перепрошивки и так же легко собрать снова.

. душераздирающее зрелище




Ну а внешний вид — что там с него? Все равно я собирался сделать для Позиционера текстильный чехольчик, поскольку это наиболее рационально для прибора, который теоретически берут в постель. А чехольчик как раз улучшит и внешний вид, и тактильные ощущения.

Кстати, не советую белую термоусадку, если пойдете по моим стопам: нарядно, конечно, но пачкается очень быстро.

Почему не отпечатал на 3D-принтере? Во-первых, у меня его нет. И знакомых владельцев нет. Во-вторых, я не умею рисовать модели. В третьих, печатать пришлось бы много, поскольку этот каркас я сначала запланировал 30х70 мм, а в процессе он трансформировался в чуть меньше 30 мм по ширине и около 65 мм по длине. Притом я в еще не успевшей схватиться глине успел прорезать выемку для кнопки, о которой сначала и вовсе забыл.

А пластик каждый раз заново печатать. Хотя не спорю, было бы гораздо круче, чем мой вариант реднековского 3D-моделирования.

Отказ от гарантий

Прежде всего хочу сказать, что Позиционер не только не имеет ни малейшего отношения к медицинским приборам, но и даже до БАДов ему как до Луны. Это эксперимент. Это побег от лени и скуки. Это попытка придумать и сделать что-то этакое своими руками.

Поэтому, конечно, его нельзя рассматривать как спасительное средство или что-то вроде того. Забавный спутник, помощник — да. И, конечно же, его можно обмануть специально, а можно — не специально. Особенно, если неправильно выбирать место для крепления.

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

Иначе никакого толка.
Планирую купить +30 Добавить в избранное +46 +85
+
avatar
  • bg777
  • 22 июля 2015, 09:50
0
Покупал недавно в оффлайне ATTINY45-20SU за 1.4$, смысл не в деньгах а в том что ждать месяц не надо…
+
avatar
  • spc
  • 22 июля 2015, 09:56
+4
Я, конечно, зажрался, но относительно моего обычного маршрута все похожие магазины (ЧиД, Десси, Кварц) находятся в каких-то гребенях. Не говоря уже о Митинском радиорынке. Зато почта — по пути домой.

А так да, согласен — много чего можно купить и на месте.
+
avatar
0
Круто, конечно. Но, имхо, теннисный мячик дешевле и проще :-)
И еще одеяло для беременных купите :-)
+
avatar
  • spc
  • 22 июля 2015, 10:16
0
Так для себя же игрушку делал ) Глядишь, от храпа потом будет спасать.
+
avatar
  • zair
  • 22 июля 2015, 10:30
+3
поделитесь свободным временем со мной, а то забыл уже что это такое ))
+
avatar
  • spc
  • 22 июля 2015, 10:42
0
Спать во сколько ложитесь? )
+
avatar
  • zair
  • 22 июля 2015, 11:14
0
В час ночи… Постройка дома + 2 мелких спиногрызов + нифигасебе еще и работа.
+
avatar
0
Ну вот когда закончите приостановите постройку дома — со временем станет лучше :-)
+
avatar
  • spc
  • 22 июля 2015, 11:49
+2
По выходным ложитесь в 4 утра.
Подарить время: сделано! :)
+
avatar
  • zair
  • 22 июля 2015, 19:52
+2
ААА! Спасибо кэп! )))
+
avatar
+1
И мне тоже скиньте, забыл где раньше качал
+
avatar
+2
Особенное спасибо — за вменяемое описание того, как «прикрутить» ATtiny к среде ардуино. Открыли мне новые горизонты! :)

Потому как даже «мелкие» платки на ATmega для подобных задач — гигантский перебор, а писать под avr в чем-то «родном» вроде avrstudio — дикое уныние!
+
avatar
+1
Согласен! Мне эта часть тоже понравилась.
+
avatar
0
Конечно здорово, но может и правда лучше такие статьи оставлять на гиктаймсе?
+
avatar
  • spc
  • 22 июля 2015, 11:50
0
Я честно пытался, но в комментариях про акселерометр было столько вопросов, что проще оказалось перепечатать.
+
avatar
0
Дали бы ссылку и всего.
+
avatar
  • spc
  • 22 июля 2015, 12:03
0
Два раза давал. Один раз в тексте, второй раз — в комментариях.
+
avatar
  • vigera
  • 22 июля 2015, 11:30
+4
За прямые руки и светлую голову без раздумий плюсую!

Но теперь диву даюсь как человечеству удалось не исчезнуть на протяжении миллионов лет эволюции без столь необходимого беременным девайса? :-)
+
avatar
+1
Опередили! Еще при прочтении преамбулы захотел сказать: и как это в течение тысяч лет беременные обходились без такой хрени… которая, как к тому же ВНЕЗАПНО оказалось,
срабатывает примерно через минуту и будит серией из 8 сигналов, которая не прерывается ни при каких обстоятельствах, кроме разряда батареи
(во, *#@!).
Автор явно пребывает на стадии подростковых заблуждений в отношении физиологии.
Особенно в части терпимости беременной женщины к подобного вида «заботам» «до полного разряда батареи», а также великой пользы прерываний ее, и без того плохого на поздних сроках, сна.
Если есть знания — примени их к чему-то действительно стОящему.
Не можешь придумать/сделать стОящее — не делай ничего!
+
avatar
  • spc
  • 26 июля 2015, 00:54
0
Забавно наблюдать, как вы видите то, что хотите увидеть и вырываете желаемое из контекста. И, как говорится, мы с вами на брудершафт не пили, чтобы на ты обращаться.

ps. Кстати, пять баллов за орфографию и пунктуацию. Редко такое в интернете встретишь, особенно при таком-то накале страстей.
+
avatar
+2
Последние две строки
Если есть знания — примени их к чему-то действительно стОящему.
Не можешь придумать/сделать стОящее — не делай ничего!
никоим образом не являются обращением к Автору обзора. Это всего-навсего сформулированные принципы, которыми я сам руководствуюсь в жизни и работе, чего и Вам советую.
И где Вы увидели накал страстей-то? Просто, вырастив троих детей, я имею некоторый опыт, суть которого и изложил в своем посте. Думаю, Вы сейчас тоже — стремительными темпами — обретаете тот же опыт :-)
P.S. Судя по Вашему обзору, голова и руки у Вас работают качественно. Но зачем уподобляться заграничным строителям потребительского общества? Посмотрите, что они насаждают людям: тыц. И это считается прогрессом в 21-то веке! Сперва — «сиди и не двигайся, наши гаджеты вместо тебя проследят за твоими детьми и за стейками», а потом — «нацепи наш другой гаджет, и сбрасывай вес (накопленный пока ты жрал стейки)».
Нет, истину-таки сказал Рей Бредбери: "…люди — идиоты. Они сделали кучу глупостей: придумывали костюмы для собак, должность рекламного менеджера и штуки вроде IPhone, не получив взамен ничего, кроме кислого послевкусия. А вот если бы мы развивали науку, осваивали Луну, Марс, Венеру… Кто знает, каким был бы мир тогда? Человечеству дали возможность бороздить космос, но оно хочет заниматься потреблением: пить пиво и смотреть сериалы." — Из интервью газете «Аргументы и факты». Вот этого-то и не хочется видеть в наших славянских кулибиных — движения стройными колоннами вслед на заморскимм «поводырями». У нас СВОЙ путь. И он должен быть правильным.
Удачи Автору и всем остальным рукастым-головастым!
P.P.S. Спасибо за то, что оценили правильнописание :-) Нынче это, к сожалению, не в почете… Э-э-х!
+
avatar
  • spc
  • 26 июля 2015, 21:03
+1
Ну если по теме, то исходя из текста, понятно, что я почти сразу осознал, скажем так, бесполезность основной полезной нагрузки устройства. Правда, о чем не писал, так это о том, что эта штука может работать и как антихрап просто потому, что говорят, в большинстве случаев, храпят на спине.

Но ладно, это мелочи. Есть и более интересные вещи, о которых я не писал. Для начала, у меня получилось уйти от готовой платы Arduino и перейти просто на микроконтроллер. Конечно, это многократно описано, но какие ощущения, когда видишь, что этот таракан о восьми ногах работает сам по себе!

Второе для многих покажется элементарным, но лично мне очень нравится алгоритм определения положения при выборе режима. Потому что для меня он получился очень компактным, красивым и универсальным. То есть — нужно просто повернуть Позиционер по одной из осей, неважно каким боком вверх. Иными словами — голова хоть немного, а поработала.

Третье вытекает из второго. Позиционер не столько прибор, сколько концепция. Концепция интерфейса. Минимум кнопок, максимум интуитивно понятных действий. Это еще одна вещь, которой я, простите, горжусь. Причем больше, чем всем остальным.

Наконец, вы удивитесь, но я этой штукой пользуюсь на работе — помогает вспомнить, что нужно пойти размяться, если засиделся за компьютером.

И еще хочу сказать, что всеми руками за то, что крайне печально наблюдать, как прикладная часть взяла верх над фундаментальной. Но, видимо, судьба такая у нас, что поделать.

ps. хотя многие штуки по ссылке тыц очень даже ничего себе.

pps. общество — понятие эфемерное. Общество само по себе не знает, чего хочет. А то, чего оно хочет формируют отдельные инициативные личности и их коллективы. Собственно, исходя из того, что мы видим, в потребленческом образе жизни заинтересованы в первую очередь те, кто руководит этой жизнью. Но это слишком долго и неинтересно обсуждать.
+
avatar
0
Общество само по себе не знает, чего хочет. А то, чего оно хочет формируют отдельные инициативные личности и их коллективы. Собственно, исходя из того, что мы видим, в потребленческом образе жизни заинтересованы в первую очередь те, кто руководит этой жизнью.
Замечательно! ПЯТЬ БАЛЛОВ! Я очень рад, что наше с Вами вИдение объективной реальности совпадает.

Но, видимо, судьба такая у нас, что поделать.
Нет, именно Вы своими усилиями и трудами делаете судьбу. А когда публикуете информацию об этом — то уже и не только свою судьбу: Вы увлекаете читателей идти по Вашим стопам, Вы становитесь НАСТАВНИКОМ. И тут очень важно правильно сформулировать цели и задачи, иначе часть аудитории, прочтя начало доклада, подсознательно отнесет Ваш материал к «устройствам для беременных», и закроет страницу. Как в том анекдоте:
— Петька, тебе нужны трусы?
— Нет…
— И мне нет… Поехали дальше!

Есть и более интересные вещи, о которых я не писал
Прекрасно, что благодаря нашему диалогу, Вы изложили-таки основные изюминки и направленность Вашей работы. А если бы Вы вынесли их в преамбулу — было бы совсем отлично: Вы бы задали вектор осмысливания прикладного применения Вашей разработки, и Ваши читатели уже отнесли бы данную статью к категории фундаментальных идеологических изысканий, а не «еще одна поделка» — вот было бы здорово!
Успехов Вам в изобретательстве и в наставничестве!
+
avatar
+1
Вот только бы жена не попыталась вникать в этот опус, а то придётся паять, что б избавить её от функции толкателя меня в бок, что б не храпел… Хотя в принципе аналогичный прибор на ардуине уже давно засунул в шлем виртуальной реальности, который вертит присоединённой к нему камерой на квадрокоптере :)
+
avatar
  • spc
  • 22 июля 2015, 12:04
0
Наденет шлем и будет толкать квадрокоптером )
+
avatar
  • cdkey
  • 22 июля 2015, 13:06
0
а я и не храплю, мне просто мотоциклы снятся )))
+
avatar
  • reo
  • 22 июля 2015, 13:19
0
подобные штуки нужны не в беременности, а после и детям. в РФ почти нет мониторов подвижности младенца при риске внезапной смерти. Штука выглядет примерно так
ebay.com/itm/301674636581
www.4baby.ru/index.php?productID=6504
если нет шевелений долго- дает сигнал, на начальном этапе побудить сделать вдох можно без реанимации, просто похлопыванием
про СВДС
ru.wikipedia.org/wiki/%D0%A1%D0%B8%D0%BD%D0%B4%D1%80%D0%BE%D0%BC_%D0%B2%D0%BD%D0%B5%D0%B7%D0%B0%D0%BF%D0%BD%D0%BE%D0%B9_%D0%B4%D0%B5%D1%82%D1%81%D0%BA%D0%BE%D0%B9_%D1%81%D0%BC%D0%B5%D1%80%D1%82%D0%B8
+
avatar
+3
Здесь я опять выбрал самый простой путь. Именно — нагуглил то, что было нужно. Первое — код для укладывания ATtiny85 в режим максимально глубокого сна с пробуждением по watchdog-таймеру.
как же надоела эта убогая копипаста. нет чтоб заглянуть в исходники <avr/sleep.h>, <avr/power.h>, <avr/wdt.h> и разобраться что вы делаете как и зачем. там все уже есть!!! чтож вы ленивые то все такие.

для примера вот этот код
/*** Setup the WDT ***/

/* Clear the reset flag. */
MCUSR &= ~(1<<WDRF);

/* In order to change WDE or the prescaler, we need to
* set WDCE (This will allow updates for 4 clock cycles).
*/
WDTCSR |= (1<<WDCE) | (1<<WDE);

/* set new watchdog timeout prescaler value */
WDTCSR = 1<<WDP0 | 1<<WDP3; /* 8.0 seconds */

/* Enable the WD interrupt (note no reset). */
WDTCSR |= _BV(WDIE);
можно заменить вот этим.

void setup_watchdog(byte sleep_time)
{
wdt_enable(sleep_time);
WDTCR |= _BV(WDIE); /* enable interrupts instead of MCU reset when watchdog is timed out
used for wake-up MCU from power-down */
}
функция wdt_enable все делает за вас!!! и описана в <avr/wdt.h>
sleep_time может принимать следущие значения WDTO_15MS, WDTO_30MS, WDTO_60MS, WDTO_120MS, WDTO_250MS, WDTO_500MS, WDTO_1S, WDTO_2S, WDTO_4S, WDTO_8S
+
avatar
  • spc
  • 22 июля 2015, 23:58
0
Спасибо!
+
avatar
  • spc
  • 30 августа 2015, 17:39
0
Наконец руки дошли попробовать на практике. Видимо, я что-то делаю не так, но результат оказался не совсем тот, что я ожидал. Код я использовал не в виде процедуры, а просто вставил в секцию setup в среде Arduino. Т.е. вот так:

void setup() {

wdt_enable(WDTO_8S);
WDTCR |= _BV(WDIE);

}


Это не вся секция, там еще выключение ADC, настройка пинов и функция tone() для сигнализации включения.

Поведение в итоге такое: контроллер спит около 16 секунд (может быть и просыпается каждые 8 секунд, но мультиметром я этого обнаружить не смог). После этого перезагружается и снова засыпает на 16 секунд.

При этом «забракованный» код отрабатывает, как положено — 8 секунд сна, просыпается, отрабатывает loop и по команде в конце — снова засыпает без перезагрузки.

Это не просьба о помощи, а просто на случай, если вам будет интересно.
+
avatar
0
Специально для вас написал подробный пример
+
avatar
  • spc
  • 15 сентября 2015, 10:55
0
Спасибо!
+
avatar
  • spc
  • 21 октября 2015, 22:48
0
Очевидно, у меня все-таки кривые руки. У меня ATtiny85-20, arduino-tiny, Arduino Mega 2560 в качестве программатора.

В ATtiny85 записал загрузчик, при загрузке кода выбрал вариант с частотой 8 МГц. Единственная модификация кода — добавил в setup() однократное включение светодиода на секунду.

Питание — CR2032.

Поведение следующее: на старте, как положено, на секунду загорается светодиод. Потом, где-то через 16-18 секунд светодиод начинает мерцать с частотой около 10 Гц. Видео не совсем это передает, но как-то передает:



И еще не могу понять вот этот фрагмент:

all_pins_output();

  digitalWrite(pinLed,HIGH);  //led blink
  delay(3000);
  digitalWrite(pinLed,LOW);

  all_pins_output();


Здесь или второй раз all_pins_output() не надо, или должно быть all_pins_input(), как мне кажется.
+
avatar
+1
Вспомнился знакомый, который к рождению ребенка делал сигнализатор мокрых пеленок. Когда я его потом спросил — пригодилось? Он ответил — ты бы слышал, как ребенок вопит, когда пеленка мокрая, сигнализатора вовсе не слышно :)
+
avatar
  • spc
  • 24 июля 2015, 09:44
0
Как в том анекдоте про то, что не все алгоритмы системных программистов подходят простым юзерам )
+
avatar
  • ksiman
  • 24 июля 2015, 10:15
0
ты бы слышал, как ребенок вопит, когда пеленка мокрая, сигнализатора вовсе не слышно :)
Ребёнок просто резервирует работу сигнализатора для повышения надёжности его работы :)
+
avatar
0
после изучения и этого обзора с комментами, появилась мысля использования акселерометра и героскопа как датчики охранного устройства, но опять же привязка к МК и прошивкам всё портит))