пятница, 23 мая 2014 г.

Травма сустава. Чем мазать?

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

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

В одном из ящиков я обнаружил большое количество упаковок с различными мазями, из которых только название "Фастум гель" оказалось знакомым благодаря рекламе.

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

На счет обезбаливающего - не знаю, но греть стало почти сразу. Особенно хорошо стало припекать когда разогрелся немного. УХ! Хорошо! :о)

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

Вернул доктору мазь, обсудили лечебный эффект от использованных препаратов. Доктору тоже "Фастум гель" не очень нравится. С "Капсикамом" он лично не сталкивался, а по опыту использования очень хвалил "Кетонал".

Резюмируя. "Фастум гель" - ерунда, не достойная чести занимать место в аптечке приличного спортсмена. "Капсикам" - прошел полевые испытания и показал неплохие результаты. "Кетонал" - то, что доктор прописал.

P.S. Может возникнуть вопрос, почему доктор изначально не предложил "Кетанол". А за время, пока я лечился у нас успели врачи поменяться. :о)


вторник, 20 мая 2014 г.

Arduino. Работаем с часами DS3231 по i2C через Wire.

Копнул более основательно часики.

Для начала запустил сканер устройств на шине i2C. Рекомендую сохранить этот скетч в свою библиотеку, чтобы он был под руками - очень полезная штука!

Сканер обнаруживает два устройства на шине i2C по следующим адресам:
1. 0x57 - это адрес микросхемы памяти AT24C32
2. 0x68 - адрес микросхемы DS3231SN

Cтоит учитывать - сканер возвращает адреса в формате шестнадцатеричной системы. Т.е в привычном нам десятичном формате получим адреса 87 и 104. Нас интересует адрес 0x68 (104). Сделал себе первую пометочку.

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

Самое интересное началось на странице 11, где присутствует следующая таблица:

Figure 1. Timekeeing Registers

Addressbit 7
MSB
bit 6bit 5bit 4bit 3bit 2bit 1bit 0
LSB
FunctionRange
00H010 secondssecondsSeconds00-59
01H010 minutesminutesMinutes00-59
02H012/24AM/PM
10 hour
10 hourhourHours1-12+AM/PM
00-23
03H00000dayDay1-7
04H0010 datedateDate00-31
05HCentury0010 monthmonthMonth/Century01-12+Century
06H10 yearyearYear00-99
07HA1M110 secondssecondsAlarm 1
seconds
00-59
08HA1M210 minutesminutesAlarm 1
minutes
00-59
09HA1M312/24AM/PM
10 hour
10 hourhourAlarm 1
hours
1-12+AM/PM
00-23
0AHA1M4DY/DT10 datedayAlarm 1 day1-7
dateAlarm 1 date1-31
0BHA2M210 minutesminutesAlarm 2 minutes00-59
0CHA2M312/24AM/PM
10 hour
10 hourhourAlarm 2 hours1-12+AM/PM
00-23
0DHA2M4DY/DT10 datedayAlarm 2 day1-7
dateAlarm 2 date1-31
0EHEOSCBBSQWCONVRS2RS1INTCNA2IEA1IEControl---
0FHOSF000EN32kHzBSYA2FA1FControl/status---
10HSIGNDATADATADATADATADATADATADATAAgign Offset---
11HSIGNDATADATADATADATADATADATADATAMSB of Temp---
12HDATADATA000000LSB of Temp---
Рекомендую все-таки скачать даташит - там есть такие замечательные отметочки в оригинальной таблице - крышечки. Как я понял, обозначают, какое состояние бита отвечает за состояние AM/PM или DY/DT, например. В моей таблице этого нет.

Первоначально интерес тут представляют строки с 00H до 06H и c 0EH до 12H. Блок с алармами (07H-0DH) пока пропущу - им займусь позже.

Далее приведен скетч, работающий напрямую с данными на DS3231.

