Connection Arduino on Google Colab - arduino

I have tried to connect Arduino Uno to Python code on Colaboratory, to send information to the Arduino using Pysireal library, but I didn't success, I kept reserve this Error on Colab:
" SerialException: [Errno 2] could not open port COM5: [Errno 2] No such file or directory: 'COM5' "
I'm sure that this port is connected, but Colab can't find the port because it’s on local environment.
What can I do to solve this Error ?? is there any way that can I connect my Arduino to Google Colab ?
Hakam Salti.
Thanks.

The Colab instance is connected to computer on the Google cloud (unless you've set up a local instance): the code doesn't execute on your machine, your typing code into a web interface that remotely runs that code, returns the result and it gets displayed back on that interface.
The Arduino is connected to your computer (a PC by the looks of the serial port).
Your question doesn't specify which way the data goes: send Arduino data to Colab, send Colab data to Arduino or bidirectional.
If you had a WIFI connected microcontroller, you could push the data online through an API, like Firebase
For USB, you'd need this sort of connection:
Arduino (OS/serial driver) <-> Browser <-> Colab
To connect the Arduino to the browser you'd need to use WebSerial or an app that has serial access that can also act as a web server (such as a WebSocket server). Since you're using Python for colab you can write a script on your PC that uses pyserial and a websocket server such as Tornado, Flask, etc. (p5.js does something like this with electron in JS and they have prebuilt apps)
The second part is getting that data which is now available to your browser, but locally, available to the Colab notebook. There are multiple ways of doing this, but this WebCam example looks like a good starting point.
Another variant of this might be:
Write a local script that acts as basic web server (http/websocket) and can access the serial port
make that local web server acessible from the internet (ngrok can help here)
access that websocket version from python (via a websocket client or http client pip package)
Update I've posted a couple of options using p5.serialport here.
For reference here are a couple of tested options using the afore mentioned p5.serial (and it's p5.serialcontrol utility):
Option 1: use Jupyter's HTML feature to run client side code (p5.serial) connecting to the p5.serialcontrol utility on your computer:
from google.colab import files
from IPython.display import HTML, Audio
from google.colab.output import eval_js
from base64 import b64decode
C_HTML = """
<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.min.js"></script>
<script language="javascript" type="text/javascript" src="https://cdn.jsdelivr.net/npm/p5.serialserver#0.0.29/lib/p5.serialport.js" onload="setupSerial();" onerror="console.warn(e)";></script>
<script>
const serialPort = 'COM13';
let serial;
let isOn = false;
function setupSerial(){
serial = new p5.SerialPort();
serial.open(serialPort);
setInterval(blink, 1000);
console.log("serial setup complete");
}
function blink(){
isOn = !isOn;
if(serial){
serial.write(isOn ? '1' : '0');
}
}
serialInterval = setInterval(checkSerial,500);
function checkSerial(){
console.log('p5.SerialPort',p5.SerialPort);
if(p5.SerialPort){
clearInterval(serialInterval);
setupSerial();
}
}
</script>
"""
def run():
display(HTML(C_HTML))
run()
Option 2: use a reverse tunnel (ngrok) to have the Python side connect to p5.serialcontrol via WebSockets (though you'd need to compose the messages p5.serialcontrol expects maually):
run p5.serialcontrol
run ngrok tcp 8081 from Terminal/Command Prompt (note you may need to setup a free to use auth token for TCP)
install webocket-client on Colab and connect to the websocket (note as opposed to using the p5.serial library in JS, you'd manually put together the messaages to send to p5.serialcontrol's websocket server (e.g. '{"method":"openserial","data":{"serialport":"COM13","serialoptions":{}}}' to open the serial port, '{"method":"write","data":"1"}' to write '1' to serial, etc.))
To install websocket-client in colab you'd use:
!pip install -q websocket-client
and here's an example that turns on LED on for 1 second then off (using the above Arduino example):
from time import sleep
import websocket
# when the websocket is open send a serial open command (on port COM13) then send a '1' then a '0' with 1 second in between
def on_open(ws):
ws.send('{"method":"openserial","data":{"serialport":"COM13","serialoptions":{}}}')
sleep(1)
print('sending ON')
wsapp.send('{"method":"write","data":"1"}')
sleep(1)
print('sending OFF')
wsapp.send('{"method":"write","data":"0"}')
def on_message(ws, message):
print(message)
def on_error(wsapp, err):
print("Got a an error: ", err)
wsapp = websocket.WebSocketApp("ws://YOUR_NGROK_TCP_SERVER_HERE",
# YOUR_NGROK_TCP_SERVER_HERE example #.tcp.ngrok.io:#####,
on_message = on_message,
on_error=on_error)
wsapp.on_open = on_open
wsapp.run_forever()
(also note run_forever() is a blocking loop: based on your application you may want to manually run open and control a websocket connection (as opposed to using WebSocketApp) or use threading, depending on what makes)

Related

Sending AT cmd to BLE OBDII device

I am building an app but I wanted to test the device before start coding.
The device that I am using is veepeak BLE OBDII. I can connect to it using other popular apps with no issue e.g. CarScanner.
When using LightBlue app (BLE testing app), I can connect & see write and notify services/chars but when sending any AT command e.g. "ATZ \r" I always get "?" response.

Flask-MQTT disconnects after 'Socket error on client <unknown>' while running on uWSGI NGINX

I have a setup where I use Flask-MQTT to connect my python Flask API to a Mosquitto broker. Whenever I run the Flask API with the development server all is well. But whenever I spin it up for production (using wsgi+nginx), the connection with Mosquitto is made, but everytime i try to publish something i get the following error:
Socket error on client <unknown>, disconnecting.
My app.ini has the processes configured to 1 (processes = 1)
My mosquitto.config has the allow_anonymous flag set to trye (allow_anonymous true)
I can't really seem to figure out what I'm doing wrong here...
Update:
So what i think is happening is that the Flask-uwsgi application is trying to connect to mosquitto more than once. There is a master process that connects with Mosquitto on initialize. Then there is a second process that is being used whenever input is given on the Flask app. I'm not sure, but I think Mosquitto only wants one connection at the time, therefor erroring on the second. So now i either need to:
A) Configure Mosquitto in a way that it accepts multiple connection from the same device
B) Configure Flask in a way that wil only use one single process (configuring processes = 1 is not enough, it will still spawn two processes)
99% of the time, a "Socket error on client <unknown>" is an authentication error. I don't know Flask, so I don't know where to point you at, but something in your code is either trying to pass a username/password that is not defined to Mosquitto, or its trying a TLS connection with an cert that Mosquitto doesn't like.
Alright, it turns I could've read that the whole multiple processes wouldn't work from the start at the official Flask-MQTT documentation. It sais right there in think letters:
Flask-MQTT is currently not suitable for the use with multiple worker
instances.
So I looked at my uwsgi app.ini file again closely and actually the answer is quite simple. I turned out i had a like in there master = true.. after I removed that it works like a charm.

Creating an HTTP server in NodeMCU through an access point created by the board

I am coding a robot, using NodeMCU (ESP8266) and want it to be remote controlled. My current solution is connecting to a nearby router, to the internet and creating a TCP HTTP server. Data is streamed from the mobile device (remote) to the NodeMCU (robot) via HTTP requests. The remote is loaded onto the mobile device through a browser with HTML/CSS/JavaScript.
What I want instead is for the NodeMCU to create its own hotspot, because:
A router is not required
The connection is more direct
I want the same TCP HTTP solution, but I don't know how to serve a webpage through a custom hotspot.
This is my code:
-- Connect to router
wifi.sta.config("ssid","password")
wifi.sta.connect()
-- Code for waiting for connection
-- Create server
srv = net.createServer(net.TCP)
srv:listen(80,function(conn)
conn:on("receive",function(conn,payload)
for line in string.gmatch(payload,'[^\r\n]+') do
s = string.find(line, "GET /&")
-- If query is there, control robot
if s ~= nil then
-- Do stuff with query
break;
-- If no query, serve webpage
else
file.open("index.html", "r")
while true do
s = file.read(1460)
if s == nil then
break
end
conn:send(s)
end
file.close()
end
break
end
conn:on("sent", function(conn) conn:close() end)
end)
end)
Creating custom hotspot:
wifi.setmode(wifi.STATIONAP)
cfg={}
cfg.ssid="custom_ssid"
cfg.pwd="custom_password"
wifi.ap.config(cfg)
So how do I make it so the mobile can access the server? How do I get/set the IP of the server? Basically, I just need it to work. Thanks!
Not sure I fully understand but I believe you're really close. Check the documentation for the AP functions at http://nodemcu.readthedocs.io/en/latest/en/modules/wifi/#wifiap-module.
wifi.ap.config(cfg) sets SSID and pwd as you noted. Your client then connects to this AP by joining the network.
If you then print wifi.ap.getip() you'll see that the device has the IP address 192.168.1.4 by default. Hence, for clients which joined this network your server is reachable at 192.168.1.4:80 unless you set a custom IP explicitly.
However, the sending of data seems broken. You have multiple conn:send(s) (in the loop) yet you also have conn:on("sent", function(conn) conn:close() end) which means that the connection will be closed after the first conn:send! Check the docs at http://nodemcu.readthedocs.io/en/latest/en/modules/net/#netsocketsend for an example as for how to do that properly.

TCP > COM1 for receiving messages and displaying on POS display pole

I currently have a Java Applet running on my web page that communicates to a display pole via COM1. However since the Java update I can no longer run self-signed Java Applets and I figure it would just be easier to send an AJAX request back to the server and have the server send a response to a TCP port on the computer...the computer would need a TCP > COM virtual adapter. How do I install a virtual adapter to go from a TCP port to COM1?
I've looked into com0com and that is just confusing as hell to me, and I don't see how to connect any ports to COM1. I've tried tcp2com but it doesn't seem to install the service in Windows 7 x64. I've tried com2tcp and the interface seems like it WOULD work (I haven't tested), but I don't want an app running on the desktop...it needs to be a service that runs in the background.
So to summarize how it would work:
Web page on comp1 sends AJAX request to server
Server sends text response to comp1 on port 999
comp1 has virtual COM port listening on port 999, sends data to COM1
pole displays data
EDIT: I'm using Win 7 x64 and tcp2com doesn't work as a service. I tried using srvany but I get an error stating that the application started then stopped. If I use powershell and pass the tcp2com as an argument, it doesn't quit but it also doesn't run. So I nixed the whole 'service' deal and put the command: powershell -windowstyle hidden "tcp2com --test tcp/999 com1" and it works...sort of. The characters that get sent are all effed. I can write "echo WTF > COM1" on another computer which has COM2TCP (different vendor) and it'll come up as a single block on the POS display pole. However if I use COM2TCP on both the server and client machines, everything works fine...but that's only a trial version and it costs several hundred dollars! On another note, is there a way to send the raw text over IP without having to use another Virtual COM > IP adapter on another computer? Sort of like how curl works but different...?
After somewhat of an exhaustive search, I came across a program called 'piracom'. It's a very simple app that lets you specify port settings for the express purpose of connecting a serial port to an listening port over the network. So this is IP > Serial. For Serial > IP I used HW-VSP3-Single as even on the piracom website it said it's compatible! I've tested and it works!
I just put a shortcut to piracom in the startup folder of my user account; the app runs off of a .ini that it updates every time you make a change...so if you run the server and hide it, on the next reboot of the pc it'll start up running and hidden with all prior settings. Easy.
Now it's a matter of installing HW-VSP3 on the server and making a method on the Rails app which will write to the virtual COM port. The only issue I can see right now is that writing echo \14Test This! > COM3 actually prints the \14...if I do that in my Java applet, it sends the "go to beginning" signal.
Addendum 1: The \14 problem was fixed by using the serialport gem for RoR. I created a method in a controller that returned head :no_content and then send data to the COM port. Calls to this method were made via jQuery's $.Ajax, using "HEAD" HTTP method. Apparently though I had to add the GET verb in Rails routes because the HEAD option isn't supported for some gimpy reason.
Addendum 2: Some garbage data was being sent to the display pole at the end of the string...turns out I needed to turn off the "NVT" option in HW-VSP3. Also keep in mind that firewalls need to be modified to allow communication.

