Выглядит оно вот так:
В самой системе это объект класса systemStates:
Частично эта тема затрагивалась в топике Медицинское оборудование, но сейчас я хочу рассказать про интеграцию в системе спортивного тренажёра.
Итак, есть эллептический тренажёр вот такого плана:
Вещь хорошая, сама по себе, но бывает достаточно сложно себя заставить на нём заниматься Я думаю, что 99% владельцев подобных штук сталкивались с тем, что первое время тренажёр используется очень активно, но потом то одно, то второе и занятия становятся в лучшем случае нерегулярными. Собственно, сужу по себе. Так вот, идея была в том, чтобы система могла напоминать о тренировках и вести статистику использования тренажёра.
Для реализации задуманного была сделана вот такая приставка:
По сути приставка выполняет несколько функций:
* Счётчик импульсов для отправки в систему с последующим рассчётом скорости (за основу взят этот проект)
* Две кнопки для выбора пользователя, который в данный момент занимается
* Датчик температуры (просто "до кучи")
Питается эта приставка от того же блока питания, что идёт на сам тренажёр (9 Вольт)
Если просто встать на тренажёр и начать заниматься, то данные, получаемые от приставки по радио-каналу (433Mhz, протокол отсюда) будут приходить, но не сохраняться в системе. Алиса при этом скажет что-то вроде "Спорт это здорово, но я не знаю, кто сейчас занимается".
Если с помощью нажатия на кнопку будет выбран текущий пользователь, то в систему будет поступать информация со счётчика импульсов, а дальше преобразовываться в пройденное расстояние и скорость. Так же будет сохранено время последней тренировки, которое используется в вышеописанном статусе здоровья (если прошло более 3х дней с последней тренировки, то статус здоровья становится жёлтым). Данные о "пробеге" сохраняются в объект пользователя и пока просто суммируются, но у меня большие планы на то, как их использовать в будущем, а именно:
* Можно устраивать сореванования, кто больше тренировался за неделю
* Можно просто вести статистику с графиками дневную/недельную/месячную и т.п.
* Можно задействовать какую-то систему бонусов, рекордов, совместных целей и т.п. (тут надо подумать над игровой моделью)
* Можно включать интересную аудио-книгу во время тренировки и выключать по окончанию
* ... (предложите варианты)
Вот, собственно, что касается пользовательской части.
Теперь о реализации.
Схема устройства (извините, не умею я их рисовать):
Компоненты:
* Контролллер Arduino UNO (любой arduino-совместимый)
* Кнопки (2 шт)
* Геркон с магнитом
* Сопротивление на 10K для кнопок и геркона (3 шт)
* Датчик температуры (Dallas 18B20)
* Сопротивление 4k7 для датчика температуры
* Передатчик на 433Mhz
* Блок питания (использовался от тренажёра)
Скетч прошивки:
КодПоказать
Код: Выделить всё
#include <OneWire.h>
#include <DallasTemperature.h>
#include <VirtualWire.h>
#include <EasyTransferVirtualWire.h>
#include <EEPROM.h> //Needed to access the eeprom read write functions
#define PIN_LED 13
#define PIN_LED_T 13
#define PIN_RF 2
#define PIN_TEMP 3
#define PIN_BUTTON1 4
#define PIN_BUTTON2 5
#define PIN_KEY 6
#define PERIOD_SEND_POWER 600 // seconds
#define PERIOD_MAX_COUNTER 5 // circles
#define PERIOD_READ_TEMP 120 // seconds
#define PERIOD_SEND_TEMP 10 // minutes
#define TEMP_ACC 0.3 // temperature accuracy
#define COMMAND_TEMP 10 // command code for temperature
#define COMMAND_INPUT_ON 20 // command code for button
#define COMMAND_COUNTER 15 // command code for counter
#define RF_SEND_COUNTER 3 // number of packets to send with transmission
unsigned int unique_device_id = 0;
int old_status=0;
long int timeSentPower=0;
long int timeCheckPowerMillis=0;
long int counterPower=0;
long int timePassedPower=0;
long int timeCheckedTemp=10000000;
long int timesentTemp=0;
float sent_temperature=0;
long int uptime = 0;
long int old_uptime = 0;
byte old_button1=0;
byte old_button2=0;
OneWire oneWire(PIN_TEMP);
DallasTemperature sensors(&oneWire);
//create object
EasyTransferVirtualWire ET;
struct SEND_DATA_STRUCTURE{
//put your variable definitions here for the data you want to send
//THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
//Struct can'e be bigger then 26 bytes for VirtualWire version
unsigned int device_id;
unsigned int destination_id;
unsigned int packet_id;
byte command;
int data;
};
//give a name to the group of data
SEND_DATA_STRUCTURE mydata;
//This function will write a 2 byte integer to the eeprom at the specified address and address + 1
void EEPROMWriteInt(int p_address, unsigned int p_value)
{
byte lowByte = ((p_value >> 0) & 0xFF);
byte highByte = ((p_value >> 8) & 0xFF);
EEPROM.write(p_address, lowByte);
EEPROM.write(p_address + 1, highByte);
}
//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
unsigned int EEPROMReadInt(int p_address)
{
byte lowByte = EEPROM.read(p_address);
byte highByte = EEPROM.read(p_address + 1);
return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}
void blinking(int count) {
for(int i=0;i<count;i++) {
digitalWrite(PIN_LED, HIGH);
delay(200);
digitalWrite(PIN_LED, LOW);
delay(200);
}
}
void sendRFData() {
Serial.print("Transmitting packets ... ");
for(int i=0;i<RF_SEND_COUNTER;i++) {
if (i>0) {
delay(200);
}
digitalWrite(PIN_LED_T, HIGH);
ET.sendData();
digitalWrite(PIN_LED_T, LOW);
}
Serial.println("DONE");
}
void setup()
{
randomSeed(analogRead(0));
pinMode(PIN_LED, OUTPUT);
pinMode(PIN_LED_T, OUTPUT);
pinMode(PIN_KEY, INPUT);
pinMode(PIN_BUTTON1, INPUT);
pinMode(PIN_BUTTON2, INPUT);
Serial.begin(9600);
ET.begin(details(mydata));
vw_set_tx_pin(PIN_RF);
vw_setup(2000); // Bits per sec
randomSeed(analogRead(0));
// Device ID
Serial.print("Getting Device ID... ");
unique_device_id=EEPROMReadInt(0);
if (unique_device_id<10000 || unique_device_id>60000 || unique_device_id==26807) {
Serial.print("N/A, updating... ");
unique_device_id=random(10000, 60000);
EEPROMWriteInt(0, unique_device_id);
}
Serial.println(unique_device_id);
mydata.device_id = unique_device_id;
mydata.destination_id = 0;
}
void loop()
{
uptime=round(millis()/1000);
if (uptime!=old_uptime) {
Serial.print("Uptime: ");
Serial.println(uptime);
old_uptime=uptime;
timeCheckedTemp++;
timesentTemp++;
}
int current_button1=digitalRead(PIN_BUTTON1);
if (current_button1!=old_button1 && current_button1==1) {
Serial.println("Button 1 pressed");
mydata.packet_id = random(65535);
mydata.command = COMMAND_INPUT_ON;
mydata.data = 1;
sendRFData();
}
old_button1=current_button1;
int current_button2=digitalRead(PIN_BUTTON2);
if (current_button2!=old_button2 && current_button2==1) {
Serial.println("Button 2 pressed");
mydata.packet_id = random(65535);
mydata.command = COMMAND_INPUT_ON;
mydata.data = 2;
sendRFData();
}
old_button2=current_button2;
if (timeCheckedTemp>PERIOD_READ_TEMP) {
// TEMP SENSOR 1
float current_temp=0;
sensors.requestTemperatures();
current_temp=sensors.getTempCByIndex(0);
if (current_temp == 0.00 || current_temp>50) {
Serial.print("Temp sensor error: ");
Serial.println(current_temp);
blinking(3);
sensors.begin();
} else {
timeCheckedTemp=0;
Serial.print("T: ");
Serial.println(current_temp);
float diff=(float)sent_temperature-(float)current_temp;
if ((current_temp<50) && (current_temp>-5) && ((abs(diff)>=TEMP_ACC) || (timesentTemp>(PERIOD_SEND_TEMP*60)))) {
Serial.print("Temperature: ");
Serial.println(current_temp);
mydata.packet_id = random(65535);
mydata.command = COMMAND_TEMP; //temp
mydata.data = (int)(current_temp*100);
sendRFData();
timesentTemp=0;
sent_temperature=current_temp;
}
}
}
timePassedPower=uptime-timeSentPower; // time passed since the last power data has been sent
int current_ir=digitalRead(PIN_KEY);
if (current_ir!=old_status) {
if ((current_ir==0) && (millis()-timeCheckPowerMillis>0.5*1000)) {
timeCheckPowerMillis=millis();
counterPower++;
Serial.print(" Power counter: ");
Serial.print(counterPower);
Serial.print(" time passed: ");
Serial.println(timePassedPower);
if ((counterPower>=PERIOD_MAX_COUNTER) || (timePassedPower>=PERIOD_SEND_POWER)) {
timeSentPower=uptime;
mydata.packet_id = random(65535);
mydata.command = COMMAND_COUNTER; //power counter
Serial.print("Sending value: ");
mydata.data = (int)counterPower*1000+(int)timePassedPower;
Serial.println(mydata.data);
sendRFData();
counterPower=0;
}
}
old_status=(int)current_ir;
} else if (timePassedPower>60 && counterPower>0) {
counterPower=0;
timeSentPower=uptime;
mydata.packet_id = random(65535);
mydata.command = COMMAND_COUNTER; //power counter
Serial.print("Sending value on idle: ");
mydata.data = (int)counterPower*1000+(int)timePassedPower;
Serial.println(mydata.data);
sendRFData();
}
}
Код: Выделить всё
} elseif ($device_id=='55889') {
runScript('trainingUnitEvent',array('command_id'=>$command_id,'data'=>$data));
Код сценария приёма данных от тренажёра (trainingUnitEvent):
сцераний trainingUnitEventПоказать
Код: Выделить всё
$data=$params['data'];
$command_id=$params['command_id'];
if ($command_id=='20' && $data=='1') {
say("Сергей, добро пожаловать на тренировку!",2);
setGlobal('ThisComputer.NowTraining','Serge');
setTimeOut('TrainingObjectTimeout',"setGlobal('ThisComputer.NowTraining','');",1*60*60); //1h
setGlobal('ThisComputer.TrainingWarning',0);
}
if ($command_id=='20' && $data=='2') {
say("Алеся, добро пожаловать на тренировку!",2);
setGlobal('ThisComputer.NowTraining','Alesya');
setTimeOut('TrainingObjectTimeout',"setGlobal('ThisComputer.NowTraining','');",1*60*60); //1h
setGlobal('ThisComputer.TrainingWarning',0);
}
if ($command_id=='15') {
$nowTraining=getGlobal('ThisComputer.NowTraining');
if (!$nowTraining) {
if (!getGlobal('ThisComputer.TrainingWarning')) {
say("Спорт это здорово, но я не знаю, кто сейчас занимается на тренажёре",2);
setGlobal('ThisComputer.TrainingWarning',1);
setTimeOut('TrainingWarningTimeout',"setGlobal('ThisComputer.TrainingWarning',0);",10*60); //10 minutes
}
} else {
/*
290 cycles
2 000 meters
1 cycle = 2 000 / 290 = 6.897 meters
*/
$counter=floor($data/1000); // cycles counter
$passed=$data-$counter*1000; // time passed (seconds)
$distance=round($counter*6.897,3);
setGlobal($nowTraining.'.trainingCounterNow',(int)getGlobal($nowTraining.'.trainingCounterNow')+$counter);
setGlobal($nowTraining.'.trainingDistanceToday',(float)getGlobal($nowTraining.'.trainingDistanceToday')+$distance);
setGlobal($nowTraining.'.trainingDistanceWeek',(float)getGlobal($nowTraining.'.trainingDistanceWeek')+$distance);
if ($passed>0) {
$speed=round($distance/$passed,2); // m / s
setGlobal($nowTraining.'.trainingSpeed',$speed);
setTimeOut($nowTraining.'gotTrainingTimeOut',"callMethod($nowTraining.'.gotTraining');",5*60);
}
}
//callMethod("ElectroCounter.onUpdate",array("counter"=>$counter,"passed"=>$passed));
}
Собственно, всё