#include <Wire.h>
 #define DS3231_I2C_ADDRESS 0x68
  
 byte seconds, minutes, hours, day, date, month, year;
 char weekDay[4];  
 byte tMSB, tLSB;
 float temp3231;
  
 void setup()
 {
   Wire.begin();
   Serial.begin(9600);
   //set control register to output square wave on pin 3 at 1Hz
   Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address
   Wire.write(0x0E); //выставляемся в 14й байт 
   Wire.write(B00000000); //сбрасываем контрольные регистры 
   Wire.write(B10001000); //выставляем 1 на статус OSF и En32kHz
   Wire.endTransmission();
 }
  
 void loop() {    
   watchConsole(); //читаем консоль. Если нажата "T" читаем из консоли следующий 7 байт и устанавливаем время.    
   get3231Date(); //получаем данные 

          //и потом их выводим через Serial.print()    
   Serial.print(weekDay); Serial.print(", "); Serial.print(month, DEC); Serial.print("/"); Serial.print(date, DEC); Serial.print("/"); Serial.print(year, DEC); Serial.print(" - ");
   Serial.print(hours, DEC); Serial.print(":"); Serial.print(minutes, DEC); Serial.print(":"); Serial.print(seconds, DEC);    
   Serial.print("   Temperature: "); Serial.print(get3231Temp());Serial.print("; ");
          Serial.println(get3231Register(0x0F)); //получаем и выводим статусовый регистр
   delay(1000);
 }
  
 // Convert normal decimal numbers to binary coded decimal
 byte decToBcd(byte val)
 {
   return ( (val/10*16) + (val%10) );
 }
  
 void watchConsole()
 {
   if (Serial.available()) {      // Look for char in serial queue and process if found
     if (Serial.read() == 84) {      //If command = "T" Set Date
       set3231Date();
       get3231Date();
       Serial.println(" ");
     }
   }
 }
    
 void set3231Date()
 {
 //T(sec)(min)(hour)(dayOfWeek)(dayOfMonth)(month)(year)
 //T(00-59)(00-59)(00-23)(1-7)(01-31)(01-12)(00-99)
 //Example: 02-Feb-09 @ 19:57:11 for the 3rd day of the week -> T1157193020209
  
   seconds = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); // Use of (byte) type casting and ascii math to achieve result. 
   minutes = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
   hours   = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
   day     = (byte) (Serial.read() - 48);
   date    = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
   month   = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
   year    = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));

   Wire.beginTransmission(DS3231_I2C_ADDRESS); //начали сессию
   Wire.write(0x00); //поставляемся в нулевой байт для чтения данных
   Wire.write(decToBcd(seconds));
   Wire.write(decToBcd(minutes));
   Wire.write(decToBcd(hours));
   Wire.write(decToBcd(day));
   Wire.write(decToBcd(date));
   Wire.write(decToBcd(month));
   Wire.write(decToBcd(year));
   Wire.endTransmission();//закрыли сессию
 }
  
 byte get3231Register(byte regNo) {
   // send request to receive data starting at register regNo
   Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address
   Wire.write(regNo); // start at register regNo
   Wire.endTransmission();
   Wire.requestFrom(DS3231_I2C_ADDRESS, 1); // request one byte
   if(Wire.available()) return  Wire.read();
 }
  
 void set3231Register(byte regNo, byte value) {
   Wire.beginTransmission(DS3231_I2C_ADDRESS);
   Wire.write(regNo);
   Wire.write(value);
   Wire.endTransmission();
 }
  
 void get3231Date()
 {
   // send request to receive data starting at register 0
   Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address
   Wire.write(0x00); // start at register 0
   Wire.endTransmission();
   Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // request seven bytes
  
   if(Wire.available()) {
     seconds = Wire.read(); // get seconds
     minutes = Wire.read(); // get minutes
     hours   = Wire.read();   // get hours
     day     = Wire.read();
     date    = Wire.read();
     month   = Wire.read(); //temp month
     year    = Wire.read();

//Конвертация происходит очень просто - битовым оператором & мы очищаем МЛАДШИЕ (0,1,2,3) четыре бита , сдвигаем все вправо на 4 позиции (очищенные биты
//вылезут при этом с левой стороны и результат умножаем на 10, т.к. в старших битах хранится информация о десятках секунд.
//После этого битовым оператором & очищаем СТАРШИЕ (4,5,6,7)четыре бита и получаем секунды. Затем суммируем десятки секунд и единиц.
     seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111));
     minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111));
     hours   = (((hours & B00110000)>>4)*10 + (hours & B00001111)); 
     day     = (day & B00000111); // 1-7
     date    = (((date & B00110000)>>4)*10 + (date & B00001111)); // 1-31
     month   = (((month & B00010000)>>4)*10 + (month & B00001111)); //msb7 is century overflow
     year    = (((year & B11110000)>>4)*10 + (year & B00001111));
   }
   else {
     //oh noes, no data!
   }
    
   switch (day) {     
     case 1:
       strcpy(weekDay, "Mon");
       break;
     case 2:
       strcpy(weekDay, "Tue");
       break;
     case 3:
       strcpy(weekDay, "Wed");
       break;
     case 4:
       strcpy(weekDay, "Thu");
       break;
     case 5:
       strcpy(weekDay, "Fri");
       break;
     case 6:
       strcpy(weekDay, "Sat");
       break;
            case 7:
       strcpy(weekDay, "Sun");
       break;
   }
 }
  
 float get3231Temp()
 {
   //temp registers (11h-12h) get updated automatically every 64s
   Wire.beginTransmission(DS3231_I2C_ADDRESS);
   Wire.write(0x11);
   Wire.endTransmission();
   Wire.requestFrom(DS3231_I2C_ADDRESS, 2);
    
   if(Wire.available()) {
     tMSB = Wire.read(); //2's complement int portion
     tLSB = Wire.read(); //fraction portion      
     temp3231 = (tMSB & B01111111); //do 2's math on Tmsb
     temp3231 += ( (tLSB >> 6) * 0.25 ); //only care about bits 7 & 8
   }
   else {
     //oh noes, no data!
   }
      
   return temp3231;
 }

