Привет, Хабр! Вас тоже огорчало, что PLS-разъёмы плат Arduino Uno и Mega установлены без соблюдения сетки с шагом 2.54 мм, отчего невозможно создать собственный шилд на базе обычной макетки под пайку?

А ещё обидно, что на упомянутых платах не предусмотрено никаких кнопок, кроме сброса, а программно управляемый светодиод есть, но всего один, если не считать присоединённых к линиям Tx и Rx, задействованным при загрузке скетча и обмене данными с компьютером. То есть, без подключения внешних компонентов почти ничего нельзя сделать.

Сегодня я соберу вариант Arduino Uno с тремя подключёнными к GPIO светодиодами и тремя кнопками, не считая сброса. А расположение разъёмов остаётся стандартным, чтобы не терять совместимости с шилдами.

Если вам нужны компактность и шаг 2.54 мм для паечных и беспаечных макетных плат, то лучше будет воспользоваться Arduino Nano. Но к нему не получится подключить стандартный шилд, в отличие от контроллера из набора OPEN-SMART UNO R3, сборкой которого мы займёмся.

▍ Процесс монтажа


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

Наш сегодняшний радиоконструктор — не исключение, и начинать мы будем с резисторов. Их здесь всего пять штук, четыре из которых номиналом 1 килоом и один 10-килоомный.



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

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



Следующий этап — установка кнопки сброса, цангового разъёма под кварцевый резонатор и пяти керамических конденсаторов, три из которых служат фильтрами питания и имеют ёмкость 100 нанофарад. Два других конденсатора по 22 пикофарады задействованы в обвязке кварца.



Затем паяем кроватку под микроконтроллер, не забывая о соблюдении ключа.



Теперь настаёт очередь разъёма для внутрисхемного последовательного программирования, пищалки, электролитического конденсатора и светодиодов.

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



Сборка нашего Ардуино близится к завершению, и теперь нужно установить разъёмы для присоединения шилдов.



В последнюю очередь паяем разъём USB-B и три красивые большие кнопки. Останется только вставить в панельки кварц и микроконтроллер.



Производители набора положили в комплект запасной кварц, один лишний резистор на 10 килоом и два на 1 килоом. Всегда приятно получать в подарок дополнительные компоненты, особенно таких ходовых номиналов, которые обычно расходуются быстрее всего!



После подачи питания по USB-кабелю плата весело замигала светодиодом. Микроконтроллер в комплекте оказался прошит скетчем «Blink a LED», что в мире Ардуино является эквивалентом «Hello, world!» — «Привет, мир!».



Благодаря этому, для проверки работоспособности собранного устройства даже не нужен компьютер. Достаточно USB повербанка или блока питания напряжением 5 вольт.

Отметим, что стабилизатор питания на плате OPEN-SMART UNO R3, в отличие от классического Arduino Uno, не предусмотрен.

▍ Что может Ардуино?


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

#include "OpenSmartUnoR3.h"

void setup() {
  Board.init(true);
}

void loop() {
  Board.loop();

}

Как видим, этот скетч просто инициализирует плату и запускает бесконечный цикл из демонстрационной библиотеки.

#ifndef OpenSmartUnoR3_h
#define OpenSmartUnoR3_h
#include "EasyBuzzer.h"

#define BUZZER 6

class OpenSmartUnoR3 {

  protected:
   bool _test;
   EasyBuzzerClass eb;

  public:
    enum SWITCH {
      SW1 = 2,
      SW2 = 3,
      SW3 = 4
    };

    enum LED {
      LED1 = 7,
      LED2 = 8,
      LED3 = 13
    };

    void init(bool test = false) {
      _test = test;
      pinMode(LED1, OUTPUT);
      pinMode(LED2, OUTPUT);
      pinMode(LED3, OUTPUT);
      pinMode(BUZZER, OUTPUT);

      pinMode(SW1, INPUT_PULLUP);
      pinMode(SW2, INPUT_PULLUP);
      pinMode(SW3, INPUT_PULLUP);
    }

    void loop() {
      if (_test) {
          setLED(LED1, getSwitch(SW1));
          setLED(LED2, getSwitch(SW2));
          setLED(LED3, getSwitch(SW3));

          if(getSwitch(SW1) && getSwitch(SW2)){
            eb.beep(1000);
          }else {
            eb.stopBeep();
          }
      }
    }

    bool getSwitch(SWITCH sw) {
        return !digitalRead(sw);
    }

