Pull to refresh

Радиотелефончик на смартфоне

Level of difficultyEasy
Reading time15 min
Views20K
image

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

Если вам интересно узнать, что из этого получилось, то прошу проследовать под кат…

Понятно, что в начале любой здравомыслящий человек посмотрит, собственно, на радиомодуль своего сотового телефона. Действительно, размер соты для уверенного приёма, может составлять несколько километров в поперечнике (у AMPS вообще до 20 км, да и даже LTE ненамного меньше). И если с одной стороны базовая станция может подать весьма мощный сигнал, который легко детектируется, то с другой стороны и сам смартфон обладая, пусть и десятками и сотнями милливатт выходной мощности, но также доносит свой голосок до станции. А если роль базовой станции сыграет другой телефон? Ясно, что расстояние будет в этом случае всё равно меньше, поскольку усилители входных сигналов на станции явно будут помощнее, чем на телефоне, но не в принципиальной же степени (тут мы сразу оговариваемся, что эксперимент проводится в местах, где нет мобильного покрытия, чтобы никому ни в коем разе не мешать).
Другое дело, что API для работы с радиомодулем в современных смартфонах, если и существует (допустим для разработки), то оно наверняка, закопано очень глубоко и совершенно недоступно для пет девелоперов. И если уж делать радиотелефон на базе сотового, то логичнее брать древние модели, по которым уже всплыла внутренняя документация, и на которых ещё можно сыскать какие-нибудь тестовые разъемы и даже к ним подпаяться для оболванивания их электронных мозгов. И таки да, буквально месяц назад на Хабре же вышла прекрасная статья по этой теме, которую я с удовольствием прочитал.

Но как и вы сами можете убедиться, процесс подъёма связи здесь весьма непростой, требует хождения по помойкам, усидчивости и прямых рук даже с исчерпывающим руководством, а результат — коннект в пределах не дальше комнаты, необходимость тащить синхросигнал с чьей-то БС, плюс некоторая вероятность прибытия людей в чёрном. Понятное дело, что какая-то практичность проекта и не подразумевалась, главное было в торжестве человеческого гения, но мне мнилось замахнуться на несколько большие расстояния, чтобы это было больше похоже на настоящую радиосвязь.

Поэтому подивившись силе разума гиков, я обратился к тому, что у меня было уже в руках — двум смартфонам фирмы Сяоми, одному поновее, на Android 12 и второму постарше, на десятке.
Ну, блютус отпал сразу, поскольку на расстоянии на котором он стабильно работает, вы без труда можете переговариваться и без использования телефонов. Работа непосредственно с GSM модулем, тоже сами понимаете, была из разряда ненаучной фантастики. Так что оставался лишь старый добрый Wi-Fi. Идея была такова: поднимаем, значит, на одном смарте локальную сеть (или local Hotspot по английски), а вторым смартфоном к этой сети цепляемся и начинаем гнать звук соответственно от их микрофонов к противоположным динамикам двумя аудио потоками — короче устанавливаем голосовую связь! А потом шаг за шагом проверяем, насколько и на каком расстоянии эта связь устойчиво поддерживается.

Итак, задача понятна, приступаем к ее решению. Начнём естественно с аудио. Как уже упоминалось, API это весьма древнее, и если вам хочется асинхронности и колбэков для записи и приёма данных, то это не сюда. Добро пожаловать в отдельный поток и в цикл while do.

Вообще, самый простой способ записи с микрофона и последующего его воспроизведения через динамик — это использование класса MediaRecorder. Там даже с потоками не надо связываться. Просто учреждаем экземпляр MediaRecorder, определяем куда писать и из какого источника (в нашем случае это микрофон.

       mAudioRecorder = new MediaRecorder();

        mAudioRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mAudioRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);
        mAudioRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        mCurrentFile = new      File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
       mAudioRecorder.setOutputFile(mCurrentFile.getPath());
       mAudioRecorder.start();