Вот как-то так. Непонятно пока, что произойдет при совпадении текущего времени с установленным в байтах 07h-0Ah и в 0Bh-0Dh, но этим займусь завтра.


понедельник, 19 мая 2014 г.

Arduino. Программирование

Начал разбираться, как использовать аппаратный будильник часов и понял, что познания мои о среде программирования в Arduino весьма поверхностны. Нашел полезный ресурс и решил поделиться. Так же справочник "языка Arduino" можно посмотреть на российском сайте Arduino.

Еще раз напомню о замечательной подборке материалов по Arduino, датчикам и библиотекам на robocraft.ru


воскресенье, 18 мая 2014 г.

Arduino. Звук - пищим динамиком

В коробке остался 8Ом-ный динамик, 0,5W. Порылся в закромах, нашел ссылку на статью о подключении динамика к Arduino.

Схема подключения

Динамик маломощный, звук тихий. Я подключал без сопротивления 100 Ом-ного. Надо помнить, что это понижающий резистор, и при больших Омах мы просто не услышим свой динамик. Если найти низкоомный потенциометр (менее или равный 1кОм), то можно будет регулировать громкость звучания динамика.

Простейший скетч

void setup() {
}

void loop() {
tone(8, 311,300);
delay(500);
noTone(8);
}
}


суббота, 17 мая 2014 г.

Arduino. HC-SR04 - ультразвуковой модуль измерения расстояния

Имеющихся в наличии датчиков все меньше и меньше и выбора особого уже не остается. :о) Сегодня подключаем датчик HC-SR04 - ультразвуковой датчик измерения расстояния.

Дополнительных библиотек к нему не надо, что существенно упрощает нам жизнь. ;о) Немного дополнительной информации можно почерпнуть отсюда или отсюда.

Схема подключения самая простая.

Скетч для работы с датчиком
#define Trig 9
#define Echo 8

void setup()
{
pinMode(Trig, OUTPUT); //инициируем как выход
pinMode(Echo, INPUT); //инициируем как вход
Serial.begin(9600);
/* задаем скорость общения. В нашем случае с компьютером */
}
unsigned int impulseTime=0;
unsigned int distance_sm=0;