    static void setLED(LED selected, bool status) {
        digitalWrite(selected, status);
    }

};

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

▍ Смотрим видео



Итак, мы убедились в работоспособности нашей платы. Теперь давайте сделаем что-нибудь более интересное. Для этого подключим к свежесобранному контроллеру Rich Shield Two от того же OPEN-SMART.

▍ Индикатор заряда батареи


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



Для управления этим индикатором используется специализированная микросхема TM1651 — драйвер светодиодов с цифровой регулировкой яркости, функцией сканирования клавиатуры и двухпроводным последовательным интерфейсом.



Скетч устанавливает яркость светодиодов на максимум и имитирует индикацию процесса заряда аккумулятора.

#include "TM1651.h"
#define CLK 10 // определяем, куда подключены выводы TM1651 
#define DIO 11
TM1651 batteryDisplay(CLK,DIO);
void setup()
{
  batteryDisplay.init();
  batteryDisplay.set(BRIGHTEST);//BRIGHT_TYPICAL = 2,BRIGHT_DARKEST = 0,BRIGHTEST = 7;
  batteryDisplay.frame(FRAME_ON);
}
void loop()
{
  charging();
}

void charging()
{
  for(uint8_t level = 0; level < 8; level ++)
  {
    batteryDisplay.displayLevel(level);
	delay(500);
  }
}

Для этого используется библиотека Battery Display.
TM1651.h
//  Author:Fred.Chu
//  Date:14 August, 2014
//
//  Applicable Module:
//                     Battery Display v1.0
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//
//  Modified record:
//
/*******************************************************************************/

#ifndef TM1651_H
#define TM1651_H
#include <inttypes.h>
#include <Arduino.h>
	//************definitions for TM1651*********************
#define ADDR_AUTO  0x40
#define ADDR_FIXED 0x44
	
#define STARTADDR  0xc0 
	/**** definitions for the frame of the battery display *******/
#define FRAME_ON   1
#define FRAME_OFF  0
	/**************definitions for brightness***********************/
#define  BRIGHT_DARKEST 0
#define  BRIGHT_TYPICAL 2
#define  BRIGHTEST      7

class TM1651
{
  public:
	uint8_t Cmd_SetData;
	uint8_t Cmd_SetAddr;
	uint8_t Cmd_DispCtrl;
	TM1651(uint8_t, uint8_t);
	void init();
	void writeByte(int8_t wr_data);//write 8bit data to tm1651
	void start(void);//send start bits
	void stop(void); //send stop bits
	void displayLevel(uint8_t Level);
	void frame(boolean FrameFlag);
	void clearDisplay(void);
	void set(uint8_t = BRIGHT_TYPICAL,uint8_t = 0x40,uint8_t = 0xc0);//To take effect the next time it displays.
  private:
	uint8_t Clkpin;
	uint8_t Datapin;
	void bitDelay();
};
#endif
TM1651.cpp
//  Author:Fred.Chu
//  Date:14 August, 2014
//
//  Applicable Module:
//                     Battery Display v1.0
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//
//  Modified record:
//
/*******************************************************************************/
#include "TM1651.h"
#include <Arduino.h>
static int8_t LevelTab[] = {0x00,0x40,0x60,0x70,0x78,0x7c,0x7e,0x7f};//Level 0~7
	
TM1651::TM1651(uint8_t Clk, uint8_t Data)
{
  Clkpin = Clk;
  Datapin = Data;
  pinMode(Clkpin,OUTPUT);
  pinMode(Datapin,OUTPUT);
}

void TM1651::init()
{
  set(BRIGHT_TYPICAL);
  clearDisplay();
}

