dotENV Files
dotENV Files
Is there a native way in Micropython to read a .env file? I know there are third-party packages out there for Python, but I was hoping there was something native to Micropython so I could read credentials and API keys from there instead of hard coding them into the script.
Re: dotENV Files
I had forgotten about this post. Having come across it again, I thought I would share what I ended up doing.
I created a file which I named config.py and uploaded that the ESP32 board. Within that file I would add any credentials, API keys, etc. that I would need. For example:
Then, wherever I needed those credentials I would just use an import statement, import config. From there it was just a matter of using it within the code as you would any other imported module:
It's by no means perfect (or even the best) but when I am writing code that I will hold in my Github repo, I can use the .gitignore file to prevent uploading the config.py file and I don't have to hard code any credentials or keys into the code. The added bonus is it is portable. I can just store that file and move it over to any new ESP32 project I am working on and everything I need is right there.
I'd really love to learn how to get the board to scan for WiFi AP's and detect any that may be open and connect to them and if none are available, then revert back to the config.py file and try to connect to that one. Or maybe even better, give it a list of AP's that it can attempt to connect to with the required credentials and try that if no open WiFi is available.
I created a file which I named config.py and uploaded that the ESP32 board. Within that file I would add any credentials, API keys, etc. that I would need. For example:
Code: Select all
WIFI_SSID = 'myssid'
WIFI_PASSWD = 'mywifipassword'
SOME_API_KEY = 'myapikey'
Code: Select all
import config
import network
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(config.WIFI_SSID, config.WIFI_PASSWD)
I'd really love to learn how to get the board to scan for WiFi AP's and detect any that may be open and connect to them and if none are available, then revert back to the config.py file and try to connect to that one. Or maybe even better, give it a list of AP's that it can attempt to connect to with the required credentials and try that if no open WiFi is available.
Re: dotENV Files
The btree module is well suited for storing key-value pair data in a file on the file system.
I wrapped it in a module called "storage" with get and put methods that take ascii values, like this:
For things which need to be written a relatively small number of times, this works fine.
You scan run an access point at the same time as the station, giving you the ability to access the device if you are in the proximity to add settings. For example, I created a small page which accepts config values and stores them so I can update device credentials from my phone, so I can boot it up and connect it to any network.
When you scan for networks, it returns a list of tuples for each network.
Looking at ports/esp32/modnetwork.c in the scan function:
so the 5th item is the auth mode. (This is also noted in the docs).
These objects are wifi_ap_record_t structs, and the 5th item is a wifi_auth_mode_t enum, both of which are defined in esp_wifi_types.h in esp-idf:
so iterating over the scan results and trying the AP's where the auth mode is 0 should work.
Remember that you may not get access to the network even if you can associate with the AP. An open access point may still require a login (like on public wifi with a captive portal).
If you successfully associate, you should check to see if you can access something known.
I wrapped it in a module called "storage" with get and put methods that take ascii values, like this:
Code: Select all
import btree
CORE_STORAGE = "data"
# Do this only once, it will overwite a previous file with a new one if it exists
def init():
with open(CORE_STORAGE, 'w+b') as f:
db = btree.open(f)
db[b"MODE"] = b"INIT"
db.close()
def get(item):
value = None
with open(CORE_STORAGE, 'r+b') as f:
db = btree.open(f)
b_item = item.encode('ascii')
value = db.get(b_item)
if value:
value = value.decode('ascii')
db.close()
return value
def put(item, value):
b_item = None
b_value = None
try:
b_item = item.encode('ascii')
b_value = value.encode('ascii')
except AttributeError:
print("storage items and values must be ascii strings")
return False
if b_item and b_value:
with open(CORE_STORAGE, 'r+b') as f:
db = btree.open(f)
db[b_item] = b_value
db.flush()
db.close()
return True
Code: Select all
>>> import storage
>>> storage.put("SSID", "My AP ESSID")
>>> ssid = storage.get("SSID")
When you scan for networks, it returns a list of tuples for each network.
Code: Select all
(b'titopuente', b'\x08\x11\x96\xc1\xc9\xdc', 6, -38, 3, False)
(b'ATTPm5PQgs', b'\x88\x96N>s0', 11, -59, 3, False)
(b'Sonic.net-209', b'\x94\xc1P\x00[:', 6, -73, 3, False)
(b'ATT5veM6wF', b'\x0c|(w?\x04', 6, -89, 3, False)
(b'Leslie Riley', b'T\xbe\xf7\xfeC\xc0', 11, -90, 3, False)
(b'DIRECT-94-HP DeskJet 3630 series', b'\xc4e\x16[=\x95', 6, -92, 3, False)
(b'xfinitywifi', b'^\xbe\xf7\xfeC\xc0', 11, -92, 0, False)
(b'ATT2UyA68M', b'8;\xc8>9\x81', 11, -94, 3, False)
(b'Wemo.Mini.1F7', b'$\xf5\xa2\x1a\xa0j', 11, -94, 0, False)
Code: Select all
t->items[0] = mp_obj_new_bytes(wifi_ap_records[i].ssid, ssid_len);
t->items[1] = mp_obj_new_bytes(wifi_ap_records[i].bssid, sizeof(wifi_ap_records[i].bssid));
t->items[2] = MP_OBJ_NEW_SMALL_INT(wifi_ap_records[i].primary);
t->items[3] = MP_OBJ_NEW_SMALL_INT(wifi_ap_records[i].rssi);
t->items[4] = MP_OBJ_NEW_SMALL_INT(wifi_ap_records[i].authmode);
t->items[5] = mp_const_false; // XXX hidden?
These objects are wifi_ap_record_t structs, and the 5th item is a wifi_auth_mode_t enum, both of which are defined in esp_wifi_types.h in esp-idf:
Code: Select all
typedef enum {
WIFI_AUTH_OPEN = 0, /**< authenticate mode : open */
WIFI_AUTH_WEP, /**< authenticate mode : WEP */
WIFI_AUTH_WPA_PSK, /**< authenticate mode : WPA_PSK */
WIFI_AUTH_WPA2_PSK, /**< authenticate mode : WPA2_PSK */
WIFI_AUTH_WPA_WPA2_PSK, /**< authenticate mode : WPA_WPA2_PSK */
WIFI_AUTH_WPA2_ENTERPRISE, /**< authenticate mode : WPA2_ENTERPRISE */
WIFI_AUTH_MAX
} wifi_auth_mode_t;
Remember that you may not get access to the network even if you can associate with the AP. An open access point may still require a login (like on public wifi with a captive portal).
If you successfully associate, you should check to see if you can access something known.
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: dotENV Files
My approach to this is to have a config.py file similar to yours but containing dummy data such as "YOUR_PASSWORD". That file is under Git control and is published. Another file my_config.py has my actual credentials. This is not under Git control so never gets uploaded. I copy that file to config.py on the hardware.
Most of my projects have a private directory containing files and notes not under Git control.
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.