void loop()
{
digitalWrite(Trig, HIGH);
/* Подаем импульс на вход trig дальномера */
delayMicroseconds(10); // равный 10 микросекундам
digitalWrite(Trig, LOW); // Отключаем
impulseTime=pulseIn(Echo, HIGH); // Замеряем длину импульса
distance_sm=impulseTime/58; // Пересчитываем в сантиметры
Serial.println(distance_sm); // Выводим на порт
delay(100);
/* ждем 0.1 секунды, Следующий импульс может быть излучён, только после исчезновения эха от предыдущего. Это время называется периодом цикла (cycle period).
Рекомендованный период между импульсами должен быть не менее 50 мс. */
}

Думаю, как подключить дальномер к УСТРОЙСТВУ и как изменить скетч - понятно. Только стоит обратить внимание на то, что Digital 8 у нас занят датчиком HC-SR501. Поэтому предлагаю Echo подключить к Digital 7, и внести необходимые изменения в блоке определения пинов.

На этом имеющиеся в наличии датчики закончились - остались только два двигателя и контроллер к ним. И еще нашел динамик 8 Ом-ный. Так что впереди борьба с колесами и музыка. :о)

При тестировании выявился такой неприятный момент - на дистанции более 2 метров расстояние фиксируется не стабильно. Т.е. и 201 покажет и 203, но может и 820 показать или 930. Причем чем дальше от приемника, тем реже на экране возникают правильные цифры. До двух метров меня дальномер "ловил" хорошо. Как вариант может попробовать сделать трубку из бумаги на излучатель, чтобы сузить сектор? Должно быть меньше помех и проще будет определять, куда излучение "полетело". Хотя, если все так просто, почему производители изначально не делают трубки более длинными? Непонятно...

Трубка не помогла. На расстоянии до 150 см фиксация четкая. Выше - начинают выскакивать 875 и другие "неправильные" числа. Вывод - датчик стабильно работает на полтора метра. Дальше - сложно будет отсеять достоверные результаты от помех.


пятница, 16 мая 2014 г.

Arduino. Датчик движения HC-SR501

Дело было вечером, делать было нечего... Решил пересмотреть свою копилку материалов по датчикам к Arduino. Наткнулся на схему проверки работоспособности датчика движения HC-SR501

Вместо рекомендуемого 4.5 кОм резистра воткнул 15 кОм (ну, что нашел). Датчик весело замигал светодиодом при движении. Много интересного про датчик можно прочитать, например, на этом ресурсе.

До подключения к плате осталось пара шагов - подключил выход Out к 8 разъему (Digital) на плате микроконтроллера. По совету, увиденному в одном из форумов подвязал Out резистором к земле. Номинал резистора оставил 15 кОм. Питание (+5В) и землю взял с платы.

Простейший скетч для проверки работы датчика с Arduino

int pirPin = 8;
int val;

void setup() {
    pinMode(pirPin,INPUT);
    Serial.begin(9600);
}

void loop() {
    val = digitalRead(pirPin); //read state of the PIR
    if (val == LOW) {
      Serial.println("No motion"); //if the value read is low, there was no motion
    }
    else {
      Serial.println("Motion!"); //if the value read was high, there was motion
    }
    delay(1000);
}

Когда все заработало, перецепил все на схему к уже собранному УСТРОЙСТВУ. Модифицировал скетч - при появлении сигнала от датчика включается подсветка экрана и горит 5 секунд.

#include Wire.h
#include DS3231.h
#include LiquidCrystal.h
/*
The circuit:
* 5V to Arduino 5V pin
* GND to Arduino GND pin
* SDA (data) to Analog #4
* SCL (clock) to Analog #5
*/

// Connect via i2c, default address #0 (A0-A2 not jumpered)
LiquidCrystal lcd(0);
DS3231 Clock;
bool h12=false;//True is 12-h, false is 24-hour.;
bool PM;
bool Century=false;