void TM1651::writeByte(int8_t wr_data)
{
 	uint8_t data = wr_data;
	
	 // 8 Data Bits
	 for(uint8_t i = 0; i < 8; i++) {
	   // CLK low
	   pinMode(Clkpin, OUTPUT);
	   bitDelay();
	
	   // Set data bit
	   if (data & 0x01)
		 pinMode(Datapin, INPUT);
	   else
		 pinMode(Datapin, OUTPUT);
	
	   bitDelay();
	
	   // CLK high
	   pinMode(Clkpin, INPUT);
	   bitDelay();
	   data = data >> 1;
	 }
	
	 // Wait for acknowledge
	 // CLK to zero
	 pinMode(Clkpin, OUTPUT);
	 pinMode(Datapin, INPUT);
	 bitDelay();
	
	 // CLK to high
	 pinMode(Clkpin, INPUT);
	 bitDelay();
	 uint8_t ack = digitalRead(Datapin);
	 if (ack == 0)
	   pinMode(Datapin, OUTPUT);
	
	
	 bitDelay();
	 pinMode(Clkpin, OUTPUT);
	 bitDelay();
  
}
//send start signal to TM1651
void TM1651::start(void)
{
	pinMode(Datapin, OUTPUT);
	 bitDelay();
} 
//End of transmission
void TM1651::stop(void)
{
  pinMode(Datapin, OUTPUT);
  bitDelay();
  pinMode(Clkpin, INPUT);
  bitDelay();
  pinMode(Datapin, INPUT);
  bitDelay(); 


}
//******************************************
void TM1651::displayLevel(uint8_t Level)
{
  if(Level > 7)return;//Level should be 0~7
  start();          //start signal sent to TM1651 from MCU
  writeByte(ADDR_FIXED);//
  stop();           //
  start();          //
  writeByte(0xc0);//
  writeByte(LevelTab[Level]);//
  stop();            //
  start();          //
  writeByte(Cmd_DispCtrl);//
  stop();           //
}

void TM1651::frame(boolean FrameFlag)
{
  int8_t SegData;
  if (FrameFlag == 1) SegData = 0x40;
  else SegData = 0x00;
  start();          //start signal sent to TM1651 from MCU
  writeByte(ADDR_AUTO);//
  stop();           //
  start();          //
  writeByte(0xc1);//
  for(uint8_t i=0;i < 3;i ++)
  {
    writeByte(SegData);        //
  }
  stop();           //
  start();          //
  writeByte(Cmd_DispCtrl);//
  stop();           //
}

void TM1651::clearDisplay(void)
{
  displayLevel(0);
  frame(FRAME_OFF);
}
//To take effect the next time it displays.
void TM1651::set(uint8_t brightness,uint8_t SetData,uint8_t SetAddr)
{
  Cmd_SetData = SetData;
  Cmd_SetAddr = SetAddr;
  Cmd_DispCtrl = 0x88 + brightness;//Set the brightness and it takes effect the next time it displays.
}

void TM1651::bitDelay()
{
	delayMicroseconds(50);
}

▍ Индикатор громкости


На плате шилда имеются простой микрофонный предусилитель и миниатюрный электретный микрофон.



А вот и скетч индикатора громкости звука. Он использует всю ту же библиотеку и просто считывает аналоговое значение с входа А2, а затем передаёт его столбчатому индикатору.

#include "TM1651.h"

#define CLK 10 // определяем, куда подключены выводы TM1651       
#define DIO 11
TM1651 batteryDisplay(CLK,DIO);

#define SOUND_PIN A2
#define MAX_SENSORVALUE 1000
#define MIN_SENSORVALUE 8

void setup() {
    batteryDisplay.init();
    batteryDisplay.set(BRIGHTEST);//BRIGHT_TYPICAL = 2,BRIGHT_DARKEST = 0,BRIGHTEST = 7;
}
void loop() {
	int sensorValue = analogRead(SOUND_PIN); 
	int level = map(sensorValue, 0, MAX_SENSORVALUE, 0, MIN_SENSORVALUE); 
	batteryDisplay.displayLevel(level);
}


▍ Светодиодная матрица


Две красные светодиодные матрицы 8x8 управляются с помощью одной микросхемы HT16K33.



Как и TM1651, она представляет собой драйвер светодиодов со сканером клавиатуры и интерфейсом I²C, только гораздо более продвинутый.



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

#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
#include <Adafruit_NeoPixel.h>

#define PIN A3
#define NUM 4
Adafruit_NeoPixel rgb = Adafruit_NeoPixel(NUM, PIN, NEO_GRB + NEO_KHZ800);

Adafruit_8x16matrix matrix = Adafruit_8x16matrix();

void setup() {
  rgb.begin();
  rgb.clear();
  rgb.show();
  matrix.begin(0x70);  // I²C адрес контроллера HT16K33
}

static const uint8_t PROGMEM
  smile_bmp[] =
  { B00111100,
    B01000010,
    B10100101,
    B10000001,
    B10100101,
    B10011001,
    B01000010,
    B00111100 },
  neutral_bmp[] =
  { B00111100,
    B01000010,
    B10100101,
    B10000001,
    B10111101,
    B10000001,
    B01000010,
    B00111100 },
  frown_bmp[] =
  { B00111100,
    B01000010,
    B10100101,
    B10000001,
    B10011001,
    B10100101,
    B01000010,
    B00111100 };