А проигрываем соответственно через MediaPlayer():

      mAudioPlayer=new MediaPlayer();
      mAudioPlayer.setDataSource(mCurrentFile.getPath());
      mAudioPlayer.prepare();
      mAudioPlayer.start();

Тут настолько всё просто, что даже не требует пояснений. Не забудьте только оформить разрешение на использование микрофона и файлового хранилища.

     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

Но как легко видеть, для передачи живого аудио потока эти классы подходят мало. Здесь нам пригодится другой класс — AudioRecord, который может работать непосредственно с байтовым потоком (raw data). А чтобы это аудио поток так же потом проигрывать в реальном режиме времени, сгодится класс AudioTrack.

Вообще AudioRecord может так же невозбранно писать аудиопоток в файл как и MediaRecorder, но нам такая его способность не понадобится.

Итак, пишем:

private AudioRecord audioRecord;
private int SampleRate = 8000;
private int minBufferSize;

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

......

 audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SampleRate,
                 AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,
                  minBufferSize*2);

Или можно по модному через билдер, получится то же самое:

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            audioRecord = new AudioRecord.Builder()
                    .setAudioSource(MediaRecorder.AudioSource.MIC)
                    .setAudioFormat(new AudioFormat.Builder()
                            .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                            .setSampleRate(SampleRate)
                            .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
                            .build())
                    .setBufferSizeInBytes(2 * minBufferSize)
                    .build();

Как мы видим, у нас там есть переменные ENCODING_PCM_16BIT, SampleRate и minBufferSize. Рассмотрим их чуть подробнее.

Для перевода аналогового потока с микрофона в цифровой вид здесь используется формат PCM — импульсно кодовая модуляция. То есть с определенной частотой дискретизации (SampleRate) уровень аудио сигнала измеряется и переводится в формат 8 или 16 двоичных разрядов (или даже в формат с плавающей запятой для особых гурманов). Какая же должна быть частота и разрядность в нашем случае? В принципе можно взять формат, хоть как для аудио CD. Там, как известно, частота дискретизации равна 44,1 кГц, а разрядность 16 бит. Но тогда наш аудио поток изрядно распухнет в размерах, даже если мы будем записывать звук в одноканальном (моно) режиме ибо:

44100 х 16 = 705 600 бит в секунду (и это не считая всяких служебных).

И именно с такой скоростью нам придется передавать их по сети. Кажется многовато.
Но опять же, мы передаем не симфоническую музыку, а просто голос. Поэтому, памятуя о ширине обычной телефонной линии в 3 кГц и теореме Котельникова, согласно которой, для восстановления сигнала данной ширины спектра, надо всего лишь удвоить частоту дискретизации, то можно ограничиться SampleRate равным 8000. Это вообще полоса в четыре килогерца — даже шире, чем нам нужно.

Можно использовать разрядность 8 бит и добиться скорости передачи потока всего лишь — 8000 x 8 = 64 000 бит в секунду, что как бы является стандартной скоростью цифровой телефонии. Поток несжатый, но нам городить лишние сущности, пока ни к чему. Правда, при уменьшении разрядности до 8 бит, появляются какие-то раздражающие фоновые шумы (хотя голос вполне разборчив), поэтому можно сильно не экономить и оставить разрядность в 16 бит, тем более, что скорость передачи данных даже 16 кбайт в секунду наш смартфон не обессилит.

Что же касается переменной minBufferSize, то это минимальный размер внутреннего буфера, при котором объект AudioRecord сможет работать.

И вычисляется он по формуле:

minBufferSize = AudioRecord.getMinBufferSize(
                SampleRate, AudioFormat.CHANNEL_IN_MONO,
                AudioFormat.ENCODING_PCM_16BIT);

Формула не слишком очевидная, поэтому ради интереса, я вывел значение minBufferSize в лог. Он оказался равен 640. Наверное, байт. Во всяком случае, на фоне формирования двухбайтных отсчетов восемь тысяч раз в секунду, цифра невелика и поэтому не зря, видимо, все советуют её (то есть размер внутреннего буфера) увеличивать раз в десять. Ну, как минимум, в два.