int pirPin = 8;
int val;
int msec;

void setup() {
// set up the LCD's number of rows and columns:
lcd.begin(16, 2);
lcd.clear();
lcd.setBacklight(LOW);
Wire.begin();
pinMode(pirPin,INPUT);
}

void loop() {
int second,minute,hour,date,month,year,temperature;

val = digitalRead(pirPin); //read state of the PIR
if (val == LOW){
lcd.setBacklight(LOW); //if the value read is low, there was no motion
}
else {
lcd.setBacklight(HIGH);
msec=millis()/1000;
}
if (millis()/1000-msec<5) lcd.setBacklight(HIGH);

year=Clock.getYear();
month=Clock.getMonth(Century);
date=Clock.getDate();
hour=Clock.getHour(h12,PM);
minute=Clock.getMinute();
second=Clock.getSecond();
temperature=Clock.getTemperature();

lcd.print("20");
lcd.print(year,DEC);
lcd.print('-');
if (month<10) lcd.print('0');
lcd.print(month,DEC);
lcd.print('-');
if (date<10) lcd.print('0');
lcd.print(date,DEC);

// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(0, 1);
if (hour<10) lcd.print('0');
lcd.print(hour,DEC);
lcd.print(':');
if (minute<10) lcd.print('0');
lcd.print(minute,DEC);
lcd.print(':');
if (second<10) lcd.print('0');
lcd.print(second,DEC);
lcd.print(' ');
lcd.print("Temp:");
lcd.print(temperature);
delay(500);
lcd.setCursor(0,0);
}

тут завтра будет фотография УСТРОЙСТВА


Arduino. DS3231 и I2C/SPI LCD1602 в одном флаконе

Сегодня мне удалось собрать первый функциональный прибор, который может работать без компьютера. Ранее, я уже писал о подключении высокоточных часов и I2C LCD 1602 по шине I2C. Следующей стояла задача совместного использования этих устройств.

В интернете нашел несколько статей по использованию шины I2C. Есть более развернутые статьи, есть менее. Интересующимся рекомендую вот эту статью. Или вот эту. В статье рекомендуют привязывать разъемы SDA и SCL к +5 вольтам. Номинал резисторов от 1кОм до 10 кОм. Я поставил 1кОм (других не нашлось).

Схема сборкиФотография

Скетч просто собрал из двух, использовавшихся ранее для часов и экрана

#include Wire.h
#include DS3231.h
#include LiquidCrystal.h
/**
The circuit:
* 5V to Arduino 5V pin
* GND to Arduino GND pin
* SDA (data) to Analog #4
* SCL (clock) to Analog #5
**/

// Connect via i2c, default address #0 (A0-A2 not jumpered)
LiquidCrystal lcd(0);
DS3231 Clock;
bool h12=false;//True is 12-h, false is 24-hour.;
bool PM;
bool Century=false;

void setup() {
// set up the LCD's number of rows and columns:
lcd.begin(16, 2);
lcd.clear();
lcd.setBacklight(LOW);
Wire.begin();
}

void loop() {
int second,minute,hour,date,month,year,temperature;

year=Clock.getYear();
month=Clock.getMonth(Century);
date=Clock.getDate();
hour=Clock.getHour(h12,PM);
minute=Clock.getMinute();
second=Clock.getSecond();
temperature=Clock.getTemperature();

lcd.print("20");
lcd.print(year,DEC);
lcd.print('-');
if (month<10) lcd.print('0');
lcd.print(month,DEC);
lcd.print('-');
if (date<10) lcd.print('0');
lcd.print(date,DEC);

// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(0, 1);
if (hour<10) lcd.print('0');
lcd.print(hour,DEC);
lcd.print(':');
if (minute<10) lcd.print('0');
lcd.print(minute,DEC);
lcd.print(':');
if (second<10) lcd.print('0');
lcd.print(second,DEC);
lcd.print(' ');
lcd.print("Temp:");
lcd.print(temperature);
delay(500);
lcd.setCursor(0,0);
}


Вот такие вот часы у меня получились. ;о)