How to write a simple HTTP server in Haskell using Network.HTTP.receiveHTTP

The module Network.HTTP
exposes the functions receiveHTTP and respondHTTP which I'd like to use for a very basic web server. I wrote a stub that just waits for clients:
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Network.HTTP
import Network
import Control.Monad
main = withSocketsDo $ do
socket <- listenOn $ PortNumber 8080
forever $ do
(handle, host, port) <- accept socket
print (host, port)
Here accpet gives me a Handle, and now I can't figure out how to use a Handle with receiveHTTP.
I found an example with Google, but it is from 2008 and does not work anymore. And I was not able to port it.
Any ideas?
You can do this, but I really think you shouldn't. HTTP can act as a server, but is designed to be used client side. I Googled a little and I can't find any examples of someone actually using respondHTTP. If you're doing client side HTTP in 2016 use http-conduit. On the server side, warp or something that depends upon it is probably what you want.
Nevertheless, here's the code.
#!/usr/bin/env stack
-- stack --resolver lts-6.3 --install-ghc runghc --package HTTP
{-# LANGUAGE RecordWildCards #-}
import Control.Monad
import qualified Data.ByteString as B
import Network.HTTP
import Network.Socket
import Network.URI
main = do
lsock <- socket AF_INET Stream defaultProtocol
bind lsock (SockAddrInet 8080 iNADDR_ANY)
listen lsock 1
forever $ do
(csock, _) <- accept lsock
hs <- socketConnection "" 8080 csock
req <- receiveHTTP hs
case req of
Left _ -> error "Receiving request failed"
Right (Request {..}) -> if uriPath rqURI == "/"
then do
respondHTTP hs $
Response (2,0,0) "OK" [] "Hello HTTP"
Network.HTTP.close hs
else do
respondHTTP hs $
Response (4,0,4) "Not found" [] "Nothing here"
Network.HTTP.close hs
The above uses Stack's support for shebang scripts. chmod +x it or run it with stack foo.hs.
The Network module is deprecated. Always use Network.Socket if you need a socket API. For something higher level, use connection.
You do the normal POSIX socket thing, then convert the connected socket to a HandleStream with socketConnection and run respondHTTP and receiveHTTP on it. socketConnection is a weird function. The first two parameters are a hostname and a port which AFAICT aren't used when running a server.
I used the RecordWildCards extension. It lets me write Right (Request {..}) in a pattern and have all the fields of the record in scope on the right hand side.
Perhaps it expects you to use the accept function from Network.Socket instead of Network? That gives you a Socket instead of a Handle, which you should be able to convert to a form that receiveHTTP can use.
Normally a Handle would be nicer to work with directly, but here the HTTP library is taking care of it for you so it expects the lower-level interface instead.
EDIT: After looking at it a bit further, it seems the socketConnection function in Network.TCP does what you need. The funny part is it's actually making the socket into a Handle internally before it reads from it, but doesn't seem to provide a way to read from an externally-provided Handle. The string parameter to the function is supposed to be the name of the remote host, but it looks like that's merely kept for reference; it's not actually initiating a connection to that host or anything.

Resources