После учреждения же всех начальных параметров, можно переходить собственно к запуску инстанса AudioRecord. Запускается он, естественно, в отдельном потоке в цикле while (true или какой-нибудь триггер).


 public void run() {
    
                try {

                audioRecord.startRecording();
                byte[] outData = new byte[minBufferSize];
                try {
                    while (mIsCapturing) {
                        // read audio data from internal mic
                        audioRecord.read(outData,0, outData.length);
                    }
                } finally {
                    audioRecord.stop();
                }
            } finally {
                audioRecord.release();
          
          }

То есть, пока переменная mIsCapturing установлена, audioRecord по готовности аудио данных, кладёт содержимое заполненного внутреннего буфера в байтовый массив outData. И нам остается лишь взять этот массив (пока он не затёрся новыми значениями) и отправить его, куда душа пожелает. Можно в байтовый поток, а потом в файл, но можно и в датаграммный пакет, который потом поедет по сети Wi Fi к нужному адресату.

Обратный процесс (из цифры в ухо), тоже больших проблем не доставляет. Главное там, задать те же параметры дискретизации, что и при записи. Здесь с байтами работает класс AudioTrack.
Правда ему нужны дополнительные параметры в лице AudioAttributes и AudioFormat поэтому писанины получается несколько больше.

AudioAttributes audioAttributes = new AudioAttributes.Builder()
                    .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) // defines the type of content being played
                    .setUsage(AudioAttributes.USAGE_MEDIA) // defines the purpose of why audio is being played in the app
                    .build();

            AudioFormat audioFormat = new AudioFormat.Builder()
                    .setEncoding(AudioFormat.ENCODING_PCM_16BIT) // we plan on reading byte arrays of data, so use the corresponding encoding
                    .setSampleRate(SampleRate)
                    .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
                    .build();

            AudioTrack audioIn = new AudioTrack(audioAttributes, audioFormat,
                    2*minBufferSize, AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE);

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

public void run() {
             byte [] inSoundData = new byte[1024];
  while (true) {
                 
                   try
                {
                    audioIn.write(inSoundData, 0, inSoundData.length);
                }
                catch (Exception e)
                {
                    // handle exception
                }
                audioIn.play();
            }
}

Тут, главное запускать audioIn.play() после прихода каждой порции данных. Потому как в интернете я сыскал пример кода, где автор шутник расположил его после цикла while. И нескоро я разобрался, почему вместо нормального воспроизведения файла (в начале я писал данные в файл), я слышал что-то вроде короткого "«блурп...» или вообще была полная тишина.

Итак, с записью и воспроизведением мы разобрались. Осталось лишь отправить и принять байты аудио данных по сети. Пока упростим задачу и примем, что сама локальная беспроводная сеть у нас есть, наш смартфон к ней подсоединён и имеет свой постоянный IP адрес. Можно конечно взять для демонстрации адрес 127.0.0.1, но мы не будем проявлять малодушие и действительно заставим звуки путешествовать по сети.

Работа с UDP протоколом (TCP, сами понимаете, здесь излишен) была рассмотрена в цикле статей про видео стриминг на Андроид устройствах, поэтому сильно подробно останавливаться на этой теме не будем.

В коде это выглядит так. Сначала учреждаем два UDP сокета, соответственно для отправки и получения данных. Номера портов пока у них одинаковые, поскольку мы работаем с одним устройством и шлём данные, хоть и по сети, но сами себе. Далее, соответственно запускаются два отдельных потока по приему и передаче аудио. И в этих же потоках начинают работать audioRecord.startRecording() и audioIn.play() , записывая и воспроизводя аудио поток.

