Що буде, якщо набрати *# 43?
*#33# – показує інформацію про блокування вихідних підтримуваних сервісів, таких як дзвінки, SMS та інших даних. *#43# – відображає інформацію про очікування дзвінка.
Що за комбінація * * # 4636 # * #?
*#*#4636#*#* Інформація про телефон, батарею та статистику використання. *#*#7780#*#* Скидання налаштувань. Видалення лише програм. Збережена копія
Як створити свій код КР?
Як безкоштовно створити QR–код за допомогою QR TIGER
- Йти до QR-ТІГР
- Виберіть одне із статичних рішень QR–коду та введіть необхідні дані.
- ВибиратиСтатичний QR–код та натиснітьСтворити QR–код
- Налаштуйте свій статичний QR–код. …
- Запустіть тестове сканування, потім натисніть Завантажити як тільки це спрацює
1Gai.Ru
На замітку: як сканувати QR-код зі знімка екрана або фотографії
Текстовий екран 16×2: інструкція, приклади використання та документація
Як приклад підключимо дисплей до керуючої плати Arduino Uno.
Підключення до Arduino
Для спрощення роботи з LCD-дисплеєм використовуйте вбудовану бібліотеку Liquid Crystal. У ній ви знайдете приклади коду із докладними коментарями.
Виведення тексту
Для виведення першої програми вітання, скористайтеся кодом на кшталт цього:
// Бібліотека для роботи з текстовим дисплеєм #include // Задаємо ім'я пінів дисплея constexpr uint8_t PIN_RS = 6; constexpr uint8_t PIN_EN = 7; constexpr uint8_t PIN_DB4 = 8; constexpr uint8_t PIN_DB5 = 9; constexpr uint8_t PIN_DB6 = 10; constexpr uint8_t PIN_DB7 = 11; /* * Створюємо об'єкт для роботи з текстовим дисплеєм * LiquidCrystal lcd(RS, EN, DB4, DB5, DB6, DB7); * Подробиці підключення: * http://wiki.amperka.ru/products:display-lcd-text-16x2 */ LiquidCrystal lcd(PIN_RS, PIN_EN, PIN_DB4, PIN_DB5, PIN_DB6, PIN_DB7); void setup() { // Встановлюємо розмір екрану // Кількість стовпців та рядків lcd.begin(16, 2); // Встановлюємо курсор у колонку 0 та рядок 0 lcd.setCursor(0, 0); // Друкуємо перший рядок lcd.print("Hello, world!"); // Встановлюємо курсор у колонку 0 та рядок 1 lcd.setCursor(0, 1); // Друкуємо другий рядок lcd.print("Do It Yourself"); } void loop() { }
Виведення тексту: кирилиця
Існує два способи виведення кирилиці на текстові дисплеї:
Розглянемо обидва способи докладніше.
Таблиця знакогенератора
Дисплейний модуль зберігає у пам'яті дві сторінки знакогенератора, які з різних символів і букв.
Для виведення символу на дисплей необхідно передати його номер у шістнадцятковій системі таблиці знакогенератора.
Так букві Я відповідає код B1 у шістнадцятковій системі. Щоб передати на екран рядок «Яndex», необхідно в явному вигляді за допомогою послідовності \x## вбудувати код символу в рядок:
Ви можете змішувати в одному рядку звичайні символи та явні коди як завгодно. Єдиний нюанс у тому, що після того, як компілятор у рядку бачить послідовність \x, він зчитує за ним усі символи, які можуть бути розрядами шістнадцяткової системи навіть якщо їх більше двох. Через це не можна використовувати символи з діапазону 0-9 та A-F слідом за двозначним кодом символу, інакше на дисплеї з'явиться неправильна інформація. Щоб обійти цей момент, можна використати той факт, що два записані рядками рядки склеюються.
Порівняйте два рядки коду для виведення напису "Яeee":
lcd.print("\xB1eee"); // помилка lcd.print("\xB1""eee"); // правильно
Використовуючи отриману інформацію, виведемо на дисплей повідомлення «Привіт, Амперко!»:
// Бібліотека для роботи з текстовим дисплеєм #include // Задаємо ім'я пінів дисплея constexpr uint8_t PIN_RS = 6; constexpr uint8_t PIN_EN = 7; constexpr uint8_t PIN_DB4 = 8; constexpr uint8_t PIN_DB5 = 9; constexpr uint8_t PIN_DB6 = 10; constexpr uint8_t PIN_DB7 = 11; /* * Створюємо об'єкт для роботи з текстовим дисплеєм * LiquidCrystal lcd(RS, EN, DB4, DB5, DB6, DB7); * Подробиці підключення: * http://wiki.amperka.ru/products:display-lcd-text-16x2 */ LiquidCrystal lcd(PIN_RS, PIN_EN, PIN_DB4, PIN_DB5, PIN_DB6, PIN_DB7); void setup() { // Встановлюємо розмір екрану // Кількість стовпців та рядків lcd.begin(16, 2); // Встановлюємо курсор у колонку 5 та рядок 0 lcd.setCursor(5, 0); // Друкуємо перший рядок lcd.print("\xA8""p""\xB8\xB3""e\xBF"); // Встановлюємо курсор у колонку 3 та рядок 1 lcd.setCursor(3, 1); // Друкуємо другий рядок lcd.print("o\xBF A\xBC\xBE""ep\xBA\xB8"); } void loop() { }
Перемикання сторінок знакогенератора
Дисплейний модуль зберігає у пам'яті дві сторінки знакогенератора. За замовчуванням встановлено нульову сторінку. Для перемикання сторінок використовуйте методи:
// Перемикання з нульової сторінки на першу command(0x101010); // Перемикання з першої сторінки на нульову command(0x101000);
Дисплей не може одночасно відображати символи різних сторінок.
Розглянемо приклад, у якому той самий рядок буде відображатися по-різному — залежно від вибраної сторінки.
// Бібліотека для роботи з текстовим дисплеєм #include // Задаємо ім'я пінів дисплея constexpr uint8_t PIN_RS = 6; constexpr uint8_t PIN_EN = 7; constexpr uint8_t PIN_DB4 = 8; constexpr uint8_t PIN_DB5 = 9; constexpr uint8_t PIN_DB6 = 10; constexpr uint8_t PIN_DB7 = 11; /* * Створюємо об'єкт для роботи з текстовим дисплеєм * LiquidCrystal lcd(RS, EN, DB4, DB5, DB6, DB7); * Подробиці підключення: * http://wiki.amperka.ru/products:display-lcd-text-16x2 */ LiquidCrystal lcd(PIN_RS, PIN_EN, PIN_DB4, PIN_DB5, PIN_DB6, PIN_DB7); void setup() { // Встановлюємо розмір екрану // Кількість стовпців та рядків lcd.begin(16, 2); // Встановлюємо курсор у колонку 5 та рядок 0 lcd.setCursor(5, 0); // Друкуємо рядок lcd.print("\x9b\x9c\x9d\x9e\x9f"); } void loop() { // Встановлюємо нульову станицю знакогенератора (за замовчуванням) lcd.command(0b101000); // Чекаємо 1 секунду delay(1000); // Встановлюємо першу сторінку знакогенератора lcd.command(0b101010); // Чекаємо 1 секунду delay(1000); }
Повну таблицю символів із кодами можна знайти у документації до екрана.
Використання бібліотеки LiquidCrystalRus
Зовсім не обов'язково мучаться з генератором, щоб вивести російський символ. Для вирішення проблеми завантажте та встановіть бібліотеку LiquidCrystalRus.
Це копія оригінальної бібліотеки LiquidCrystal із додаванням російської мови. Доданий до бібліотеки код трансформує російські символи UTF8 у правильні коди для текстового екрана.
Як приклад виведемо фразу "Привіт від Амперки" на дисплей.
// Бібліотека для роботи з текстовим дисплеєм #include // Задаємо ім'я пінів дисплея constexpr uint8_t PIN_RS = 6; constexpr uint8_t PIN_EN = 7; constexpr uint8_t PIN_DB4 = 8; constexpr uint8_t PIN_DB5 = 9; constexpr uint8_t PIN_DB6 = 10; constexpr uint8_t PIN_DB7 = 11; /* * Створюємо об'єкт для роботи з текстовим дисплеєм * LiquidCrystalRus lcd(RS, EN, DB4, DB5, DB6, DB7); * Подробиці підключення: * http://wiki.amperka.ru/products:display-lcd-text-16x2 */ LiquidCrystalRus lcd(PIN_RS, PIN_EN, PIN_DB4, PIN_DB5, PIN_DB6, PIN_DB7); void setup() { // Встановлюємо розмір екрану // Кількість стовпців та рядків lcd.begin(16, 2); // Встановлюємо курсор у колонку 5 та рядок 0 lcd.setCursor(5, 0); // Друкуємо перший рядок lcd.print("Вітання"); // Встановлюємо курсор у колонку 3 та рядок 1 lcd.setCursor(3, 1); // Друкуємо другий рядок lcd.print("від Амперки"); } void loop() { }
Приклади роботи для Espruino
Як приклад підключимо дисплей до керуючої плати Iskra JS.
Як зробити екран підтвердження СМС-коду на iOS
Через нас проходить багато проектів та оцінок, функціонал там часто повторюється, тому я вирішив показати, як ми вирішуємо типові завдання, і поділитись цим з вами. Почнемо ми від початку.Як правило, початком додатків служить авторизація. Розглянемо класичний випадок із введенням номера телефону та смської та зупинимося докладніше на екрані підтвердження смс.

