WiFi + Bluetooth

Posted on
Page
of 2
/ 2
Next
  • Hi, I want an Espruino board with both WiFi and Bluetooth but it seems like none has both? What is the best solution to achieve this? Is some kind of combination possible and if so how are the devices connected?

    Thank you

  • Your best bet is to use one of the Bluetooth devices and to add WiFi to it - generally you'd use an ESP8266 module which handles all the TCP/IP and communicates via Serial.

    Hope that helps!

  • Great! No, unfortunately I don't stock them at the moment...

    The Crazepony ones look spot on, and the arduino shield might work, but I'd recommend https://www.amazon.co.uk/ARCELI-ESP-12E-­ESP8266-Wireless-Arduino/dp/B07DMZVQ4D/r­ef=sr_1_14 as that one looks identical to the one I've tested

  • Brilliant - thank you very much - both ordered and looking forward to test :)

  • If there was a device that had both, you could run EspruinoHub on it... ☺

  • I now have the WiFi shield but fail to understand the instructions here . I'm sure it's just me lacking knowledge but I don't understand the difference of using it 'as-is' or 'as a shield'. Do I need to re-wire or solder anything or can I just connect the pins from the shield to the Pixl.js? There seems to be different ways of connecting described on the page and I'm not sure which parts of the instruction I need to follow to add the wifi shield as simple as possible. Grateful for any clarification that could help me :)

  • I'd suggest the 'as-is' method.

    Basically, plug the shield onto the Pixl.js, but then you need to add an additional 3 wires, which you can do on the shield itself:

    • Connect a jumper lead from the TXD pin on Debug Port to D0 on the Arduino header
    • Connect a jumper lead from the RXD pin on Debug Port to D1 on the Arduino header
    • Connect Vin to GND

    The third one is something you can do with a solder-jumper on Pixl.js itself, but in the interests of not getting a soldering iron out you can do it just with wire.


    1 Attachment

    • esp8266_shield_wiring.png
  • Many thanks for the super clear reply and image! I followed it but unfortunately I receive the error;

    Uncaught No 'ready' after AT+RST
    

    when running the below code (from the instruction page). Any idea what I may be doing wrong?

    var WIFI_NAME = "x";
    var WIFI_PASS = "x";
    var wifi;
    
    function getPage() {
      require("http").get("http://www.pur3.co.­uk/hello.txt", function(res) {
        console.log("Response: ",res);
        res.on('data', function(d) {
          console.log("--->"+d);
        });
      });
    }
    
    function go() {
      Serial1.setup(115200,{rx:D0,tx:D1});
      wifi = require("ESP8266WiFi_0v25").connect(Seri­al1, function(err) {
        if (err) throw err;
        console.log("Connecting to WiFi");
        wifi.connect(WIFI_NAME, WIFI_PASS, function(err) {
          if (err) throw err;
          console.log("Connected");
          // Now you can do something, like an HTTP request
          getPage();
        });
      });
    }
    
    function onInit() {
      Terminal.setConsole();
    }
    
    go();
    

    1 Attachment

    • esp.jpg
  • Espruino Wifi requires the AT commandset firmware be available on the wifi shield. See also https://www.espruino.com/ESP8266#gotchas­

    Maybe you need to check if you have the correct firmware on the shield? https://www.espruino.com/ESP8266#firmwar­e-versions

  • I get an error when trying to verify the firmware version. Running this;

    var serial = Serial2;
    var pins = { rx: D0, tx : D1 };
    function test(baud) {
      serial.removeAllListeners();
      var l="";
      serial.on('data', function(d) {l+=d;});
      serial.setup(baud, pins);
      serial.write("AT+GMR\r\n");
      setTimeout(function(){console.log(JSON.s­tringify(l));},800);
    }
    //digitalWrite(B9,1); // enable on Pico Shim V2
    setTimeout(function() { test(9600); }, 2000);
    setTimeout(function() { test(115200); }, 3000);
    setTimeout(function() { test(57600); }, 4000);
    setTimeout(function() { console.log("Done!"); }, 5000);
    

    Gives me this response;

    >Uncaught ReferenceError: "Serial2" is not defined
     at line 1 col 14
    var serial = Serial2;
                 ^
    Uncaught Error: Cannot read property 'removeAllListeners' of undefined
     at line 4 col 7
    serial.removeAllListeners();
          ^
    in function "test" called from line 12 col 10
    test(9600);
             ^
    in function called from system
    Uncaught Error: Cannot read property 'removeAllListeners' of undefined
     at line 4 col 7
    serial.removeAllListeners();
          ^
    in function "test" called from line 13 col 12
    test(115200);
               ^
    in function called from system
    Uncaught Error: Cannot read property 'removeAllListeners' of undefined
     at line 4 col 7
    serial.removeAllListeners();
          ^
    in function "test" called from line 14 col 11
    test(57600);
              ^
    in function called from system
    Done!
    
  • I don't know if your Espruino has Serial2 but have you tried Serial1 like you did in your previous example?

  • Changing to Serial1 returns empty strings;

    >var serial = Serial1;
    =Serial1
    >var pins = { rx: D0, tx : D1 };
    ={ rx: D0, tx: D1 }
    >function test(baud) {
    :  serial.removeAllListeners();
    :  var l="";
    :  serial.on('data', function(d) {l+=d;});
    :  serial.setup(baud, pins);
    :  serial.write("AT+GMR\r\n");
    :  setTimeout(function(){console.log(JSON.s­tringify(l));},800);
    :}
    =function (baud) { ... }
    >//digitalWrite(B9,1); // enable on Pico Shim V2
    =undefined
    >setTimeout(function() { test(9600); }, 2000);
    =1
    >setTimeout(function() { test(115200); }, 3000);
    =2
    >setTimeout(function() { test(57600); }, 4000);
    =3
    >setTimeout(function() { console.log("Done!"); }, 5000);
    =4
    ""
    ""
    ""
    Done!
    >
    
  • I brought out one of mine that I bought sometime back and didn't actually use, and it looks like these boards have some sort of demo firmware in them by default (not the AT command set that Espruino or Arduino is expecting).

    You can check if yours is the same by turning it on and seeing if you have something like "ai-thinker" show up in your wifi networks.

    If I connect to mine with the password "ai-thinker" and go to

    192.168.4.1/client?command=info
    

    I see some information as a webpage, which tells me mine does NOT have the AT commandset.

    You might need to flash your own firmware on the shield in that case as mentioned here https://www.espruino.com/ESP8266#updatin­g-esp8266-firmware

    Maybe others might have more ideas that don't involve flashing the wifi devboard? It's relatively easy for me so I don't really think twice about it, but others might have a different technique.


    1 Attachment

    • IMG_20201115_131243.jpg
  • @parasquid - many thanks for helping and making the effort to test!

    I see the shield as FaryLink_ABC123 in the network but I'm not able to get the info page you mention. Anyway some googling indicated that these shields probably needs new firmware so I'll follow the instructions you linked to. I had to order the USB-TTL adaptor described so have to wait for that though.

  • @user118421,

    you not need the USB-TTL adaptor, you can use the Espruino as that link. You can configure it to pass data thru... that is how I updated my very first ESP8266 ESP-01 and later the ESP-09.

    Before you though move into more things, check the wiring, measure voltages, and - flip tx w/ rx. Even though latter is trivial, I was not the first to have it the other way around... :

    Check this conversation about Espruino on ESP8266 ESP-09 - 1 powerful cm2 out. Even though you see the USB-to-TTL adapter on the first pic, reading thru the posts shows you that I did talk to the module thru an Espruino first, and even updated the software.

    I assume you use a Pixl. Did you power it with 5V from USB? Can you measure the voltage on the ESP8266 Module? It may well be that there is something missing.

    There two links you may check out as well (if you not already did): https://claus.bloggt.es/2017/02/26/ardui­no-esp8266-wifi-shield-elecshop-ml-by-wa­ngtongze-comparison/ and https://claus.bloggt.es/2017/01/14/using­-esp8266-shield-esp-12e-elecshop-ml-by-w­angtongze-with-an-arduino-uno/

    To just talk to the ESP8266 - shield not stacked onto the Pixl - connect D0, D1 and GND from Pixl with RX, TX and GND of the shield, and 5V AND GND of a 5V source to GND and 5V of the shield, all debug port pins. Furthermore, check in what mode the module is. The way I interpret the red-white dip switches on the shield, they should be in the run mode... but you never trust... you measure. Even though I used the ESP-09 to describe required signals on the pins (ports) for run mode, the pins (ports) are the same on ESP-12. ESP-12 just exposes more pins (ports) than the ESP-01 or ESP-09.

    For updating the ESP8266 software thru Espruino, I took the hints from https://www.espruino.com/WiFi - Advanced ESP8266 Reflashing. I use the Pico to do it. I've not verified (yet)if you can do it with a Pixl as well.

    Regarding the code you posted in #9, I would place the invocation of your go() as last inside the onInit() ( after the line with Terminal.setConsole(); ).

  • I see the shield as FaryLink_ABC123

    Argh - so I guess that means it has been programmed with some other firmware.

    However, it looks like you might not have set SW1 and SW2 to 'off'? https://www.espruino.com/arduino-esp8266­#using-as-is

    So maybe you could do that and give it another try with the test code you'd posted above in #13 ?

    I actually recently added something to handle firmware updates to the Espruino WiFi via the Web IDE, so if you don't have a USB-TTL converter then let me know and I can see if I can bodge something up to allow you to do firmware updates straight through Pixl.js.

  • Dug up the ESP8266 (black PCB) and connected it to a Pixl.js

    This is the code I used to detect the baud rate, and this then get the hello page.

    // blewifi.js
    // Pixl - ESP8266 ESP-01
    //
    // Pixl    Color  ESP8266  Run   Pgm
    //                (ESP-01) Mode3 Mode1
    // -----------------------------------
    // RX D0  - YEL - UTXD                
    // TX D1  - BLU - URXD                
    // GND    - BLK - GND                 
    // 3.3V   - RED - VCC                 
    //    D8  - WHT - RST      H     H
    //    D9  - BRN - GPIO0    H|F   L
    //    D10 - GRN - GPIO2    H     H
    //    D11 - ORG - CH_PD    H|3.3 H|3.3
    //
    // *F=Float, 3.3=3.3V,
    //  H/L w/ Resistor to rail or MC pin
    //
    pinMode(D8 ,"output"); D8.set();
    pinMode(D9 ,"output"); D9.set();
    pinMode(D10,"output"); D10.set();
    pinMode(D11,"output"); D11.set();
    var serial=Serial1
      , pins={rx:D0,tx:D1}
      , baud=115200 // 76800
      , wifi,wifiMod=require("ESP8266WiFi_0v25")­
    //  Use ESP8266WiFi here  ^^^^^^^^^^^^^^^^ (and 9600 baud)
    //  if you have an ESP8266 with firmware older than 0.25 (?)
      , WIFI_NAME="mySSID"
      , WIFI_PASS="myPassword"
      ;
    
    function hello(altBaud) { // for run
      altBaud=altBaud||baud;
      serial.setup(altBaud, pins);
      console.log("Connecting to ESP8266");
      Terminal.println("Connecting to ESP8266");
      wifi=wifiMod.connect(serial, function(err) {
        if (err) throw err;
        console.log("Connecting to WiFi");
        Terminal.println("Connecting to WiFi");
        wifi.connect(WIFI_NAME, WIFI_PASS, function(err) {
          if (err) throw err;
          console.log("Connected");
          Terminal.println("Connected");
          // Now you can do something, like an HTTP request
          require("http").get("http://www.pur3.co.­uk/hello.txt", function(res) {
            console.log("Response: ",res);
            res.on('data', function(d) {
              console.log("--->"+d);
              Terminal.println("--->"+d);
             });
          });
        });
      });
    }
    
    function pgm() {
      D9.reset();
      D8.reset();D8.set(); // reset
    }
    
    function test() {
      setTimeout(function() { test0(  9600); }, 4000);
      setTimeout(function() { test0(115200); }, 8000);
      setTimeout(function() { test0( 76800); },12000);
      setTimeout(function() { test0( 57600); },16000);
      setTimeout(function() { test0( 74880); },20000);
      setTimeout(function() { console.log("Done!"); },24000);
    }
    
    function test0(baud) {
      serial.removeAllListeners();
      var l="";
      serial.on('data', function(d) {l+=d;});
      serial.setup(baud, pins);
      D8.reset();D8.set(); // reset
      setTimeout(function(){
        serial.write("AT+GMR\r\n");
        setTimeout(function(){
          console.log(baud,":",JSON.stringify(l));­
          Terminal.println(baud,":",JSON.stringify­(l));
          },1000);
      },200);
    }
    

    After upload of the code, I enter in the IDE console: test().

    And get this back:

    >test()
    =undefined
    9600 : ""
    115200 : "AT+GMR\r\r\nAT version:0.40.0.0(Aug  8 2015 14:45:58)\r\nSDK version:1.3.0\r\nAi-Thinker Technology Co.,Ltd.\r\nBuild:1.3.0.2 Sep 11 2015 11:48:04\r\nOK\r\n"
    76800 : "\xAB\xE5\xF0"
    57600 : "eEC\xF8"
    74880 : "\xAA\xA5\xFD"
    Done!
    > 
    

    Because 'clear' shows with 115200 baud, I enter hello(115200), and get this back:

    >hello(115200)
    Connecting to ESP8266
    =undefined
    Connecting to WiFi
    Connected
    Response:  httpCRs: { "hdrs": true,
      "headers": {
        "Date": "Mon, 16 Nov 2020 13:52:53 GMT",
        "Server": "Apache/2.4.18 (Ubuntu)",
        "Last-Modified": "Fri, 15 Nov 2013 15:42:26 GMT",
        "ETag": "\"d-4eb390b887c80\"",
        "Accept-Ranges": "bytes",
        "Content-Length": "13",
        "Connection": "close",
        "Content-Type": "text/plain"
       },
      "cRcv": 0,
      "httpVersion": "1.1",
      "statusCode": "200",
      "statusMessage": "OK"
     }
    --->Hello World!
    > 
    

    Unfortunately, I have no plain ESP-12X for testing, but I have another ESP-01 (blue PCB) with even older firmware. I'll try to update it. test() returns:

    >test()
    =undefined
    9600 : "\xFE"
    115200 : "\x10\2"
    76800 : "\r\n ets Jan  8 2013,rst cause:2, boot mode:(3,6)\r\n\r\no"
    57600 : "GDZ\x98Q(`\xC9h\v\xCC\xBB\xC9@Pu\xA8u\x­1DP\x11\5T\xFD"
    74880 : "\r\n ets Jan  8 2013,rst cause:2, boot mode:(3,6)\r\n\r\nload 0x40100000, len 28460, room 16 \r\ntail 12\r\nchksum 0xc3\r\nho 0 tail 12 room 4\r\nload 0x3ffe8000, len 3124, room 12 \r\ntail 8\r\nchksum 0xae\r\nload 0x3ffe8c40, len 8476, room 0 \r\ntail 12\r\nchksum 0x49\r\ncsum 0x49\r\nRTD"
    Done!
    >
    

    And it never got beyond Uncaught No 'ready' after AT+RST exception when connecting to the ESP8266 module, even though it worked when I used it 5+ years ago.

    >hello(74880)
    Connecting to ESP8266
    =undefined
    Uncaught No 'ready' after AT+RST
    >
    

    (PS: Screen shot of PixleJS w/ test() with a different sequence and no 74880 baud check.)


    1 Attachment

    • BLE_WiFi_PixlJS_ESP8266_ESP-01_black.jpg
  • Many thanks for all advice!

    Yes, the Pixl is powered with 5v USB. If I measure on the ESP8266 it shows 3.3V.

    Flipping tx with rx gives same result (empty string)

    I also still just get empty strings as response if I set SW1 and SW2 to 'off'.

    Same thing with the first piece of code in #18 from allObjects which returns this;

    >
    >test()
    =undefined
    9600 : ""
    115200 : ""
    76800 : ""
    57600 : ""
    74880 : ""
    Done!
    

    I did order the USB-TTL converter so I'll give the firmware update instructions a shot when I receive it. I'll update here when I have tried that. Sorry if I missed trying something out but as I'm all new to this it's easy to get lost in all details :)

  • @user118421,

    your device should at least 'cough'...

    I notice that on subsequent try, all can return with space... But when I disconnect all from power and reconnect power, then upload the code again, and make a run, I get responses ('coughs' and clear).

  • @Gordon / @user118421,

    I tried with this piece of code modelled after code I used previously and is also mentioned at https://www.espruino.com/ESP8266 to update my 'blue' ESP8266 ESP-01:

    // ESP01BridgeOnPixl2.js
    // PixlJS plays the role of USB-SERIAL to talk to ESP8266
    //
    // setup for update ESP8266
    //
    // Pixl - ESP8266 ESP-01
    //
    // Pixl    Color  ESP8266  Run   Pgm
    //                (ESP-01) Mode3 Mode1
    // -----------------------------------
    // RX D0  - YEL - UTXD                
    // TX D1  - BLU - URXD                
    // GND    - BLK - GND                 
    // 3.3V   - RED - VCC                 
    //    D8  - WHT - RST      H     H
    //    D9  - BRN - GPIO0    H|F   L
    //    D10 - GRN - GPIO2    H     H
    //    D11 - ORG - CH_PD    H|3.3 H|3.3
    //
    // *F=Float, 3.3=3.3V,
    //  H/L w/ Resistor to rail or MC pin
    //
    // see https://www.espruino.com/WiFi
    // Advanced ESP8266 Reflashing:
    // install MacOS xcode and python 3.8+ and pip
    // (curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py)
    // install esptool (python3 -m pip install esptool) into current dir
    // browse https://www.espruino.com/files/ and download required
    // ESP8266 firmware into current dir; latest for ESP-01:
    // https://www.espruino.com/files/ESP8266_A­T25-SDK112-512k.bin
    // execute folloing cmd value in terminal window in current dir
    var cmd=`
    python3 ./esptool.py --chip esp8266 --no-stub \\
    --port /dev/cu.Bluetooth-Incoming-Port \\
    write_flash --flash_mode dio 0 ESP8266_AT25-SDK112-512k.bin
    `;
    
    function onInit() {
    pinMode(D8 ,"output"); D8.set();
    pinMode(D9 ,"output");
    pinMode(D10,"output");
    pinMode(D11,"output");
    // Setup serial
    Serial1.setup(74880,{rx:D0,tx:D1});
    // Bridge Serial and Bluetooth
    Serial1.on('data',d=>Bluetooth.write(d))­;
    Bluetooth.on('data',d=>Serial1.write(d))­;
    // boot module in bootloader mode
    digitalWrite(D10, 1); // GPIO2
    digitalWrite(D8 , 0); // RST reset...
    digitalWrite(D8 , 1); // ...reset
    digitalWrite(D11, 0); // CH_PD make sure WiFi starts off
    digitalWrite(D9 , 0); // GPIO0 into boot mode
    digitalWrite(D11, 1); // CH=PD turn on wifi
    console.log("Now disconnect IDE and exec this command in termianl:");
    console.log(cmd);
    LoopbackA.setConsole();
    }
    
    setTimeout(onInit,9999);
    

    This is the console output, indicating that connection does not happen. I'm not sure about the port - I used /dev/cu.Blue... and /dev/tty.Blue... - but both fail the same way:

    • Console output:

      >
      ets Jan  8 2013,rst cause:1, boot mode:(1,0)
      Now disconnect IDE and exec this command in termianl:
      python3 ./esptool.py --chip esp8266 --no-stub \
      --port /dev/tty.Bluetooth-Incoming-Port \
      write_flash --flash_mode dio 0 ESP8266_AT25-SDK112-512k.bin
      Prompt not detected - upload failed. Trying to recover...
      
    • Terminal (McaOS) output:

      $ python3 ./esptool.py --chip esp8266 --no-stub \
      > --port /dev/tty.Bluetooth-Incoming-Port \
      > write_flash --flash_mode dio 0 ESP8266_AT25-SDK112-512k.bin
      esptool.py v3.1-dev
      Serial port /dev/tty.Bluetooth-Incoming-Port
      Connecting........_____....._____.....__­___....._____....._____....._____.....__­___
      
      A fatal error occurred: Failed to connect to ESP8266: Timed out waiting for packet header
      $
      
  • @user118421 thanks for giving this all a go - the USB-TTL converter will definitely be the easiest way to get this working.

    You mentioned you bought an ESP01 as well? You could try wiring that up to Pixl.js with some jumper cables, and that will likely work straight away.

    @allObjects Bluetooth LE UART doesn't appear as a device in MacOS/Linux, so unfortunately what you're proposing won't work :( You could go to https://espruino.github.io/EspruinoWebID­E/, open up https://github.com/espruino/EspruinoTool­s/blob/master/core/flasherESP8266.js#L10­7-L131 in the Chrome Devtools and change the pin names, then run the flasher...

    Also, Pixl.js doesn't have a voltage regulator that's specced to be powerful enough to handle powering an ESP866 directly (hence why I'd suggested the board). However it's unlikely to cause any problems - it might just not work reliably. Could you actually get a WiFi connection on it? That's great news if so - it makes life far easier.

  • @Gordon,

    Ic.

    Assuming the regulator on PixlJS was chosen with the optimum for PixlJS needs in mind, and a cap big enough to help over the current bursts could cause a reset - or at least a brown out - on connection.

    So you suggest a change within the IDE. You already use that solution for flashing the ESP8266 on the Espruino-Wifi. You mention that an earlier post of this conversation.

    I looked at the code and it is really the most user friendly solution: just wiring the things 'correctly' and use the flasher dialog to execute.

    I took a peek at the code and wonder how to make the different pins known to the flasher. Currently, it is fixed to the pins as defined for Espruino-Wifi. But if I wire Original or Pico up the same way, I can use the current IDE to flash 'any' of the ESP8266... (except you check - for 'safety' and against 'brazen' users - the connected board... haha). Technically, a configuration option for the pins would do it - given for Espruino-Wifi - and user define-able for other boards. And for passing to the function, the option object could be the candidate.

    For the first, simple implementation with a lot of user responsibility, an extra entry field for passing a pins object in JSON would be just fine. Empty field means the Espruino-Wifi defaults, not-empty field uses JSON.parse() for formal correctness and simple check for presence of the required pins provide already enough user safety.

  • I have not received the TTL USB adaptor yes but tried this;

    I connected the Pixl.js to the ESP8266 Esp-01 like this;
    Pixl.js <-> Esp01
    GND <-> GND
    3.3V <-> EN
    3.3V <-> 3.3V
    D0 <-> RX
    D1 <-> TX

    Then ran this;

    var serial = Serial1;
    var pins = { rx: D1, tx : D0 };
    function test(baud) {
      serial.removeAllListeners();
      var l="";
      serial.on('data', function(d) {l+=d;});
      serial.setup(baud, pins);
      serial.write("AT+GMR\r\n");
      setTimeout(function(){console.log(JSON.s­tringify(l));},800);
    }
    setTimeout(function() { test(9600); }, 2000);
    setTimeout(function() { test(115200); }, 3000);
    setTimeout(function() { test(57600); }, 4000);
    setTimeout(function() { console.log("Done!"); }, 5000);
    

    The result;

    "AT+GMR\r\r\nAT version:1.1.0.0(May 11 2016 18:09:56)\r\nSDK version:1.5.4(baaeaebb)\r\ncompile time:May 20 2016 15:08:19\r\nOK\r\n"")m\u00E9oI\r\n"
    

    However, when I then run this the Pixl.js just disconnects from my PC Espruino Web IDE bluetooth and enters into a boot loop..;

    var WIFI_NAME = "x";
    var WIFI_PASS = "x";
    
    Serial1.setup(115200, { rx: D1, tx : D0 });
    var wifi = require("ESP8266WiFi_0v25").connect(Seri­al1, function(err) {
      //                ^^^^^^^^^^^^^^^^
      //                Use ESP8266WiFi here (and 9600 baud) if you have an ESP8266 with firmware older than 0.25
      if (err) throw err;
      console.log("Connecting to WiFi");
      wifi.connect(WIFI_NAME, WIFI_PASS, function(err) {
        if (err) throw err;
        console.log("Connected");
        // Now you can do something, like an HTTP request
        require("http").get("http://www.pur3.co.­uk/hello.txt", function(res) {
          console.log("Response: ",res);
          res.on('data', function(d) {
            console.log("--->"+d);
          });
        });
      });
    });
    
  • when I then run this the Pixl.js just disconnects from my PC Espruino Web IDE bluetooth

    Ahh - that's probably because of the power usage issues mentioned above. That's a shame.

    Still, at least you know that when the correct firmware is on the ESP8266, it'll all work ok.

    I'll work on a tweak to the Web IDE firmware updater for you, which should allow you to update without the dongle.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

WiFi + Bluetooth

Posted by Avatar for user118421 @user118421

Actions