Итак:

    private byte inSoundData[];
    DatagramSocket udpSocketOut;
    DatagramSocket udpSocketIn;

    String ip_address = "IP адрес устройства в локальной сети";
    InetAddress address;
    int portOut = 40000;// выход для пакетов со звуком/40000
    int portIn = 40000;// вход для пакетов со звуком// 40000

   protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

   mIsCapturing = true;
              
  try {
            udpSocketOut = new DatagramSocket();
            udpSocketIn = new DatagramSocket(portIn);
            Log.i(LOG_TAG, "  создали udp каналы");
        } catch (Exception e) {
            Log.i(LOG_TAG, "  Не создали udp каналы " + e);
        }

        new AudioSend();// для отправки звука по UDP
        new Udp_recipient();// запускаем прием UDP пакетов

        try {
            address = InetAddress.getByName(ip_address);
            Log.i(LOG_TAG, "  есть адрес");
        } catch (Exception e) {

            Log.i(LOG_TAG, "  нет адреса " + e);
        }
}

    private class Udp_recipient extends Thread {
        Udp_recipient() {
            Log.i(LOG_TAG, "запустили прием данных по udp");
            start();
        }

        public void run() {
        while (true) {
                try {
                    byte buffer[] = new byte[32768];
                    DatagramPacket p = new DatagramPacket(buffer, buffer.length);
                    udpSocketIn.receive(p);
                    byte bBuffer[] = p.getData();
                    inSoundData = new byte[p.getLength()];
                    synchronized (inSoundData) {
                        for (int i = 0; i < inSoundData.length; i++) {
                            inSoundData[i] = bBuffer[i];
                        }
                    }
                }
                catch (Exception e) {
                    Log.i(LOG_TAG, e + "hggh ");
                }

                try
                {
                    audioIn.write(inSoundData, 0, inSoundData.length);
                }
                catch (Exception e)
                {
                    // handle exception
                }
                audioIn.play();
            }
        }
    }

    public class AudioSend extends Thread {
        AudioSend() {
            Log.i(LOG_TAG, "запустили отправку данных по udp");
            start();
        }
        @Override
        public void run() {
           android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO)
                try {
            Log.i(LOG_TAG, "запустили отправку ");
                audioRecord.startRecording();
                byte[] outData = new byte[minBufferSize];
                try {
                    while (mIsCapturing) {
                       // read audio data from internal mic
                        audioRecord.read(outData,0, outData.length);// получили в байтовый массив порцию звука
                        // set audio data to UDP сокет
                        try{
                            //  Log.i(LOG_TAG, " outDate.length : " + outDate.length);
                            DatagramPacket packet = new DatagramPacket(outData, outData.length, address, portOut);
                            udpSocketOut.send(packet);
                        }
                        catch (Exception e)
                        {
                            Log.i(LOG_TAG, "не отправили UDP пакет");
                        }
                    }
                } finally {
                    audioRecord.stop();
                }

            } finally {
                audioRecord.release();
                Log.i(LOG_TAG, "остановили tred");
            }
        }
    }

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

В принципе, для телефонизации поселка и походного стана (где нет мобильного покрытия) этого достаточно. Устанавливаем в сельсовете или шатре предводителя мощный роутер с внешней антенной, а вокруг в избах или палатках дружины размещаем маршрутизаторы попроще (как репитеры). Подключаетесь к сетке главного роутера и можете бесплатно разговаривать по смартфонам хоть сутками. Там надо лишь допилить регистрацию абонентов и опцию выбора кому звонить.

Но поскольку я хотел от лишнего звена, роутера избавиться, то пришлось двигаться дальше, дабы сконфигурировать и запустить беспроводную локалку непосредственно у себя на смартфоне.

