Многие, кто видит Intel Edison впервые, обычно видят этот модуль уже установленным на плате расширения Arduino, которая имеет ощутимый размер, не предполагающий никакого мобильного использования. Поэтому большинству разработчиков даже в голову не приходит, что Edison может быть использован в компактном или носимом устройстве.
Как видно на фото, сам вычислительный модуль Intel Edison достаточно мал. Основная трудность использования его в таком виде состоит в том, что контакты для подключения периферии очень малы. Используемый разъем Hirose имеет 70 контактов при длине около 1 см. Одним из решений этой проблемы является набор Xadow Wearable Kit for Intel Edison от Seeed, содержащий в себе необходимые переходники и небольшие датчики, позволяющий разработать действительно компактное носимое устройство.
Набор состоит из небольших плат расширения с различной функциональностью, которые подключаются при помощи гибкого плоского кабеля FCC. Платы можно подключать друг за другом как гирлянду, формируя необходимую функциональность устройства.
Вот, что входит в этот набор:
- Xadow — Edison – основной разъем для подключения Intel Edison.
- Xadow — 3Axis Accelerometer — датчик ускорения по трем осям.
- Xadow — Edison Programmer – модуль подключения к компьютеру по USB.
- Xadow — Barometer BMP 180 – барометр и термометр.
- Xadow — Edison SD – модуль подключения SD карты.
- Battery – батарея.
- Xadow — Q Touch Sensor – сенсорные кнопки.
- Digital RGB LED Flexi-Strip — лента из пяти управляемых RGB-светодиодов.
- Xadow — NFC — NFC считыватель.
- Xadow — Breakout — плата с разъемами для подключения обычных датчиков.
- Xadow – Buzzer – пищалка.
- Xadow — Vibration Motor – вибромотор.
- Xadow — OLED – монохромный OLED дисплей разрешением 128x64.
- NFC Tags – три 3 NFC-метки.
- FFC cable package – набор из шлейфов.
- Power cable White, Red, Yellow – разноцветные проводочки.
Замечания
Перед тем как начать описывать работу, хочу сразу обратить внимание, на некоторые особенности и проблемы которые возникли у меня в процессе работе и возможные методы их решения. Во-первых, я сразу рекомендую обновить прошивку платы.
Текущею версию можно посмотреть командой
configure_edison –version
На момент написания статьи это 159.
Также желательно обновить установленные библиотеки. Должно быть соединение с интернетом.
opkg update
opkg upgrade
Все примеры в статьи приведены для Arduino IDE. Разработчик набора Seeed, приводит только их.
Я попытался использовать плату и через Intel XDK for IoT и через С++, но ничего не заработало.
Возможно это из-за того, что плата схематически отличается от стандартной платы расширения Arduino. Еще есть какие-то проблемы при работе с I2C. Не всегда стабильно работает акселерометр. Стабильно он работал в игре, когда был подключен после OLED дисплея, может это совпадение, а может есть какая-то особенность. У меня так и не получилось заставить работать плату барометра и термометра.
Иногда терялось подключение к плате из Arduino IDE, не мог скомпилироваться и передаться скетч. Если такое происходит, надо на плате остановить работающий скетч:
systemctl stop clloader
затем надо очистить папку /sketch, затем на компьютере закрыть все работающие Arduino IDE. Выключить плату Intel Edison (shutdown now), затем включить снова, подержав кнопку PWR пока не загорится светодиод.
Иногда может потребоваться сделать так, чтобы скетч сам запускался при подачи питания на плату Edison. Это можно сделать следующим образом. Создать папку /etc/init.d, в ней создать файл automateSketch.sh, в который поместить две строки:
#!/bin/sh
exec /sketch/sketch.elf /dev/ttyGS0 /dev/ttyGS0
Сделать файл выполняемым:
chmod +x automateSketch.sh
И добавить его в загрузку:
update-rc.d automateSketch.sh defaults
Начало работы
У вас должны быть установлены на компьютере все драйвера для Intel Edison.
Вся работы с модулями из этого набора выполняется через Arduino скетчи, поэтому, если у вас еще не установлена среда разработки Arduino IDE, надо её установить. Скачать её можно с сайта www.arduino.cc.
Чтобы работать с платой, надо подключить Xadow-Edison Programmer модуль. Должны быть подключены оба USB разъема, так как по одному подается питающее напряжение, а второй обеспечивает подключение Arduino IDE. Переключатель на плате должен стоять в положении Device. В Arduino IDE надо выбрать плату Intel Edison через Boards Manager.
После завершения загрузки системы на Edison (около 30 сек) надо найти номер виртуального порта в диспетчере устройств на компьютере.
Intel Edison Virtual Com Port (COM25) — надо выбрать в Arduino IDE.
USB Serial Port (COM29) — можно использовать для подключение к консоли, например, через Putty.
Подключение модулей
Любые подключения я рекомендую делать, отключив питание от модуля Intel Edison. Это уменьшит вероятность выхода элементов из строя. Также иногда при горячем подключении перестают нормально загружаться скетчи и всё равно приходится делать выключение и включение платы.
Все модули подключаются с использованием FFC – гибкого плоского шлейфа. Они отличаются длиной и шириной. Ширина должна соответствовать разъему, в который он подключается. Все шлейфы подключаются синей частью вверх и контактами вниз. Есть два типа разъемов на платах. Первый открывается вверх как крышка. Отрыв его, шлейф надо легко вставить до упора, примерно на 3 мм. Крышку закрыть.
Второй тип разъема представляет собой выдвигающийся зажим. Он менее удобен в использовании. Его надо поддеть по краям и вытянуть примерно на 1 мм. Действовать надо аккуратно, я, например, умудрился сломать одну из ножек разъёма. Вставить шлейф надо с небольшим усилием до упора, он должен войти примерно на 3 мм. Затем закрыть разъем, задвинув его обратно. Закрывается он с некоторым усилием.
Вставленные шлейфы должны хорошо держаться и не выпадать сами из разъемов.
Все модули имеют разъемы на двух сторонах, но у каждого модуля и у основной платы с одной стороны на углах сделаны скосы. Все платы должны быть расположены так, чтобы всё эти метки находились у всех модулей с одной стороны, например, слева.
Arduino библиотеки
Для работы с модулями потребуются библиотеки. Их можно взять по адресу github.com/Seeed-Studio/Xadow_Edison_Demos Надо скачать весь архив одним .ZIP файлом. Скорее всего, установить сразу весь архив не получится, поэтому папки с нужными библиотеками надо будет добавлять через меню среды разработки.
Battery
Обычный Li-Ion аккумулятор. Напряжение 3.7V. Ёмкость 500 mAh.
Xadow — Edison
Это основная плата для подключения модуля Edison. По размеру она немного больше самого модуля Edison. Установленный в разъем, Edison крепко держится и без крепёжных винтов. Но для надежности можно использовать винты от Arduino модуля.
Имеется разъем для подключения батареи. По периметру расположены четыре разъема для плат расширения. Верхний разъем для подключения платы Edison-Programmer. Боковые — для плат с датчиками и актюаторами. Нижний широкий разъем для подключения модуля с SD картой.
Есть две кнопки PWR и FW_RCVR. PWR позволяет включать и выключать устройство длинным нажатием. 9 секунд — чтобы выключить, 2 секунды — чтобы включить.
Есть небольшой зеленый светодиод рядом с разъемом для батареи, который показывает режим питания. Эксперименты показали следующее. Если он мигает, то питание происходит от USB. Если он горит непрерывно, то происходит зарядка подключенного аккумулятора. Если он не горит, то аккумулятор заряжен.
Собственно, этой платы достаточно, чтобы заработал Edison. Ну, конечно, еще понадобится батарея.
Именно это компактное работающее устройство произвело на меня большое впечатление. Небольшая платка с батарейкой — миниатюрный компьютер с настоящим Linux’ом, к которому можно подключиться по WiFi.
Xadow — Edison Programmer
Плата для подключения к компьютеру. Можно использовать для отладки и питания от компьютера. Содержит два Micro USB разъема:
— с надписью UART используется для подключения к компьютеру через COM и для подачи питающего напряжения.
— с надписью Device/Host используется для работы через Arduino IDE. Режим работы Device или Host выбирается выключателем. Для Arduino надо выбирать Device.
Xadow — Edison SD
Содержит разъем для подключения SD карты. Подключается к нижнему разъему основной платы самым широким шлейфом.
В устройствах SD карта находится в /dev/mmcblk1
Монтирование делается как обычно в Linux. Надо создать где-нибудь пустую папку, например:
mkdir /home/data
Затем подмонтировать карту в эту папку:
mount /dev/mmcblk1 /home/data
Чтобы при загрузки Linux карта монтировалась автоматически, надо в файл /etc/fstab добавить строку
/dev/mmcblk1 /home/data auto auto 0 0
Xadow – OLED 12864
OLED экран разрешением 128x64 точки.
Описание экрана на сайте seeed.
Экран подключается по интерфейсу I2C. Адрес устройства 0x3C.
Программа, которая выводит на экран текст и просто байты.
#include <Wire.h>
#include <SeeedOLED.h>
void setup()
{
Wire.begin();
SeeedOled.init(); //initialze SEEED OLED display
SeeedOled.clearDisplay(); //clear the screen and set start position to top left corner
SeeedOled.setBrightness(255);
SeeedOled.setNormalDisplay(); //Set display to normal mode (i.e non-inverse mode)
SeeedOled.setPageMode(); //Set addressing mode to Page Mode
SeeedOled.setTextXY(0,0); //Set the cursor to Xth Page, Yth Column
SeeedOled.putString("Xadow and Edison"); //Print the String
SeeedOled.setTextXY(7,0); //Set the cursor to Xth Page, Yth Column
for(int k = 0; k < 128; k++)
{
SeeedOled.sendData(k);
}
}
void loop() {
}
Скорость, с которой можно перерисовать весь экран составляет несколько кадров в секунду.
Экран содержит 8 строк, в каждой по 16 символов.
В библиотеке SeeedOLED есть следующие функции:
init() – инициализация экрана.
sendCommand(unsigned char command)
setBrightness(unsigned char Brightness) – установка яркости
setHorizontalMode()
setPageMode()
setTextXY(unsigned char Row, unsigned char Column) – установка позиции текстового курсора. Если экран расположит в альбомной ориентации, то первый параметр это номер строки, второй параметр, номер столбца. На самом деле устанавливает позицию для вывода любых данных.
clearDisplay() – очистка экрана.
sendData(unsigned char Data) – посылка данных на экран
putChar(unsigned char C) – вывод символа. Код 32-127
putString(const char *String) – вывод строки
putNumber(long long_num) — вывод целого числа
putFloat(float floatNumber,unsigned char decimal) — вывод float числа
putFloat(float floatNumber) — вывод float числа
drawBitmap(unsigned char *bitmaparray,int bytes) – отрисовывает картинку. Картинка выводится по текущим координатам.
setHorizontalScrollProperties(bool direction,unsigned char startPage, unsigned char endPage, unsigned char scrollSpeed)
activateScroll() – включает скроллинг
deactivateScroll() – выключает скроллинг.
setNormalDisplay() – устанавливает нормальное, не инверсное отображение. Сохраняет текущее изображение.
setInverseDisplay() – меняет цвет экрана на инверсный. Сохраняет текущее изображение.
Xadow – Buzzer. Пищалка
Пищалка подключена к двум контактам 11 и 13. Их надо одновременно включать и выключать. Чтобы создать звук, надо периодически подавать 0 и 1 на эти два контакта.
Эксперименты показали, что достаточно на один из входов подать 1 и периодически менять состояние другого, звук тоже воспроизводится.
Пример вывода звука:
void buzzerInit()
{
pinMode(11,OUTPUT);
pinMode(13,OUTPUT);
}
void buzzerOn()
{
digitalWrite(11,HIGH);
digitalWrite(13,HIGH);
}
void buzzerOff()
{
digitalWrite(11,LOW);
digitalWrite(13,LOW);
}
void buzzerSignal(int t_ms)
{
unsigned long cur = millis();
while((millis()-cur) < t_ms )
{
buzzerOn();
delayMicroseconds(150);
buzzerOff();
delayMicroseconds(150);
}
}
void setup()
{
buzzerInit();
buzzerSignal(1000);
}
void loop()
{
}
Так как воспроизведение делается программно, то звук получается не особенно чистым. Было бы лучше воспользоваться ШИМ модуляцией.
Что интересно, у меня не заработал аналог этого скетча, который я переписал на Intel XDK for IoT и С++. Пищалка просто молчала.
Xadow — Vibration Motor. Вибромотор
Управление вибромотором похоже на работу с пищалкой. Его можно включить и выключить.
// Xadow - Vibro
void vibroInit()
{
pinMode(11,OUTPUT);
pinMode(10,OUTPUT);
}
void vibroOn()
{
digitalWrite(11,HIGH);
digitalWrite(10,HIGH);
}
void vibroOff()
{
digitalWrite(11,LOW);
digitalWrite(10,LOW);
}
void setup()
{
vibroInit();
vibroOn();
delay(500);
vibroOff();
}
void loop()
{
}
Так же как и в случае с пищалкой, можно управлять одним пином, предварительно выставив на другом 1.
Xadow — Q Touch Sensor
Плата содержит с обратной стороны три сенсорные кнопки. Подключение по протоколу I2C по адресу 0x1B.
Чувствительность достаточно высокая. Срабатывает даже через несколько слоев бумаги или пленки. Поэтому в конечном устройстве кнопки можно спрятать за нарисованными картинками.
Программа из примера, выводит номер нажатой кнопки:
#include <Wire.h>
#include "Seeed_QTouch.h"
void setup()
{
Serial.begin(9600);
Wire.begin();
}
void loop()
{
int tn = QTouch.touchNum();
if(tn>=0)
{
Serial.print("KEY");
Serial.print(tn);
Serial.println(" touched");
}
delay(10);
}
Xadow – NFC
http://www.seeedstudio.com/wiki/Xadow_-_NFC
NFC считыватель. Подключение по I2C, SPI, UART. Работает на частоте 13.56 МГц. Работает на чтение и на запись. Поддерживает протокол ISO14443 Type A and Type B. Поддерживает P2P соединение. Есть антенна и провод для её подключения.
В наборе есть три чистые NFC метки, на которые можно записать информацию.
Для работы с этим модулем надо установить библиотеки NDEF, PN532, PN532_HSU, PN532_I2C, PN532_SPI из общей библиотеки примеров от Seeed.
Для тестирования меток и считывателя можно взять программу из примеров, которая считывает значение с поднесенной метки и показывает его в консоли NDEF->readTag
Метки, которые идут в комплекте имеют серийные номера. Они не отформатированы и выдают примерно следующее:
Tag is not NDEF formatted.
NFC Tag - Mifare Classic
UID 6E A5 0B 01
Чтобы записать на метки какую-нибудь информацию, надо их сначала отформатировать. Пример NDEF->FormatTag:
Для записи информации в метку, можно взять пример NDEF->WriteTag.
Вот результат считывания с записанной метки:
NFC Tag - Mifare Classic
UID 5E B1 FB 01
NDEF Message 1 record, 28 bytes
NDEF Record
TNF 0x1 Well Known
Type Length 0x1 1
Payload Length 0x18 24
Type 55 U
Payload 00 49 6E 74 65 6C 20 45 64 69 73 6F 6E 20 77 69 74 68 20 58 61 64 6F 77 .Intel Edison with Xadow
Record is 28 bytes
Xadow — 3Axis Accelerometer
Датчик ускорения. Работает по трем осям. Измеряемый диапазон до ±16 g. Подключается по протоколу I2C с адресом 0x53. Используется чип ADXL345. Надо подключать библиотеку DigitalAccelerometer_ADXL345 из комплекта.
Есть четыре диапазона измерения ±2g, ±4g, ±8g, ±16g. Они отличаются точностью и коэффициентом пересчета.
Даташит на чип pdf1.alldatasheet.com/datasheet-pdf/view/254714/AD/ADXL345.html
Данные выдаются 16-разрядными числами. В дополнительном коде. Библиотека выдает числа как int, поэтому их надо скорректировать, например, так:
void correct(int &a)
{
if( a > 32767 )
a = -(65536 - a);
}
Если включен диапазон ±2g, то получаемое значение надо делить на 256. Как я уже писал, у меня были трудности с его работой. Иногда он исчезал и в консоли Linux появлялись ошибки I2C. Но подобного поведения не было, когда я подключил к основному модулю сначала экран, а затем акселерометр.
Xadow — Barometer BMP 180
Барометр и термометр. Предел измерения давления 300-1100 гПа (что соответствует -500..+9000 м над уровнем моря). Подключается по I2C по адресу 0x77.
К сожалению, у меня так и не получилось заставить его работать. Возможно, это была ошибка в I2C интерфейсе, может я испортил модуль подключив его не с той стороны.
Xadow – Breakout
Плата для подключения обычных датчиков. Содержит контакты 3.3V, SCL, SDA, TX0, GND, SCK, MOS1, MOS0, A5. Содержит две площадки для размещения разъема, к которому можно подключать стандартные модули seeed. Один подписан Serial, другой I2C. Используется для подключения ленты из светодиодов.
Digital RGB LED Flexi-Strip
Лента на 5 светодиодов. RGB светодиоды WS2812B. Они управляются по одному проводу.
Для того, чтобы их подключить, придется немного попаять. Нужно использовать плату Xadow-Breakout.
Передача информации делается программно, поэтому иногда бывают ошибки при передаче информации и возникают случайные вспышки светодиодов.
Программа бегущий огонёк:
#include <Wire.h>
#include "Seeed_ws2812.h"
#define SIG_PIN 12
#define LEN_NUM 5
WS2812 strip = WS2812(LEN_NUM, SIG_PIN);
void setup() {
strip.begin();
//Serial.begin(115200);
}
int pos = 0;
void loop() {
strip.WS2812SetRGB(pos,255,0,0);
strip.WS2812Send();
delay(100);
strip.WS2812SetRGB(pos,0,0,0);
strip.WS2812Send();
delay(100);
pos = (pos+1)%LEN_NUM;
}
Создание игры
Мне захотелось собрать что-нибудь из этого набора. В голову пришла идея следующей игры. Коробка с игрой содержит сверху ленту светодиодов. На ней могут загораться огни. В зависимости от места, где они включились и от цвета, надо наклонить коробку на 45 градусов в определенном направлении. Например, если загорелся центральный светодиод зелёным, то надо наклонить вперед от себя. Если центральный загорелся красным, то надо наклонить к себе. Если загорелся боковой крайний зеленый светодиод справа, то наклон производится вправо, если загорелся он же, но красным, то наклон надо делать влево. Игра не просто проверяет реакцию, но и заставляет сначала сообразить, а потом принимать решение.
Нам потребуются:
- гироскоп,
- OLED экран,
- лента светодиодов,
- модуль расширения для подключения ленты,
- плата с кнопками,
- батарея.
Соединяем всё в следующем порядке. Со стороны разъема батареи подключаем Breakout, потом сенсорные кнопки. С другой стороны экран, а затем гироскоп. Корпус я склеил из плотной цветной бумаги. Для экрана прорезал отверстие. Плату с кнопками поместил под экран. Как я уже писал ранее, кнопки очень чувствительные. На лицевой стороне над кнопками я нарисовал их обозначения.
Все платы приклеены скотчем через бумагу, чтобы ничего не испортить.
В Linux настроен автозапуск скетча.
Включается игра нажатием на кнопку PWR в течение примерно 9 секунд на основном модуле. Выключается нажатием в течении 3 секунд.
При запуске игры появляется меню, из которого можно из запустить игру кнопкой A или вызвать справку об игре кнопкой B. Возврат из справки кнопкой со стрелкой.
Игра состоит из пяти серий. В каждой зажигается случайный светодиод и надо наклонить коробку в соответствующую сторону. При правильном повороте будет написано You Win! При неправильном или если доступное время закончилось, то You Lose!
Весь код игры я поместил в один файл и постарался сделать его максимально простым, чтобы в нем было легче разобраться.
Исходный код скетча игры
#include <Wire.h>
#include <Seeed_ws2812.h>
#include <SeeedOLED.h>
#include <Seeed_QTouch.h>
#include <ADXL345.h>
// состояния игры
const int stateMenu = 1;
const int stateCount = 3;
const int stateHelp = 4;
// кнопки
const int keyBack = 0;
const int keyA = 1;
const int keyB = 2;
// LED лента
const int ledSigPin = 12;
const int numLed = 5;
// ответы
const int answerLeft = 1;
const int answerRight = 2;
const int answerUp = 3;
const int answerDown = 4;
const int colorGreen = 1;
const int colorRed = 2;
const int numLevels = 6;
const int answerID = 5;
const int angleScale = 100;
const int angleReact = 100;//тангенс угла срабатывания * angleScale
int numWins;// число побед
int numLosts;// число проигрышей
int gameState;// состояние игры
const int numSets = 5;// число сетов в игре
// кодирование уровней
int levels[numLevels][numLed+1] = { {colorGreen,0,0,0,0,answerLeft},
{colorRed,0,0,0,0,answerRight},
{0,0,0,0,colorGreen,answerRight},
{0,0,0,0,colorRed,answerLeft},
{0,0,colorGreen,0,0,answerUp},
{0,0,colorRed,0,0,answerRight}};
// правильный ответ
int rightAnswer;
WS2812 strip = WS2812(numLed, ledSigPin);
ADXL345 adxl;
// текущий уровень игры
int gameLevel;
// инициализация акселерометра
void initAccel()
{
adxl.powerOn();
//set activity/ inactivity thresholds (0-255)
adxl.setActivityThreshold(75); //62.5mg per increment
adxl.setInactivityThreshold(75); //62.5mg per increment
adxl.setTimeInactivity(10); // how many seconds of no activity is inactive?
//look of activity movement on this axes - 1 == on; 0 == off
adxl.setActivityX(1);
adxl.setActivityY(1);
adxl.setActivityZ(1);
//look of inactivity movement on this axes - 1 == on; 0 == off
adxl.setInactivityX(1);
adxl.setInactivityY(1);
adxl.setInactivityZ(1);
//look of tap movement on this axes - 1 == on; 0 == off
adxl.setTapDetectionOnX(0);
adxl.setTapDetectionOnY(0);
adxl.setTapDetectionOnZ(1);
//set values for what is a tap, and what is a double tap (0-255)
adxl.setTapThreshold(50); //62.5mg per increment
adxl.setTapDuration(15); //625us per increment
adxl.setDoubleTapLatency(80); //1.25ms per increment
adxl.setDoubleTapWindow(200); //1.25ms per increment
//set values for what is considered freefall (0-255)
adxl.setFreeFallThreshold(7); //(5 - 9) recommended - 62.5mg per increment
adxl.setFreeFallDuration(45); //(20 - 70) recommended - 5ms per increment
//setting all interrupts to take place on int pin 1
//I had issues with int pin 2, was unable to reset it
adxl.setInterruptMapping( ADXL345_INT_SINGLE_TAP_BIT, ADXL345_INT1_PIN );
adxl.setInterruptMapping( ADXL345_INT_DOUBLE_TAP_BIT, ADXL345_INT1_PIN );
adxl.setInterruptMapping( ADXL345_INT_FREE_FALL_BIT, ADXL345_INT1_PIN );
adxl.setInterruptMapping( ADXL345_INT_ACTIVITY_BIT, ADXL345_INT1_PIN );
adxl.setInterruptMapping( ADXL345_INT_INACTIVITY_BIT, ADXL345_INT1_PIN );
//register interrupt actions - 1 == on; 0 == off
adxl.setInterrupt( ADXL345_INT_SINGLE_TAP_BIT, 1);
adxl.setInterrupt( ADXL345_INT_DOUBLE_TAP_BIT, 1);
adxl.setInterrupt( ADXL345_INT_FREE_FALL_BIT, 1);
adxl.setInterrupt( ADXL345_INT_ACTIVITY_BIT, 1);
adxl.setInterrupt( ADXL345_INT_INACTIVITY_BIT, 1);
}
void initGame()
{
SeeedOled.clearDisplay();
SeeedOled.setNormalDisplay();
SeeedOled.setPageMode();
switchToMenu();
}
void setup()
{
strip.begin();
SeeedOled.init();
initAccel();
initGame();
}
void cleanLED()
{
for( int led = 0; led < numLed; led++ )
strip.WS2812SetRGB(led,0,0,0);
}
void updateLED()
{
strip.WS2812Send();
}
void drawMenu()
{
SeeedOled.clearDisplay();
SeeedOled.setTextXY(0,2);
SeeedOled.putString("Edison Game");
SeeedOled.setTextXY(2,1);
SeeedOled.putString("A - Start");
SeeedOled.setTextXY(4,1);
SeeedOled.putString("B - Help");
}
void drawHelp()
{
SeeedOled.clearDisplay();
char *messages[] = { "Center green -",
"tilt forward." ,
"Center red -" ,
"tilt backward.",
"Green edge -" ,
"tilt same side." ,
"Red edge - tilt",
"other side."
};
for( int k = 0; k < 8; k++ )
{
SeeedOled.setTextXY(k,0);
SeeedOled.putString(messages[k]);
}
}
void makeNewLevel()
{
gameLevel = rand() % numLevels;
rightAnswer = levels[gameLevel][answerID];
}
void showGameStart()
{
SeeedOled.clearDisplay();
SeeedOled.setTextXY(0,0);
SeeedOled.putString("Press Start");
}
int getKeys()
{
int tn = QTouch.touchNum();
return tn;
}
int haveTime = 20;
void showGameLevel()
{
for( int k = 0; k < numLed; k++ )
{
int color = levels[gameLevel][k];
switch(color)
{
case colorRed : strip.WS2812SetRGB(k,255,0,0); break;
case colorGreen: strip.WS2812SetRGB(k,0,255,0); break;
default : strip.WS2812SetRGB(k,0,0,0);
}
}
updateLED();
}
void switchToSets()
{
numWins = 0;
numLosts = 0;
switchToPlay();
}
//------------------------------------
void switchToPlay()
{
cleanLED();
updateLED();
SeeedOled.clearDisplay();
SeeedOled.setTextXY(3,3);
SeeedOled.putString("Get ready!");
int waitTime = 2000 + (rand()%4)*500;
delay(waitTime);
makeNewLevel();
SeeedOled.setTextXY(3,3);
SeeedOled.putString(" ");
haveTime = 100;
gameState = stateCount;
}
void doPlay()
{
if( haveTime >= 0 )
{
SeeedOled.setTextXY(3,1);
char str[100];
sprintf(str,"-- TILT NOW --");
SeeedOled.putString(str);
SeeedOled.setTextXY(5,6);
sprintf(str,"%d ",haveTime);
SeeedOled.putString(str);
showGameLevel();
int answerID = getAnswerID();
haveTime--;
if( answerID !=0 || haveTime < 0)
{
char *message;
if( answerID != rightAnswer || haveTime < 0)
{
numLosts++;
message = "You lose!";
}
else
{
numWins++;
message = "You win!";
}
cleanLED();
updateLED();
SeeedOled.clearDisplay();
SeeedOled.setTextXY(3,4);
SeeedOled.putString(message);
delay(1000);
if( numWins + numLosts == numSets )
{
switchToMenu();
}
else
{
switchToPlay();
}
}
}
}
//-----------------------
void switchToHelp()
{
drawHelp();
gameState = stateHelp;
cleanLED();
updateLED();
}
void doHelp()
{
cleanLED();
updateLED();
int key = getKeys();
if( key == keyBack )
switchToMenu();
}
//-----------------------
void switchToMenu()
{
drawMenu();
gameState = stateMenu;
cleanLED();
updateLED();
}
void doMenu()
{
int key = getKeys();
if( key == keyA )
switchToSets();
if( key == keyB )
switchToHelp();
}
//-----------------------
void correct(int &a)
{
if( a > 32767 )
a = -(65536 - a);
}
int getAnswerID()
{
int x,y,z;
adxl.readXYZ(&x, &y, &z);
correct(x);
correct(y);
correct(z);
int v1 = angleReact;
if( z != 0 )
v1 = y*angleScale/z;
if( v1 > angleReact )
return answerRight;
if( v1 < -angleReact )
return answerLeft;
int v2 = angleReact;
if( z != 0 )
v2 = x*angleScale/z;
if( v2 > angleReact )
return answerUp;
if( v2 < -angleReact )
return answerDown;
return 0;
}
void gameStep()
{
switch(gameState)
{
case stateCount: doPlay();break;
case stateMenu: doMenu();break;
case stateHelp: doHelp();break;
}
}
void loop()
{
gameStep();
delay(10);
}