void loop() {
  
  matrix.clear();
  matrix.drawBitmap(0, 0, smile_bmp, 8, 8, LED_ON);
  matrix.writeDisplay();
  delay(500);
  
  matrix.clear();
  matrix.drawBitmap(0, 8, neutral_bmp, 8, 8, LED_ON);
  matrix.writeDisplay();
  delay(500);

  matrix.clear();
  matrix.drawBitmap(0, 0, frown_bmp, 8, 8, LED_ON);
  matrix.writeDisplay();
  delay(500);

  matrix.clear();      // очищаем дисплей
  matrix.drawPixel(0, 0, LED_ON); // рисуем пиксель в программном буфере  
  matrix.writeDisplay();  // выгружаем изменения в дисплей
  delay(500);

  matrix.clear();
  matrix.drawLine(0,0, 7,15, LED_ON);
  matrix.writeDisplay();  
  delay(500);

  matrix.clear();
  matrix.drawRect(0,0, 8,16, LED_ON);
  matrix.fillRect(2,2, 4,12, LED_ON);
  matrix.writeDisplay();  
  delay(500);

  matrix.clear();
  matrix.drawCircle(3,8, 3, LED_ON);
  matrix.writeDisplay();  
  delay(500);

  matrix.setTextSize(1);
  matrix.setTextWrap(false);  
  matrix.setTextColor(LED_ON);
  matrix.setRotation(1);
  for (int8_t x=7; x>=-69; x--) {
    matrix.clear();
    matrix.setCursor(x,0);
    matrix.print("RUVDS @ Habr");
    matrix.writeDisplay();
    delay(100);
  }
  delay(2000);
  matrix.setRotation(0);
}


▍ Энкодер


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



Вращение ручки энкодера преобразуется в угол наклона линии на матричном светодиодном экране.



А если нажать эту ручку, то экран погаснет до следующего её поворота.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
#include <RotaryEncoder.h>
#include <Adafruit_NeoPixel.h>

#define PIN A3
#define NUM 4
Adafruit_NeoPixel rgb = Adafruit_NeoPixel(NUM, PIN, NEO_GRB + NEO_KHZ800);
#define encoder_button 4
RotaryEncoder encoder(3, 2);

#define CLOCKWISE 1
#define ANTI_CLOCKWISE -0

Adafruit_8x16matrix matrix = Adafruit_8x16matrix();
void rotatePointer(uint8_t,uint8_t);

void setup() {
  rgb.begin();
  rgb.clear();
  rgb.show();
  pinMode(encoder_button,INPUT);
  matrix.begin(0x70);  
  matrix.setRotation(1);
  matrix.drawLine(0,3, 15,4, LED_ON);
  matrix.writeDisplay(); 
}

void loop() {
  static int pos = 0;
  if(digitalRead(encoder_button)==1)
  {
    delay(10);
    if(digitalRead(encoder_button)==1)
    {
       matrix.clear();
       matrix.writeDisplay(); 
     }
   }
  encoder.tick();
  int newPos = encoder.getPosition();
  uint8_t pointerDirection;
  if (pos != newPos) {
    RotaryEncoder::Direction currentDirection = encoder.getDirection();
	if (currentDirection == RotaryEncoder::Direction::COUNTERCLOCKWISE) {
          pointerDirection = ANTI_CLOCKWISE;
        }
		else pointerDirection = CLOCKWISE;
    rotatePointer(pointerDirection,1);
	rotatePointer(pointerDirection,1);
    pos = newPos;
  } // if
}