Для этой цели современный Android, вроде как, предлагает прекрасное современное (на колбэках!) технологическое решение local-only hotspot, чтобы приложения могли соединяться с друг другом по Wi-Fi для обмена данными. И даже заботливо предупреждает, что мол, сеть будет только внутренняя, а в Сеть на ней не выйдешь. Ну, нам в принципе, этого не требуется. Инициализация же этого дела выглядит примерно так:


        wifiManager = (WifiManager) this.getApplicationContext().getSystemService(Context.WIFI_SERVICE);

        wifiManager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() {
            @Override
            public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) {
                super.onStarted(reservation);
                Log.d(LOG_TAG, "Wifi Hotspot is on now");
                mReservation = reservation;
                currentConfig = mReservation.getWifiConfiguration();
                Log.d(LOG_TAG, "THE PASSWORD IS: "
                        + currentConfig.preSharedKey
                        + " \n SSID is : "
                        + currentConfig.SSID);

После чего вы получаете сетку с уже предустановленными названием (Android плюс чего-то там) и паролем. Нет, ну ладно сеть можно найти по слову Андроид в названии, а вот как бедному приложению пытающемуся к ней присоединиться, получить пароль??? Может можно сконфигурировать свою сеть с нужными названием и паролем? Смотрим страницу SoftApConfiguration (на официальном сайте!), и находим там ссылку SoftApConfiguration.Builder. И видим: «404 | страница не найдена».

Самое интересное, что в интернете примеры работы с этим билдером есть. Есть он и в доках Андроида. А в Android Studio — нет. Я и студию обновил, после чего текущие проекты жестко забаговались и слетели некоторые настройки (как вам такое: чтобы исправить внезапно возникшие ошибки Gradle, надо кое-где в файле сборки вставить слово version. Stack overflow не даст соврать); и поставил версию SDK API 33 Tiramisu, но все было тщетно, SoftApConfiguration.Builder не воспринимался системой никак.

После этого я стал примерно понимать ощущения безвестного участника Stack Overflow, который задал вопрос, а почему мол, эта новая сеть hidden, то есть скрыта. На что бездушный бот ответил ему, что он такие общие вопросы не понимает, пишите дескать пример кода. А я его понял, LocalOnlyHotspot вроде есть, и всё для его конфигурации имеется, а на практике — его нет.

Чуть позже на этом же форуме я нашел что:
  1. Только системные приложения могут конфигурировать LocalHotspot
  2. setSoftApConfiguration() это защищенное/ скрытое API, которым вы можете пользоваться только, если у вас кастомный SDK или SDK от третьих лиц, но всё равно в приложении оно работать не будет, если приложение не системное.

То есть копеечный роутер на линуксе сеть поднимает, а пет разработчик на Aндроиде видит вместо этого от Андроид разработчиков шиш. Что там такого в этой беспроводной локалке страшного и ужасного, что энтузиастам палки в колеса суют?

Но может быть, я неправ, и они наоборот вместо копания в IP адресах, просто предоставляют гикам продвинутые инструменты более высокого уровня? Вот например есть же Wi-Fi Direct и еще Wi-Fi Aware. Может надо пользоваться именно ими?

Действительно, на официальной странице прямо так и указано: Wi-Fi Direct (P2P) позволяет устройствам с соответствующим аппаратным обеспечением устанавливать друг с другом связь по Wi Fi без промежуточной точки доступа. А в обсуждениях всё того же Stack Overflow, народ также писал, что Wi-Fi Direct работает вместе с беспроводной сетью (то есть без потери основного Wi Fi соединения) и даже не требует ее отключения. Это, мол, огромное достижение.

Обнадёженный этой информацией, я начал знакомиться Wi-Fi Direct поближе. На первый взгляд всё было прекрасно. Можно сказать, вместо ассемблера работаешь с современным языком высокого уровня: подписчики, чаты, сообщения, группы, публикации и т.д.

В соответствии с руководством я успешно создал экземпляр WifiP2pManager и даже зарегистрировался в Wi-Fi P2P фреймворке вызвав метод initialize(). Но тут, я нечаянно выключил тот самый основной Wi Fi, короче отсоединился от своей домашней сети.

И всё! Тут же пришел интент, что Wi-Fi P2P недоступен!

Как оказалось, мне показалась, что всё это всего лишь обёртка над тривиальным Wi Fi. То есть, в принципе, правильно написали, что Wi-Fi Direct не мешает работе основному Wi Fi соединению. Просто забыли написать, что без основного Wi Fi соединения, Wi-Fi Direct в принципе работать не может.

Но это была ложная тревога. Просто при выходе из домашней локалки я отключил и Wi Fi модуль смартфона. А для Wi-Fi Direct он должен работать, не важно, подключен он при этом к какой-либо сети или нет. Правда, это я уяснил через пару дней, в течении которых я пытался сконфигурировать и поднять беспроводную локалку — дедовскими способами через методы Reflection API.

Это выглядит примерно так, сначала конфигурируем сеть:

WifiConfiguration wcfg = new WifiConfiguration();
       wcfg.SSID = " SSID NAME";
       wcfg.networkId = Int;
       wcfg.preSharedKey = "password";
       wcfg.hiddenSSID = false;
       wcfg.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
       wcfg.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
       wcfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
       wcfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
       wcfg.allowedProtocols.set(WifiConfiguration.Protocol.WPA);


А потом делаем финт ушами:

 Method method = mWifiManager.getClass().getMethod("setWifiApConfiguration",
                  wcfg.getClass());
                 method.invoke(mWifiManager, wcfg);

И всё, сеть сконфигурирована. И даже можно получить ее параметры стандартными get-методами

Но современные (Android 12) и относительно современные (Android 10) смартфоны эти reflection методы не видят в упор, и код взлетел лишь на стареньком Самсунге с шестым Андроидом.

Но радовался я недолго, так как сеть мало сконфигурировать, её надо ещё и включить!
На это есть метод:

Method method1 = mWifiManager.getClass().getMethod("setWifiApEnabled",
                        WifiConfiguration.class, boolean.class);

                method1.invoke(mWifiManager, wcfg, true);


Но он выдал исключение, которое привело меня к разрешению CONNECTIVITY_INTERNAL, которое, как оказалось, дается только системным приложениям.

Рутовать для этого даже старый телефон мне было лень и дальнейший поиск, так сказать, на этом остановился.

Я уже собирался вернуться к Wi-Fi Direct, но поскольку сокеты там пишутся немного по другому и вообще другая идеология, то вылетал весь код написаный для обычной Wi Fi сети и надо было писать новый. Поэтому радиотелефон на Wi Fi Direct я отложил для нового поста на Хабре, а затем здраво рассудил, что коль для инициализации своей сетки, надо сделать приложение системным, то почему бы не воспользоваться уже действующим системным приложением, которое работает, наверное, на всех современных телефонах. Это — Точка Доступа. Там, правда, надо на нее нажать пальцем, потом вбить свое название и пароль, что конечно, не комильфо. Но для проверки принципа и такой подход сойдёт.

Далее, со второго смарта надо подсоединиться к этой точке доступа (тоже ручками), и уже в параметрах мы увидим там и IP адрес шлюза (это первый смартофон) и свой IP адрес в этой сети. Теперь, если указать эти адреса в текстах программ, то радиотелефон у нас уже, можно сказать, заработал!

image

Теперь наступила пора полевых исследований. В следующую субботу, поколотив друг друга палками на тренировке по исторической реконструкции, я взял напарника с собой на тихую питерскую улицу и поведал ему свою тайну. Давно не видывал я такого детского восторга в глазах взрослого небритого мужика. Да? Рация на телефоне? Так это ж можно…

Восторги правда поутихли, когда наступили собственно практические испытания. Итог — зона уверенного приема в городе сто метров. На ста десяти/двадцати просто теряется сеть, а с ней, сами понимаете, и возможность живого общения. Это если на частоте 2,4 ГГц. На 5 ГГц ещё меньше, даже уже точно не считали.

На этом, в принципе, исследования завершились. Ради интереса можно ещё попробовать запустить связь на Wi Fi Direct, но скорее всего на дальность это не повлияет, так как всё зависит от мощности и чувствительности Wi Fi модуля, а у нас к нему доступа нет.
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
Total votes 60: ↑60 and ↓0+60
Comments65

Articles