Важливо: у прикладі коду на github буде повноцінний приклад із введенням номера телефону та кодом, але екран введення номера телефону зовсім нудний, тому сьогодні ми вводимо код 🙂
Виглядає не дуже складно, але, якщо придивитися, функціонал екрану досить великий, а саме:
- надіслати код на сервер;
- увімкнути таймер повторного відправлення + відобразити візуально;
- після завершення таймера показати кнопку "надіслати ще раз";
- надіслати повторний запит на отримання коду;
- відобразити усі помилки;
- обробити успішне підтвердження коду
Якщо спробувати розділити екран на UI та логіку, виходить приблизно така взаємодія між логікою та інтерфейсом.

Можна, звичайно, відправити всю логіку про таймери і isLoading на View шар, але мені більше подобається відносити це до логіки. Особливо враховуючи те, що я великий шанувальник MVVM+Rx (і це використовуватиму в статті), це більш ніж доречно виглядає. Ну та гаразд.
ViewModel у разі грає роль якогось «перетворювача» користувацьких дій: вона має input і output (видно на картинці вище). За навігацію відповідатиме «хтось ще»наприклад координатор.
З боку UI нам будуть цікаві такі компоненти:
final class ConfirmCodeViewController: BaseViewController < /// поле введення коду private lazy var codeTextField = CodeTextField() /// лейбл для відображення помилок private lazy var errorLabel = UILabel() /// один лоадер для запитів на відправку коду та на повторний запит коду private lazy var loader = UIActivityIndicatorView() /// лейбл із зворотним відліком для повторного відправлення коду private lazy var timerLabel = UILabel() /// кнопка повторного відправлення коду private lazy var retryButton = UIButton(type: .system) /// це все буде у стекві private lazy var stackView = UIStackView() >ViewModel буде виглядати так:
/// Наприклад, після успішного підтвердження коду нам можуть запропонувати ввести перс. дані enum AuthResult < case success case needPersonalData >protocol ConfirmCodeViewModelProtocol < /// Введений користувачем код для підтвердження var code: AnyObserver < get >/// Користувач натиснув на «відправити повторно» var getNewCode/ var didAuthorize: Driver < get >/// Один індикатор на всі запити на цьому екрані var isLoading: Driver < get >/// Помилки з усіх запитів на цьому екрані var errors: Driver < get >/// Таймер надсилання нового коду var newCodeTimer: Driver < get >/// Запросили новий код при натисканні на «відправити знову» var didRequestNewCode: Driver < get >/// Таймер відправки нового коду запущено var codeTimerIsActive: Driver < get >>Зверніть увагу, що за такого підходу ми намагаємося не використовувати PublishSubject, BehaviourRelay і тп, щоб чітко розділити input і output у ViewModel. Тепер давайте це все зв'яжемо.
View віддає такі потоки даних:
let codeText = codeTextField.rx.text.share() codeText .bind(to:viewModel.code) .disposed(by: disposeBag) retryButton.rx.tap .bind(to:viewModel.getNewCode) .disposed(by: disposeBag)ViewModel буде якось (покажу нижче) обробляти введення коду користувача, а також робити запит на повторне надсилання коду, якщо ми натиснемо кнопку.
Спочатку давайте подивимося ViewModel повністю, далі розберемо її докладніше.
ViewModel розглянемо «по шматочкам»:
let _codeSubject = PublishSubject() self.code = _codeSubject.asObserver() let codeObservable = _codeSubject.asObservable() let validCodeObservable = codeObservable.filter
_codeSubject — це потік даних із текстового поля введення коду.
validCodeObservable — відфільтровує значення потрібної довжини, які ми надсилатимемо на сервер.
Вище ми домовилися, що PublishSubject не використовуємо, але всередині від того ж коду потрібен як AnyObserver , а й Observable , щоб використовувати його, наприклад, для відправки коду на сервер. Надалі я планую використовувати таку техніку: AnyObserver або Observable у публічному інтерфейсі та PublishSubject усередині.
let codeEvents: Observable> = validCodeObservable .flatMap < (code) в authService.confirmCode(code: code, token: token).materialize() >.share()Власне, відправка коду на сервер:) Звертаємо увагу на .materialize(). Оскільки ми плануємо використовувати цей Observable у реактивних ланцюжках, ми не хочемо отримати помилку та переривати їх. materialize дозволяє загорнути всі значення та помилки в Result і тим самим ми ніколи не перервемо реактивний ланцюжок через помилку.
Раніше я описував інший варіант за допомогою RxAction, його також можна використовувати для створення потоків подій значень, помилок та isLoading.
Стан завантаження
Тут є досить цікавий момент. Якщо ми отримали валідний код, готовий до відправки, ми відображаємо інтерфейс завантаження. Якщо ми отримали відповідь від сервера, це означає, що нам потрібно приховати стан завантаження. Таким чином, ми можемо взяти ці потоки даних (на прикладах вище), змапити їх у true або false і забиндувати в isLoading .
.elements( ) працює як фільтр і пропускає лише значення з codeEvents та ігнорує помилки. Нагадаю, що тип значень codeEvents — це Result , що є частиною RxSwiftExt.
Таймер повторного надсилання коду
Таймер включається за наступних подій:
- ми надіслали код на підтвердження (validCodeObservable.mapTo(Void())) ;
- ми перезапитали код (didRequestNewCode);
- відразу при заході на екран (.startWith(Void())) .
Саме це описано в рядку Observable.merge. Сам таймер стає стандартними засобами RxSwift. Зупиняємо таймер за допомогою оператора take(while:), поки значення таймера не буде 0.
Лейбл з таймером та кнопка «перенаправити» повинні ховатися/показуватися залежно від того, чи активний таймер:
viewModel.codeTimerIsActive .drive(retryButton.rx.isHidden) .disposed(by: disposeBag) viewModel.codeTimerIsActive .not() .drive(timerLabel.rx.isHidden) .disposed(by: disposeBag)За помилки відправки та запиту нового коду у нас буде відповідати один потік даних errors.
errors = codeEvents.errors().merge(with: fetchNewCode.errors()) .compactMap < ($0 as? ErrorType)?.localizedDescription >.asDriver(onErrorJustReturn: "")Також заборонимо редагувати код, крім того, як він відправляється:
viewModel.isLoading .not() .drive(codeTextField.rx.isEnabled) .disposed(by: disposeBag)ViewModel вийшла досить тестована, тому давайте напишемо тести! Я наведу приклади тестів, які будуть показувати, як ViewModel реагує на введення користувача. Створимо допоміжний метод, який створюватиме потік подій введення коду. Увага, використовується RxTest!
class ConfirmCodeViewModelTests: XCTestCase < // properties // methods //MARK:- Helpers private func bindCodeInputEvents( _ events: [Recorded>] = [.next(100, "1"), .next(200, "11"), .next(300, "111"), .next(400, "1111")]) < codeInputEvents = scheduler.createHotObservable(events) codeInputEvents.bind(to:viewModel.code).disposed(by: disposeBag) >> Наприклад, таймер відправки нового коду повинен запускатися і коректно відпрацьовує відразу після відкриття екрану – напишемо такий тест:
func test_timerInvokedAutomatically() < let sut = scheduler.start(created: 0, subscribed: 0, disposed: 1000) < self.viewModel.newCodeTimer >XCTAssertEqual(sut.events, [.next(1, 2), .next , 1), .next(3, 0)]) >Або ось такий: перевіримо, що у нас передається на UI подію про помилки
func test_errorEmmitedValueAtFailure() throws < bindCodeInputEvents() setConfirmCodeResult(.error(0, MockError.confirmFailure)) let sut = scheduler.start < self.viewModel.errors >XCTAssertEqual(sut. ]) >Повний код тестів, та й узагалі весь приклад можна знайти тут. Вимоги можуть трохи змінюватися від проекту до проекту (наприклад, код можна відправляти по кнопці а не автоматично), але цей код досить нескладно пристосувати до подібних змін.
Ну а якщо вам сподобався наш підхід, вам цікаві великі проекти з мобільної розробки, тоді Напишіть нам.