int8_t x1=0;
int8_t x2=15;
int8_t y1=3;
int8_t y2=4;
void rotatePointer(uint8_t direction, uint8_t steps)
{
    if(direction==CLOCKWISE){
	  if(x1==0 && y1>0)y1 -= steps;
	  else if(y1==0 && x1<15) x1 += steps;
	  else if(x1==15 && y1<7) y1 += steps;
	  else if(y1==7 && x1>0) x1 -= steps;
	
	  if(x2==0 && y2>0)y2 -= steps;
	  else if(y2==0 && x2<15) x2 += steps;
	  else if(x2==15 && y2<7) y2 += steps;
	  else if(y2==7 && x2>0) x2 -= steps;
	}
	else{
		if(x1==0 && y1<7)y1 += steps;
	  else if(y1==7 && x1<15) x1 += steps;
	  else if(x1==15 && y1>0) y1 -= steps;
	  else if(y1==0 && x1>0) x1 -= steps;
	
	  if(x2==0 && y2<7)y2 += steps;
	  else if(y2==7 && x2<15) x2 += steps;
	  else if(x2==15 && y2>0) y2 -= steps;
	  else if(y2==0 && x2>0) x2 -= steps;
	}
  matrix.clear();
  matrix.drawLine(x1,y1, x2,y2, LED_ON);
  matrix.writeDisplay();  
}


▍ Джойстик


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



Данный скетч перемещает по матричному экрану светящуюся точку согласно командам с джойстика.

#include <OS_SingleJoystick.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
#include <Adafruit_NeoPixel.h>

#define PIN A3
#define NUM 4
Adafruit_NeoPixel rgb = Adafruit_NeoPixel(NUM, PIN, NEO_GRB + NEO_KHZ800);

Adafruit_8x16matrix matrix = Adafruit_8x16matrix();
#define SW_PIN 0xff // Джойстик не нажимной.
#define JOYSTCK_X A0
#define JOYSTCK_Y A1

SingleJoystick joystick(JOYSTCK_X, JOYSTCK_Y);

uint8_t coordinate_x = 7;
uint8_t coordinate_y = 3;
void setup() {
  rgb.begin();
  rgb.clear();
  rgb.show();
  matrix.begin(0x70);  
  matrix.setRotation(1);
  matrix.clear();    
  matrix.drawPixel(coordinate_x, coordinate_y, LED_ON);  
  matrix.writeDisplay();    
}

void loop() {
  joystickControlLED();
  delay(100); 
}

void joystickControlLED()
{
	if(joystick.isChange())
  {
	int x,y; 
    x=joystick.nowX;
    y=joystick.nowY;
	uint8_t operation;
    operation = joystick.multipleRead();

    switch (operation) {
      case MOVE_UP:
		if(coordinate_y>0) coordinate_y -= 1; 
		break;
      case MOVE_DOWN:
		if(coordinate_y<7) coordinate_y += 1; 
		break;
      case MOVE_RIGHT:
		if(coordinate_x<15) coordinate_x += 1;
		break;
      case MOVE_LEFT:
	   if(coordinate_x>0) coordinate_x -= 1; 
	   break;
      default:
       break;
    }
    matrix.clear();    
    matrix.drawPixel(coordinate_x, coordinate_y, LED_ON);  
    matrix.writeDisplay();
  }
}


▍ Электронный термометр




А этот скетч выводит на матричный дисплей показания температуры и отображает её изменения на столбчатом индикаторе.

#include <OneWire.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
#include "TM1651.h"
#include <Adafruit_NeoPixel.h>

#define PIN A3
#define NUM 4
Adafruit_NeoPixel rgb = Adafruit_NeoPixel(NUM, PIN, NEO_GRB + NEO_KHZ800);

#define CLK 10    
#define DIO 11
TM1651 batteryDisplay(CLK,DIO);
OneWire  ds(5);  
Adafruit_8x16matrix matrix = Adafruit_8x16matrix();
uint8_t level = 2;
int8_t temp0;
void setup()
{
  rgb.begin();
  rgb.clear();
  rgb.show();
  matrix.begin(0x70);  
  matrix.setRotation(1);
  batteryDisplay.init();
  batteryDisplay.set(BRIGHTEST);//BRIGHT_TYPICAL = 2,BRIGHT_DARKEST = 0,BRIGHTEST = 7;
  temp0 = readTemp();
  batteryDisplay.displayLevel(level);
}
void loop()
{
  int8_t celsius;
  celsius = readTemp();
  displayTemp(celsius);
  batteryDisplay.displayLevel(level+celsius-temp0);
}
int8_t readTemp()
{
	byte data[12];
  float celsius;
  ds.reset();
  ds.skip();
  ds.write(0x44, 0);      
  delay(750);     
  ds.reset();
  ds.skip();
  ds.write(0xBE);         
  for (unsigned char i = 0; i < 9; i++) {       
    data[i] = ds.read();
  }

    int16_t raw = (data[1] << 8) | data[0];
  byte cfg = (data[4] & 0x60);
  if (cfg == 0x00) raw = raw & ~7; 
  else if (cfg == 0x20) raw = raw & ~3; 
  else if (cfg == 0x40) raw = raw & ~1; 
  celsius = (float)raw / 16.0;
  return celsius;
}
void displayTemp(int8_t temp)
{
	matrix.clear();
    matrix.setCursor(0,0);
    matrix.print(temp);
	matrix.print('c');
    matrix.writeDisplay();
}


