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

Но если вы довольны работой с переключателями всех типов, то, возможно, эта статья не для вас, продолжайте делать то, как вам удобно. Однако, если переключатели для вас в новинку, вы боретесь с ними или хотите использовать другой набор инструментов и методов для их включения в свои проекты, тогда читайте дальше – библиотека switch_lib предлагает такую поддержку.
Вкратце, библиотека switch_lib предоставляет набор расширенных возможностей для считывания значений переключателей разных типов и различных схем подключения, а также устраняет проблемы разработчика, связанные с «шумом» переключения. Действительно, switch_lib дает разработчику выбор способа подключения коммутатора, поддерживая обе наиболее распространенные схемы подключения без дополнительных компонентов, помимо проводов, и, при желании, устанавливать понижающие резисторы на 10 кОм. Библиотека switch_lib предоставляет программный подход к управлению и контролю переключателя.


Библиотека switch_lib обеспечивает следующие функции и возможности:
- распределение динамической памяти в зависимости от количества переключателей, которые вы хотите включить в свой проект
- возможности многопозиционного типа
- смешивание разных типов переключателей
- поддержка схемы подключения с двумя переключателями, прозрачная для разработчика программного обеспечения
- поддержка как кнопочных, так и тумблерных переключателей простая настройка переключателя, с переключением выхода переключателя или без него
- возможность подключения цифрового выходного контакта к любому переключателю для автоматического переключения выходных контактов без кодирования конечным пользователем
- настраиваемый и автоматический дребезг коммутационных цепей
- общая функция считывания значения переключателя (независимая от типа переключателя)
- функция чтения специального кнопочного переключателя
- перехват ошибок из функций чтения и связывания
- прямой доступ ко всем переменным управления переключателем
- отчет о состоянии управления переключателем через последовательный монитор
- подробное руководство пользователя
Руководство пользователя предоставляет исчерпывающее описание объема и возможностей библиотеки switch_lib, включая скетчи рабочих примеров. Чтобы полностью оценить возможности библиотеки switch_lib, загрузите руководство пользователя с github, перейдя по ссылке https://github.com/ronbentley1/switch_lib-Arduino-Library/blob/main/switch_lib_user_guide%2C%20v1.00.pdf. Далее также будут представлены основные файлы библиотеки.
switch_lib.h
#ifndef switches_h
#define switches_h
#include <Arduino.h>
class Switches
{
public:
Switches(byte max_switches);
#define button_switch 1 // differentiates switch type
#define toggle_switch 2 // ditto
#define circuit_C1 INPUT // switch circuit requires an external pull down 10k ohm resistor
#define circuit_C2 INPUT_PULLUP // switch circuit requires no other components beyond the switch
#define switched true // signifies switch has been pressed/switch cycle complete
#define on true // used for toggle switch status
#define not_used true // helps self document code
#define bad_params -2 // invalid add_swith paramters
#define add_failure -1 // add_swith could not insert a given switch, ie no space left
#define link_success 0 // output successfully linked to a switch
#define link_failure -1 // output pin could not be linked to a switch
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % Switch Control Sruct(ure) Declaration %
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
struct switch_control {
byte switch_type; // type of switch connected
byte switch_pin; // digital input pin assigned to the switch
byte switch_circuit_type; // the type of circuit wired to the switch
bool switch_on_value; // used for BUTTON SWITCHES only - defines what "on" means
bool switch_pending; // records if switch in transition or not
long unsigned int switch_db_start;// records debounce start time when associated switch starts transition
bool switch_status; // used for TOGGLE SWITCHES only - current state of toggle switch.
byte switch_out_pin; // the digital pin mapped to this switch, if any
bool switch_out_pin_status; // the status of the mapped pin
} *switches; // memory will be created when class is initiated
// Functions available to end users
bool read_switch (byte sw);
bool read_toggle_switch (byte sw);
bool read_button_switch (byte sw);
int add_switch (byte sw_type, byte sw_pin, byte circ_type);
int link_switch_to_output(byte switch_id, byte output_pin, bool HorL);
int num_free_switch_slots ();
void set_debounce (int period);
void print_switch (byte sw);
void print_switches ();
private:
byte _num_entries = 0; // used for adding switches to switch control structure/list
byte _max_switches = 0; // max switches user has initialise
int _debounce = 10; // 10 milliscs if not specified by user code
};
#endif
switch_lib.cpp
#include <Arduino.h>
#include <switch_lib.h>
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Set up switch cotrol structure and initialise internal variables.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Switches::Switches(byte max_switches)
{
// Establish the switch control structure (switches) of the size required
switches = malloc(sizeof(*switches) * max_switches);
if (switches == NULL) {
// malloc failure
Serial.begin(9600);
Serial.println("!!Failure to acquire memory of required size - PROGRAM TERMINATED!!");
Serial.flush();
exit(1);
}
// Initialise private variables
_num_entries = 0; // will be incremented each time a switch is added, up to _max_switches
_max_switches = max_switches; // transfer to internal variable
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Generic switch read function.
// Read the switch defined by the function parameter.
// Function returns a value indicating if the switch
// has undergone a transition or not.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool Switches::read_switch(byte sw) {
bool sw_status;
if (sw < 0 || sw >= _num_entries) return !switched; // out of range, slot 'sw' is not configured with a switch
if (switches[sw].switch_type == button_switch) {
sw_status = read_button_switch(sw);
} else {
sw_status = read_toggle_switch(sw);
}
// now determine if switch has output pin associated and if switched
// flip the output's status, ie HIGH->LOW, or LOW->HIGH
if (sw_status == switched && switches[sw].switch_out_pin > 0)
{
// flip the output level of associated switch output pin, if defined
bool status = switches[sw].switch_out_pin_status; // last status value of out_pin
status = HIGH - status; // flip the status value
switches[sw].switch_out_pin_status = status; // update current status value
digitalWrite(switches[sw].switch_out_pin, status);
}
return sw_status;
} // End of read_switch
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Generic toggle switch read function.
// Test the toggle switch to see if its status has changed since last look.
// Note that although switch status is a returned value from the function,
// the current status of the switch ('switches[sw].switch_status') is always
// maintained and can be tested outside of the function at any point/time.
// It will either have a status of 'on' or '!on' (ie off).
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool Switches::read_toggle_switch(byte sw) {
byte switch_pin_reading = digitalRead(switches[sw].switch_pin); // test current state of toggle pin
if (switches[sw].switch_circuit_type == circuit_C2) {
// Need to invert HIGH/LOW if circuit design sets
// pin HIGH representing switch in off state.
// ie inititialised as INPUT_PULLUP
switch_pin_reading = !switch_pin_reading;
}
if (switch_pin_reading != switches[sw].switch_status && !switches[sw].switch_pending) {
// Switch change detected so start debounce cycle
switches[sw].switch_pending = true;
switches[sw].switch_db_start = millis(); // set start of debounce timing
}
if (switches[sw].switch_pending) {
// We are in the switch transition cycle so check if debounce period has elapsed
if (millis() - switches[sw].switch_db_start >= _debounce) {
// Debounce period elapsed so assume switch has settled down after transition
switches[sw].switch_status = !switches[sw].switch_status; // flip status
switches[sw].switch_pending = false; // cease transition cycle
return switched;
}
}
return !switched;
} // End of read_toggle_switch
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Read button switch function.
// Generic button switch read function.
// Reading is controlled by:
// a. the function parameter which indicates which switch
// is to be polled, and
// b. the switch control struct(ure), referenced by a).
//
// Note that this function works in a nonexclusive way
// and incorporates debounce code.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool Switches::read_button_switch(byte sw) {
byte switch_pin_reading = digitalRead(switches[sw].switch_pin);
if (switch_pin_reading == switches[sw].switch_on_value) {
// Switch is pressed (ON), so start/restart debounce process
switches[sw].switch_pending = true;
switches[sw].switch_db_start = millis(); // start elapse timing
return !switched; // now waiting for debounce to conclude
}
if (switches[sw].switch_pending && switch_pin_reading != switches[sw].switch_on_value) {
// Switch was pressed, now released (OFF), so check if debounce time elapsed
if (millis() - switches[sw].switch_db_start >= _debounce) {
// dounce time elapsed, so switch press cycle complete
switches[sw].switch_pending = false;
return switched;
}
}
return !switched;
} // End of read_button_switch
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Add given switch to switch control structure, but validate
// given paramters and ensure there is a free slot.
//
// Return values from add_switch are:
//
// >= 0 the switch control structure entry number ('switch_id')
// for the switch added,
// -1 add_falure - no slots available in the switch
// control structure,
// -2 bad_params - given paramter(s) for switch are
// not valid.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int Switches::add_switch(byte sw_type, byte sw_pin, byte circ_type) {
if ((sw_type != button_switch && sw_type != toggle_switch) ||
(circ_type != circuit_C1 && circ_type != circuit_C2)) return bad_params; // bad paramters
if (_num_entries < _max_switches) {
// room to add another switch...initialise the switch's
// data depending on type of switch and circuit
switches[_num_entries].switch_type = sw_type;
switches[_num_entries].switch_pin = sw_pin;
switches[_num_entries].switch_circuit_type = circ_type;
switches[_num_entries].switch_pending = false;
switches[_num_entries].switch_db_start = 0;
if (circ_type == circuit_C1) {
switches[_num_entries].switch_on_value = HIGH;
} else {
switches[_num_entries].switch_on_value = LOW;
}
if (sw_type == button_switch) {
switches[_num_entries].switch_status = not_used;
} else {
switches[_num_entries].switch_status = !on;
}
pinMode(sw_pin, circ_type); // establish pin set up
// ensure no mapping to an output pin until created explicitly
switches[_num_entries].switch_out_pin = 0;
switches[_num_entries].switch_out_pin_status = LOW; // set LOW unless explicitly changed
_num_entries++; // point to next free slot
return _num_entries - 1; // return 'switch_id' - given switch now added to switch control structure
} else return add_failure; // no room left to add another switch!
} // End add_switch
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Link or delink the given switch to the given digital pin as an output
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int Switches::link_switch_to_output(byte switch_id, byte output_pin, bool HorL){
if(switch_id > _num_entries) {return link_failure;} // no such switch
if (output_pin == 0){
// delink this output from this switch, set te output to the required level first
if (switches[switch_id].switch_out_pin == 0){
// no output pin previously defined
return link_failure;
}
// set existing pin to level required state before clearing the link
digitalWrite(switches[switch_id].switch_out_pin, HorL);
}else {
// initialise given output pin
pinMode(output_pin, OUTPUT);
digitalWrite(output_pin, HorL); // set to param value until switched
}
switches[switch_id].switch_out_pin = output_pin;
switches[switch_id].switch_out_pin_status = HorL;
return link_success; // success
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Return the number of slots left unused
// in the switch control structure.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int Switches::num_free_switch_slots() {
return _max_switches - _num_entries;
} // End num_free_switch_slots
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Set debounce period (milliseconds).
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void Switches::set_debounce(int period) {
if (period >= 0) _debounce = period;
} // End set_debounce
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Print given switch control data.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void Switches::print_switch(byte sw) {
if (0 <= sw && sw < _num_entries ) {
Serial.print("slot: ");
Serial.print(sw);
Serial.print(" sw_type= ");
Serial.print(switches[sw].switch_type);
Serial.print("\tsw_pin= ");
Serial.print(switches[sw].switch_pin);
Serial.print("\tcirc_type= ");
Serial.print(switches[sw].switch_circuit_type);
Serial.print("\tpending= ");
Serial.print(switches[sw].switch_pending);
Serial.print("\tdb_start= ");
Serial.print(switches[sw].switch_db_start);
Serial.print("\ton_value= ");
Serial.print( switches[sw].switch_on_value);
Serial.print("\tsw_status= ");
Serial.println(switches[sw].switch_status);
Serial.print("\t\t\top_pin= ");
Serial.print(switches[sw].switch_out_pin);
Serial.print("\top_status= ");
Serial.println(switches[sw].switch_out_pin_status);
Serial.flush();
}
} // End print_switch
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Print all switch control data set up.
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void Switches::print_switches() {
Serial.println(F("\nDeclared & configured switches:"));
for (byte sw = 0; sw < _num_entries; sw++) {
print_switch(sw);
}
} // End print_switches
Список ключей библиотеки
# data and data structures
Switches KEYWORD1
switches KEYWORD2
switch_configured KEYWORD2
switch_type KEYWORD2
switch_pin KEYWORD2
switch_circuit_type KEYWORD2
switch_pending KEYWORD2
switch_db_start KEYWORD2
switch_on_value KEYWORD2
switch_status KEYWORD2
switch_out_pin KEYWORD2
switch_out_pin_status KEYWORD2
# private variables
_debounce KEYWORD2
_max_switches KEYWORD2
_num_entries KEYWORD2
# macro definitions
switched KEYWORD2
bad_params KEYWORD2
add_failure KEYWORD2
link_success KEYWORD2
link_failure KEYWORD2
on KEYWORD2
configured KEYWORD2
button_switch KEYWORD2
toggle_switch KEYWORD2
not_used KEYWORD2
circuit_C1 KEYWORD2
circuit_C2 KEYWORD2
# functions
read_switch KEYWORD2
read_toggle_switch KEYWORD2
read_button_switch KEYWORD2
add_switch KEYWORD2
link_switch_to_output KEYWORD2
num_free_switch_slots KEYWORD2
set_debounce KEYWORD2
print_switch KEYWORD2
print_switches KEYWORD2
© digitrode.ru