Когда я разбирался с программируемыми калькуляторами, то думал, как бы элегантнее протестировать функциональность устройства. Один из известных способов проверки – это реализация какой-либо игры.
Игр для калькуляторов, как на просторах бывшего СССР, так и за рубежом громадное количество, остаётся только выбрать. Наиболее популярная — это «Посадка на Луну». Однако, для меня она показалась скучной и неинтересной, а сам код сложным и запутанным. Поэтому мой выбор пал на крестики-нолики, так как все мы играли в них в школе, и мне стало интересно сыграть в неё с калькулятором.
Реализовать игру решил на модели HP-32S, поскольку он мне очень полюбился за красоту архитектурной реализации и удобство программирования.
❯ Основа программы
В предыдущей своей статье "Калькуляторы с обратной польской нотацией" я делал обзор литературы для программируемых калькуляторов. Среди которой была замечательная книга А.Г. Гайшут "Калькулятор твой помощник и соперник в играх".
В этой книге приводится огромное количество примеров игр на калькуляторе и, в частности, пример игры в крестики-нолики:
Мне не удалось найти описания логики работы этих игр, а автор книги, к сожалению, умер в 2015 году. Попытка искать ответ в публикациях автора, любезно предоставленных на его личном сайте, не увенчалась успехом.
Поэтому придётся разбираться с этой программой самостоятельно. Приведу текст и описание программы из книги под спойлером:
Описание программы Крестики-нолики из книги:
Главная задача – это перенос этого кода с МК-61 на калькулятор HP-32S. Для начала, разберёмся как эта программа работает.
❯ Проверка программы на МК-61
Первое, что я сделал – это перенабрал код из книги в формате, который понимает онлайн-эмулятор МК-61.
Кстати, если интересно, то можно попробовать поиграть в эмуляторе, чтобы понять принцип работы. Для этого копируем код, вставляем в область «Код программы:» и нажимаем кнопку «Ввести в память». Картинка из книжки выше подсказывает нам, что калькулятор даёт координаты, куда ставить "X", а мы ему в ответ передаём координаты, куда ставить "O".
Координаты для игры
Чтобы начать играть на клавиатуре калькулятора, нужно нажать кнопку [С/П]. В ответ будет выведено число, первое число всегда «9» (центр поля). В ответ необходимо ввести свою координату, например, «2» и нажать [С/П]. И так далее, пока вы не проиграете (калькулятор выведет «77», либо будет ничья (калькулятор выведет «0»).
Калькулятор победил
Чтобы посмотреть последний ход калькулятора, надо обменять регистры X и Y местами, для этого нужно нажать на клавишу [⟷].
Проверка показала, что всё прекрасно работает как в эмуляторе, так и на живом калькуляторе, и ошибок в программе нет.
❯ Анализ кода программы для калькулятора МК-61
Для понимания работы программы, я переписал её на python. Конечно, даже при переносе на привычный язык программирования, код будет выглядеть немного диковато, поскольку реализовывался на совершенно иных принципах, но он хотя бы будет читаемым для остальных пользователей.
Особенность программирования МК-61 в том, что он пропускает команду перехода, если условие истинно, и исполняет — если ложно! Поэтому все условия для python пришлось инвертировать. Плюс, я для удобства ввёл дополнительные функции, которые также перенёс впоследствии в HP-32S: функция вывода координат крестиков и ввода ноликов, функция ничья и победа калькулятора:
Первое – инициализирую регистры калькулятора:
После всех подпрограмм идёт головная программа:
Можно увидеть, что в любом случае в самом начале крестик будет стоять на координате 9. Вся основная логика сокрыта в подпрограмме.
Из всего кода я понял, что второй ход калькулятора будет на единицу меньше оппонента, а если ход был в координату «1», то равен восьми. Но вот что делает остальная логика программы, особенно зачем там тригонометрическая функция, для меня осталось загадкой. Буду рад читателям, если кто-то сможет прояснить, как же работает эта программа.
И, да, код вполне себе работоспособен, в чём несложно убедиться:
❯ Перенос кода на HP-32S
Напомню, что калькулятор HP-32S, который есть у меня, принадлежит семейству калькуляторов HP10B/14B/17B/17BII/19BII/20S/21S/22S/27S/28S/32S/32SII/42S, таким образом, всё, что приводится ниже, с небольшими адаптациями можно будет перенести и на другие модели этой серии.
Трудозатраты в предыдущей главе, по переносу кода на python, были проделаны с двумя целями:
Понять, как же работает этот код (увы, не выполнено).
Более удобно переносить на другую модель калькулятора.
Этакая программная блок-схема, которая позволяет понять, какие регистры нужны, какие переходы и прочее.
Вооружившисьдокументацией на калькулятор HP-32S, я переписал программу крестиков-ноликов с питона для него. Для удобства я делал это в таблицах Exel. Как я уже говорил, особенность калькулятора в том, что он маркирует каждую строку буквой и цифрой, а любая метка – это смена буквы. Таблицы идеально подходят для этого.
Ниже под спойлером, приведён код программы. Если вы хоть немного знаете ассемблер и какой-то другой язык программирования, хоть тот же BASIC, то без труда сможете понять, что же там происходит.
Код программы для калькулятора HP-32S:
В силу того, что на калькуляторе HP-32S можно сделать вывод на экран конкретного регистра (с указанием имени регистра), а также запрос ввода другого конкретного регистра, то ввод-вывод становится чуть более интерактивным и интересным.
Лучше один раз увидеть, чем тысячу раз прочитать.
❯ Выводы
Изначально задача казалась мне такой простой, но заняла у меня достаточно приличное время. Её ценность состояла в том, что мне удалось разобраться — как же программировать для калькулятора HP-32S. В результате оказалось, что из модельного ряда калькуляторов, с которыми я занимался — эта версия оказалась самая дружелюбная и удобная.
Другой задачей, которую я хотел решить — это разобраться, каким образом изобретались подобные программы для микрокалькуляторов. Из-за того, что у калькулятора ограничена память программ, производилась какая-то дичайшая оптимизация, поэтому она выглядит так запутанно. Но, к моему сожалению, ни толковой литературы, ни описания, как это делалось, мне не удалось найти.
Поэтому, если у вас, уважаемые читатели, есть идеи о том, как же работает программа крестики-нолики (можно анализировать код python), то я с удовольствием их выслушаю.
В общем давно валялся в тумбочке e-ink дисплей, хотелось куда-то применить. Слепил календарь на тумбочку. Календарь с гугл аккаунта, погода с openweathermap. Контроллер esp-32. Живет месяц на одном заряде аккумулятора. Обновление раз в час, ну или по кнопке. Знаю что кнопки уродские, но какие были такие и воткнул. :)
Самое сложное было данные с гугла вытянуть. Единственный способ который нашел рабочий - это сделать скрипт на Google Apps Script
С момента выхода первой части статьи из рубрики «сам себе экосистема» прошёл уже практически год! За это время, мы успели с вами реализовать клиенты VK и YouTube, которые работают на Android 2.2+, а также на Windows Phone 8, написать небольшую 2D-игру с нуля весом менее 1Мб, которая работает практически везде и довести существующее приложение до ума, дабы оно работало даже на смартфоне с дисплеем 240x320! Но на дворе 2024 год, люди стремительно переходят из соц. сетей в продвинутые мессенджеры и уже сложно себе представить современного человека, который не пользовался бы «телегой» или даже «вайбером» в качестве основного средства общения. Поэтому я решил реализовать клиент Telegram на смартфоне 14-летней давности на базе официальной реализации MTProto от команды Telegram — TDLib. Сегодня мы с вами: узнаем новые причины мотивации вернуть в строй смартфоны прошлых лет, напишем на C# реле-сервер, который обрабатывает пакеты MTProto и кодирует их в простой текстовый формат датасетов, который можно моментально обработать даже при нестабильном GPRS-соединении на 21-летнем Siemens C60, а также узнаем о разработке миниатюрных Android-приложений на базе «голого» API-системы, которые не тянут за собой никаких зависимостей, в том числе и AppCompat/androidx. Интересно? Тогда жду вас под катом!
На дворе уже стукнул 2024 год, современные смартфоны предлагают какие-то немыслимые мощности относительно тех, которые когда-то были в первых Android-девайсах. Сейчас за сотню баксов можно купить смартфон с хорошей 1080p IPS-матрицей, 4Гб ОЗУ и 8-ядерным шустрым чипсетом, который вполне способен плавно тянуть даже стремительно «жиреющие» на ресурсы клиенты социальных сетей, банков и прочие необходимые в повседневной жизни приложения. И казалось бы: всё хорошо, покупай себе редмик раз в год или айфон раз в несколько лет и наслаждайся всеми прелестями работы современных приложений…
Для многих людей смартфон — это лишь инструмент, повседневный компаньон, который помогает облегчить выполнение каких-то задач. Им совершенно не важно, как он выглядит, как ощущается в руках, какой у него дисплей и железо «под капотом», лишь бы работал да и нормально. Но есть и другая категория людей, для которых телефоны, смартфоны и любые портативные гаджеты — это не просто утилитарный девайс, а настоящее инженерное произведение искусства, с которого буквально сдувают пылинки и стараются до последнего пользоваться ими как повседневными устройствами. Хотите пример? Смотрите ниже:
Фактически, среди современных смартфонов по сути и нет представителей такого нынче вымершего форм-фактора, как сайдслайдеры с физической QWERTY-клавиатурой, боковые раскладушки с двумя дисплеями и даже из QWERTY-моноблоков есть только смартфоны от Unihertz. Даже среди моноблоков с тачскринами нет никакого разнообразия, лишь без-рамочные одинаковые девайсы за исключением устройств от Sony.
Galaxy S Plus
Раньше меня часто спрашивали, мол, да как ты вообще можешь пользоваться смартфоном 10-летней давности, на котором давно нет официальных клиентов популярных сервисов и только недавно, с развитием блога, мне перестали задавать этот вопрос, поняв, что это бесполезно — ведь это дело принципа и порыва энтузиазма! Смотрите сами: у нас уже есть простенькие, но вполне рабочие клиенты ВК, YouTube, сейчас я допиливаю клиент «Сбера» на СМСках, реализую карты OpenStreetMap (правда пока без адекватной навигации), а в будущем планирую написать приложение для мониторинга погоды и трекинга посылок. Кроме того, в рамках этой статьи мы реализуем с вами клиент Telegram: так чем же это не функционал современного смартфона?
Но хорошо, с функционалом разобрались, однако для многих читателей слова «старый смартфон» это прямые синонимы «тормозной смартфон», мол «фуу, да как можно пользоваться этим тормозным кирпичом, он же лагает в последней версии моей ВКшечки!». Но давайте поставим вопрос ребром: может, это не столько девайсы немощные, сколько сами приложения, с кодовой базой, которая тянется более 10 лет, откровенно жиреют, обрастают костылями и хаками после далеко не одного поколения программистов, которые над ними работали? :) Один, вот, предпочитал пользоваться чистым AppCompat'ом, другой решил притащить зависимость, которая, например, оптимизирует виртуализацию ListView, третий решил заменить всю сериализацию Json со встроенных классов в Android на что-то стороннее и реализовал это костылями и вот так, по чуть-чуть изначально оптимальный и шустрый код превращается в неповоротливое УГ, которое не рефакторили кучу лет.
На видео Galaxy Pocket Neo — очень дешёвый Android-смартфон из 2011 года с 1-ядерным чипсетом на ~800МГц и 256Мб ОЗУ. При этом всём, Android софтварно рисует все анимации на процессоре, без участия GPU.
А значит у стареньких девайсов всё равно есть шанс быть полезными и стать полноценными повседневными смартфонами даже спустя более чем десять лет после выхода! И в сегодняшнем материале, я вам расскажу об особенностях разработки самопального клиента Telegram с собственным прокси-сервером, которое концептуально допускает реализацию даже на кнопочном Siemens C60 2003 года. Как? Читаем ниже!
❯ Принцип работы
В отличии от ВК (который разрабатывали те же самые люди, что и Telegram), API которого построено на базе REST-запросов и концепции Longpolling'а для моментального получения событий с сервера, Telegram построен на базе собственного протокола под названием MTProto, который может работать поверх любого «транспорта» (протокола нижнего уровня) — TCP, HTTP, WebSocket и т.п. Сам по себе MTProto в современном виде, разработка прожженного математика Николая Дурова и его команды — протокол относительно сложный для реализации «на коленке» и в первую очередь требует довольно серьезного понимания принципов работы современной криптографии, да и документирован он всё ещё не особо хорошо. Кроме того, у MTProto весьма интересный бинарный формат пакетов, эдакий велосипед Protobuf. В долгосрочной перспективе поддерживать свой велосипед MTProto может быть весьма проблематично, учитывая не самую лучшую документацию.
Но городить велосипед и не нужно, поскольку у команды Telegram есть официальная реализация MTProto — библиотека TDLib, которая инкапсулирует в себе не только детали реализации протокола, но и сетевой ввод/вывод и выбор транспорта, хранение базы данных сообщений и авторизации, автоматическую загрузку фото и видео, конвертация объектов из бинарного формата MTProto в JSON и полная многопоточность и частичная потоко-безопасность. С одной стороны это плюс — уже готовое решение для реализации клиента на новой поддерживаемой платформе, где есть OpenSSL (можно статически слинковать), zlib (линкуется статически), сокеты и файловый ввод/вывод, а также довольно неплохой механизм JSON-based API, которое позволяет использовать библиотеку в любом языке, который поддерживает вызов C-функций, а с другой и минус — библиотека довольно много весит, в одиночку прибавляя ~20Мб веса приложения для каждой архитектуры, у неё течёт память и у нее странный механизм получения данных с сервера (например, нельзя ответить на сообщение, зная его ID, если сообщение предварительно не загружено, при том что на сервере весь ответ — это просто ID, на какое сообщение прилетел ответ).
Понятное дело, что на стареньком смартфоне использовать оригинальный TDLib будет проблематичным — даже если собрать либы современным NDK и запилить JNI-интерфейс, библиотека «жрёт» много ОЗУ (20-100Мб «вхолостую», в зависимости от числа диалогов и частоты прилетающих событий, плюс со временем течет до 1-2Гб, если не использовать базу данных сообщений. Скорее всего, это косяк в реализации пулов, объекты из которых выгружаются при сбросе в базу, но не выгружаются при высоком потреблении ОЗУ) и уж тем-более TDLib не запустить на любимых кнопочных Java-сонериках! Поэтому я решил написать прокси-сервер, который отправляет команды, слушает ивенты TDLib и предоставляет REST-like API для клиентских программ, которые просто вызывают какой-либо метод, а в ответ получают простой и короткий строковой датасет только с необходимыми полями, весом до 10Кб (что позволяет его быстро загрузить даже с GPRS-интернетом), который можно быстро распарсить даже на преусловутом Siemens C60!
К сожалению, поскольку TDLib прожорлив, я не смогу захостить на своём сервере инстансы для читателей, которые хотят поюзать приложение, поэтому вам придется ставить и запускать сервер на своём VDS/компьютере с белым IP/роутере, если под него есть .NET Core :)
Клиентом же будет выступать Android-смартфон, где приложение будет фронтэндом данных с сервера. Ничего сложного на первое время нет: первое окно — это список диалогов, второе окно — список сообщений в диалоге + поле для написания сообщения, третье окно — информация о пользователе. Всё это я реализовал за три дня не-напряжной работы «на коленке».
Давайте же перейдем к реализации сервера!
❯ Прокси-сервер
Сервер я решил писать на C#, поскольку у .NET Core сейчас всё очень хорошо с кроссплатформенностью и производительностью. Его можно даже на Raspberry Pi запустить :)
Итак, какая-же архитектура такого сервера может быть? Программа инициализирует TDLib, начинает слушать её события в отдельном потоке, пока в основном потоке крутится HTTP-сервер, который обрабатывает каждый отдельный запрос с клиентского приложения. Почему синхронно? Потому что TDLib фактически не возвращает никаких идентификаторов для возвращаемых датасетов, дабы их можно было отличить друг от друга. Приведу пример: у нас есть метод getChatHistory, который возвращает n-сообщений. При этом TDLib сам определяет, сколько хочет сообщений вернуть (и в первый вызов возвращает одно сообщение вне зависимости от настрое и отправляем пакет message n-раз. При этом в пакете message нет какого-либо ID, который позволял бы ассоциировать текущий объект с какой-либо операцией. Увы!
Начинаем с коммуникации с TDLib. Для работы с библиотекой, мы будем использовать json-интерфейс. Для .NET есть биндинги через C++/CLI, но в таком случае, сервер не будет работать на Linux. Для работы с библиотекой хватит лишь три функции: CreateClientID, которая аллокейтит новый инстанс клиента, Send, которая асинхронно отправляет JSON-объект с командой, которую затем обработает TDLib и Receive, которая ждёт N-секунд и возвращает в виде ASCII-строки (!) JSON-объект с описанием события или данными после одного из запросов. За это у нас отвечает класс TDLibInterface, который объявляет PInvoke-методы для вызова нативных методов из библиотеки. .NET Core сам подгрузит библиотеку tdjson (причём на Linux он добавит ей префикс а-ля libtdjson.so, а на Windows загрузит tdjson.dll) и сам разберется с маршаллингом аргументов функций: например, string автоматически преобразует в const char*. Тем не менее, с const char* возвратами нужно быть аккуратнее — у меня был SIGSEGV, пока я ручками не конвертировал их в обычную строку.
З.Ы: На Пикабу нет отдельного тега для кода, а вставить листинги картинками я не могу из-за ограничения на 25 медиаэлементов. Так что листинги будут совсем без табов, но алгоритм их работы понять можно :)
Позволю себе чуточку критики в сторону TDLib. Во первых, почему нет s-версии функции с возможностью указать длину входной строки, а tdjson полагается исключительно на \0 в конце строки? Во вторых, почему const char*, а не wchar_t*? Сейчас юникод во входной строке приходится escape'ами превращать в \u-последовательности. После этого, нам нужно написать обёртку над TDLib, которая будет вызывать для зарегистрированных событий специальные функции, называемые коллбэками. При этом закомментированный WriteLine снизу — это «дебаг» для того, чтобы узнать названия неизвестных мне ивентов :)
В каждом объекте, полученном с помощью receive, есть поле "@type", которое содержит в себе имя класса возвращаемого объекта. Первый же вопрос от читателей — почему я использую JObject с ручным дерганьем нужных полей и вручную пишу JSON в виде строковых литералов вместо нормальной сериализации/десериализации? Ответ прост: во-первых, для актуализации Data-классов придется писать кодогенератор из TL-схемы, а во-вторых иногда TDLib может возвращать немного разные объекты в JSON, из-за чего приходится мудрить с атрибутами на этих самых Data-классах, иначе десериализатор выбросит исключение. Это решается нормальными юнит-тестами на всех вариантах данных, но зачем себе в колени стрелять, если нужен конкретный фиксированный функционал и лишь малое число от всех полей, возвращаемых TDLib?
string recv = NativeInterface.Receive(10.0d);
if (recv != null) { JObject json = JObject.Parse(recv);
if (!handlers.ContainsKey(type)) { //Console.WriteLine("Unknown event type: {0}", type); continue; }
handlers[type](recv, json); }
Теперь переходим к самому интересному — обработке событий и реализации синхронного клиента, который позволяет без async/await просто запросить список сообщений и сразу же его получить (такой подход может быть полезен и юзерботам, которые не хотят размазывать стейты по всей программе). Почему без асинков? Честно сказать, мне они просто не нравятся: как привык к концепции wait/notify и коллбэков из Java, так их и юзаю всю жизнь :)
Сначала TDLib запрашивает параметры инициализации (стейт authorizationStateWaitTdlibParameters), затем если пользователь не авторизован — запрашивает номер телефона и код подтверждения (плюс дополнительные шаги для авторизации если они есть). В конце, TDLib возвращает стейт Ready, что означает готовность библиотеки к работе:
После этого, можно начать работу с данными. Обратите внимание, мой подход потоко-небезопасен, его нельзя дергать из нескольких потоков одновременно! В коде ниже, я вызываю метод для фетча сообщений, а затем в соответствующем коллбэке от TDLib обрабатываю данные (дабы статья не разрасталась на 20+ минут, я чуть урезал все листинги).
public List<Message> QueryMessagesInChat(long chatId, long lastMessage, int count) { messages.Clear();
public User QueryUser(long userId) { string json = Utils.Format("{\"@type\": \"getUser\", \"user_id\": \"{0}\" }", userId); NativeInterface.Send(InstanceID, json);
waitHandle.WaitOne(); return user; }
Переходим к реализации самого сервера, для наших целей хватит обычного HttpListener. Сначала мы зарегистрируем все поддерживаемые методы и занесем их в ассоциативный список ключ-значение. Сами методы реализованы в виде делегатов, которые принимают лишь один аргумент — список параметров из строки запроса, а возвращают строку — все ответы, за исключением особых (связанных с загрузкой вложений) — текстовые.
Переходим к обработке запроса. Метод ищет, зарегистрирован ли запрошенный метод и если да, то парсит строку запроса, которая начинается с "?", которую затем передаёт в виде коллекции ключ->значения обработчику метода:
А сами методы, в свою очередь, дергают соответствующие функции из клиента и формируют на их основе датасет в примитивном формате:
public staticstring QueryChats(Dictionary<string, string> args) { if(args.ContainsKey("count")) { int count = int.Parse(args["count"]); StringBuilder ret = new StringBuilder();
В результате получаем вот такой простой датасет, который, как я и говорил, легко распарсить и на Siemens C60, и на Atmega328 — да где угодно! В целом, такой сервер можно использовать для реализации бота в телеграме, который будет передавать показания каких-то датчиков, сигнализацию и прочие клевые штуки!
Переходим к реализации клиента, т.е. приложения на Android. Здесь будет не менее интересно!
❯ Пилим для Android
В геймдеве есть своеобразный мем — некоторые инди-разработчики сначала начинают делать меню, вместо основного геймплея, что становится предметом насмешек среди других разработчиков. Но в разработке приложений для смартфонов всё по другому — здесь как-раз таки хорошо заранее продумывать макет будущего приложения!
Поскольку у нас с вами мессенджер, то главный экран должен представлять из себя список чатов (ListView) и верхнюю панельку, где в будущем могут разместиться настройки и свайп-менюшка:
Такой вот простой макет.
Каждый пункт меню — это тоже отдельный layout, в котором мы по шаблону строим внешний вид будущего элемента списка. На немолодых устройствах есть смысл использовать как можно меньше контейнеров в layout'е, поскольку пересчет позиций и размеров элементов — одна из самых «тяжелых» операций в UI-фреймворке вообще. Кроме того, не стоит использовать кучу картинок и drawable — в Android 2.x всё 2D рисуется софтварно, аппаратное ускорение появилось только в 3.0 (частично).
Но дабы в списке диалогов что-то появилось, нужно сначала реализовать фетчинг (получение) этих самых диалогов с сервера! Сам объект, который занимается обработкой запросов называется ClientManager и является синглтоном — он в единственном экземпляре на все время работы программы. Помимо менеджмента «ноды» (т.е. прокси-сервера), токена для авторизации и обработчика ошибок, ClientManager реализует метод для асинхронного запроса информации с сервера и, собственно, формирует строки запросов с помощью соответствующих методов:
Подгрузка чатов и сообщений реализована через Adapter — концепция «виртуальных» списков, которая предполагает что система создаст не 50 элементов интерфейса на каждую кнопку чата, а только 5 и будет их виртуально «мотать по кругу», обновляя только данные в уже существующих элементах. Это позволяет значительно ускорить отрисовку, учитывая то, что Android 2.x Canvas рисуется программно.
Ну вы уже явно замучились видеть простыни кода, давайте посмотрим что у нас вышло!
Шустренько, да? А ведь это ультрабюджетник Alcatel OT-916D, один из последних массовых дешевых QWERTY-смартфонов за 5 000 рублей из 2012 года. Кстати, смартфон подарил мне читатель chuvakoff с Хабра!
Переходим к окну чата. Основной макет почти такой-же, как и у основного окна: только добавилась панелька для ввода сообщения снизу.
Концептуально, всё тоже самое — запрашиваем данные с сервера, парсим их и загружаем в адаптер, благодаря чему мы сможем листать наш диалог. Однако в сообщения я добавил контекстное меню с стандартными фишками типа копирования, ответа и прочих подобных действий. Поскольку у нас нет ни пушей, ни еще каких-либо средств для поулчения данных о новых сообщениях, я раз в определенный интервал просто получаю сообщения — и если новый датасет отличается от старого — обновляю окошко чата.
Переходим к реализации поля для ввода сообщения. Здесь всё просто — на серверсайде за это отвечает метод SendMessage. Однако для того, чтобы с нашего клиента можно было ответить на другие сообщения, я ввёл также «контекст ответа», в котором запоминается сообщение, на которое мы хотим ответить. Telegram также поддерживает Markdown, однако его полная поддержка пока не реализована.
В остальном же, функционал конечно пока совсем базовый, однако клиент работает очень шустро даже бюджетной X10 Mini Pro и позволяет чатится с моими читателями в Telegram. В будущем хотелось бы допилить:
Поддержка картинок: Сейчас уже есть кривоватый механизм кэширования изображений на стороне сервера, который позволяет загружать аватарки чатов. В будущем, я добавлю поддержку «галерей» с картинками!
Поддержка голосовых сообщений: Не все их любят, но они порой удобны и выручают. Реализую как прослушивание, так и запись!
Подробный просмотр профилей и менеджмент чатов: Удаление сообщений, чатов и прочие фишечки из официальных клиентов.
Казалось бы — до официальных клиентов ещё очень далеко. Но сам факт, чтобы всё это работало достаточно шустро на девайсах, которым уже более 10 лет!
❯ Звучит интересно! Как заюзать твой клиент?
Тут всё очень и очень просто! В первую очередь, нам понадобится ПК с белым IP, роутер (если под него есть сборка dotnet), либо VDS. Виртуальные сервера сейчас стоят копейки, у ТаймВеба есть тариф за 188 рублей в месяц, которого с головой хватит для нашего сервера.
Такая вот рекламная интеграция (к слову, прокси для всех приложений уже более года крутятся именно на мощностях TimeWeb Cloud)!
Программа сначала запросит номер телефона, а затем код подтверждения Telegram. После этого будет создана папка tdlib/, где будут хранится данные вашей сессии, а также файл authkey.txt, где хранится случайный ключ для сессии (md5 phone_number + response code + псевдослучайное число). Не оставляйте его в /var/www/!
Если всё нормально, программа начнёт слушать порт 13377 на всех сетевых интерфейсах, в т.ч и в локальной сети. После этого, ставим уже предварительно собранный, либо собираем сами в Android Studio APK и в окне авторизации пишем адрес ноды и ключ авторизации. Если всё настроено верно — программа запомнит сервер и будет работать без проблем! Вот так всё легко :) Как видите — всё очень и очень просто!
Кроме того, буквально за пару дней до публикации статьи я сел вечерком из интереса что-нить под Java-телефоны попилить… и, как и обещал, реализовал Proof of Concept возможности работы Telegram даже на сонериках, которым скоро 20 лет стукнет! А ведь если ещё чуть заморочится, можно запустить приложение даже на преусловутых монохромных сименсах!
❯ Заключение
Вот такой у нас получился проект с реализацией лёгкого, примитивного, но тем не менее рабочего клиента Telegram, который на клиентской части вообще не использует никаких зависимостей. Вес собранного APK в release-версии — всего 54 килобайта! Понятное дело что с ростом функционала, вес программы будет увеличиваться, но я обещаю — больше 1Мб он не вырастет :)
Ну а вам, моим читателям, надеюсь было интересно прочитать такой «двойной материал» не только о разработке сетевой части без использования Apache/nginx/IIS, но и UI-фронтэнда для Android-смартфонов, которым уже более 10 лет! Исходный код проекта можно найти на моём GitHub: как приложения, так и сервера, а также убедиться в отсутствии каких либо закладок и, если совсем не доверяете, собрать бинарники сами! Для сборки понадобится VS2017 или свежее, а также Android Studio 2.3.2 (если собираете для Android 2.1 и ниже).
Друзья! Сейчас на Хабре опросы сломаны, поэтому если у вас есть желание, вы можете проголосовать в комментариях: какой стиль статей вам больше нравится — где больше конкретики и кода с пояснением как конкретно работает та или иная часть программы, или наоборот стиль ближе к научпопу, где фрагментов кода нет, или их значительно меньше? Пишите своё мнение о проекте в комментариях!
Кроме того, у меня есть канал в Telegram, куда я публикую бэкстейдж статей, ссылки на новый материал, свои наработки, а также посты о ремонте девайсов и различные мысли.
Статья подготовлена при поддержке TimeWeb Cloud. Подписывайтесь на меня и @Timeweb.Cloud, чтобы не пропускать новые статьи каждую неделю!
Мы постарались сделать каждый город, с которого начинается еженедельный заед в нашей новой игре, по-настоящему уникальным. Оценить можно на странице совместной игры Torero и Пикабу.
Пикабу, привет! Ловите немного кликбейта вам в ленту... А если серъезно, то ровно год назад я писал про разработку прототипа интерактивной светодиодной платформы. Я тогда и не верил, что смогу найти партнера-инвестора на vc.ru и что через год у нас будет полноценная игровая в Москве с выручкой 1,2 миллиона рублей в месяц.
Суть развлечения заключается в том, что люди должны выполнять различные игровые сценарии: от обычных “Классиков” и “Пол – это лава”, до игры в “Пинг-Понг” на 32 квадратных метрах площади, покрытой специальными светодиодными плитками, оснащенными датчиками нажатия.
Запуск и первая выручка
На запуск потратили порядка 7 млн. руб. Открылись в октябре, в операционный плюс вышли в январе, а за март сделали уже 1,2 млн. выручки. Мы считаем это весьма достойным результатом для нашего не совсем удачного формата игровой (об этом расскажу чуть ниже) и, вполне вероятно, март так и останется самым прибыльным месяцем в этом году:
Выручка за март:1 212 305 руб;
Средняя загрузка:51,8% (работаем с 10 до 23ч);
Средняя выручка в час:5 801 руб;
Основные статьи расходов:
Аренда + коммуналка + интернет:133 200 руб;
Зарплаты + налоги: 265 894 руб;
Маркетинг:100 000 руб;
Роялти (10%):121 231 руб;
Другое (сервисы, расходники):65 327 руб;
Чистая прибыль за март:526 653 руб;
Заголовок и вступление получились немного кликбейтными. Общие показатели выглядят далеко не так радужно, но мы искренне считаем, что сможем продолжать зарабатывать и построить целую сеть игровых!
Ниже – показатели за первые полгода работы. На старте много экспериментировали и откровенно теряли деньги, а потому несколько месяцев проработали в минус и только сейчас наверстываем:
Общая выручка за первые полгода:3 628 030 руб;
Чистая прибыль: 609 514 руб;
Ну, как есть… мы за открытость! В плюсе – уже хорошо!
Не очень удачный формат заведения
Помещение мы сняли в цоколе небольшого бизнес-центра и делали ремонт полностью с нуля с возведением стен. Изначально планировали разбить заведение на четыре зоны (и разбили): зона ресепшена – 18 м2, зона отдыха – 29 м2, большая прямоугольная игровая – 34 м2 и малая квадратная игровая – 19 м2. На старте денег хватило только на электронику для одной большой игровой, а помещение второй комнаты просто отремонтировали и начали использовать в качестве подсобки.
Процесс сборки пикселей в подсобке и последующей выкладки в игровой.
Уже после открытия мы поняли, что поднять средний чек за счет добавления второй игровой не выйдет, т.к разным компаниям будет тесно в одной зоне отдыха, к тому же существенная выручка идет с аренды под праздники/корпоративы. Короче, надо было строить две зоны отдыха + одну игровую: в таком формате можно было бы чередовать доступ людей в игровую и повышать средний чек, так делают многие VR арены. Итого, у нас теперь есть большая подсобка в 20 квадратных метров. Тупо вышло…
Как сеть оффлайн квестов может быть IT-компанией?
С самого начала мы позиционируем себя IT-компанией. Казалось бы, где сеть оффлайн квестов, а где IT-компания?
Светящиеся плитки на полу – далеко не рокетсайнс. Мы прекрасно понимаем, что невозможно будет тягаться с Китаем только на поприще электроники, это явно не сможет стать основной долгосрочной деятельностью. Мы сразу делаем большой упор именно на софт: мы УЖЕ в разы круче китайцев, продолжаем выстраивать свою экосистему и уже сейчас закладываем архитектуру для масштабирования проекта.
Наш первый дежурный дашборд, который скоро пополнится новыми городами
Главная идея – сделать работу всей сети прозрачной и поддерживаемой. Ведь лучшая проблема – это та, которая не дошла до клиента. А для этого нужна хорошая система превентивной диагностики и мониторинга с алерт менеджментом. Этим сейчас и занимаемся.
В IT тоже любят поговорку: “Быстро поднятое упавшим не считается!”
Open-source игры, а значит можешь и ты!
Помимо наличия у нас визуального конструктора для механики “Пол – это лава”, про который я упоминал в предыдущей статье, мы разработали собственную уникальную веб-платформу для написания и отладки игр на языке Lua и готовы выдать доступ всем желающим попробовать свои силы в разработке пиксельных игр!
Веб-платформа для разработки и тестирования пиксельных игр
Язык Lua – очень простой в изучении и использовании, но, в то же время, один из самых быстрых и производительных скриптовых языков программирования! Де-факто он является стандартом в игровой индустрии и используется, например, в таких играх, как World of Warcraft, Roblox, Minecraft и Angry Birds.
Его использование уже позволило нам сильно ускориться в выпуске новых игр за счёт привлечения фрилансеров, а в будущем мы рассматриваем идею платить авторам денежное вознаграждение, соизмеримое с количеством запусков их игр, т.е направлять фиксированный процент от выручки сети на поощрение сторонних разработчиков. А сейчас договариваемся о сотрудничестве с онлайн-школами программирования и хотим запустить курс обучения разработке игр под нашу платформу.
Новый конструктив и проблемы производства в Китае
Первую версию пикселей мы делали из рамок на алюминиевом профиле, но такой конструктив оказался очень трудозатратен в сборке и не годился для массового производства (вы прочувствовали всю боль сборки по фото выше?), поэтому новую версию мы решили делать из пластика. Изготовление формы для литья и десяти тестовых образцов в Китае обошлось нам порядка 600 тыс. руб. Почувствовал себя конструктором:
Кто читал меня ранее, знает, что я хейтил китайские пиксели за крайне дешевый и легко горючий пластик:
Мы считаем негоже гнать 3кВт мощности под детскими ногами с таким материалом. Интересно, есть ли на этот счет какие-то пожарные нормы? Ничего не нашел.
А вот видео, как я пытаюсь поджечь наш новый пиксель из специального огнестойкого угленаполненного полиамида:
Да, мой первый прототип выглядел именно как гаражное производство и был собран из говна и палок, но мы то не стоим на месте. Посмотрите, какая красота сейчас получилась:
Заключение
Бизнес модель работает, и емкость рынка позволяет развиваться. Сейчас мы активно занимаемся вопросом открытия собственных дополнительных точек в Москве и первых точек партнеров в других городах.
Я безмерно благодарен людям за поддержку первой статьи, за крутые знакомства и положительную обраную связь!
Буду рад ответить на любые вопросы в комментариях, а кому интересно следить за развитием проекта, подписывайтесь на телеграм канал: @pixel_quest, пишутам исключительносам и не чаще одного раза в 4-7 дней, маркетологов выгнал, больше планирую рассказывать про техническую составляющую проекта с постепенным уклоном в IT, дополнительно публикую ежемесячные финансовые отчеты.
Где я подробно рассказываю о том, как реализовал клиент современного мессенджера Telegram на Android 1.5+ и выше. Таким образом, Telegram будет работать даже на самом первом Android-смартфоне в мире, T-Mobile G1, причём на стоковой прошивке!
Несмотря на то, что студенческие годы далеко позади, электрогитара и увлечение музыкой остались в моей жизни как хобби. А инженерный бэкграунд и неугасаемое любопытство привели к тому, что несколько месяцев назад я увлёкся темой изготовления звукоснимателей и начал погружаться в этот удивительный мир, изучая и конспектируя литературу. Но теория должна подкрепляться практикой, поэтому в какой-то момент мне понадобился намоточный станок и я решил его изготовить самостоятельно. В наличии имеется 3d-принтер, в Компас 3D работать немного умею и с Arduino факультативно знаком, а вот с ТММ (Теория Машин и Механизмов) уже всё гораздо печальнее, но это не повод сдаваться!
О намотке катушек звукоснимателей
Данная статья именно про изготовление намоточного станка, но так как планируется мотать катушки звукоснимателей для электрогитары, то придётся учитывать определённую специфику при его проектировании.
Для начала разберёмся с типами намотки, их всего 2:
Ручная намотка - двигатель вращает катушку, а оператор контролирует натяжение и укладку провода (провод скользит между пальцев). Повторяемость характеристик при таком методе намотки остаётся весьма условной и зависит от опыта оператора. Отсюда и легенды про гипотетическую "бабу Зину с Фендера", которая в 60-х мотала датчики с "тем самым" звуком :) В наше время, звукосниматели намотанные вручную, называют "бутиковые" - звучит солидно, хоть и сомнительно.
Автоматическая намотка - шаг намотки, натяжение, скорость, паттерн укладки - всё контролируется высокоточным станком с ЧПУ. Тут уже не забалуешь, поэтому повторяемость характеристик остаётся высокой, что на мой взгляд является несомненным преимуществом.
Оба типа намотки остаются сегодня востребованы, но станок для ручной намотки значительно проще по своей конструкции, поэтому я решил двигаться от простого к сложному и остановился на первом варианте. Но от идеи создания станка для автоматической намотки я не отказываюсь - некоторые наработки имеются.
Теперь про толщину провода - он очень тонкий. Например если взять тот же AWG 42, то диаметр медной жилы составит всего 0,0635 мм. Мотать такой провод нужно очень осторожно - лишние нагрузки могут привести к его растяжению или обрыву, а ни того, ни другого мне не надо.
Первый неудачный прототип станка
Первый вариант намоточного станка оказался не очень удачным, так как я несколько спешил - уж очень хотелось послушать как звучит "бутиковый" звукосниматель :D
Однако этот прототип очень наглядно продемонстрировал все возможные проблемы и после их устранения мне удалось добиться нужного качества намотки, поэтому я считаю чрезвычайно важным его продемонстрировать.
Началось всё со сборки макета и написания к нему скетча:
За основу я взял Arduino UNO (точнее плату c Aliexpress, внешне напоминающую Arduino UNO), так же заказал джойстик и дисплей 1602 + I2C, чтобы задействовать минимум пинов на плате.
Чтобы было удобно задавать точное число витков, я решил использовать биполярный шаговый двигатель HANPOSE 17HS4401 в форм-факторе Nema 17. Двигатель реально классный, одно удовольствие с ним работать! А вот с драйвером я промахнулся и вначале поставил L298N. Он достаточно быстро нагревается и двигатель начинает пропускать шаги - это уже выяснилось в процессе намотки первых образцов. В последствии я поставил драйвер TB6560, который отлично справляется со своей задачей.
Далее в Компас 3D я спроектировал первые детали станка, в том числе корпус и основание и распечатал, после чего начал сборку:
Само основание сделано так, что можно добавлять разные модули - это очень помогло обкатать некоторые конструктивные решения, затем улучшить их. А вот корпус блока управления нужно было сделать разборным - поместить туда всю электронику и закрутить гайки - было отдельным квестом. На фото ниже частично собранный станок:
Далее был допечатан укладчик и проведены первые испытания:
Теперь я расскажу о проблемах данного решения:
Начну с программного обеспечения - для управления шаговым двигателем первоначально я использовал стандартную библиотеку Stepper. К сожалению она не сильно гибкая в настройках и подходит только для самых простых случаев. Например двигатель запускался сразу с заданной скоростью без ускорения, что создавало в моменте высокую нагрузку на провод и он просто рвался. В итоговом скетче, который будет ниже, использована другая библиотека - AccelStepper.
На укладчике нет ни демпфера, ни натяжителя - предполагалось что я буду слегка притормаживать бобину рукой, но это оказалось плохим решением. Можно было просто выкинуть укладчик из конструкции и укладывать провод пальцами, но мне захотелось чтобы натяжение контролировалось именно станком - таким образом можно было бы свести к минимуму обрывы провода.
С держателем бобины тоже не всё гладко - бобина раскручивалась по инерции и провод путался, а если её притормаживать рукой, то через некоторое время происходил обрыв от малейшего неосторожного движения. Появилась задача придумать очень деликатный тормоз бобины: провод должен сходить максимально легко, при этом бобина не должна раскручиваться по инерции и путать провод.
Работа над ошибками и итоговый вариант
Я распечатал совершенно новый держатель бобины:
Здесь уже 2 точки опоры вала и запроектирован магнитный тормоз на небольших неодимовых магнитах (5х2 мм). Усилие можно регулировать как количеством магнитов на тормозном диске, так и расстоянием между магнитами, которое регулируется на держателе. Готовый держатель бобины выглядит так:
На держателе по кругу расположены 10 магнитов и ещё буквально по паре магнитов на тормозных дисках с 2-х сторон, на мой взгляд этого достаточно для создания оптимального усилия - тут главное не переборщить. Вал с катушкой установлен на подшипники 608ZZ, таким образом вращение достаточно свободное, чтобы легко сходил провод, но при этом магниты не дают бобине раскручиваться по инерции.
Далее настала очередь укладчика - нужно было сделать конструкцию более жёсткой, добавить демпфер и регулируемый натяжитель провода. Демпфер нужен для компенсации дополнительной нагрузки на провод, которая возникает из-за вытянутой формы катушки.
Кроме этого, был случайно проведён незапланированный краш-тест станка, в результате чего уцелели не только лишь все детали. Пришлось в качестве основания взять лист фанеры размером 30х30 см. и смонтировать всё заново.
Вот так выглядит обновлённый укладчик:
Конструкция стала более жёсткой, люфты ушли. Вместо прецизионных валов я использовал гладкие мебельные болты DIN603 (8х100 мм) из Леруа Мерлен - по ним прекрасно скользит укладчик. Для станка с автоматической намоткой такой номер не пройдёт - там очень важна точность, а для ручной намотки такое решение очень даже подходит.
В качестве демпфера выступает журавль на пружине и ограничителем хода. Основание журавля установлено на подшипник 688ZZ, чтобы избежать лишних люфтов. Те же подшипники используются в роликах. А вал червячного механизма уже на подшипниках 608ZZ.
В качестве натяжителя выступает конструкция, которая зажимает проволоку между двумя войлочными дисками - это довольно распространённое решение и тоже показывает себя хорошо на практике. Винтами можно регулировать силу натяжения провода, от которой в конечном итоге зависит и качество намотки, и характеристики катушки. Для станка с автоматической намоткой натяжение необходимо ещё и измерять, чтобы можно было точно отрегулировать станок.
На заднем плане виден новый драйвер шагового двигателя - TB6560, про который я рассказывал ранее. Он хорошо справляется со своей задачей и не перегревается при долгой работе + в нем присутствует масса настроек (ограничение тока, делитель шагов). Такой драйвер можно использовать и для станка с автонамоткой.
Готовый результат
Так выглядит готовый станок целиком. На этом фото уже намотана первая тысяча витков на катушку звукоснимателя:
Впечатления от станка у меня положительные, работать укладчиком вполне удобно, провод пока ни разу не порвался в процессе намотки и натяжение провода постоянное. Все проблемы первой версии исправлены и появились наработки для того, чтобы в будущем сделать станок уже с автонамоткой. Но, как я уже писал, для автонамотки требования к станку значительно выше и конструктивные решения будут совсем другими, иначе не получится выдержать точный шаг и укладка провода будет идти плохо.
Общая схема электронной начинки станка выглядит так:
В меню станка есть 2 настройки: число витков и скорость намотки. Запуск двигателя происходит плавно с ускорением, а остановка с замедлением, что исключает возникновение ударной нагрузки на провод. После того, как заданное число витков намотано - двигатель останавливается, а намотанное количество запоминается - это позволяет мотать катушку в несколько этапов и по завершении каждой итерации на экране будет высвечиваться точное число намотанных витков на катушке.
Метод runToPosition() является блокирующим, так что подсчёта витков в режиме реального времени нет. Данный метод не рекомендуется вызывать в цикле, как сделано у меня - в библиотеке AccelStepper есть асинхронные методы, которые предназначены для вызова в цикле, но нужно обеспечить при этом быструю работу самого цикла. В моём случае такой возможности нет, так как та же операция обновления экрана не очень быстрая, а ещё нужно проверять состояние кнопок с поправкой на дребезг контактов. Можно добавить ещё одну плату Arduino только для управления двигателем и обеспечить уже там быстрый цикл, а первую плату оставить на пользовательский интерфейс и настроить обмен информацией между ними, тогда должно получится отображать число намотанных витков уже в процессе намотки без ущерба скорости вращения двигателя, но в данном станке такую доработку выполнять я не планирую.
Результат работы намоточного станка
С помощью данного намоточного станка я успешно изготовил первые образцы звукоснимателей и теперь они проходят испытания:
Он же в одной из моих электрогитар:
Про сами звукосниматели рассказывать пока рано - ещё предстоит много экспериментов, измерений и доведений до ума. Но если будет интересно, в будущем напишу статью и на эту тему.
Добрый вечер.Прошу помощи у силы Пикабу. Имеется следующая проблема:
Не так давно, в рамках затопления квартиры, была залита водой микроволновая печь Samsung MS22M8054AK. После просушки данная микроволновая печь не включалась и не подавала признаков жизни. На досуге решил заняться ее ремонтом. В рамках ее диагностики были обнаружены подтеки воды на плате управления.
Внешний вид поврежденной платы управления
Плата управления была заменена на аналогичную плату.
Фото вновь установленной платы
После этого микроволновая печь начала подавать хоть какие то признаки жизни: начала гореть лампа в микроволновой печи при открывании дверцы, при включении начала появляться индикация на дисплее, но через 5 секунд после самодиагностики она сменяется на код ошибки C-F2.
Стал изучать, что это за ошибка такая C-F2, как понял она говорит о проблеме в обмене данными между платой управления и клавиатурой (панелью управления) микроволновой печи. В данной микроволновой печи установлена не обычная пленочная клавиатура, а действительно полностью сенсорная ёмкостная панель управления. Для преобразования сигналов с данной сенсорной панели в сигналы понятные плате управления стоит noname плата драйвера на базе контроллера Infineon CY8C4124AZI-S433.
Фото платы драйвера. Промежуточное звено между платой управления и сенсорной панелью.
Визуальна данная плата драйвера целая, следов повреждений и подтеков на ней. Шлейфа визуально целые. Подозреваю что проблема в контроллере Infineon, возможно он был поврежден в процессе залития водой, или уже вторично от КЗ на плате управления.
Я так понимаю просто его замена никакой пользы не несет, так как еще нужна программа записанная в его память.
Пробовал относить в "официальный сервисный центр Samsung" - вынесли великий вердикт, что неисправна сенсорная панель (Хотя всю историю выше я им и так озвучил при сдаче в ремонт микроволновой печи).
Отдельно плату драйвера или сенсорную панель в продаже найти не удалось.
Может кто то сможет подсказать куда копать дальше?
Может кто то имел дело с такими контроллерами и сможет за разумную плату попробовать считать программную часть со старого контроллера, записать на новый и заменить его на плате?