▍ Адресуемые RGB светодиоды


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



Мы будем зажигать всю линейку разными цветами, чередуя это с эффектом жёлтых бегущих огней.

#include <Adafruit_NeoPixel.h>#define PIN A3
#define NUM 4
Adafruit_NeoPixel rgb = Adafruit_NeoPixel(NUM, PIN, NEO_GRB + NEO_KHZ800);
#define LEFT_TO_RIGHT 0
#define RIGHT_TO_LEFT 1

void setup() {
  rgb.begin();
  rgb.clear();
  rgb.show();
}

void loop() {
 rgb.setBrightness(40);
  colorWipe(rgb.Color(100, 100, 100), 0); // Белый
  delay(600);
  colorWipe(rgb.Color(50, 0, 0), 0); // Красный
  delay(600);
  colorWipe(rgb.Color(0, 50, 0), 0); // Зелёный
  delay(600);
  colorWipe(rgb.Color(0, 0, 50), 0); // Синий
  delay(600);
  colorWipe(rgb.Color(0, 0, 0), 0); // Чёрный
  delay(600);
  runLED(0xffff00, LEFT_TO_RIGHT); // Жёлтый
  delay(600);
  colorWipe(rgb.Color(0, 0, 0), 0); // Чёрный
  delay(600);
  runLED(0xffff00, RIGHT_TO_LEFT);// Жёлтый
  delay(600);
}

// Последовательно заполняем пиксели цветом один за другим
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<NUM; i++) {
      rgb.setPixelColor(i, c);
  }
  rgb.show();
}
void runLED(uint32_t c,  uint8_t direction) {
  if(direction==LEFT_TO_RIGHT) 
    for(uint8_t i=0; i<NUM; i++) {
      rgb.setPixelColor(i, c);
	  rgb.show();
      delay(150);
    }
  else 
    for(uint8_t i=NUM; i>0; i--) {
      rgb.setPixelColor(i-1, c);
	  rgb.show();
      delay(150);
    }
}

void theaterChase(uint32_t c, uint8_t wait) {
  for (int j=0; j<10; j++) {  
    for (int q=0; q < 3; q++) {
      for (int i=0; i < rgb.numPixels(); i=i+3) {
        rgb.setPixelColor(i+q, c);    // зажигаем каждый третий пиксель
      }
      rgb.show();     
      delay(wait);     
      for (int i=0; i < rgb.numPixels(); i=i+3) {
        rgb.setPixelColor(i+q, 0);        // гасим каждый третий пиксель
      }
    }
  }
}


▍ Наблюдения и выводы


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

Нажатие кнопки сброса центрального контроллера на них также не влияет, и «лишние» светодиоды гаснут только после выключения питания платы. Так и должно быть, ведь контроллеры светодиодов работают автономно, и линия общего сброса, к которой можно было бы просто присоединить кнопку, не предусмотрена.

Напишите в комментариях свои идеи по использованию Ардуино-совместимой платы с тремя светодиодами и тремя кнопками самого по себе, а также в сочетании с Rich Shield Two.

Telegram-канал со скидками, розыгрышами призов и новостями IT ?

Комментарии (26)


  1. VladimirFarshatov
    04.04.2024 09:55
    +2

    Хорошая и развернутая статья, но вот такая Ардуинка может много больше:

    https://vk.com/id484853030?z=photo484853030_456239018%2Fphoto_feed484853030

    К ней была сделана дополнительная плата ОЗУ, расширяющая внутреннее ОЗУ до 520килобайт сегментированно-прямой адресации. С ней возможностей стало ещё больше.. ;)


    1. f45d07
      04.04.2024 09:55
      +6

      Не у всех есть возможность посмотреть публикацию в ВК, где требуется авторизация


      1. trinxery
        04.04.2024 09:55
        +5

        Hidden text

        Развитие плат Ардуино - Мега2560 с оперативной памятью на 512 килобайт прямого доступа.

        Последний релиз самой платы Меги. Есть изготовленные в Китае 10 плат, пока в процессе "запайки деталей". Особенности разработки:

        1. В отличии от типовой Ардуино Мега2560, тут все рабочие контакты Мега2560 выведены на разьемы платы и сгруппированы по назначению: Левая сторона - интерфейсы I2C, SPI, UART0 и подача питания на плату (по центру). Правая сторона - Интерфейс расширения ОЗУ, использован в отдельной плате расширения ОЗУ до 512 килобайт (есть рабочий "пилотный" вариант платы + доработка файла линковки для работы с массивами из ПО) Верх - Таймеры 0,1,3 + UART1,3 + доп. ноги + АЦП 0..7; Низ - Таймеры 2,4,5 + UART2 + под. ноги + АЦП8..15.

        2. Все контакты платы, кроме правого интерфейса ОЗУ - сдвоенные для возможности пайки "вертикального разьема" бутербродом И горизонтального выхода на строенные контакты "сигнал-земля-питание" для подключения периферии по типу "серводвигатель";

        3. "большой кварц" и его дополнительное экранирование (отдельная земля) - для повышения стабильности работы платы;

        4. Специальная проводка земли блока АЦП с целью повышения надежности сьема показаний;

        5. Увеличенные емкости развязочных конденсаторов микроконтроллера с понижением их ESR - повышение стабильности работы и снижение влияния помех и тока потребления;

        6. Усиленный стабилизатор питания на плате - 5в до 5А для надежного питания нескольких серводвигателей одновременно;

        7. Удобный габарит платы 56х88мм (чуть больше коробки на 3хLi-ion 18650) и согласованный с размерами Лего (кратно 8мм) - позволяет заменить блок EV3 в его габаритах и крепежом.

        Основное назначение - кружки робототехники и обучения программированию микроконтроллеров - "инструмент преподавателя". :)

        К этой плате есть герберы из kicad, есть 10шт "пробной партии", есть пробный изготовленный и рабочий образец (предыдущий вариант разводки) с платой расширения ОЗУ.


    1. Lunathecat Автор
      04.04.2024 09:55

      Спасибо!


    1. voldemar_d
      04.04.2024 09:55
      +2

      520 кБ - это хорошо, но если достаточно 128 кБ, можно к любой Arduino подключить по SPI маленькую микросхему 23LC1024 вместо целой платы расширения.


      1. VladimirFarshatov
        04.04.2024 09:55
        +1

        По spi можно, но оперировать большими массивами данных медленнее на два порядка, чем напрямую.


        1. voldemar_d
          04.04.2024 09:55
          +1

          В каких задачах на Arduino может потребоваться быстро оперировать данными в пол мегабайта?


          1. VladimirFarshatov
            04.04.2024 09:55
            +1

            Ну, к примеру, был сделан "осцилоскоп" с сохранением серии снимков и их последующим усреднением перед показом.. Обработка картинок для цветного экранчика .. Ещё делал "пульсоксиметр" на базе фоторезистора, а поскольку там измерялка "пол-потолок-палец", тоже снималось много сэмплов с усреднением..

            Какие-то куски выкладывал в свое время на разных ресурсах.

            +512кб Меге 2560 оно может и не так уж и надо, но .. SRAM 512kb 70ns покупались за смешные 40-60руб. Почему нет-то, если шина позволяет?

            Там контроллер - проще некуда.. :) Впрочем .. где-то тут уже выкладывал какие-то ссылки на АлексГауверовский сайт, что тогда публиковалось.. можно поискать ещё разок.


            1. voldemar_d
              04.04.2024 09:55
              +1

              Если найдете, было бы интересно почитать. Просто для усреднения серии снимков, кмк, уже помощнее МК нужен, чем Arduino.


              1. VladimirFarshatov
                04.04.2024 09:55

                Да вполне нормальный контроллер - 16Мгц тактовая, вполне. В Космос, в свое время летали машинки послабее и существенно. Осцилоскоп поднимал до почти 500 килосэмплов в сек. Пробовал делать несколько кадров со сдвигом на 1-2-4-8 тактов, но там оно плавает не устойчиво.. или у меня не получилось толком поднять семплированием частоту выше 500.

                Не, если конечно складывать 2+2 переводя замер во double, да библиотекой на Питоне с обратным преобразованием в целое через 64 байтное в байт .. то да, слабоват.. :)


  1. NickDoom
    04.04.2024 09:55
    +1

    И снова пЧичка на фотке предвещает занимательные электронные игрушки :)

    Если я всё-таки доберусь до своего кухонного таймера с минимумом ног у контра — надо будет не забыть сделать на фотке узнаваемый силуэт и написать в нём «а у меня такой пЧички нет!» :-D


    1. Lunathecat Автор
      04.04.2024 09:55
      +2

      Будет интересно!


  1. ThingCrimson
    04.04.2024 09:55
    +1

    О! Ардуина! А я как раз вчера перед сном ворочался, и думал — а не попробовать ли мне запилить необычный метроном, который совсем молчит, только светодиодом мигает? Скажем, Arduino Nano, красный LED для первой доли, 4 зелёных (для ритмов от 2/4 до 5/4 и придумать как задавать число долей), энкодер для задания темпа, ну и наверно дисплейчик мелкий или несколько 7-сегментных индикаторов для отображения темпа…

    Но потом подумал, а хватит ли realtime-вости для слабенького МК, программируемого не в машинных кодах, а через IDE на С++? И заснул.


    1. Lunathecat Автор
      04.04.2024 09:55
      +1

      Хорошая идея! Таймеры у МК есть, обработчик прерывания написать можно.


      1. ThingCrimson
        04.04.2024 09:55
        +1

        Спасибо! Тогда сделаю запись в ToDo, может и дойдут руки хотя бы до макета. Мне ведь не много надо: чтобы при, скажем, 120 BPM длительность доли была 500 мс (пусть ±5 мс), а не то 430, а то 550.


    1. VladimirFarshatov
      04.04.2024 09:55

      За глаза хватит. Он ещё и траекторию спутника на орбите отследит междду делом .. не очень точно (нет встроенного float) .. так, "плюс-минус" .. ;)


      1. voldemar_d
        04.04.2024 09:55

        Есть софтовый double.


    1. voldemar_d
      04.04.2024 09:55

      Имхо, более чем хватит.


  1. geher
    04.04.2024 09:55
    +3

    Вас тоже огорчало, что PLS-разъёмы плат Arduino Uno и Mega установлены без соблюдения сетки с шагом 2.54 мм, отчего невозможно создать собственный шилд на базе обычной макетки под пайку?

    Для ленивых и неэкономных есть специальные готовые макетки под шилды.


    1. NetBUG
      04.04.2024 09:55
      +1

      Почему неэкономных? Оно стоит меньше доллара вроде


      1. geher
        04.04.2024 09:55
        +1

        То, что я видел под разъемы ардуины, стоит дороже "просто макеток". Не намного, но все же. А если разъемы уже распаяны (для истинно ленивого - лучший выбор), то еще дороже


        1. NetBUG
          04.04.2024 09:55
          +1

          Ну, ёжику понятно, что дешевле делать квадратные километры макетки с шагом 0.1", а не выпиливать фигурную плату, оно там центов по десять будет получаться.

          Но, условно, 43 цента за такую платку (не реклама, просто нашлось в выдаче) – это дешевле, чем делать самому на фрезере (даже если он есть и полностью настроен и готов к работе), дешевле (с учётом доставки), чем делать по промоакции за $2 на jlcpcb и тем более где-то вне Азии.

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

          Что не отменяет вопросов к тому, зачем двадцать лет назад сделали непонятно какой шаг на плате с тогда-ещё-не-древней атмегой :)


          1. geher
            04.04.2024 09:55
            +2

            Не ленивый и экономный спаяет свою "ардуину" и свои модули к ней без этой ерунды с нарушением шага.

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

            Что не отменяет вопросов к тому, зачем двадцать лет назад сделали непонятно какой шаг на плате с тогда-ещё-не-древней атмегой

            Наверное, они ее просто развели по принципу "как получилось", не особо думая о том, что потом появится народ с параллельно-перпендикулярными макетками, который захочет эти макетки присоединить к ардуине.


            1. VladimirFarshatov
              04.04.2024 09:55

              Насколько помню, там были дебаты в Сети "какую макетку пилить" .. победила, та что победила. Почему? А хз, не помню уже. Тоже дивился зачем?


  1. JIexa21
    04.04.2024 09:55
    +2

    Да почему "кроватка" то??? ПАНЕЛЬ!


  1. serafims
    04.04.2024 09:55
    +1

    Дмэумаю, имеет смысл прошивать МК загрузчиком и ставить сразу без кроваток в плату, заодно и ISP разъем не нужен будет.