Merge pull request #1785 from openmv/pylint

misc: Add Python linter workflow.
This commit is contained in:
Ibrahim Abdelkader 2023-02-19 22:17:34 +02:00 committed by GitHub
commit a9e5c41a71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1416 additions and 943 deletions

51
.github/workflows/python-linter.yml vendored Normal file
View File

@ -0,0 +1,51 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: '🔎 Python Linter'
on:
push:
branches:
- 'master'
paths:
- 'scripts/libraries/*.py'
pull_request:
types:
- opened
- reopened
- synchronize
branches:
- 'master'
paths:
- 'scripts/libraries/*.py'
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10"]
steps:
- name: '⏳ Checkout repository'
uses: actions/checkout@v3
- name: '🐍 Set up Python ${{ matrix.python-version }}'
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: '🛠 Install dependencies'
run: |
python -m pip install --upgrade pip
python -m pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: '😾 Lint with flake8'
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 --count --select=E9,F63,F7,F82 --ignore=F821 --show-source --statistics scripts/libraries/
flake8 --count --max-complexity=15 --max-line-length=120 --ignore=F821,E722,E741,C901,E713,W605,E203,W503,F841,F403,F405 --statistics scripts/libraries/

View File

@ -0,0 +1,63 @@
# OpenMV M7 I2C interface with Garmin Lidar Lite V3 - By: Grant Phillips - Sun Apr 8 2018
# Returns a basic distance reading from the lidar in cm for the target point and prints to console
# Uses default lidar settings. For more advanced settings, see the I2C commands in the manual:
# https://static.garmin.com/pumac/LIDAR_Lite_v3_Operation_Manual_and_Technical_Specifications.pdf
# I2C Control of LIDAR Lite V3
# 1. Write 0x04 to register 0x00
# 2. Read register 0x01. Repeat until bit 0 (LSB) goes low.
# 3. Read two bytes from 0x8f (high byte 0x0f then low byte 0x10) to obtain 16 bit measurement in cm
# HARDWARE CONNECTIONS:
# Connect the lidar SCL line (green) to I2C 2 SCL on openMV (Pin 4)
# Connect the lidar SDA line (blue) to I2C 2 SDA on openMV (pin 5)
# 680uF filter capacitor in parallel with the lidar
# 10k pullup resistors on the SCL and SDA lines to +5Vdc
import pyb
from pyb import I2C
lidarReady = bytearray([0xFF]) # holds the returned data for ready check
lidarReadyCheck = bytes([1]) # to compare bit 0 of lidarReady
startBuf = bytearray([0x00, 0x04]) # step 1 address and data
readyBuf = bytearray([0x01]) # step 2 address for readiness check
distBuf = bytearray([0x8F]) # step 3 address for distance reading
distance = -1 # variable for distance reading
# I2C setup
Lidar = I2C(2, I2C.MASTER) # initialise I2C 2 bus in master mode
while True:
distance = -1 # reset to -1 so we know when we get a real reading
try: # handles errors thrown up if we have an I2C error
# Step 1 Write 0x04 to register 0x00
Lidar.send(startBuf, 0x62) # this is making it read (laser visible)
# Step 2 Read register 0x01 and wait for bit 0 to go low
while lidarReady[0] & readyBuf[0]:
Lidar.send(readyBuf, 0x62)
lidarReady = Lidar.recv(1, 0x62)
pyb.delay(50) # This seems to help reduce errors on the I2C bus
lidarReady = bytearray([0xFF]) # reset the ready check data for next reading
# Step 3 Read the distance measurement from 0x8f (0x0f and 0x10)
Lidar.send(distBuf, 0x62)
dist = Lidar.recv(2, 0x62)
distance = dist[0]
distance <<= 8 # move 2 bytes into a 16 bit int
distance |= dist[1]
pyb.delay(100) # allow time between readings, can go faster but more errors
except OSError: # reninitialise i2c bus if error
Lidar.init(I2C.MASTER)
print("error, reinitialising")
if distance > -1:
print("Distance:", distance, "cm")

View File

@ -1,3 +1,6 @@
from apds9960.device import APDS9960, uAPDS9960 from apds9960.device import APDS9960, uAPDS9960
__all__ = [ 'APDS9960', 'uAPDS9960', ] __all__ = [
"APDS9960",
"uAPDS9960",
]

View File

@ -7,7 +7,7 @@ APDS9960_GESTURE_SENSITIVITY_1 = 50
APDS9960_GESTURE_SENSITIVITY_2 = 20 APDS9960_GESTURE_SENSITIVITY_2 = 20
# APDS9960 device IDs # APDS9960 device IDs
APDS9960_DEV_ID = [0xab, 0x9c, 0xa8, -0x55] APDS9960_DEV_ID = [0xAB, 0x9C, 0xA8, -0x55]
# APDS9960 times # APDS9960 times
APDS9960_TIME_FIFO_PAUSE = 30 APDS9960_TIME_FIFO_PAUSE = 30
@ -21,11 +21,11 @@ APDS9960_REG_AILTH = 0x85
APDS9960_REG_AIHTL = 0x86 APDS9960_REG_AIHTL = 0x86
APDS9960_REG_AIHTH = 0x87 APDS9960_REG_AIHTH = 0x87
APDS9960_REG_PILT = 0x89 APDS9960_REG_PILT = 0x89
APDS9960_REG_PIHT = 0x8b APDS9960_REG_PIHT = 0x8B
APDS9960_REG_PERS = 0x8c APDS9960_REG_PERS = 0x8C
APDS9960_REG_CONFIG1 = 0x8d APDS9960_REG_CONFIG1 = 0x8D
APDS9960_REG_PPULSE = 0x8e APDS9960_REG_PPULSE = 0x8E
APDS9960_REG_CONTROL = 0x8f APDS9960_REG_CONTROL = 0x8F
APDS9960_REG_CONFIG2 = 0x90 APDS9960_REG_CONFIG2 = 0x90
APDS9960_REG_ID = 0x92 APDS9960_REG_ID = 0x92
APDS9960_REG_STATUS = 0x93 APDS9960_REG_STATUS = 0x93
@ -35,40 +35,40 @@ APDS9960_REG_RDATAL = 0x96
APDS9960_REG_RDATAH = 0x97 APDS9960_REG_RDATAH = 0x97
APDS9960_REG_GDATAL = 0x98 APDS9960_REG_GDATAL = 0x98
APDS9960_REG_GDATAH = 0x99 APDS9960_REG_GDATAH = 0x99
APDS9960_REG_BDATAL = 0x9a APDS9960_REG_BDATAL = 0x9A
APDS9960_REG_BDATAH = 0x9b APDS9960_REG_BDATAH = 0x9B
APDS9960_REG_PDATA = 0x9c APDS9960_REG_PDATA = 0x9C
APDS9960_REG_POFFSET_UR = 0x9d APDS9960_REG_POFFSET_UR = 0x9D
APDS9960_REG_POFFSET_DL = 0x9e APDS9960_REG_POFFSET_DL = 0x9E
APDS9960_REG_CONFIG3 = 0x9f APDS9960_REG_CONFIG3 = 0x9F
APDS9960_REG_GPENTH = 0xa0 APDS9960_REG_GPENTH = 0xA0
APDS9960_REG_GEXTH = 0xa1 APDS9960_REG_GEXTH = 0xA1
APDS9960_REG_GCONF1 = 0xa2 APDS9960_REG_GCONF1 = 0xA2
APDS9960_REG_GCONF2 = 0xa3 APDS9960_REG_GCONF2 = 0xA3
APDS9960_REG_GOFFSET_U = 0xa4 APDS9960_REG_GOFFSET_U = 0xA4
APDS9960_REG_GOFFSET_D = 0xa5 APDS9960_REG_GOFFSET_D = 0xA5
APDS9960_REG_GOFFSET_L = 0xa7 APDS9960_REG_GOFFSET_L = 0xA7
APDS9960_REG_GOFFSET_R = 0xa9 APDS9960_REG_GOFFSET_R = 0xA9
APDS9960_REG_GPULSE = 0xa6 APDS9960_REG_GPULSE = 0xA6
APDS9960_REG_GCONF3 = 0xaA APDS9960_REG_GCONF3 = 0xAA
APDS9960_REG_GCONF4 = 0xaB APDS9960_REG_GCONF4 = 0xAB
APDS9960_REG_GFLVL = 0xae APDS9960_REG_GFLVL = 0xAE
APDS9960_REG_GSTATUS = 0xaf APDS9960_REG_GSTATUS = 0xAF
APDS9960_REG_IFORCE = 0xe4 APDS9960_REG_IFORCE = 0xE4
APDS9960_REG_PICLEAR = 0xe5 APDS9960_REG_PICLEAR = 0xE5
APDS9960_REG_CICLEAR = 0xe6 APDS9960_REG_CICLEAR = 0xE6
APDS9960_REG_AICLEAR = 0xe7 APDS9960_REG_AICLEAR = 0xE7
APDS9960_REG_GFIFO_U = 0xfc APDS9960_REG_GFIFO_U = 0xFC
APDS9960_REG_GFIFO_D = 0xfd APDS9960_REG_GFIFO_D = 0xFD
APDS9960_REG_GFIFO_L = 0xfe APDS9960_REG_GFIFO_L = 0xFE
APDS9960_REG_GFIFO_R = 0xff APDS9960_REG_GFIFO_R = 0xFF
# APDS9960 bit fields # APDS9960 bit fields
APDS9960_BIT_PON = 0b00000001 APDS9960_BIT_PON = 0b00000001
APDS9960_BIT_AEN = 0b00000010 APDS9960_BIT_AEN = 0b00000010
APDS9960_BIT_PEN = 0b00000100 APDS9960_BIT_PEN = 0b00000100
APDS9960_BIT_WEN = 0b00001000 APDS9960_BIT_WEN = 0b00001000
APSD9960_BIT_AIEN =0b00010000 APSD9960_BIT_AIEN = 0b00010000
APDS9960_BIT_PIEN = 0b00100000 APDS9960_BIT_PIEN = 0b00100000
APDS9960_BIT_GEN = 0b01000000 APDS9960_BIT_GEN = 0b01000000
APDS9960_BIT_GVALID = 0b00000001 APDS9960_BIT_GVALID = 0b00000001
@ -111,7 +111,7 @@ APDS9960_GGAIN_8X = 3
APDS9960_LED_BOOST_100 = 0 APDS9960_LED_BOOST_100 = 0
APDS9960_LED_BOOST_150 = 1 APDS9960_LED_BOOST_150 = 1
APDS9960_LED_BOOST_200 = 2 APDS9960_LED_BOOST_200 = 2
APDS9960_LED_BOOST_300 = 3 APDS9960_LED_BOOST_300 = 3
# Gesture wait time values # Gesture wait time values
APDS9960_GWTIME_0MS = 0 APDS9960_GWTIME_0MS = 0
@ -124,33 +124,33 @@ APDS9960_GWTIME_30_8MS = 6
APDS9960_GWTIME_39_2MS = 7 APDS9960_GWTIME_39_2MS = 7
# Default values # Default values
APDS9960_DEFAULT_ATIME = 219 # 103ms APDS9960_DEFAULT_ATIME = 219 # 103ms
APDS9960_DEFAULT_WTIME = 246 # 27ms APDS9960_DEFAULT_WTIME = 246 # 27ms
APDS9960_DEFAULT_PROX_PPULSE = 0x87 # 16us, 8 pulses APDS9960_DEFAULT_PROX_PPULSE = 0x87 # 16us, 8 pulses
APDS9960_DEFAULT_GESTURE_PPULSE = 0x89 # 16us, 10 pulses APDS9960_DEFAULT_GESTURE_PPULSE = 0x89 # 16us, 10 pulses
APDS9960_DEFAULT_POFFSET_UR = 0 # 0 offset APDS9960_DEFAULT_POFFSET_UR = 0 # 0 offset
APDS9960_DEFAULT_POFFSET_DL = 0 # 0 offset APDS9960_DEFAULT_POFFSET_DL = 0 # 0 offset
APDS9960_DEFAULT_CONFIG1 = 0x60 # No 12x wait (WTIME) factor APDS9960_DEFAULT_CONFIG1 = 0x60 # No 12x wait (WTIME) factor
APDS9960_DEFAULT_LDRIVE = APDS9960_LED_DRIVE_100MA APDS9960_DEFAULT_LDRIVE = APDS9960_LED_DRIVE_100MA
APDS9960_DEFAULT_PGAIN = APDS9960_PGAIN_4X APDS9960_DEFAULT_PGAIN = APDS9960_PGAIN_4X
APDS9960_DEFAULT_AGAIN = APDS9960_AGAIN_4X APDS9960_DEFAULT_AGAIN = APDS9960_AGAIN_4X
APDS9960_DEFAULT_PILT = 0 # Low proximity threshold APDS9960_DEFAULT_PILT = 0 # Low proximity threshold
APDS9960_DEFAULT_PIHT = 50 # High proximity threshold APDS9960_DEFAULT_PIHT = 50 # High proximity threshold
APDS9960_DEFAULT_AILT = 0xffff # Force interrupt for calibration APDS9960_DEFAULT_AILT = 0xFFFF # Force interrupt for calibration
APDS9960_DEFAULT_AIHT = 0 APDS9960_DEFAULT_AIHT = 0
APDS9960_DEFAULT_PERS = 0x11 # 2 consecutive prox or ALS for int. APDS9960_DEFAULT_PERS = 0x11 # 2 consecutive prox or ALS for int.
APDS9960_DEFAULT_CONFIG2 = 0x01 # No saturation interrupts or LED boost APDS9960_DEFAULT_CONFIG2 = 0x01 # No saturation interrupts or LED boost
APDS9960_DEFAULT_CONFIG3 = 0 # Enable all photodiodes, no SAI APDS9960_DEFAULT_CONFIG3 = 0 # Enable all photodiodes, no SAI
APDS9960_DEFAULT_GPENTH = 40 # Threshold for entering gesture mode APDS9960_DEFAULT_GPENTH = 40 # Threshold for entering gesture mode
APDS9960_DEFAULT_GEXTH = 30 # Threshold for exiting gesture mode APDS9960_DEFAULT_GEXTH = 30 # Threshold for exiting gesture mode
APDS9960_DEFAULT_GCONF1 = 0x40 # 4 gesture events for int., 1 for exit APDS9960_DEFAULT_GCONF1 = 0x40 # 4 gesture events for int., 1 for exit
APDS9960_DEFAULT_GGAIN = APDS9960_GGAIN_4X APDS9960_DEFAULT_GGAIN = APDS9960_GGAIN_4X
APDS9960_DEFAULT_GLDRIVE = APDS9960_LED_DRIVE_100MA APDS9960_DEFAULT_GLDRIVE = APDS9960_LED_DRIVE_100MA
APDS9960_DEFAULT_GWTIME = APDS9960_GWTIME_2_8MS APDS9960_DEFAULT_GWTIME = APDS9960_GWTIME_2_8MS
APDS9960_DEFAULT_GOFFSET = 0 # No offset scaling for gesture mode APDS9960_DEFAULT_GOFFSET = 0 # No offset scaling for gesture mode
APDS9960_DEFAULT_GPULSE = 0xc9 # 32us, 10 pulses APDS9960_DEFAULT_GPULSE = 0xC9 # 32us, 10 pulses
APDS9960_DEFAULT_GCONF3 = 0 # All photodiodes active during gesture APDS9960_DEFAULT_GCONF3 = 0 # All photodiodes active during gesture
APDS9960_DEFAULT_GIEN = 0 # Disable gesture interrupts APDS9960_DEFAULT_GIEN = 0 # Disable gesture interrupts
# gesture directions # gesture directions
APDS9960_DIR_NONE = 0 APDS9960_DIR_NONE = 0

View File

@ -3,6 +3,7 @@ from apds9960.exceptions import *
from time import sleep_ms from time import sleep_ms
class APDS9960: class APDS9960:
class GestureData: class GestureData:
def __init__(self): def __init__(self):
@ -78,7 +79,6 @@ class APDS9960:
self._write_byte_data(APDS9960_REG_GCONF3, APDS9960_DEFAULT_GCONF3) self._write_byte_data(APDS9960_REG_GCONF3, APDS9960_DEFAULT_GCONF3)
self.setGestureIntEnable(APDS9960_DEFAULT_GIEN) self.setGestureIntEnable(APDS9960_DEFAULT_GIEN)
def getMode(self): def getMode(self):
return self._read_byte_data(APDS9960_REG_ENABLE) return self._read_byte_data(APDS9960_REG_ENABLE)
@ -92,19 +92,18 @@ class APDS9960:
# change bit(s) in ENABLE register */ # change bit(s) in ENABLE register */
if mode == APDS9960_MODE_ALL: if mode == APDS9960_MODE_ALL:
if enable: if enable:
reg_val = 0x7f reg_val = 0x7F
else: else:
reg_val = 0x00 reg_val = 0x00
else: else:
if enable: if enable:
reg_val |= (1 << mode); reg_val |= 1 << mode
else: else:
reg_val &= ~(1 << mode); reg_val &= ~(1 << mode)
# write value to ENABLE register # write value to ENABLE register
self._write_byte_data(APDS9960_REG_ENABLE, reg_val) self._write_byte_data(APDS9960_REG_ENABLE, reg_val)
# start the light (R/G/B/Ambient) sensor # start the light (R/G/B/Ambient) sensor
def enableLightSensor(self, interrupts=True): def enableLightSensor(self, interrupts=True):
self.setAmbientLightGain(APDS9960_DEFAULT_AGAIN) self.setAmbientLightGain(APDS9960_DEFAULT_AGAIN)
@ -117,7 +116,6 @@ class APDS9960:
self.setAmbientLightIntEnable(False) self.setAmbientLightIntEnable(False)
self.setMode(APDS9960_MODE_AMBIENT_LIGHT, False) self.setMode(APDS9960_MODE_AMBIENT_LIGHT, False)
# start the proximity sensor # start the proximity sensor
def enableProximitySensor(self, interrupts=True): def enableProximitySensor(self, interrupts=True):
self.setProximityGain(APDS9960_DEFAULT_PGAIN) self.setProximityGain(APDS9960_DEFAULT_PGAIN)
@ -131,11 +129,10 @@ class APDS9960:
self.setProximityIntEnable(False) self.setProximityIntEnable(False)
self.setMode(APDS9960_MODE_PROXIMITY, False) self.setMode(APDS9960_MODE_PROXIMITY, False)
# start the gesture recognition engine # start the gesture recognition engine
def enableGestureSensor(self, interrupts=True): def enableGestureSensor(self, interrupts=True):
self.resetGestureParameters() self.resetGestureParameters()
self._write_byte_data(APDS9960_REG_WTIME, 0xff) self._write_byte_data(APDS9960_REG_WTIME, 0xFF)
self._write_byte_data(APDS9960_REG_PPULSE, APDS9960_DEFAULT_GESTURE_PPULSE) self._write_byte_data(APDS9960_REG_PPULSE, APDS9960_DEFAULT_GESTURE_PPULSE)
self.setLEDBoost(APDS9960_LED_BOOST_300) self.setLEDBoost(APDS9960_LED_BOOST_300)
self.setGestureIntEnable(interrupts) self.setGestureIntEnable(interrupts)
@ -152,16 +149,14 @@ class APDS9960:
self.setGestureMode(False) self.setGestureMode(False)
self.setMode(APDS9960_MODE_GESTURE, False) self.setMode(APDS9960_MODE_GESTURE, False)
# check if there is a gesture available # check if there is a gesture available
def isGestureAvailable(self): def isGestureAvailable(self):
val = self._read_byte_data(APDS9960_REG_GSTATUS) val = self._read_byte_data(APDS9960_REG_GSTATUS)
# shift and mask out GVALID bit # shift and mask out GVALID bit
val &= APDS9960_BIT_GVALID; val &= APDS9960_BIT_GVALID
return (val == APDS9960_BIT_GVALID)
return val == APDS9960_BIT_GVALID
# processes a gesture event and returns best guessed gesture # processes a gesture event and returns best guessed gesture
def readGesture(self): def readGesture(self):
@ -174,7 +169,7 @@ class APDS9960:
return APDS9960_DIR_NONE return APDS9960_DIR_NONE
# keep looping as long as gesture data is valid # keep looping as long as gesture data is valid
while(self.isGestureAvailable()): while self.isGestureAvailable():
# read the current FIFO level # read the current FIFO level
fifo_level = self._read_byte_data(APDS9960_REG_GFLVL) fifo_level = self._read_byte_data(APDS9960_REG_GFLVL)
@ -197,7 +192,7 @@ class APDS9960:
# filter and process gesture data, decode near/far state # filter and process gesture data, decode near/far state
if self.processGestureData(): if self.processGestureData():
if self.decodeGesture(): if self.decodeGesture():
#***TODO: U-Turn Gestures # ***TODO: U-Turn Gestures
pass pass
# reset data # reset data
@ -215,18 +210,14 @@ class APDS9960:
self.resetGestureParameters() self.resetGestureParameters()
return motion return motion
# turn the APDS-9960 on # turn the APDS-9960 on
def enablePower(self): def enablePower(self):
self.setMode(APDS9960_MODE_POWER, True) self.setMode(APDS9960_MODE_POWER, True)
def disablePower(self): def disablePower(self):
# turn the APDS-9960 off # turn the APDS-9960 off
self.setMode(APDS9960_MODE_POWER, False) self.setMode(APDS9960_MODE_POWER, False)
# ******************************************************************************* # *******************************************************************************
# ambient light and color sensor controls # ambient light and color sensor controls
# ******************************************************************************* # *******************************************************************************
@ -271,7 +262,6 @@ class APDS9960:
return l + (h << 8) return l + (h << 8)
# ******************************************************************************* # *******************************************************************************
# Proximity sensor controls # Proximity sensor controls
# ******************************************************************************* # *******************************************************************************
@ -280,7 +270,6 @@ class APDS9960:
def readProximity(self): def readProximity(self):
return self._read_byte_data(APDS9960_REG_PDATA) return self._read_byte_data(APDS9960_REG_PDATA)
# ******************************************************************************* # *******************************************************************************
# High-level gesture controls # High-level gesture controls
# ******************************************************************************* # *******************************************************************************
@ -302,12 +291,11 @@ class APDS9960:
self.gesture_state_ = 0 self.gesture_state_ = 0
self.gesture_motion_ = APDS9960_DIR_NONE self.gesture_motion_ = APDS9960_DIR_NONE
def processGestureData(self): def processGestureData(self):
"""Processes the raw gesture data to determine swipe direction """Processes the raw gesture data to determine swipe direction
Returns: Returns:
bool: True if near or far state seen, False otherwise. bool: True if near or far state seen, False otherwise.
""" """
u_first = 0 u_first = 0
d_first = 0 d_first = 0
@ -326,11 +314,12 @@ class APDS9960:
if self.gesture_data_.total_gestures <= 32 and self.gesture_data_.total_gestures > 0: if self.gesture_data_.total_gestures <= 32 and self.gesture_data_.total_gestures > 0:
# find the first value in U/D/L/R above the threshold # find the first value in U/D/L/R above the threshold
for i in range(0, self.gesture_data_.total_gestures): for i in range(0, self.gesture_data_.total_gestures):
if self.gesture_data_.u_data[i] > APDS9960_GESTURE_THRESHOLD_OUT and \ if (
self.gesture_data_.d_data[i] > APDS9960_GESTURE_THRESHOLD_OUT and \ self.gesture_data_.u_data[i] > APDS9960_GESTURE_THRESHOLD_OUT
self.gesture_data_.l_data[i] > APDS9960_GESTURE_THRESHOLD_OUT and \ and self.gesture_data_.d_data[i] > APDS9960_GESTURE_THRESHOLD_OUT
self.gesture_data_.r_data[i] > APDS9960_GESTURE_THRESHOLD_OUT: and self.gesture_data_.l_data[i] > APDS9960_GESTURE_THRESHOLD_OUT
and self.gesture_data_.r_data[i] > APDS9960_GESTURE_THRESHOLD_OUT
):
u_first = self.gesture_data_.u_data[i] u_first = self.gesture_data_.u_data[i]
d_first = self.gesture_data_.d_data[i] d_first = self.gesture_data_.d_data[i]
l_first = self.gesture_data_.l_data[i] l_first = self.gesture_data_.l_data[i]
@ -338,16 +327,17 @@ class APDS9960:
break break
# if one of the _first values is 0, then there is no good data # if one of the _first values is 0, then there is no good data
if u_first == 0 or d_first == 0 or l_first == 0 or r_first == 0: if u_first == 0 or d_first == 0 or l_first == 0 or r_first == 0:
return False return False
# find the last value in U/D/L/R above the threshold # find the last value in U/D/L/R above the threshold
for i in reversed(range(0, self.gesture_data_.total_gestures)): for i in reversed(range(0, self.gesture_data_.total_gestures)):
if self.gesture_data_.u_data[i] > APDS9960_GESTURE_THRESHOLD_OUT and \ if (
self.gesture_data_.d_data[i] > APDS9960_GESTURE_THRESHOLD_OUT and \ self.gesture_data_.u_data[i] > APDS9960_GESTURE_THRESHOLD_OUT
self.gesture_data_.l_data[i] > APDS9960_GESTURE_THRESHOLD_OUT and \ and self.gesture_data_.d_data[i] > APDS9960_GESTURE_THRESHOLD_OUT
self.gesture_data_.r_data[i] > APDS9960_GESTURE_THRESHOLD_OUT: and self.gesture_data_.l_data[i] > APDS9960_GESTURE_THRESHOLD_OUT
and self.gesture_data_.r_data[i] > APDS9960_GESTURE_THRESHOLD_OUT
):
u_last = self.gesture_data_.u_data[i] u_last = self.gesture_data_.u_data[i]
d_last = self.gesture_data_.d_data[i] d_last = self.gesture_data_.d_data[i]
l_last = self.gesture_data_.l_data[i] l_last = self.gesture_data_.l_data[i]
@ -386,9 +376,10 @@ class APDS9960:
# determine Near/Far gesture # determine Near/Far gesture
if self.gesture_ud_count_ == 0 and self.gesture_lr_count_ == 0: if self.gesture_ud_count_ == 0 and self.gesture_lr_count_ == 0:
if abs(ud_delta) < APDS9960_GESTURE_SENSITIVITY_2 and \ if (
abs(lr_delta) < APDS9960_GESTURE_SENSITIVITY_2: abs(ud_delta) < APDS9960_GESTURE_SENSITIVITY_2
and abs(lr_delta) < APDS9960_GESTURE_SENSITIVITY_2
):
if ud_delta == 0 and lr_delta == 0: if ud_delta == 0 and lr_delta == 0:
self.gesture_near_count_ += 1 self.gesture_near_count_ += 1
elif ud_delta != 0 or lr_delta != 0: elif ud_delta != 0 or lr_delta != 0:
@ -401,23 +392,23 @@ class APDS9960:
self.gesture_state_ = APDS9960_STATE_FAR self.gesture_state_ = APDS9960_STATE_FAR
return True return True
else: else:
if abs(ud_delta) < APDS9960_GESTURE_SENSITIVITY_2 and \ if (
abs(lr_delta) < APDS9960_GESTURE_SENSITIVITY_2: abs(ud_delta) < APDS9960_GESTURE_SENSITIVITY_2
and abs(lr_delta) < APDS9960_GESTURE_SENSITIVITY_2
):
if ud_delta == 0 and lr_delta == 0:
self.gesture_near_count_ += 1
if ud_delta == 0 and lr_delta == 0: if self.gesture_near_count_ >= 10:
self.gesture_near_count_ += 1 self.gesture_ud_count_ = 0
self.gesture_lr_count_ = 0
if self.gesture_near_count_ >= 10: self.gesture_ud_delta_ = 0
self.gesture_ud_count_ = 0 self.gesture_lr_delta_ = 0
self.gesture_lr_count_ = 0
self.gesture_ud_delta_ = 0
self.gesture_lr_delta_ = 0
return False return False
def decodeGesture(self): def decodeGesture(self):
"""Determines swipe direction or near/far state. """Determines swipe direction or near/far state."""
"""
# return if near or far event is detected # return if near or far event is detected
if self.gesture_state_ == APDS9960_STATE_NEAR: if self.gesture_state_ == APDS9960_STATE_NEAR:
@ -462,44 +453,37 @@ class APDS9960:
return True return True
# ******************************************************************************* # *******************************************************************************
# Getters and setters for register values # Getters and setters for register values
# ******************************************************************************* # *******************************************************************************
def getProxIntLowThresh(self): def getProxIntLowThresh(self):
"""Returns the lower threshold for proximity detection """Returns the lower threshold for proximity detection"""
"""
return self._read_byte_data(APDS9960_REG_PILT) return self._read_byte_data(APDS9960_REG_PILT)
def setProxIntLowThresh(self, threshold): def setProxIntLowThresh(self, threshold):
"""Sets the lower threshold for proximity detection. """Sets the lower threshold for proximity detection."""
"""
self._write_byte_data(APDS9960_REG_PILT, threshold) self._write_byte_data(APDS9960_REG_PILT, threshold)
def getProxIntHighThresh(self): def getProxIntHighThresh(self):
"""Returns the high threshold for proximity detection. """Returns the high threshold for proximity detection."""
"""
return self._read_byte_data(APDS9960_REG_PIHT) return self._read_byte_data(APDS9960_REG_PIHT)
def setProxIntHighThresh(self, threshold): def setProxIntHighThresh(self, threshold):
"""Sets the high threshold for proximity detection. """Sets the high threshold for proximity detection."""
"""
self._write_byte_data(APDS9960_REG_PIHT, threshold) self._write_byte_data(APDS9960_REG_PIHT, threshold)
def getLEDDrive(self): def getLEDDrive(self):
"""Returns LED drive strength for proximity and ALS. """Returns LED drive strength for proximity and ALS.
Value LED Current Value LED Current
0 100 mA 0 100 mA
1 50 mA 1 50 mA
2 25 mA 2 25 mA
3 12.5 mA 3 12.5 mA
Returns: Returns:
int: the value of the LED drive strength int: the value of the LED drive strength
""" """
val = self._read_byte_data(APDS9960_REG_CONTROL) val = self._read_byte_data(APDS9960_REG_CONTROL)
@ -509,14 +493,14 @@ class APDS9960:
def setLEDDrive(self, drive): def setLEDDrive(self, drive):
"""Sets LED drive strength for proximity and ALS. """Sets LED drive strength for proximity and ALS.
Value LED Current Value LED Current
0 100 mA 0 100 mA
1 50 mA 1 50 mA
2 25 mA 2 25 mA
3 12.5 mA 3 12.5 mA
Args: Args:
drive (int): value for the LED drive strength drive (int): value for the LED drive strength
""" """
val = self._read_byte_data(APDS9960_REG_CONTROL) val = self._read_byte_data(APDS9960_REG_CONTROL)
@ -528,18 +512,17 @@ class APDS9960:
self._write_byte_data(APDS9960_REG_CONTROL, val) self._write_byte_data(APDS9960_REG_CONTROL, val)
def getProximityGain(self): def getProximityGain(self):
"""Returns receiver gain for proximity detection. """Returns receiver gain for proximity detection.
Value Gain Value Gain
0 1x 0 1x
1 2x 1 2x
2 4x 2 4x
3 8x 3 8x
Returns: Returns:
int: the value of the proximity gain int: the value of the proximity gain
""" """
val = self._read_byte_data(APDS9960_REG_CONTROL) val = self._read_byte_data(APDS9960_REG_CONTROL)
@ -549,14 +532,14 @@ class APDS9960:
def setProximityGain(self, drive): def setProximityGain(self, drive):
"""Returns receiver gain for proximity detection. """Returns receiver gain for proximity detection.
Value Gain Value Gain
0 1x 0 1x
1 2x 1 2x
2 4x 2 4x
3 8x 3 8x
Args: Args:
drive (int): value for the proximity gain drive (int): value for the proximity gain
""" """
val = self._read_byte_data(APDS9960_REG_CONTROL) val = self._read_byte_data(APDS9960_REG_CONTROL)
@ -568,35 +551,34 @@ class APDS9960:
self._write_byte_data(APDS9960_REG_CONTROL, val) self._write_byte_data(APDS9960_REG_CONTROL, val)
def getAmbientLightGain(self): def getAmbientLightGain(self):
"""Returns receiver gain for the ambient light sensor (ALS). """Returns receiver gain for the ambient light sensor (ALS).
Value Gain Value Gain
0 1x 0 1x
1 4x 1 4x
2 16x 2 16x
3 64x 3 64x
Returns: Returns:
int: the value of the ALS gain int: the value of the ALS gain
""" """
val = self._read_byte_data(APDS9960_REG_CONTROL) val = self._read_byte_data(APDS9960_REG_CONTROL)
# shift and mask out ADRIVE bits # shift and mask out ADRIVE bits
return (val & 0b00000011) return val & 0b00000011
def setAmbientLightGain(self, drive): def setAmbientLightGain(self, drive):
"""Sets the receiver gain for the ambient light sensor (ALS). """Sets the receiver gain for the ambient light sensor (ALS).
Value Gain Value Gain
0 1x 0 1x
1 4x 1 4x
2 16x 2 16x
3 64x 3 64x
Args: Args:
drive (int): value for the ALS gain drive (int): value for the ALS gain
""" """
val = self._read_byte_data(APDS9960_REG_CONTROL) val = self._read_byte_data(APDS9960_REG_CONTROL)
@ -607,18 +589,17 @@ class APDS9960:
self._write_byte_data(APDS9960_REG_CONTROL, val) self._write_byte_data(APDS9960_REG_CONTROL, val)
def getLEDBoost(self): def getLEDBoost(self):
"""Get the current LED boost value. """Get the current LED boost value.
Value Gain Value Gain
0 100% 0 100%
1 150% 1 150%
2 200% 2 200%
3 300% 3 300%
Returns: Returns:
int: the LED boost value int: the LED boost value
""" """
val = self._read_byte_data(APDS9960_REG_CONFIG2) val = self._read_byte_data(APDS9960_REG_CONFIG2)
@ -628,14 +609,14 @@ class APDS9960:
def setLEDBoost(self, boost): def setLEDBoost(self, boost):
"""Sets the LED current boost value. """Sets the LED current boost value.
Value Gain Value Gain
0 100% 0 100%
1 150% 1 150%
2 200% 2 200%
3 300% 3 300%
Args: Args:
boost (int): value for the LED boost boost (int): value for the LED boost
""" """
val = self._read_byte_data(APDS9960_REG_CONFIG2) val = self._read_byte_data(APDS9960_REG_CONFIG2)
@ -647,12 +628,11 @@ class APDS9960:
self._write_byte_data(APDS9960_REG_CONFIG2, val) self._write_byte_data(APDS9960_REG_CONFIG2, val)
def getProxGainCompEnable(self): def getProxGainCompEnable(self):
"""Gets proximity gain compensation enable. """Gets proximity gain compensation enable.
Returns: Returns:
bool: True if compensation is enabled, False if not bool: True if compensation is enabled, False if not
""" """
val = self._read_byte_data(APDS9960_REG_CONFIG3) val = self._read_byte_data(APDS9960_REG_CONFIG3)
@ -663,8 +643,8 @@ class APDS9960:
def setProxGainCompEnable(self, enable): def setProxGainCompEnable(self, enable):
"""Sets the proximity gain compensation enable. """Sets the proximity gain compensation enable.
Args: Args:
enable (bool): True to enable compensation, False to disable enable (bool): True to enable compensation, False to disable
""" """
val = self._read_byte_data(APDS9960_REG_CONFIG3) val = self._read_byte_data(APDS9960_REG_CONFIG3)
@ -675,20 +655,19 @@ class APDS9960:
self._write_byte_data(APDS9960_REG_CONFIG3, val) self._write_byte_data(APDS9960_REG_CONFIG3, val)
def getProxPhotoMask(self): def getProxPhotoMask(self):
"""Gets the current mask for enabled/disabled proximity photodiodes. """Gets the current mask for enabled/disabled proximity photodiodes.
Bit Photodiode Bit Photodiode
3 UP 3 UP
2 DOWN 2 DOWN
1 LEFT 1 LEFT
0 RIGHT 0 RIGHT
1 = disabled, 0 = enabled 1 = disabled, 0 = enabled
Returns: Returns:
int: Current proximity mask for photodiodes. int: Current proximity mask for photodiodes.
""" """
val = self._read_byte_data(APDS9960_REG_CONFIG3) val = self._read_byte_data(APDS9960_REG_CONFIG3)
@ -698,16 +677,16 @@ class APDS9960:
def setProxPhotoMask(self, mask): def setProxPhotoMask(self, mask):
"""Sets the mask for enabling/disabling proximity photodiodes. """Sets the mask for enabling/disabling proximity photodiodes.
Bit Photodiode Bit Photodiode
3 UP 3 UP
2 DOWN 2 DOWN
1 LEFT 1 LEFT
0 RIGHT 0 RIGHT
1 = disabled, 0 = enabled 1 = disabled, 0 = enabled
Args: Args:
mask (int): 4-bit mask value mask (int): 4-bit mask value
""" """
val = self._read_byte_data(APDS9960_REG_CONFIG3) val = self._read_byte_data(APDS9960_REG_CONFIG3)
@ -718,52 +697,49 @@ class APDS9960:
self._write_byte_data(APDS9960_REG_CONFIG3, val) self._write_byte_data(APDS9960_REG_CONFIG3, val)
def getGestureEnterThresh(self): def getGestureEnterThresh(self):
"""Gets the entry proximity threshold for gesture sensing. """Gets the entry proximity threshold for gesture sensing.
Returns: Returns:
int: current entry proximity threshold int: current entry proximity threshold
""" """
return self._read_byte_data(APDS9960_REG_GPENTH) return self._read_byte_data(APDS9960_REG_GPENTH)
def setGestureEnterThresh(self, threshold): def setGestureEnterThresh(self, threshold):
"""Sets the entry proximity threshold for gesture sensing. """Sets the entry proximity threshold for gesture sensing.
Args: Args:
threshold (int): threshold proximity value needed to start gesture mode threshold (int): threshold proximity value needed to start gesture mode
""" """
self._write_byte_data(APDS9960_REG_GPENTH, threshold) self._write_byte_data(APDS9960_REG_GPENTH, threshold)
def getGestureExitThresh(self): def getGestureExitThresh(self):
"""Gets the exit proximity threshold for gesture sensing. """Gets the exit proximity threshold for gesture sensing.
Returns: Returns:
int: current exit proximity threshold int: current exit proximity threshold
""" """
return self._read_byte_data(APDS9960_REG_GEXTH) return self._read_byte_data(APDS9960_REG_GEXTH)
def setGestureExitThresh(self, threshold): def setGestureExitThresh(self, threshold):
"""Sets the exit proximity threshold for gesture sensing. """Sets the exit proximity threshold for gesture sensing.
Args: Args:
threshold (int): threshold proximity value needed to end gesture mode threshold (int): threshold proximity value needed to end gesture mode
""" """
self._write_byte_data(APDS9960_REG_GEXTH, threshold) self._write_byte_data(APDS9960_REG_GEXTH, threshold)
def getGestureGain(self): def getGestureGain(self):
"""Gets the gain of the photodiode during gesture mode. """Gets the gain of the photodiode during gesture mode.
Value Gain Value Gain
0 1x 0 1x
1 2x 1 2x
2 4x 2 4x
3 8x 3 8x
Returns: Returns:
int: the current photodiode gain int: the current photodiode gain
""" """
val = self._read_byte_data(APDS9960_REG_GCONF2) val = self._read_byte_data(APDS9960_REG_GCONF2)
@ -773,14 +749,14 @@ class APDS9960:
def setGestureGain(self, gain): def setGestureGain(self, gain):
"""Sets the gain of the photodiode during gesture mode. """Sets the gain of the photodiode during gesture mode.
Value Gain Value Gain
0 1x 0 1x
1 2x 1 2x
2 4x 2 4x
3 8x 3 8x
Args: Args:
gain (int): the value for the photodiode gain gain (int): the value for the photodiode gain
""" """
val = self._read_byte_data(APDS9960_REG_GCONF2) val = self._read_byte_data(APDS9960_REG_GCONF2)
@ -792,18 +768,17 @@ class APDS9960:
self._write_byte_data(APDS9960_REG_GCONF2, val) self._write_byte_data(APDS9960_REG_GCONF2, val)
def getGestureLEDDrive(self): def getGestureLEDDrive(self):
"""Gets the drive current of the LED during gesture mode. """Gets the drive current of the LED during gesture mode.
Value LED Current Value LED Current
0 100 mA 0 100 mA
1 50 mA 1 50 mA
2 25 mA 2 25 mA
3 12.5 mA 3 12.5 mA
Returns: Returns:
int: the LED drive current value int: the LED drive current value
""" """
val = self._read_byte_data(APDS9960_REG_GCONF2) val = self._read_byte_data(APDS9960_REG_GCONF2)
@ -813,41 +788,40 @@ class APDS9960:
def setGestureLEDDrive(self, drive): def setGestureLEDDrive(self, drive):
"""Sets LED drive strength for proximity and ALS. """Sets LED drive strength for proximity and ALS.
Value LED Current Value LED Current
0 100 mA 0 100 mA
1 50 mA 1 50 mA
2 25 mA 2 25 mA
3 12.5 mA 3 12.5 mA
Args: Args:
drive (int): value for the LED drive current drive (int): value for the LED drive current
""" """
val = self._read_byte_data(APDS9960_REG_GCONF2) val = self._read_byte_data(APDS9960_REG_GCONF2)
# set bits in register to given value # set bits in register to given value
drive &= 0b00000011; drive &= 0b00000011
drive = drive << 3; drive = drive << 3
val &= 0b11100111; val &= 0b11100111
val |= drive; val |= drive
self._write_byte_data(APDS9960_REG_GCONF2, val) self._write_byte_data(APDS9960_REG_GCONF2, val)
def getGestureWaitTime(self): def getGestureWaitTime(self):
"""Gets the time in low power mode between gesture detections. """Gets the time in low power mode between gesture detections.
Value Wait time Value Wait time
0 0 ms 0 0 ms
1 2.8 ms 1 2.8 ms
2 5.6 ms 2 5.6 ms
3 8.4 ms 3 8.4 ms
4 14.0 ms 4 14.0 ms
5 22.4 ms 5 22.4 ms
6 30.8 ms 6 30.8 ms
7 39.2 ms 7 39.2 ms
Returns: Returns:
int: the current wait time between gestures int: the current wait time between gestures
""" """
val = self._read_byte_data(APDS9960_REG_GCONF2) val = self._read_byte_data(APDS9960_REG_GCONF2)
@ -857,18 +831,18 @@ class APDS9960:
def setGestureWaitTime(self, time): def setGestureWaitTime(self, time):
"""Sets the time in low power mode between gesture detections. """Sets the time in low power mode between gesture detections.
Value Wait time Value Wait time
0 0 ms 0 0 ms
1 2.8 ms 1 2.8 ms
2 5.6 ms 2 5.6 ms
3 8.4 ms 3 8.4 ms
4 14.0 ms 4 14.0 ms
5 22.4 ms 5 22.4 ms
6 30.8 ms 6 30.8 ms
7 39.2 ms 7 39.2 ms
Args: Args:
time (int): value for the wait time time (int): value for the wait time
""" """
val = self._read_byte_data(APDS9960_REG_GCONF2) val = self._read_byte_data(APDS9960_REG_GCONF2)
@ -879,84 +853,83 @@ class APDS9960:
self._write_byte_data(APDS9960_REG_GCONF2, val) self._write_byte_data(APDS9960_REG_GCONF2, val)
def getLightIntLowThreshold(self): def getLightIntLowThreshold(self):
"""Gets the low threshold for ambient light interrupts. """Gets the low threshold for ambient light interrupts.
Returns: Returns:
int: threshold current low threshold stored on the APDS9960 int: threshold current low threshold stored on the APDS9960
""" """
return self._read_byte_data(APDS9960_REG_AILTL) | (self._read_byte_data(APDS9960_REG_AILTH) << 8) return self._read_byte_data(APDS9960_REG_AILTL) | (
self._read_byte_data(APDS9960_REG_AILTH) << 8
)
def setLightIntLowThreshold(self, threshold): def setLightIntLowThreshold(self, threshold):
"""Sets the low threshold for ambient light interrupts. """Sets the low threshold for ambient light interrupts.
Args: Args:
threshold (int): low threshold value for interrupt to trigger threshold (int): low threshold value for interrupt to trigger
""" """
# break 16-bit threshold into 2 8-bit values # break 16-bit threshold into 2 8-bit values
self._write_byte_data(APDS9960_REG_AILTL, threshold & 0x00ff) self._write_byte_data(APDS9960_REG_AILTL, threshold & 0x00FF)
self._write_byte_data(APDS9960_REG_AILTH, (threshold & 0xff00) >> 8) self._write_byte_data(APDS9960_REG_AILTH, (threshold & 0xFF00) >> 8)
def getLightIntHighThreshold(self): def getLightIntHighThreshold(self):
"""Gets the high threshold for ambient light interrupts. """Gets the high threshold for ambient light interrupts.
Returns: Returns:
int: threshold current low threshold stored on the APDS9960 int: threshold current low threshold stored on the APDS9960
""" """
return self._read_byte_data(APDS9960_REG_AIHTL) | (self._read_byte_data(APDS9960_REG_AIHTH) << 8) return self._read_byte_data(APDS9960_REG_AIHTL) | (
self._read_byte_data(APDS9960_REG_AIHTH) << 8
)
def setLightIntHighThreshold(self, threshold): def setLightIntHighThreshold(self, threshold):
"""Sets the high threshold for ambient light interrupts. """Sets the high threshold for ambient light interrupts.
Args: Args:
threshold (int): high threshold value for interrupt to trigger threshold (int): high threshold value for interrupt to trigger
""" """
# break 16-bit threshold into 2 8-bit values # break 16-bit threshold into 2 8-bit values
self._write_byte_data(APDS9960_REG_AIHTL, threshold & 0x00ff) self._write_byte_data(APDS9960_REG_AIHTL, threshold & 0x00FF)
self._write_byte_data(APDS9960_REG_AIHTH, (threshold & 0xff00) >> 8) self._write_byte_data(APDS9960_REG_AIHTH, (threshold & 0xFF00) >> 8)
def getProximityIntLowThreshold(self): def getProximityIntLowThreshold(self):
"""Gets the low threshold for proximity interrupts. """Gets the low threshold for proximity interrupts.
Returns: Returns:
int: threshold current low threshold stored on the APDS9960 int: threshold current low threshold stored on the APDS9960
""" """
return self._read_byte_data(APDS9960_REG_PILT) return self._read_byte_data(APDS9960_REG_PILT)
def setProximityIntLowThreshold(self, threshold): def setProximityIntLowThreshold(self, threshold):
"""Sets the low threshold for proximity interrupts. """Sets the low threshold for proximity interrupts.
Args: Args:
threshold (int): low threshold value for interrupt to trigger threshold (int): low threshold value for interrupt to trigger
""" """
self._write_byte_data(APDS9960_REG_PILT, threshold) self._write_byte_data(APDS9960_REG_PILT, threshold)
def getProximityIntHighThreshold(self): def getProximityIntHighThreshold(self):
"""Gets the high threshold for proximity interrupts. """Gets the high threshold for proximity interrupts.
Returns: Returns:
int: threshold current high threshold stored on the APDS9960 int: threshold current high threshold stored on the APDS9960
""" """
return self._read_byte_data(APDS9960_REG_PIHT) return self._read_byte_data(APDS9960_REG_PIHT)
def setProximityIntHighThreshold(self, threshold): def setProximityIntHighThreshold(self, threshold):
"""Sets the high threshold for proximity interrupts. """Sets the high threshold for proximity interrupts.
Args: Args:
threshold (int): high threshold value for interrupt to trigger threshold (int): high threshold value for interrupt to trigger
""" """
self._write_byte_data(APDS9960_REG_PIHT, threshold) self._write_byte_data(APDS9960_REG_PIHT, threshold)
def getAmbientLightIntEnable(self): def getAmbientLightIntEnable(self):
"""Gets if ambient light interrupts are enabled or not. """Gets if ambient light interrupts are enabled or not.
Returns: Returns:
bool: True if interrupts are enabled, False if not bool: True if interrupts are enabled, False if not
""" """
val = self._read_byte_data(APDS9960_REG_ENABLE) val = self._read_byte_data(APDS9960_REG_ENABLE)
return (val >> 4) & 0b00000001 == 1 return (val >> 4) & 0b00000001 == 1
@ -964,24 +937,23 @@ class APDS9960:
def setAmbientLightIntEnable(self, enable): def setAmbientLightIntEnable(self, enable):
"""Turns ambient light interrupts on or off. """Turns ambient light interrupts on or off.
Args: Args:
enable (bool): True to enable interrupts, False to turn them off enable (bool): True to enable interrupts, False to turn them off
""" """
val = self._read_byte_data(APDS9960_REG_ENABLE) val = self._read_byte_data(APDS9960_REG_ENABLE)
# set bits in register to given value # set bits in register to given value
val &= 0b11101111; val &= 0b11101111
if enable: if enable:
val |= 0b00010000 val |= 0b00010000
self._write_byte_data(APDS9960_REG_ENABLE, val) self._write_byte_data(APDS9960_REG_ENABLE, val)
def getProximityIntEnable(self): def getProximityIntEnable(self):
"""Gets if proximity interrupts are enabled or not. """Gets if proximity interrupts are enabled or not.
Returns: Returns:
bool: True if interrupts are enabled, False if not bool: True if interrupts are enabled, False if not
""" """
val = self._read_byte_data(APDS9960_REG_ENABLE) val = self._read_byte_data(APDS9960_REG_ENABLE)
return (val >> 5) & 0b00000001 == 1 return (val >> 5) & 0b00000001 == 1
@ -989,24 +961,23 @@ class APDS9960:
def setProximityIntEnable(self, enable): def setProximityIntEnable(self, enable):
"""Turns proximity interrupts on or off. """Turns proximity interrupts on or off.
Args: Args:
enable (bool): True to enable interrupts, False to turn them off enable (bool): True to enable interrupts, False to turn them off
""" """
val = self._read_byte_data(APDS9960_REG_ENABLE) val = self._read_byte_data(APDS9960_REG_ENABLE)
# set bits in register to given value # set bits in register to given value
val &= 0b11011111; val &= 0b11011111
if enable: if enable:
val |= 0b00100000 val |= 0b00100000
self._write_byte_data(APDS9960_REG_ENABLE, val) self._write_byte_data(APDS9960_REG_ENABLE, val)
def getGestureIntEnable(self): def getGestureIntEnable(self):
"""Gets if gesture interrupts are enabled or not. """Gets if gesture interrupts are enabled or not.
Returns: Returns:
bool: True if interrupts are enabled, False if not bool: True if interrupts are enabled, False if not
""" """
val = self._read_byte_data(APDS9960_REG_GCONF4) val = self._read_byte_data(APDS9960_REG_GCONF4)
return (val >> 1) & 0b00000001 == 1 return (val >> 1) & 0b00000001 == 1
@ -1014,8 +985,8 @@ class APDS9960:
def setGestureIntEnable(self, enable): def setGestureIntEnable(self, enable):
"""Turns gesture-related interrupts on or off. """Turns gesture-related interrupts on or off.
Args: Args:
enable (bool): True to enable interrupts, False to turn them off enable (bool): True to enable interrupts, False to turn them off
""" """
val = self._read_byte_data(APDS9960_REG_GCONF4) val = self._read_byte_data(APDS9960_REG_GCONF4)
@ -1026,24 +997,19 @@ class APDS9960:
self._write_byte_data(APDS9960_REG_GCONF4, val) self._write_byte_data(APDS9960_REG_GCONF4, val)
def clearAmbientLightInt(self): def clearAmbientLightInt(self):
"""Clears the ambient light interrupt. """Clears the ambient light interrupt."""
"""
self._read_byte_data(APDS9960_REG_AICLEAR) self._read_byte_data(APDS9960_REG_AICLEAR)
def clearProximityInt(self): def clearProximityInt(self):
"""Clears the proximity interrupt. """Clears the proximity interrupt."""
"""
self._read_byte_data(APDS9960_REG_PICLEAR) self._read_byte_data(APDS9960_REG_PICLEAR)
def getGestureMode(self): def getGestureMode(self):
"""Tells if the gesture state machine is currently running. """Tells if the gesture state machine is currently running.
Returns: Returns:
bool: True if gesture state machine is running, False if not bool: True if gesture state machine is running, False if not
""" """
val = self._read_byte_data(APDS9960_REG_GCONF4) val = self._read_byte_data(APDS9960_REG_GCONF4)
return val & 0b00000001 == 1 return val & 0b00000001 == 1
@ -1051,8 +1017,8 @@ class APDS9960:
def setGestureMode(self, enable): def setGestureMode(self, enable):
"""Turns gesture-related interrupts on or off. """Turns gesture-related interrupts on or off.
Args: Args:
enable (bool): True to enter gesture state machine, False to turn them off enable (bool): True to enter gesture state machine, False to turn them off
""" """
val = self._read_byte_data(APDS9960_REG_GCONF4) val = self._read_byte_data(APDS9960_REG_GCONF4)
@ -1061,8 +1027,7 @@ class APDS9960:
if enable: if enable:
val |= 0b00000001 val |= 0b00000001
self._write_byte_data(APDS9960_REG_GCONF4, val) self._write_byte_data(APDS9960_REG_GCONF4, val)
# ******************************************************************************* # *******************************************************************************
# Raw I2C Reads and Writes # Raw I2C Reads and Writes
@ -1074,12 +1039,10 @@ class APDS9960:
def _write_byte_data(self, cmd, val): def _write_byte_data(self, cmd, val):
return self.bus.write_byte_data(self.address, cmd, val) return self.bus.write_byte_data(self.address, cmd, val)
def _read_i2c_block_data(self, cmd, num): def _read_i2c_block_data(self, cmd, num):
return self.bus.read_i2c_block_data(self.address, cmd, num) return self.bus.read_i2c_block_data(self.address, cmd, num)
class uAPDS9960(APDS9960): class uAPDS9960(APDS9960):
""" """
APDS9960 for MicroPython APDS9960 for MicroPython
@ -1087,6 +1050,7 @@ class uAPDS9960(APDS9960):
sensor = uAPDS9960(bus=I2C_instance, sensor = uAPDS9960(bus=I2C_instance,
address=APDS9960_I2C_ADDR, valid_id=APDS9960_DEV_ID) address=APDS9960_I2C_ADDR, valid_id=APDS9960_DEV_ID)
""" """
def _read_byte_data(self, cmd): def _read_byte_data(self, cmd):
return self.bus.readfrom_mem(self.address, cmd, 1)[0] return self.bus.readfrom_mem(self.address, cmd, 1)[0]

View File

@ -1,6 +1,12 @@
class ADPS9960InvalidDevId(ValueError): class ADPS9960InvalidDevId(ValueError):
def __init__(self, id, valid_ids): def __init__(self, id, valid_ids):
Exception.__init__(self, "Device id 0x{} is not a valied one (valid: {})!".format(format(id, '02x'), ', '.join(["0x{}".format(format(i, '02x')) for i in valid_ids]))) Exception.__init__(
self,
"Device id 0x{} is not a valied one (valid: {})!".format(
format(id, "02x"), ", ".join(["0x{}".format(format(i, "02x")) for i in valid_ids])
),
)
class ADPS9960InvalidMode(ValueError): class ADPS9960InvalidMode(ValueError):
def __init__(self, mode): def __init__(self, mode):

View File

@ -1,17 +1,17 @@
# The MIT License (MIT) # The MIT License (MIT)
# #
# Copyright (c) 2013-2021 Damien P. George # Copyright (c) 2013-2021 Damien P. George
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights # in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is # copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions: # furnished to do so, subject to the following conditions:
# #
# The above copyright notice and this permission notice shall be included in # The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software. # all copies or substantial portions of the Software.
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

View File

@ -11,9 +11,9 @@ MAGGYRO_MODE = 0x06
AMG_MODE = 0x07 AMG_MODE = 0x07
IMUPLUS_MODE = 0x08 IMUPLUS_MODE = 0x08
COMPASS_MODE = 0x09 COMPASS_MODE = 0x09
M4G_MODE = 0x0a M4G_MODE = 0x0A
NDOF_FMC_OFF_MODE = 0x0b NDOF_FMC_OFF_MODE = 0x0B
NDOF_MODE = 0x0c NDOF_MODE = 0x0C
AXIS_P0 = bytes([0x21, 0x04]) AXIS_P0 = bytes([0x21, 0x04])
AXIS_P1 = bytes([0x24, 0x00]) AXIS_P1 = bytes([0x24, 0x00])
@ -24,75 +24,91 @@ AXIS_P5 = bytes([0x21, 0x01])
AXIS_P6 = bytes([0x21, 0x07]) AXIS_P6 = bytes([0x21, 0x07])
AXIS_P7 = bytes([0x24, 0x05]) AXIS_P7 = bytes([0x24, 0x05])
_MODE_REGISTER = 0x3d _MODE_REGISTER = 0x3D
_POWER_REGISTER = 0x3e _POWER_REGISTER = 0x3E
_AXIS_MAP_CONFIG = 0x41 _AXIS_MAP_CONFIG = 0x41
class BNO055: class BNO055:
def __init__(self, i2c, address=0x28, mode = NDOF_MODE, axis = AXIS_P4): def __init__(self, i2c, address=0x28, mode=NDOF_MODE, axis=AXIS_P4):
self.i2c = i2c self.i2c = i2c
self.address = address self.address = address
if self.read_id() != bytes([0xA0, 0xFB, 0x32, 0x0F]): if self.read_id() != bytes([0xA0, 0xFB, 0x32, 0x0F]):
raise RuntimeError('Failed to find expected ID register values. Check wiring!') raise RuntimeError("Failed to find expected ID register values. Check wiring!")
self.operation_mode(CONFIG_MODE) self.operation_mode(CONFIG_MODE)
self.system_trigger(0x20)# reset self.system_trigger(0x20) # reset
pyb.delay(700) pyb.delay(700)
self.power_mode(0x00)#POWER_NORMAL self.power_mode(0x00) # POWER_NORMAL
self.axis(axis) self.axis(axis)
self.page(0) self.page(0)
pyb.delay(10) pyb.delay(10)
self.operation_mode(mode) self.operation_mode(mode)
self.system_trigger(0x80) # external oscillator self.system_trigger(0x80) # external oscillator
pyb.delay(200) pyb.delay(200)
def read_registers(self, register, size=1): def read_registers(self, register, size=1):
return(self.i2c.readfrom_mem(self.address, register, size)) return self.i2c.readfrom_mem(self.address, register, size)
def write_registers(self, register, data): def write_registers(self, register, data):
self.i2c.writeto_mem(self.address, register, data) self.i2c.writeto_mem(self.address, register, data)
def operation_mode(self, mode=None): def operation_mode(self, mode=None):
if mode: if mode:
self.write_registers(_MODE_REGISTER, bytes([mode])) self.write_registers(_MODE_REGISTER, bytes([mode]))
else: else:
return(self.read_registers(_MODE_REGISTER, 1)[0]) return self.read_registers(_MODE_REGISTER, 1)[0]
def system_trigger(self, data): def system_trigger(self, data):
self.write_registers(0x3f, bytes([data])) self.write_registers(0x3F, bytes([data]))
def power_mode(self, mode=None): def power_mode(self, mode=None):
if mode: if mode:
self.write_registers(_POWER_REGISTER, bytes([mode])) self.write_registers(_POWER_REGISTER, bytes([mode]))
else: else:
return(self.read_registers(_POWER_REGISTER, 1)) return self.read_registers(_POWER_REGISTER, 1)
def page(self, num=None): def page(self, num=None):
if num: if num:
self.write_registers(0x3f, bytes([num])) self.write_registers(0x3F, bytes([num]))
else: else:
self.read_registers(0x3f) self.read_registers(0x3F)
def temperature(self): def temperature(self):
return(self.read_registers(0x34, 1)[0]) return self.read_registers(0x34, 1)[0]
def read_id(self): def read_id(self):
return(self.read_registers(0x00, 4)) return self.read_registers(0x00, 4)
def axis(self, placement=None): def axis(self, placement=None):
if placement: if placement:
self.write_registers(_AXIS_MAP_CONFIG, placement) self.write_registers(_AXIS_MAP_CONFIG, placement)
else: else:
return(self.read_registers(_AXIS_MAP_CONFIG, 2)) return self.read_registers(_AXIS_MAP_CONFIG, 2)
def quaternion(self): def quaternion(self):
data = struct.unpack("<hhhh", self.read_registers(0x20, 8)) data = struct.unpack("<hhhh", self.read_registers(0x20, 8))
return [d/(1<<14) for d in data] #[w, x, y, z] return [d / (1 << 14) for d in data] # [w, x, y, z]
def euler(self): def euler(self):
data = struct.unpack("<hhh", self.read_registers(0x1A, 6)) data = struct.unpack("<hhh", self.read_registers(0x1A, 6))
return [d/16 for d in data] # [yaw, roll, pitch] return [d / 16 for d in data] # [yaw, roll, pitch]
def accelerometer(self): def accelerometer(self):
data = struct.unpack("<hhh", self.read_registers(0x08, 6)) data = struct.unpack("<hhh", self.read_registers(0x08, 6))
return [d/100 for d in data] #[x, y, z] return [d / 100 for d in data] # [x, y, z]
def magnetometer(self): def magnetometer(self):
data = struct.unpack("<hhh", self.read_registers(0x0E, 6)) data = struct.unpack("<hhh", self.read_registers(0x0E, 6))
return [d/16 for d in data] # [x, y, z] return [d / 16 for d in data] # [x, y, z]
def gyroscope(self): def gyroscope(self):
data = struct.unpack("<hhh", self.read_registers(0x14, 6)) data = struct.unpack("<hhh", self.read_registers(0x14, 6))
return [d/900 for d in data] #[x, y, z] return [d / 900 for d in data] # [x, y, z]
def linear_acceleration(self): def linear_acceleration(self):
data = struct.unpack("<hhh", self.read_registers(0x28, 6)) data = struct.unpack("<hhh", self.read_registers(0x28, 6))
return [d/100 for d in data] #[x, y, z] return [d / 100 for d in data] # [x, y, z]
def gravity(self): def gravity(self):
data = struct.unpack("<hhh", self.read_registers(0x2e, 6)) data = struct.unpack("<hhh", self.read_registers(0x2E, 6))
return [d/100 for d in data] #[x, y, z] return [d / 100 for d in data] # [x, y, z]

View File

@ -4,13 +4,14 @@
# Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io> # Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
# #
# This work is licensed under the MIT license, see the file LICENSE for details. # This work is licensed under the MIT license, see the file LICENSE for details.
# #
# HTS221 driver based on public domain driver. # HTS221 driver based on public domain driver.
import time import time
import struct import struct
class HTS221():
class HTS221:
def __init__(self, i2c, data_rate=1, dev_addr=0x5F): def __init__(self, i2c, data_rate=1, dev_addr=0x5F):
self.bus = i2c self.bus = i2c
self.odr = data_rate self.odr = data_rate
@ -18,7 +19,7 @@ class HTS221():
# Set configuration register # Set configuration register
# Humidity and temperature average configuration # Humidity and temperature average configuration
self.bus.writeto_mem(self.slv_addr, 0x10, b'\x1B') self.bus.writeto_mem(self.slv_addr, 0x10, b"\x1B")
# Set control register # Set control register
# PD | BDU | ODR # PD | BDU | ODR
@ -38,13 +39,13 @@ class HTS221():
# Temperature Calibration values # Temperature Calibration values
raw = self.read_reg(0x35, 1) raw = self.read_reg(0x35, 1)
self.T0 = ((raw & 0x03) * 256) + self.read_reg(0x32, 1) self.T0 = ((raw & 0x03) * 256) + self.read_reg(0x32, 1)
self.T1 = ((raw & 0x0C) * 64) + self.read_reg(0x33, 1) self.T1 = ((raw & 0x0C) * 64) + self.read_reg(0x33, 1)
self.T2 = self.read_reg(0x3C, 2) self.T2 = self.read_reg(0x3C, 2)
self.T3 = self.read_reg(0x3E, 2) self.T3 = self.read_reg(0x3E, 2)
def read_reg(self, reg_addr, size): def read_reg(self, reg_addr, size):
fmt = 'B' if size == 1 else 'H' fmt = "B" if size == 1 else "H"
reg_addr = reg_addr if size == 1 else reg_addr|0x80 reg_addr = reg_addr if size == 1 else reg_addr | 0x80
return struct.unpack(fmt, self.bus.readfrom_mem(self.slv_addr, reg_addr, size))[0] return struct.unpack(fmt, self.bus.readfrom_mem(self.slv_addr, reg_addr, size))[0]
def humidity(self): def humidity(self):
@ -55,4 +56,6 @@ class HTS221():
temp = self.read_reg(0x2A, 2) temp = self.read_reg(0x2A, 2)
if temp > 32767: if temp > 32767:
temp -= 65536 temp -= 65536
return ((self.T1 - self.T0) / 8.0) * (temp - self.T2) / (self.T3 - self.T2) + (self.T0 / 8.0) return ((self.T1 - self.T0) / 8.0) * (temp - self.T2) / (self.T3 - self.T2) + (
self.T0 / 8.0
)

View File

@ -4,7 +4,7 @@
# Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io> # Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
# Copyright (c) 2021 Arduino SA # Copyright (c) 2021 Arduino SA
# #
# Authors: # Authors:
# Ibrahim Abdelkader <iabdalkader@openmv.io> # Ibrahim Abdelkader <iabdalkader@openmv.io>
# Sebastian Romero <s.romero@arduino.cc> # Sebastian Romero <s.romero@arduino.cc>
# #
@ -15,46 +15,72 @@
from utime import sleep_ms, ticks_ms from utime import sleep_ms, ticks_ms
from pyb import UART, Pin from pyb import UART, Pin
MODE_ABP = 0 MODE_ABP = 0
MODE_OTAA = 1 MODE_OTAA = 1
RF_MODE_RFO = 0 RF_MODE_RFO = 0
RF_MODE_PABOOST = 1 RF_MODE_PABOOST = 1
BAND_AS923 = 0 BAND_AS923 = 0
BAND_AU915 = 1 BAND_AU915 = 1
BAND_EU868 = 5 BAND_EU868 = 5
BAND_KR920 = 6 BAND_KR920 = 6
BAND_IN865 = 7 BAND_IN865 = 7
BAND_US915 = 8 BAND_US915 = 8
BAND_US915_HYBRID = 9 BAND_US915_HYBRID = 9
CLASS_A = 'A' CLASS_A = "A"
CLASS_B = 'B' CLASS_B = "B"
CLASS_C = 'C' CLASS_C = "C"
class LoraError(Exception): pass
class LoraErrorTimeout(LoraError): pass
class LoraErrorParam(LoraError): pass
class LoraErrorBusy(LoraError): pass
class LoraErrorOverflow(LoraError): pass
class LoraErrorNoNetwork(LoraError): pass
class LoraErrorRX(LoraError): pass
class LoraErrorUnknown(LoraError): pass
class Lora(): class LoraError(Exception):
pass
class LoraErrorTimeout(LoraError):
pass
class LoraErrorParam(LoraError):
pass
class LoraErrorBusy(LoraError):
pass
class LoraErrorOverflow(LoraError):
pass
class LoraErrorNoNetwork(LoraError):
pass
class LoraErrorRX(LoraError):
pass
class LoraErrorUnknown(LoraError):
pass
class Lora:
LoraErrors = { LoraErrors = {
"": LoraErrorTimeout, # empty buffer "": LoraErrorTimeout, # empty buffer
"+ERR": LoraError, "+ERR": LoraError,
"+ERR_PARAM": LoraErrorParam, "+ERR_PARAM": LoraErrorParam,
"+ERR_BUSY": LoraErrorBusy, "+ERR_BUSY": LoraErrorBusy,
"+ERR_PARAM_OVERFLOW": LoraErrorOverflow, "+ERR_PARAM_OVERFLOW": LoraErrorOverflow,
"+ERR_NO_NETWORK": LoraErrorNoNetwork, "+ERR_NO_NETWORK": LoraErrorNoNetwork,
"+ERR_RX": LoraErrorRX, "+ERR_RX": LoraErrorRX,
"+ERR_UNKNOWN": LoraErrorUnknown "+ERR_UNKNOWN": LoraErrorUnknown,
} }
def __init__(self, uart=None, rst_pin=None, boot_pin=None, band=BAND_EU868, poll_ms=300000, debug=False): def __init__(
self, uart=None, rst_pin=None, boot_pin=None, band=BAND_EU868, poll_ms=300000, debug=False
):
self.debug = debug self.debug = debug
self.uart = uart self.uart = uart
self.rst_pin = rst_pin self.rst_pin = rst_pin
@ -75,44 +101,43 @@ class Lora():
# Restart module # Restart module
self.restart() self.restart()
def init_modem(self): def init_modem(self):
# Portenta vision shield configuration # Portenta vision shield configuration
if not self.rst_pin: if not self.rst_pin:
self.rst_pin = Pin('PC6', Pin.OUT_PP, Pin.PULL_UP, value=1) self.rst_pin = Pin("PC6", Pin.OUT_PP, Pin.PULL_UP, value=1)
if not self.boot_pin: if not self.boot_pin:
self.boot_pin = Pin('PG7', Pin.OUT_PP, Pin.PULL_DOWN, value=0) self.boot_pin = Pin("PG7", Pin.OUT_PP, Pin.PULL_DOWN, value=0)
if not self.uart: if not self.uart:
self.uart = UART(8, 19200) self.uart = UART(8, 19200)
#self.uart = UART(1, 19200) # Use external module # self.uart = UART(1, 19200) # Use external module
self.uart.init(19200, bits=8, parity=None, stop=2, timeout=250, timeout_char=100) self.uart.init(19200, bits=8, parity=None, stop=2, timeout=250, timeout_char=100)
def debug_print(self, data): def debug_print(self, data):
if self.debug: if self.debug:
print(data) print(data)
def is_arduino_firmware(self): def is_arduino_firmware(self):
return 'ARD-078' in self.fw_version return "ARD-078" in self.fw_version
def configure_class(self, _class): def configure_class(self, _class):
self.send_command("+CLASS=", _class) self.send_command("+CLASS=", _class)
def configure_band(self, band): def configure_band(self, band):
self.send_command("+BAND=", band) self.send_command("+BAND=", band)
if (band == BAND_EU868 and self.is_arduino_firmware()): if band == BAND_EU868 and self.is_arduino_firmware():
self.send_command("+DUTYCYCLE=", 1) self.send_command("+DUTYCYCLE=", 1)
return True return True
def set_baudrate(self, baudrate): def set_baudrate(self, baudrate):
self.send_command("+UART=", baudrate) self.send_command("+UART=", baudrate)
def set_autobaud(self, timeout=10000): def set_autobaud(self, timeout=10000):
start = ticks_ms() start = ticks_ms()
while ((ticks_ms() - start) < timeout): while (ticks_ms() - start) < timeout:
if (self.send_command('', timeout=200, raise_error=False) == '+OK'): if self.send_command("", timeout=200, raise_error=False) == "+OK":
sleep_ms(200) sleep_ms(200)
while (self.uart.any()): while self.uart.any():
self.uart.readchar() self.uart.readchar()
return True return True
return False return False
@ -123,18 +148,18 @@ class Lora():
return dev + " " + fw_ver return dev + " " + fw_ver
def get_device_eui(self): def get_device_eui(self):
return self.send_command("+DEVEUI?") return self.send_command("+DEVEUI?")
def factory_default(self): def factory_default(self):
self.send_command("+FACNEW") self.send_command("+FACNEW")
def restart(self): def restart(self):
if (self.set_autobaud() == False): if self.set_autobaud() is False:
raise(LoraError("Failed to set autobaud")) raise (LoraError("Failed to set autobaud"))
# Different delimiter as REBOOT response EVENT doesn't end with '\r'. # Different delimiter as REBOOT response EVENT doesn't end with '\r'.
if (self.send_command("+REBOOT", delimiter="+EVENT=0,0", timeout=10000) != "+EVENT=0,0"): if self.send_command("+REBOOT", delimiter="+EVENT=0,0", timeout=10000) != "+EVENT=0,0":
raise(LoraError("Failed to reboot module")) raise (LoraError("Failed to reboot module"))
sleep_ms(1000) sleep_ms(1000)
self.fw_version = self.get_fw_version() self.fw_version = self.get_fw_version()
self.configure_band(self.band) self.configure_band(self.band)
@ -205,72 +230,74 @@ class Lora():
def join(self, timeout_ms): def join(self, timeout_ms):
if self.send_command("+JOIN", timeout=timeout_ms) != "+ACK": if self.send_command("+JOIN", timeout=timeout_ms) != "+ACK":
return False return False
response = self.receive('\r', timeout=timeout_ms) response = self.receive("\r", timeout=timeout_ms)
return response == "+EVENT=1,1" return response == "+EVENT=1,1"
def get_join_status(self): def get_join_status(self):
return int(self.send_command("+NJS?")) == 1 return int(self.send_command("+NJS?")) == 1
def get_max_size(self): def get_max_size(self):
if (self.is_arduino_firmware()): if self.is_arduino_firmware():
return 64 return 64
return int(self.send_command("+MSIZE?", timeout=2000)) return int(self.send_command("+MSIZE?", timeout=2000))
def poll(self): def poll(self):
if ((ticks_ms() - self.last_poll_ms) > self.poll_ms): if (ticks_ms() - self.last_poll_ms) > self.poll_ms:
self.last_poll_ms = ticks_ms() self.last_poll_ms = ticks_ms()
# Triggers a fake write # Triggers a fake write
self.send_data('\0', True) self.send_data("\0", True)
def send_data(self, buff, confirmed=True): def send_data(self, buff, confirmed=True):
max_len = self.get_max_size() max_len = self.get_max_size()
if (len(buff) > max_len): if len(buff) > max_len:
raise(LoraError("Packet exceeds max length")) raise (LoraError("Packet exceeds max length"))
if self.send_command("+CTX " if confirmed else "+UTX ", len(buff), data=buff) != "+OK": if self.send_command("+CTX " if confirmed else "+UTX ", len(buff), data=buff) != "+OK":
return False return False
if confirmed: if confirmed:
response = self.receive('\r', timeout=10000) response = self.receive("\r", timeout=10000)
return response == "+ACK" return response == "+ACK"
return True return True
def receive_data(self, timeout=1000): def receive_data(self, timeout=1000):
response = self.receive('\r', timeout=timeout) response = self.receive("\r", timeout=timeout)
if response.startswith("+RECV"): if response.startswith("+RECV"):
params = response.split("=")[1].split(",") params = response.split("=")[1].split(",")
port = params[0] port = params[0]
length = int(params[1]) length = int(params[1])
dummy_data_length = 2 # Data starts with \n\n sequence dummy_data_length = 2 # Data starts with \n\n sequence
data = self.receive(max_bytes=length+dummy_data_length, timeout=timeout)[dummy_data_length:] data = self.receive(max_bytes=length + dummy_data_length, timeout=timeout)[
return {'port' : port, 'data' : data} dummy_data_length:
]
return {"port": port, "data": data}
def receive(self, delimiter=None, max_bytes=None, timeout=1000): def receive(self, delimiter=None, max_bytes=None, timeout=1000):
buf = [] buf = []
start = ticks_ms() start = ticks_ms()
while ((ticks_ms() - start) < timeout): while (ticks_ms() - start) < timeout:
while (self.uart.any()): while self.uart.any():
buf += chr(self.uart.readchar()) buf += chr(self.uart.readchar())
if max_bytes and len(buf) == max_bytes: if max_bytes and len(buf) == max_bytes:
data = ''.join(buf) data = "".join(buf)
self.debug_print(data) self.debug_print(data)
return data return data
if (len(buf) and delimiter != None): if len(buf) and delimiter is not None:
data = ''.join(buf) data = "".join(buf)
trimmed = data[0:-1] if data[-1] == '\r' else data trimmed = data[0:-1] if data[-1] == "\r" else data
if (isinstance(delimiter, str) and len(delimiter) == 1 and buf[-1] == delimiter): if isinstance(delimiter, str) and len(delimiter) == 1 and buf[-1] == delimiter:
self.debug_print(trimmed) self.debug_print(trimmed)
return trimmed return trimmed
if (isinstance(delimiter, str) and trimmed == delimiter): if isinstance(delimiter, str) and trimmed == delimiter:
self.debug_print(trimmed) self.debug_print(trimmed)
return trimmed return trimmed
if (isinstance(delimiter, list) and trimmed in delimiter): if isinstance(delimiter, list) and trimmed in delimiter:
self.debug_print(trimmed) self.debug_print(trimmed)
return trimmed return trimmed
data = ''.join(buf) data = "".join(buf)
self.debug_print(data) self.debug_print(data)
return data[0:-1] if len(data) != 0 and data[-1] == '\r' else data return data[0:-1] if len(data) != 0 and data[-1] == "\r" else data
def available(self): def available(self):
return self.uart.any() return self.uart.any()
@ -279,17 +306,17 @@ class Lora():
self.change_mode(MODE_OTAA) self.change_mode(MODE_OTAA)
self.send_command("+APPEUI=", appEui) self.send_command("+APPEUI=", appEui)
self.send_command("+APPKEY=", appKey) self.send_command("+APPKEY=", appKey)
if (devEui): if devEui:
self.send_command("+DEVEUI=", devEui) self.send_command("+DEVEUI=", devEui)
network_joined = self.join(timeout) network_joined = self.join(timeout)
# This delay was in MKRWAN.h # This delay was in MKRWAN.h
#delay(1000); # delay(1000);
return network_joined return network_joined
def join_ABP(self, nwkId, devAddr, nwkSKey, appSKey, timeout=60000): def join_ABP(self, nwkId, devAddr, nwkSKey, appSKey, timeout=60000):
self.change_mode(MODE_ABP) self.change_mode(MODE_ABP)
# Commented in MKRWAN.h # Commented in MKRWAN.h
#self.send_command("+IDNWK=", nwkId) # self.send_command("+IDNWK=", nwkId)
self.send_command("+DEVADDR=", devAddr) self.send_command("+DEVADDR=", devAddr)
self.send_command("+NWKSKEY=", nwkSKey) self.send_command("+NWKSKEY=", nwkSKey)
self.send_command("+APPSKEY=", appSKey) self.send_command("+APPSKEY=", appSKey)
@ -299,13 +326,13 @@ class Lora():
def handle_error(self, command, data): def handle_error(self, command, data):
if not data.startswith("+ERR") and data != "": if not data.startswith("+ERR") and data != "":
return return
if (data in self.LoraErrors): if data in self.LoraErrors:
raise(self.LoraErrors[data]('Command "%s" has failed!'%command)) raise (self.LoraErrors[data]('Command "%s" has failed!' % command))
raise(LoraError('Command: "%s" failed with unknown status: "%s"'%(command, data))) raise (LoraError('Command: "%s" failed with unknown status: "%s"' % (command, data)))
def send_command(self, cmd, *args, delimiter='\r', data=None, timeout=1000, raise_error=True): def send_command(self, cmd, *args, delimiter="\r", data=None, timeout=1000, raise_error=True):
# Write command and args # Write command and args
uart_cmd = 'AT'+cmd+''.join([str(x) for x in args])+'\r' uart_cmd = "AT" + cmd + "".join([str(x) for x in args]) + "\r"
self.debug_print(uart_cmd) self.debug_print(uart_cmd)
self.uart.write(uart_cmd) self.uart.write(uart_cmd)
@ -321,6 +348,6 @@ class Lora():
if raise_error: if raise_error:
self.handle_error(cmd, response) self.handle_error(cmd, response)
if cmd.endswith('?'): if cmd.endswith("?"):
return response.split('=')[1] return response.split("=")[1]
return response return response

View File

@ -5,15 +5,16 @@
# v1.0 2016.4 # v1.0 2016.4
# v2.0 2019.7 # v2.0 2019.7
class LPS22H():
LPS22_CTRL_REG1 = const(0x10)
LPS22_CTRL_REG2 = const(0x11)
LPS22_STATUS = const(0x27)
LPS22_TEMP_OUT_L = const(0x2B)
LPS22_PRESS_OUT_XL = const(0x28)
LPS22_PRESS_OUT_L = const(0x29)
def __init__(self, i2c, addr = 0x5C): class LPS22H:
LPS22_CTRL_REG1 = const(0x10)
LPS22_CTRL_REG2 = const(0x11)
LPS22_STATUS = const(0x27)
LPS22_TEMP_OUT_L = const(0x2B)
LPS22_PRESS_OUT_XL = const(0x28)
LPS22_PRESS_OUT_L = const(0x29)
def __init__(self, i2c, addr=0x5C):
self.i2c = i2c self.i2c = i2c
self.addr = addr self.addr = addr
self.tb = bytearray(1) self.tb = bytearray(1)
@ -30,8 +31,10 @@ class LPS22H():
else: else:
self.getreg(LPS22_CTRL_REG1) self.getreg(LPS22_CTRL_REG1)
self.oneshot = oneshot self.oneshot = oneshot
if oneshot: self.rb[0] &= 0x0F if oneshot:
else: self.rb[0] |= 0x10 self.rb[0] &= 0x0F
else:
self.rb[0] |= 0x10
self.setreg(LPS22_CTRL_REG1, self.rb[0]) self.setreg(LPS22_CTRL_REG1, self.rb[0])
def int16(self, d): def int16(self, d):
@ -46,12 +49,12 @@ class LPS22H():
return self.rb[0] return self.rb[0]
def get2reg(self, reg): def get2reg(self, reg):
return self.getreg(reg) + self.getreg(reg+1) * 256 return self.getreg(reg) + self.getreg(reg + 1) * 256
def ONE_SHOT(self, b): def ONE_SHOT(self, b):
if self.oneshot: if self.oneshot:
self.setreg(LPS22_CTRL_REG2, self.getreg(LPS22_CTRL_REG2) | 0x01) self.setreg(LPS22_CTRL_REG2, self.getreg(LPS22_CTRL_REG2) | 0x01)
self.getreg(0x28 + b*2) self.getreg(0x28 + b * 2)
while 1: while 1:
if self.getreg(LPS22_STATUS) & b: if self.getreg(LPS22_STATUS) & b:
return return
@ -59,23 +62,27 @@ class LPS22H():
def temperature(self): def temperature(self):
self.ONE_SHOT(2) self.ONE_SHOT(2)
try: try:
return self.int16(self.get2reg(LPS22_TEMP_OUT_L))/100 return self.int16(self.get2reg(LPS22_TEMP_OUT_L)) / 100
except MemoryError: except MemoryError:
return self.temperature_irq() return self.temperature_irq()
def pressure(self): def pressure(self):
self.ONE_SHOT(1) self.ONE_SHOT(1)
try: try:
return (self.getreg(LPS22_PRESS_OUT_XL) + self.get2reg(LPS22_PRESS_OUT_L) * 256)/4096 return (self.getreg(LPS22_PRESS_OUT_XL) + self.get2reg(LPS22_PRESS_OUT_L) * 256) / 4096
except MemoryError: except MemoryError:
return self.pressure_irq() return self.pressure_irq()
def altitude(self): def altitude(self):
return (((1013.25 / self.pressure())**(1/5.257)) - 1.0) * (self.temperature() + 273.15) / 0.0065 return (
(((1013.25 / self.pressure()) ** (1 / 5.257)) - 1.0)
* (self.temperature() + 273.15)
/ 0.0065
)
def temperature_irq(self): def temperature_irq(self):
self.ONE_SHOT(2) self.ONE_SHOT(2)
return self.int16(self.get2reg(LPS22_TEMP_OUT_L))//100 return self.int16(self.get2reg(LPS22_TEMP_OUT_L)) // 100
def pressure_irq(self): def pressure_irq(self):
self.ONE_SHOT(1) self.ONE_SHOT(1)

View File

@ -143,7 +143,7 @@ class LSM6DSOX:
self.reset() self.reset()
# Load and configure MLC if UCF file is provided # Load and configure MLC if UCF file is provided
if ucf != None: if ucf is not None:
self.load_mlc(ucf) self.load_mlc(ucf)
# Set Gyroscope datarate and scale. # Set Gyroscope datarate and scale.

View File

@ -45,39 +45,41 @@ while (True):
""" """
import array import array
class LSM9DS1: class LSM9DS1:
WHO_AM_I = const(0xf) WHO_AM_I = const(0xF)
CTRL_REG1_G = const(0x10) CTRL_REG1_G = const(0x10)
INT_GEN_SRC_G = const(0x14) INT_GEN_SRC_G = const(0x14)
OUT_TEMP = const(0x15) OUT_TEMP = const(0x15)
OUT_G = const(0x18) OUT_G = const(0x18)
CTRL_REG4_G = const(0x1e) CTRL_REG4_G = const(0x1E)
STATUS_REG = const(0x27) STATUS_REG = const(0x27)
OUT_XL = const(0x28) OUT_XL = const(0x28)
FIFO_CTRL_REG = const(0x2e) FIFO_CTRL_REG = const(0x2E)
FIFO_SRC = const(0x2f) FIFO_SRC = const(0x2F)
OFFSET_REG_X_M = const(0x05) OFFSET_REG_X_M = const(0x05)
CTRL_REG1_M = const(0x20) CTRL_REG1_M = const(0x20)
OUT_M = const(0x28) OUT_M = const(0x28)
SCALE_GYRO = [(245,0),(500,1),(2000,3)] SCALE_GYRO = [(245, 0), (500, 1), (2000, 3)]
SCALE_ACCEL = [(2,0),(4,2),(8,3),(16,1)] SCALE_ACCEL = [(2, 0), (4, 2), (8, 3), (16, 1)]
def __init__(self, i2c, address_gyro=0x6B, address_magnet=0x1E): def __init__(self, i2c, address_gyro=0x6B, address_magnet=0x1E):
self.i2c = i2c self.i2c = i2c
self.address_gyro = address_gyro self.address_gyro = address_gyro
self.address_magnet = address_magnet self.address_magnet = address_magnet
# check id's of accelerometer/gyro and magnetometer # check id's of accelerometer/gyro and magnetometer
if (self.read_id_magnet() != b'=') or (self.read_id_gyro() != b'h'): if (self.read_id_magnet() != b"=") or (self.read_id_gyro() != b"h"):
raise OSError("Invalid LSM9DS1 device, using address {}/{}".format( raise OSError(
address_gyro,address_magnet)) "Invalid LSM9DS1 device, using address {}/{}".format(address_gyro, address_magnet)
)
# allocate scratch buffer for efficient conversions and memread op's # allocate scratch buffer for efficient conversions and memread op's
self.scratch = array.array('B',[0,0,0,0,0,0]) self.scratch = array.array("B", [0, 0, 0, 0, 0, 0])
self.scratch_int = array.array('h',[0,0,0]) self.scratch_int = array.array("h", [0, 0, 0])
self.init_gyro_accel() self.init_gyro_accel()
self.init_magnetometer() self.init_magnetometer()
def init_gyro_accel(self, sample_rate=6, scale_gyro=0, scale_accel=0): def init_gyro_accel(self, sample_rate=6, scale_gyro=0, scale_accel=0):
""" Initalizes Gyro and Accelerator. """Initalizes Gyro and Accelerator.
sample rate: 0-6 (off, 14.9Hz, 59.5Hz, 119Hz, 238Hz, 476Hz, 952Hz) sample rate: 0-6 (off, 14.9Hz, 59.5Hz, 119Hz, 238Hz, 476Hz, 952Hz)
scale_gyro: 0-2 (245dps, 500dps, 2000dps ) scale_gyro: 0-2 (245dps, 500dps, 2000dps )
scale_accel: 0-3 (+/-2g, +/-4g, +/-8g, +-16g) scale_accel: 0-3 (+/-2g, +/-4g, +/-8g, +-16g)
@ -91,7 +93,7 @@ class LSM9DS1:
mv = memoryview(self.scratch) mv = memoryview(self.scratch)
# angular control registers 1-3 / Orientation # angular control registers 1-3 / Orientation
mv[0] = ((sample_rate & 0x07) << 5) | ((self.SCALE_GYRO[scale_gyro][1] & 0x3) << 3) mv[0] = ((sample_rate & 0x07) << 5) | ((self.SCALE_GYRO[scale_gyro][1] & 0x3) << 3)
mv[1:4] = b'\x00\x00\x00' mv[1:4] = b"\x00\x00\x00"
i2c.writeto_mem(addr, CTRL_REG1_G, mv[:5]) i2c.writeto_mem(addr, CTRL_REG1_G, mv[:5])
# ctrl4 - enable x,y,z, outputs, no irq latching, no 4D # ctrl4 - enable x,y,z, outputs, no irq latching, no 4D
# ctrl5 - enable all axes, no decimation # ctrl5 - enable all axes, no decimation
@ -106,8 +108,8 @@ class LSM9DS1:
i2c.writeto_mem(addr, CTRL_REG4_G, mv[:6]) i2c.writeto_mem(addr, CTRL_REG4_G, mv[:6])
# fifo: use continous mode (overwrite old data if overflow) # fifo: use continous mode (overwrite old data if overflow)
i2c.writeto_mem(addr, FIFO_CTRL_REG, b'\x00') i2c.writeto_mem(addr, FIFO_CTRL_REG, b"\x00")
i2c.writeto_mem(addr, FIFO_CTRL_REG, b'\xc0') i2c.writeto_mem(addr, FIFO_CTRL_REG, b"\xc0")
self.scale_gyro = 32768 / self.SCALE_GYRO[scale_gyro][0] self.scale_gyro = 32768 / self.SCALE_GYRO[scale_gyro][0]
self.scale_accel = 32768 / self.SCALE_ACCEL[scale_accel][0] self.scale_accel = 32768 / self.SCALE_ACCEL[scale_accel][0]
@ -122,26 +124,26 @@ class LSM9DS1:
i2c = self.i2c i2c = self.i2c
addr = self.address_magnet addr = self.address_magnet
mv = memoryview(self.scratch) mv = memoryview(self.scratch)
mv[0] = 0x40 | (sample_rate << 2) # ctrl1: high performance mode mv[0] = 0x40 | (sample_rate << 2) # ctrl1: high performance mode
mv[1] = scale_magnet << 5 # ctrl2: scale, normal mode, no reset mv[1] = scale_magnet << 5 # ctrl2: scale, normal mode, no reset
mv[2] = 0x00 # ctrl3: continous conversion, no low power, I2C mv[2] = 0x00 # ctrl3: continous conversion, no low power, I2C
mv[3] = 0x08 # ctrl4: high performance z-axis mv[3] = 0x08 # ctrl4: high performance z-axis
mv[4] = 0x00 # ctr5: no fast read, no block update mv[4] = 0x00 # ctr5: no fast read, no block update
i2c.writeto_mem(addr, CTRL_REG1_M, mv[:5]) i2c.writeto_mem(addr, CTRL_REG1_M, mv[:5])
self.scale_factor_magnet = 32768 / ((scale_magnet+1) * 4 ) self.scale_factor_magnet = 32768 / ((scale_magnet + 1) * 4)
def calibrate_magnet(self, offset): def calibrate_magnet(self, offset):
""" """
offset is a magnet vecor that will be substracted by the magnetometer offset is a magnet vecor that will be substracted by the magnetometer
for each measurement. It is written to the magnetometer's offset register for each measurement. It is written to the magnetometer's offset register
""" """
offset = [int(i*self.scale_factor_magnet) for i in offset] offset = [int(i * self.scale_factor_magnet) for i in offset]
mv = memoryview(self.scratch) mv = memoryview(self.scratch)
mv[0] = offset[0] & 0xff mv[0] = offset[0] & 0xFF
mv[1] = offset[0] >> 8 mv[1] = offset[0] >> 8
mv[2] = offset[1] & 0xff mv[2] = offset[1] & 0xFF
mv[3] = offset[1] >> 8 mv[3] = offset[1] >> 8
mv[4] = offset[2] & 0xff mv[4] = offset[2] & 0xFF
mv[5] = offset[2] >> 8 mv[5] = offset[2] >> 8
self.i2c.writeto_mem(self.address_magnet, OFFSET_REG_X_M, mv[:6]) self.i2c.writeto_mem(self.address_magnet, OFFSET_REG_X_M, mv[:6])
@ -158,28 +160,30 @@ class LSM9DS1:
mv = memoryview(self.scratch_int) mv = memoryview(self.scratch_int)
f = self.scale_factor_magnet f = self.scale_factor_magnet
self.i2c.readfrom_mem_into(self.address_magnet, OUT_M | 0x80, mv) self.i2c.readfrom_mem_into(self.address_magnet, OUT_M | 0x80, mv)
return (mv[0]/f, mv[1]/f, mv[2]/f) return (mv[0] / f, mv[1] / f, mv[2] / f)
def read_gyro(self): def read_gyro(self):
"""Returns gyroscope vector in degrees/sec.""" """Returns gyroscope vector in degrees/sec."""
mv = memoryview(self.scratch_int) mv = memoryview(self.scratch_int)
f = self.scale_gyro f = self.scale_gyro
self.i2c.readfrom_mem_into(self.address_gyro, OUT_G | 0x80, mv) self.i2c.readfrom_mem_into(self.address_gyro, OUT_G | 0x80, mv)
return (mv[0]/f, mv[1]/f, mv[2]/f) return (mv[0] / f, mv[1] / f, mv[2] / f)
def read_accel(self): def read_accel(self):
"""Returns acceleration vector in gravity units (9.81m/s^2).""" """Returns acceleration vector in gravity units (9.81m/s^2)."""
mv = memoryview(self.scratch_int) mv = memoryview(self.scratch_int)
f = self.scale_accel f = self.scale_accel
self.i2c.readfrom_mem_into(self.address_gyro, OUT_XL | 0x80, mv) self.i2c.readfrom_mem_into(self.address_gyro, OUT_XL | 0x80, mv)
return (mv[0]/f, mv[1]/f, mv[2]/f) return (mv[0] / f, mv[1] / f, mv[2] / f)
def iter_accel_gyro(self): def iter_accel_gyro(self):
"""A generator that returns tuples of (gyro,accelerometer) data from the fifo.""" """A generator that returns tuples of (gyro,accelerometer) data from the fifo."""
while True: while True:
fifo_state = int.from_bytes(self.i2c.readfrom_mem(self.address_gyro, FIFO_SRC, 1),'big') fifo_state = int.from_bytes(
if fifo_state & 0x3f: self.i2c.readfrom_mem(self.address_gyro, FIFO_SRC, 1), "big"
)
if fifo_state & 0x3F:
# print("Available samples=%d" % (fifo_state & 0x1f)) # print("Available samples=%d" % (fifo_state & 0x1f))
yield self.read_gyro(),self.read_accel() yield self.read_gyro(), self.read_accel()
else: else:
break break

View File

@ -1,51 +1,284 @@
import struct import struct
class ModbusRTU():
class ModbusRTU:
def __init__(self, uart, slave_id=0x01, register_num=30): def __init__(self, uart, slave_id=0x01, register_num=30):
self.SLAVE_ID = slave_id self.SLAVE_ID = slave_id
self.uart = uart self.uart = uart
self.register_num = register_num self.register_num = register_num
self.REGISTER = [0]*self.register_num self.REGISTER = [0] * self.register_num
self.CRC16_TABLE = [ self.CRC16_TABLE = [
0x0000,0xC0C1,0xC181,0x0140,0xC301,0x03C0,0x0280,0xC241,0xC601, 0x0000,
0x06C0,0x0780,0xC741,0x0500,0xC5C1,0xC481,0x0440,0xCC01,0x0CC0, 0xC0C1,
0x0D80,0xCD41,0x0F00,0xCFC1,0xCE81,0x0E40,0x0A00,0xCAC1,0xCB81, 0xC181,
0x0B40,0xC901,0x09C0,0x0880,0xC841,0xD801,0x18C0,0x1980,0xD941, 0x0140,
0x1B00,0xDBC1,0xDA81,0x1A40,0x1E00,0xDEC1,0xDF81,0x1F40,0xDD01, 0xC301,
0x1DC0,0x1C80,0xDC41,0x1400,0xD4C1,0xD581,0x1540,0xD701,0x17C0, 0x03C0,
0x1680,0xD641,0xD201,0x12C0,0x1380,0xD341,0x1100,0xD1C1,0xD081, 0x0280,
0x1040,0xF001,0x30C0,0x3180,0xF141,0x3300,0xF3C1,0xF281,0x3240, 0xC241,
0x3600,0xF6C1,0xF781,0x3740,0xF501,0x35C0,0x3480,0xF441,0x3C00, 0xC601,
0xFCC1,0xFD81,0x3D40,0xFF01,0x3FC0,0x3E80,0xFE41,0xFA01,0x3AC0, 0x06C0,
0x3B80,0xFB41,0x3900,0xF9C1,0xF881,0x3840,0x2800,0xE8C1,0xE981, 0x0780,
0x2940,0xEB01,0x2BC0,0x2A80,0xEA41,0xEE01,0x2EC0,0x2F80,0xEF41, 0xC741,
0x2D00,0xEDC1,0xEC81,0x2C40,0xE401,0x24C0,0x2580,0xE541,0x2700, 0x0500,
0xE7C1,0xE681,0x2640,0x2200,0xE2C1,0xE381,0x2340,0xE101,0x21C0, 0xC5C1,
0x2080,0xE041,0xA001,0x60C0,0x6180,0xA141,0x6300,0xA3C1,0xA281, 0xC481,
0x6240,0x6600,0xA6C1,0xA781,0x6740,0xA501,0x65C0,0x6480,0xA441, 0x0440,
0x6C00,0xACC1,0xAD81,0x6D40,0xAF01,0x6FC0,0x6E80,0xAE41,0xAA01, 0xCC01,
0x6AC0,0x6B80,0xAB41,0x6900,0xA9C1,0xA881,0x6840,0x7800,0xB8C1, 0x0CC0,
0xB981,0x7940,0xBB01,0x7BC0,0x7A80,0xBA41,0xBE01,0x7EC0,0x7F80, 0x0D80,
0xBF41,0x7D00,0xBDC1,0xBC81,0x7C40,0xB401,0x74C0,0x7580,0xB541, 0xCD41,
0x7700,0xB7C1,0xB681,0x7640,0x7200,0xB2C1,0xB381,0x7340,0xB101, 0x0F00,
0x71C0,0x7080,0xB041,0x5000,0x90C1,0x9181,0x5140,0x9301,0x53C0, 0xCFC1,
0x5280,0x9241,0x9601,0x56C0,0x5780,0x9741,0x5500,0x95C1,0x9481, 0xCE81,
0x5440,0x9C01,0x5CC0,0x5D80,0x9D41,0x5F00,0x9FC1,0x9E81,0x5E40, 0x0E40,
0x5A00,0x9AC1,0x9B81,0x5B40,0x9901,0x59C0,0x5880,0x9841,0x8801, 0x0A00,
0x48C0,0x4980,0x8941,0x4B00,0x8BC1,0x8A81,0x4A40,0x4E00,0x8EC1, 0xCAC1,
0x8F81,0x4F40,0x8D01,0x4DC0,0x4C80,0x8C41,0x4400,0x84C1,0x8581, 0xCB81,
0x4540,0x8701,0x47C0,0x4680,0x8641,0x8201,0x42C0,0x4380,0x8341, 0x0B40,
0x4100,0x81C1,0x8081,0x4040] 0xC901,
0x09C0,
0x0880,
0xC841,
0xD801,
0x18C0,
0x1980,
0xD941,
0x1B00,
0xDBC1,
0xDA81,
0x1A40,
0x1E00,
0xDEC1,
0xDF81,
0x1F40,
0xDD01,
0x1DC0,
0x1C80,
0xDC41,
0x1400,
0xD4C1,
0xD581,
0x1540,
0xD701,
0x17C0,
0x1680,
0xD641,
0xD201,
0x12C0,
0x1380,
0xD341,
0x1100,
0xD1C1,
0xD081,
0x1040,
0xF001,
0x30C0,
0x3180,
0xF141,
0x3300,
0xF3C1,
0xF281,
0x3240,
0x3600,
0xF6C1,
0xF781,
0x3740,
0xF501,
0x35C0,
0x3480,
0xF441,
0x3C00,
0xFCC1,
0xFD81,
0x3D40,
0xFF01,
0x3FC0,
0x3E80,
0xFE41,
0xFA01,
0x3AC0,
0x3B80,
0xFB41,
0x3900,
0xF9C1,
0xF881,
0x3840,
0x2800,
0xE8C1,
0xE981,
0x2940,
0xEB01,
0x2BC0,
0x2A80,
0xEA41,
0xEE01,
0x2EC0,
0x2F80,
0xEF41,
0x2D00,
0xEDC1,
0xEC81,
0x2C40,
0xE401,
0x24C0,
0x2580,
0xE541,
0x2700,
0xE7C1,
0xE681,
0x2640,
0x2200,
0xE2C1,
0xE381,
0x2340,
0xE101,
0x21C0,
0x2080,
0xE041,
0xA001,
0x60C0,
0x6180,
0xA141,
0x6300,
0xA3C1,
0xA281,
0x6240,
0x6600,
0xA6C1,
0xA781,
0x6740,
0xA501,
0x65C0,
0x6480,
0xA441,
0x6C00,
0xACC1,
0xAD81,
0x6D40,
0xAF01,
0x6FC0,
0x6E80,
0xAE41,
0xAA01,
0x6AC0,
0x6B80,
0xAB41,
0x6900,
0xA9C1,
0xA881,
0x6840,
0x7800,
0xB8C1,
0xB981,
0x7940,
0xBB01,
0x7BC0,
0x7A80,
0xBA41,
0xBE01,
0x7EC0,
0x7F80,
0xBF41,
0x7D00,
0xBDC1,
0xBC81,
0x7C40,
0xB401,
0x74C0,
0x7580,
0xB541,
0x7700,
0xB7C1,
0xB681,
0x7640,
0x7200,
0xB2C1,
0xB381,
0x7340,
0xB101,
0x71C0,
0x7080,
0xB041,
0x5000,
0x90C1,
0x9181,
0x5140,
0x9301,
0x53C0,
0x5280,
0x9241,
0x9601,
0x56C0,
0x5780,
0x9741,
0x5500,
0x95C1,
0x9481,
0x5440,
0x9C01,
0x5CC0,
0x5D80,
0x9D41,
0x5F00,
0x9FC1,
0x9E81,
0x5E40,
0x5A00,
0x9AC1,
0x9B81,
0x5B40,
0x9901,
0x59C0,
0x5880,
0x9841,
0x8801,
0x48C0,
0x4980,
0x8941,
0x4B00,
0x8BC1,
0x8A81,
0x4A40,
0x4E00,
0x8EC1,
0x8F81,
0x4F40,
0x8D01,
0x4DC0,
0x4C80,
0x8C41,
0x4400,
0x84C1,
0x8581,
0x4540,
0x8701,
0x47C0,
0x4680,
0x8641,
0x8201,
0x42C0,
0x4380,
0x8341,
0x4100,
0x81C1,
0x8081,
0x4040,
]
def any(self): def any(self):
return self.uart.any() return self.uart.any()
def clear(self): def clear(self):
self.REGISTER = [0]*self.register_num self.REGISTER = [0] * self.register_num
def crc16(self, data): def crc16(self, data):
crc = 0xFFFF crc = 0xFFFF
for char in data: for char in data:
crc = (crc >> 8) ^ self.CRC16_TABLE[((crc) ^ char) & 0xFF] crc = (crc >> 8) ^ self.CRC16_TABLE[((crc) ^ char) & 0xFF]
return struct.pack('<H',crc) return struct.pack("<H", crc)
def handle(self, debug = False):
def handle(self, debug=False):
REQUEST = self.uart.read() REQUEST = self.uart.read()
if debug: if debug:
print("GOT REQUEST: ", REQUEST) print("GOT REQUEST: ", REQUEST)
@ -53,79 +286,79 @@ class ModbusRTU():
error_check = REQUEST[-2:] error_check = REQUEST[-2:]
function_code = REQUEST[1] function_code = REQUEST[1]
data = REQUEST[2:] data = REQUEST[2:]
RESPONSE = struct.pack('b', self.SLAVE_ID) RESPONSE = struct.pack("b", self.SLAVE_ID)
if self.crc16(REQUEST[:-2]) != error_check: if self.crc16(REQUEST[:-2]) != error_check:
if debug: if debug:
print("crc not match") print("crc not match")
return 0 # do nothing return 0 # do nothing
if self.SLAVE_ID != additional_address: if self.SLAVE_ID != additional_address:
if debug: if debug:
print("OpenMV slave id: ", self.SLAVE_ID) print("OpenMV slave id: ", self.SLAVE_ID)
print("got cmd id: ", additional_address) print("got cmd id: ", additional_address)
return 0 # do nothing return 0 # do nothing
if function_code == 0x03: if function_code == 0x03:
starting_address = struct.unpack('>h', data[:2])[0] starting_address = struct.unpack(">h", data[:2])[0]
quantity_of_registers = struct.unpack('>h', data[2:4])[0] quantity_of_registers = struct.unpack(">h", data[2:4])[0]
response_registers = [] response_registers = []
try: try:
for i in range(starting_address, starting_address+quantity_of_registers): for i in range(starting_address, starting_address + quantity_of_registers):
response_registers.append(self.REGISTER[i]) response_registers.append(self.REGISTER[i])
except IndexError as err: except IndexError as err:
RESPONSE += struct.pack('b', function_code|0x80) RESPONSE += struct.pack("b", function_code | 0x80)
RESPONSE += struct.pack('b', 0x02) # Illegal Data Address RESPONSE += struct.pack("b", 0x02) # Illegal Data Address
if debug: if debug:
print("Illegal Data Address: ") print("Illegal Data Address: ")
print(err) print(err)
else: else:
RESPONSE += struct.pack('b', function_code) RESPONSE += struct.pack("b", function_code)
RESPONSE += struct.pack('b', 2*quantity_of_registers) RESPONSE += struct.pack("b", 2 * quantity_of_registers)
for b in response_registers: for b in response_registers:
RESPONSE += struct.pack('>h', b) RESPONSE += struct.pack(">h", b)
elif function_code == 0x06: elif function_code == 0x06:
register_address = struct.unpack('>h', data[:2])[0] register_address = struct.unpack(">h", data[:2])[0]
register_value = struct.unpack('>h', data[2:4])[0] register_value = struct.unpack(">h", data[2:4])[0]
try: try:
self.REGISTER[register_address] = register_value self.REGISTER[register_address] = register_value
except IndexError as err: except IndexError as err:
RESPONSE += struct.pack('b', function_code|0x80) RESPONSE += struct.pack("b", function_code | 0x80)
RESPONSE += struct.pack('b', 0x02) # Illegal Data Address RESPONSE += struct.pack("b", 0x02) # Illegal Data Address
if debug: if debug:
print("Illegal Data Address: ") print("Illegal Data Address: ")
print(err) print(err)
else: else:
RESPONSE += struct.pack('b', function_code) RESPONSE += struct.pack("b", function_code)
RESPONSE += struct.pack('>h', register_address) RESPONSE += struct.pack(">h", register_address)
RESPONSE += struct.pack('>h', self.REGISTER[register_address]) RESPONSE += struct.pack(">h", self.REGISTER[register_address])
elif function_code == 0x10: elif function_code == 0x10:
starting_address = struct.unpack('>h', data[:2])[0] starting_address = struct.unpack(">h", data[:2])[0]
quantity_of_registers = struct.unpack('>h', data[2:4])[0] quantity_of_registers = struct.unpack(">h", data[2:4])[0]
byte_of_registers = struct.unpack('b', data[4:5])[0] byte_of_registers = struct.unpack("b", data[4:5])[0]
try: try:
if byte_of_registers != 2*quantity_of_registers: if byte_of_registers != 2 * quantity_of_registers:
raise struct.error raise struct.error
values = struct.unpack('>%dh'%quantity_of_registers, data[5:]) values = struct.unpack(">%dh" % quantity_of_registers, data[5:])
except struct.error as err: except struct.error as err:
RESPONSE += struct.pack('b', function_code|0x80) RESPONSE += struct.pack("b", function_code | 0x80)
RESPONSE += struct.pack('b', 0x03) # Illegal Data Value RESPONSE += struct.pack("b", 0x03) # Illegal Data Value
if debug: if debug:
print("Illegal Data Value, data length error") print("Illegal Data Value, data length error")
else: else:
try: try:
for i in range(quantity_of_registers): for i in range(quantity_of_registers):
self.REGISTER[starting_address+i] = values[i] self.REGISTER[starting_address + i] = values[i]
except IndexError as err: except IndexError as err:
RESPONSE += struct.pack('b', function_code|0x80) RESPONSE += struct.pack("b", function_code | 0x80)
RESPONSE += struct.pack('b', 0x02) # Illegal Data Address RESPONSE += struct.pack("b", 0x02) # Illegal Data Address
if debug: if debug:
print("Illegal Data Address: ") print("Illegal Data Address: ")
print(err) print(err)
else: else:
RESPONSE += struct.pack('b', function_code) RESPONSE += struct.pack("b", function_code)
RESPONSE += struct.pack('>h', starting_address) RESPONSE += struct.pack(">h", starting_address)
RESPONSE += struct.pack('>h', quantity_of_registers) RESPONSE += struct.pack(">h", quantity_of_registers)
else: else:
RESPONSE += struct.pack('b', function_code|0x80) RESPONSE += struct.pack("b", function_code | 0x80)
RESPONSE += struct.pack('b', 0x01) # Illegal Function RESPONSE += struct.pack("b", 0x01) # Illegal Function
RESPONSE += self.crc16(RESPONSE) RESPONSE += self.crc16(RESPONSE)
if debug: if debug:
print("FUNCTION CODE: ", function_code) print("FUNCTION CODE: ", function_code)

View File

@ -24,15 +24,24 @@
import usocket as socket import usocket as socket
import ustruct as struct import ustruct as struct
from ubinascii import hexlify
class MQTTException(Exception): class MQTTException(Exception):
pass pass
class MQTTClient:
def __init__(self, client_id, server, port=0, user=None, password=None, keepalive=0, class MQTTClient:
ssl=False, ssl_params={}): def __init__(
self,
client_id,
server,
port=0,
user=None,
password=None,
keepalive=0,
ssl=False,
ssl_params={},
):
if port == 0: if port == 0:
port = 8883 if ssl else 1883 port = 8883 if ssl else 1883
self.client_id = client_id self.client_id = client_id
@ -60,7 +69,7 @@ class MQTTClient:
sh = 0 sh = 0
while 1: while 1:
b = self.sock.read(1)[0] b = self.sock.read(1)[0]
n |= (b & 0x7f) << sh n |= (b & 0x7F) << sh
if not b & 0x80: if not b & 0x80:
return n return n
sh += 7 sh += 7
@ -83,6 +92,7 @@ class MQTTClient:
self.sock.connect(addr) self.sock.connect(addr)
if self.ssl: if self.ssl:
import ussl import ussl
self.sock = ussl.wrap_socket(self.sock, **self.ssl_params) self.sock = ussl.wrap_socket(self.sock, **self.ssl_params)
premsg = bytearray(b"\x10\0\0\0\0\0") premsg = bytearray(b"\x10\0\0\0\0\0")
msg = bytearray(b"\x04MQTT\x04\x02\0\0") msg = bytearray(b"\x04MQTT\x04\x02\0\0")
@ -102,15 +112,15 @@ class MQTTClient:
msg[6] |= self.lw_retain << 5 msg[6] |= self.lw_retain << 5
i = 1 i = 1
while sz > 0x7f: while sz > 0x7F:
premsg[i] = (sz & 0x7f) | 0x80 premsg[i] = (sz & 0x7F) | 0x80
sz >>= 7 sz >>= 7
i += 1 i += 1
premsg[i] = sz premsg[i] = sz
self.sock.write(premsg[0:i + 2]) self.sock.write(premsg[0 : i + 2])
self.sock.write(msg) self.sock.write(msg)
#print(hex(len(msg)), hexlify(msg, ":")) # print(hex(len(msg)), hexlify(msg, ":"))
self._send_str(self.client_id) self._send_str(self.client_id)
if self.lw_topic: if self.lw_topic:
self._send_str(self.lw_topic) self._send_str(self.lw_topic)
@ -139,13 +149,13 @@ class MQTTClient:
sz += 2 sz += 2
assert sz < 2097152 assert sz < 2097152
i = 1 i = 1
while sz > 0x7f: while sz > 0x7F:
pkt[i] = (sz & 0x7f) | 0x80 pkt[i] = (sz & 0x7F) | 0x80
sz >>= 7 sz >>= 7
i += 1 i += 1
pkt[i] = sz pkt[i] = sz
#print(hex(len(pkt)), hexlify(pkt, ":")) # print(hex(len(pkt)), hexlify(pkt, ":"))
self.sock.write(pkt[0:i + 1]) self.sock.write(pkt[0 : i + 1])
self._send_str(topic) self._send_str(topic)
if qos > 0: if qos > 0:
self.pid += 1 self.pid += 1
@ -171,7 +181,7 @@ class MQTTClient:
pkt = bytearray(b"\x82\0\0\0") pkt = bytearray(b"\x82\0\0\0")
self.pid += 1 self.pid += 1
struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid) struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid)
#print(hex(len(pkt)), hexlify(pkt, ":")) # print(hex(len(pkt)), hexlify(pkt, ":"))
self.sock.write(pkt) self.sock.write(pkt)
self._send_str(topic) self._send_str(topic)
self.sock.write(qos.to_bytes(1, "little")) self.sock.write(qos.to_bytes(1, "little"))
@ -179,7 +189,7 @@ class MQTTClient:
op = self.wait_msg() op = self.wait_msg()
if op == 0x90: if op == 0x90:
resp = self.sock.read(4) resp = self.sock.read(4)
#print(resp) # print(resp)
assert resp[1] == pkt[2] and resp[2] == pkt[3] assert resp[1] == pkt[2] and resp[2] == pkt[3]
if resp[3] == 0x80: if resp[3] == 0x80:
raise MQTTException(resp[3]) raise MQTTException(resp[3])
@ -191,7 +201,7 @@ class MQTTClient:
# messages processed internally. # messages processed internally.
def wait_msg(self): def wait_msg(self):
res = self.sock.read(1) res = self.sock.read(1)
if res == b"" or res == None: if res == b"" or res is None:
return None return None
self.sock.setblocking(True) self.sock.setblocking(True)
if res == b"\xd0": # PINGRESP if res == b"\xd0": # PINGRESP
@ -199,7 +209,7 @@ class MQTTClient:
assert sz == 0 assert sz == 0
return None return None
op = res[0] op = res[0]
if op & 0xf0 != 0x30: if op & 0xF0 != 0x30:
return op return op
sz = self._recv_len() sz = self._recv_len()
topic_len = self.sock.read(2) topic_len = self.sock.read(2)

View File

@ -1,41 +1,46 @@
import pyb, micropython, array, uctypes import micropython
import array
import uctypes
micropython.alloc_emergency_exception_buf(100) micropython.alloc_emergency_exception_buf(100)
class MutexException(OSError): class MutexException(OSError):
pass pass
class Mutex: class Mutex:
@micropython.asm_thumb @micropython.asm_thumb
def _acquire(r0, r1): # Spinlock: wait on the semaphore. Return on success. def _acquire(r0, r1): # Spinlock: wait on the semaphore. Return on success.
label(LOOP) label(LOOP)
ldr(r0, [r1, 0]) # Wait for lock to be zero ldr(r0, [r1, 0]) # Wait for lock to be zero
cmp(r0, 0) cmp(r0, 0)
bne(LOOP) # Another process has the lock: spin on it bne(LOOP) # Another process has the lock: spin on it
cpsid(0) # OK, we have lock at this instant disable interrupts cpsid(0) # OK, we have lock at this instant disable interrupts
ldr(r0, [r1, 0]) # and re-check in case an interrupt occurred ldr(r0, [r1, 0]) # and re-check in case an interrupt occurred
cmp(r0, 0) cmp(r0, 0)
itt(ne) # if someone got in first re-enable ints itt(ne) # if someone got in first re-enable ints
cpsie(0) # and start polling again cpsie(0) # and start polling again
b(LOOP) b(LOOP)
mov(r0, 1) # We have an exclusive access mov(r0, 1) # We have an exclusive access
str(r0, [r1, 0]) # set the lock str(r0, [r1, 0]) # set the lock
cpsie(0) cpsie(0)
@micropython.asm_thumb @micropython.asm_thumb
def _attempt(r0, r1): # Nonblocking. Try to lock. Return 0 on success, 1 on fail def _attempt(r0, r1): # Nonblocking. Try to lock. Return 0 on success, 1 on fail
cpsid(0) # disable interrupts cpsid(0) # disable interrupts
ldr(r0, [r1, 0]) ldr(r0, [r1, 0])
cmp(r0, 0) cmp(r0, 0)
bne(FAIL) # Another process has the lock: fail bne(FAIL) # Another process has the lock: fail
mov(r2, 1) # No lock mov(r2, 1) # No lock
str(r2, [r1, 0]) # set the lock str(r2, [r1, 0]) # set the lock
label(FAIL) label(FAIL)
cpsie(0) # enable interrupts cpsie(0) # enable interrupts
def __init__(self): def __init__(self):
self.lock = array.array('i', (0,)) # 1 if a process has the lock else 0 self.lock = array.array("i", (0,)) # 1 if a process has the lock else 0
# POSIX API pthread_mutex_lock() blocks the thread till resource is available. # POSIX API pthread_mutex_lock() blocks the thread till resource is available.
def __enter__(self): def __enter__(self):
self._acquire(uctypes.addressof(self.lock)) self._acquire(uctypes.addressof(self.lock))
return self return self
@ -43,13 +48,12 @@ class Mutex:
def __exit__(self, *_): def __exit__(self, *_):
self.lock[0] = 0 self.lock[0] = 0
# POSIX pthread_mutex_unlock() # POSIX pthread_mutex_unlock()
def release(self): def release(self):
if self.lock[0] == 0: if self.lock[0] == 0:
raise MutexException('Semaphore already released') raise MutexException("Semaphore already released")
self.lock[0] = 0 self.lock[0] = 0
# POSIX pthread_mutex_trylock() API. When mutex is not available the function returns immediately # POSIX pthread_mutex_trylock() API. When mutex is not available the function returns immediately
def test(self): # Nonblocking: try to acquire, return True if success. def test(self): # Nonblocking: try to acquire, return True if success.
return self._attempt(uctypes.addressof(self.lock)) == 0 return self._attempt(uctypes.addressof(self.lock)) == 0

View File

@ -1,4 +1,4 @@
''' """
Example: Example:
from pid import PID from pid import PID
pid1 = PID(p=0.07, i=0, imax=90) pid1 = PID(p=0.07, i=0, imax=90)
@ -6,22 +6,24 @@ while(True):
error = 50 #error should be caculated, target - mesure error = 50 #error should be caculated, target - mesure
output=pid1.get_pid(error,1) output=pid1.get_pid(error,1)
#control value with output #control value with output
''' """
from pyb import millis from pyb import millis
from math import pi, isnan from math import pi, isnan
class PID: class PID:
_kp = _ki = _kd = _integrator = _imax = 0 _kp = _ki = _kd = _integrator = _imax = 0
_last_error = _last_derivative = _last_t = 0 _last_error = _last_derivative = _last_t = 0
_RC = 1/(2 * pi * 20) _RC = 1 / (2 * pi * 20)
def __init__(self, p=0, i=0, d=0, imax=0): def __init__(self, p=0, i=0, d=0, imax=0):
self._kp = float(p) self._kp = float(p)
self._ki = float(i) self._ki = float(i)
self._kd = float(d) self._kd = float(d)
self._imax = abs(imax) self._imax = abs(imax)
self._last_derivative = float('nan') self._last_derivative = float("nan")
def get_pid(self, error, scaler): def get_pid(self, error, scaler):
tnow = millis() tnow = millis()
dt = tnow - self._last_t dt = tnow - self._last_t
@ -38,19 +40,22 @@ class PID:
self._last_derivative = 0 self._last_derivative = 0
else: else:
derivative = (error - self._last_error) / delta_time derivative = (error - self._last_error) / delta_time
derivative = self._last_derivative + \ derivative = self._last_derivative + (
((delta_time / (self._RC + delta_time)) * \ (delta_time / (self._RC + delta_time)) * (derivative - self._last_derivative)
(derivative - self._last_derivative)) )
self._last_error = error self._last_error = error
self._last_derivative = derivative self._last_derivative = derivative
output += self._kd * derivative output += self._kd * derivative
output *= scaler output *= scaler
if abs(self._ki) > 0 and dt > 0: if abs(self._ki) > 0 and dt > 0:
self._integrator += (error * self._ki) * scaler * delta_time self._integrator += (error * self._ki) * scaler * delta_time
if self._integrator < -self._imax: self._integrator = -self._imax if self._integrator < -self._imax:
elif self._integrator > self._imax: self._integrator = self._imax self._integrator = -self._imax
elif self._integrator > self._imax:
self._integrator = self._imax
output += self._integrator output += self._integrator
return output return output
def reset_I(self): def reset_I(self):
self._integrator = 0 self._integrator = 0
self._last_derivative = float('nan') self._last_derivative = float("nan")

View File

@ -5,7 +5,13 @@
# #
# This work is licensed under the MIT license, see the file LICENSE for details. # This work is licensed under the MIT license, see the file LICENSE for details.
import gc, network, omv, pyb, select, socket, stm, struct import gc
import omv
import pyb
import select
import socket
import stm
import struct
class rpc: class rpc:

View File

@ -5,7 +5,10 @@
# #
# This work is licensed under the MIT license, see the file LICENSE for details. # This work is licensed under the MIT license, see the file LICENSE for details.
import network, pyb, re, socket, struct import pyb
import re
import socket
import struct
class rtsp_server: class rtsp_server:

View File

@ -3,23 +3,24 @@ import framebuf
from pyb import SPI from pyb import SPI
# register definitions # register definitions
SET_CONTRAST = const(0x81) SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xa4) SET_ENTIRE_ON = const(0xA4)
SET_NORM_INV = const(0xa6) SET_NORM_INV = const(0xA6)
SET_DISP = const(0xae) SET_DISP = const(0xAE)
SET_MEM_ADDR = const(0x20) SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21) SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22) SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40) SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xa0) SET_SEG_REMAP = const(0xA0)
SET_MUX_RATIO = const(0xa8) SET_MUX_RATIO = const(0xA8)
SET_COM_OUT_DIR = const(0xc0) SET_COM_OUT_DIR = const(0xC0)
SET_DISP_OFFSET = const(0xd3) SET_DISP_OFFSET = const(0xD3)
SET_COM_PIN_CFG = const(0xda) SET_COM_PIN_CFG = const(0xDA)
SET_DISP_CLK_DIV = const(0xd5) SET_DISP_CLK_DIV = const(0xD5)
SET_PRECHARGE = const(0xd9) SET_PRECHARGE = const(0xD9)
SET_VCOM_DESEL = const(0xdb) SET_VCOM_DESEL = const(0xDB)
SET_CHARGE_PUMP = const(0x8d) SET_CHARGE_PUMP = const(0x8D)
class SSD1306: class SSD1306:
def __init__(self, width, height, external_vcc): def __init__(self, width, height, external_vcc):
@ -35,27 +36,37 @@ class SSD1306:
def init_display(self): def init_display(self):
for cmd in ( for cmd in (
SET_DISP | 0x00, # off SET_DISP | 0x00, # off
# address setting # address setting
SET_MEM_ADDR, 0x00, # horizontal SET_MEM_ADDR,
0x00, # horizontal
# resolution and layout # resolution and layout
SET_DISP_START_LINE | 0x00, SET_DISP_START_LINE | 0x00,
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
SET_MUX_RATIO, self.height - 1, SET_MUX_RATIO,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 self.height - 1,
SET_DISP_OFFSET, 0x00, SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12, SET_DISP_OFFSET,
0x00,
SET_COM_PIN_CFG,
0x02 if self.height == 32 else 0x12,
# timing and driving scheme # timing and driving scheme
SET_DISP_CLK_DIV, 0x80, SET_DISP_CLK_DIV,
SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1, 0x80,
SET_VCOM_DESEL, 0x30, # 0.83*Vcc SET_PRECHARGE,
0x22 if self.external_vcc else 0xF1,
SET_VCOM_DESEL,
0x30, # 0.83*Vcc
# display # display
SET_CONTRAST, 0xff, # maximum SET_CONTRAST,
SET_ENTIRE_ON, # output follows RAM contents 0xFF, # maximum
SET_NORM_INV, # not inverted SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
# charge pump # charge pump
SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14, SET_CHARGE_PUMP,
SET_DISP | 0x01): # on 0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01,
): # on
self.write_cmd(cmd) self.write_cmd(cmd)
self.fill(0) self.fill(0)
self.show() self.show()
@ -97,27 +108,27 @@ class SSD1306:
def text(self, string, x, y, col=1): def text(self, string, x, y, col=1):
self.framebuf.text(string, x, y, col) self.framebuf.text(string, x, y, col)
class SSD1306_I2C(SSD1306): class SSD1306_I2C(SSD1306):
def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False): def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
self.i2c = i2c self.i2c = i2c
self.addr = addr self.addr = addr
self.temp = bytearray(2) self.temp = bytearray(2)
super().__init__(width, height, external_vcc) super().__init__(width, height, external_vcc)
def write_cmd(self, cmd): def write_cmd(self, cmd):
self.temp[0] = 0x80 # Co=1, D/C#=0 self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd self.temp[1] = cmd
self.i2c.writeto(self.addr, self.temp) self.i2c.writeto(self.addr, self.temp)
def write_data(self, buf): def write_data(self, buf):
self.temp[0] = self.addr << 1 self.temp[0] = self.addr << 1
self.temp[1] = 0x40 # Co=0, D/C#=1 self.temp[1] = 0x40 # Co=0, D/C#=1
self.i2c.start() self.i2c.start()
self.i2c.write(self.temp) self.i2c.write(self.temp)
self.i2c.write(buf) self.i2c.write(buf)
self.i2c.stop() self.i2c.stop()
class SSD1306_SPI(SSD1306): class SSD1306_SPI(SSD1306):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
@ -142,7 +153,7 @@ class SSD1306_SPI(SSD1306):
self.cs.high() self.cs.high()
def write_framebuf(self): def write_framebuf(self):
self.spi.init(SPI.MASTER,baudrate=self.rate, polarity=0, phase=0) self.spi.init(SPI.MASTER, baudrate=self.rate, polarity=0, phase=0)
self.cs.high() self.cs.high()
self.dc.high() self.dc.high()
self.cs.low() self.cs.low()

View File

@ -1,17 +1,19 @@
import pyb import pyb
class Motor():
class Motor:
def __init__(self, channel): def __init__(self, channel):
if channel == 1: if channel == 1:
self.pin1 = pyb.Pin('P3', pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE) self.pin1 = pyb.Pin("P3", pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE)
self.pin2 = pyb.Pin('P2', pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE) self.pin2 = pyb.Pin("P2", pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE)
tim = pyb.Timer(4, freq=1000) tim = pyb.Timer(4, freq=1000)
self.power = tim.channel(1, pyb.Timer.PWM, pin=pyb.Pin("P7"), pulse_width_percent=0) self.power = tim.channel(1, pyb.Timer.PWM, pin=pyb.Pin("P7"), pulse_width_percent=0)
elif channel == 2: elif channel == 2:
self.pin1 = pyb.Pin('P1', pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE) self.pin1 = pyb.Pin("P1", pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE)
self.pin2 = pyb.Pin('P0', pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE) self.pin2 = pyb.Pin("P0", pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE)
tim = pyb.Timer(4, freq=1000) tim = pyb.Timer(4, freq=1000)
self.power = tim.channel(2, pyb.Timer.PWM, pin=pyb.Pin("P8"), pulse_width_percent=0) self.power = tim.channel(2, pyb.Timer.PWM, pin=pyb.Pin("P8"), pulse_width_percent=0)
def set_speed(self, pwm): def set_speed(self, pwm):
if pwm < 0: if pwm < 0:
self.pin1.low() self.pin1.low()
@ -21,13 +23,14 @@ class Motor():
self.pin2.low() self.pin2.low()
self.power.pulse_width_percent(abs(pwm)) self.power.pulse_width_percent(abs(pwm))
class Stepper():
class Stepper:
def __init__(self, stepnumber=200, rpms=2, power=50): def __init__(self, stepnumber=200, rpms=2, power=50):
self.stepnumber = stepnumber self.stepnumber = stepnumber
self.pin1 = pyb.Pin('P3', pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE) self.pin1 = pyb.Pin("P3", pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE)
self.pin2 = pyb.Pin('P2', pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE) self.pin2 = pyb.Pin("P2", pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE)
self.pin3 = pyb.Pin('P1', pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE) self.pin3 = pyb.Pin("P1", pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE)
self.pin4 = pyb.Pin('P0', pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE) self.pin4 = pyb.Pin("P0", pyb.Pin.OUT_PP, pyb.Pin.PULL_NONE)
tim = pyb.Timer(4, freq=1000) tim = pyb.Timer(4, freq=1000)
self.power1 = tim.channel(1, pyb.Timer.PWM, pin=pyb.Pin("P7"), pulse_width_percent=0) self.power1 = tim.channel(1, pyb.Timer.PWM, pin=pyb.Pin("P7"), pulse_width_percent=0)
self.power2 = tim.channel(2, pyb.Timer.PWM, pin=pyb.Pin("P8"), pulse_width_percent=0) self.power2 = tim.channel(2, pyb.Timer.PWM, pin=pyb.Pin("P8"), pulse_width_percent=0)
@ -36,13 +39,13 @@ class Stepper():
self.phase = self.phase_list() self.phase = self.phase_list()
def phase_list(self): def phase_list(self):
phase_list = [(1,0,0,0), (0,0,1,0), (0,1,0,0), (0,0,0,1)] phase_list = [(1, 0, 0, 0), (0, 0, 1, 0), (0, 1, 0, 0), (0, 0, 0, 1)]
while True: while True:
for p in phase_list: for p in phase_list:
yield p yield p
def set_speed(self, rpms): def set_speed(self, rpms):
self.delay_time = int(1000000/(rpms*self.stepnumber)/2) self.delay_time = int(1000000 / (rpms * self.stepnumber) / 2)
def set_power(self, power): def set_power(self, power):
self.power1.pulse_width_percent(power) self.power1.pulse_width_percent(power)
@ -56,4 +59,3 @@ class Stepper():
self.pin3.value(phase[2]) self.pin3.value(phase[2])
self.pin4.value(phase[3]) self.pin4.value(phase[3])
pyb.udelay(self.delay_time) pyb.udelay(self.delay_time)

View File

@ -42,24 +42,36 @@ verbose_l = 0
client_busy = False client_busy = False
# Interfaces: (IP-Address (string), IP-Address (integer), Netmask (integer)) # Interfaces: (IP-Address (string), IP-Address (integer), Netmask (integer))
_month_name = ("", "Jan", "Feb", "Mar", "Apr", "May", "Jun", _month_name = (
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec") "",
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
)
class FTP_client: class FTP_client:
def __init__(self, ftpsocket, local_addr): def __init__(self, ftpsocket, local_addr):
self.command_client, self.remote_addr = ftpsocket.accept() self.command_client, self.remote_addr = ftpsocket.accept()
self.remote_addr = self.remote_addr[0] self.remote_addr = self.remote_addr[0]
self.command_client.settimeout(_COMMAND_TIMEOUT) self.command_client.settimeout(_COMMAND_TIMEOUT)
log_msg(1, "FTP Command connection from:", self.remote_addr) log_msg(1, "FTP Command connection from:", self.remote_addr)
self.command_client.setsockopt(socket.SOL_SOCKET, self.command_client.setsockopt(
_SO_REGISTER_HANDLER, socket.SOL_SOCKET, _SO_REGISTER_HANDLER, self.exec_ftp_command
self.exec_ftp_command) )
self.command_client.sendall("220 Hello, this is the {}.\r\n".format(sys.platform)) self.command_client.sendall("220 Hello, this is the {}.\r\n".format(sys.platform))
self.cwd = '/' self.cwd = "/"
self.fromname = None self.fromname = None
# self.logged_in = False # self.logged_in = False
self.act_data_addr = self.remote_addr self.act_data_addr = self.remote_addr
self.DATA_PORT = 20 self.DATA_PORT = 20
self.active = True self.active = True
@ -69,13 +81,12 @@ class FTP_client:
try: try:
for fname in uos.listdir(path): for fname in uos.listdir(path):
data_client.sendall(self.make_description(path, fname, full)) data_client.sendall(self.make_description(path, fname, full))
except Exception as e: # path may be a file name or pattern except Exception: # path may be a file name or pattern
path, pattern = self.split_path(path) path, pattern = self.split_path(path)
try: try:
for fname in uos.listdir(path): for fname in uos.listdir(path):
if self.fncmp(fname, pattern): if self.fncmp(fname, pattern):
data_client.sendall( data_client.sendall(self.make_description(path, fname, full))
self.make_description(path, fname, full))
except: except:
pass pass
@ -83,20 +94,18 @@ class FTP_client:
global _month_name global _month_name
if full: if full:
stat = uos.stat(self.get_absolute_path(path, fname)) stat = uos.stat(self.get_absolute_path(path, fname))
file_permissions = ("drwxr-xr-x" file_permissions = "drwxr-xr-x" if (stat[0] & 0o170000 == 0o040000) else "-rw-r--r--"
if (stat[0] & 0o170000 == 0o040000)
else "-rw-r--r--")
file_size = stat[6] file_size = stat[6]
tm = stat[7] & 0xffffffff tm = stat[7] & 0xFFFFFFFF
tm = localtime(tm if tm < 0x80000000 else tm - 0x100000000) tm = localtime(tm if tm < 0x80000000 else tm - 0x100000000)
if tm[0] != localtime()[0]: if tm[0] != localtime()[0]:
description = "{} 1 owner group {:>10} {} {:2} {:>5} {}\r\n".\ description = "{} 1 owner group {:>10} {} {:2} {:>5} {}\r\n".format(
format(file_permissions, file_size, file_permissions, file_size, _month_name[tm[1]], tm[2], tm[0], fname
_month_name[tm[1]], tm[2], tm[0], fname) )
else: else:
description = "{} 1 owner group {:>10} {} {:2} {:02}:{:02} {}\r\n".\ description = "{} 1 owner group {:>10} {} {:2} {:02}:{:02} {}\r\n".format(
format(file_permissions, file_size, file_permissions, file_size, _month_name[tm[1]], tm[2], tm[3], tm[4], fname
_month_name[tm[1]], tm[2], tm[3], tm[4], fname) )
else: else:
description = fname + "\r\n" description = fname + "\r\n"
return description return description
@ -125,22 +134,22 @@ class FTP_client:
# Just a few special cases "..", "." and "" # Just a few special cases "..", "." and ""
# If payload start's with /, set cwd to / # If payload start's with /, set cwd to /
# and consider the remainder a relative path # and consider the remainder a relative path
if payload.startswith('/'): if payload.startswith("/"):
cwd = "/" cwd = "/"
for token in payload.split("/"): for token in payload.split("/"):
if token == '..': if token == "..":
cwd = self.split_path(cwd)[0] cwd = self.split_path(cwd)[0]
elif token != '.' and token != '': elif token != "." and token != "":
if cwd == '/': if cwd == "/":
cwd += token cwd += token
else: else:
cwd = cwd + '/' + token cwd = cwd + "/" + token
return cwd return cwd
def split_path(self, path): # instead of path.rpartition('/') def split_path(self, path): # instead of path.rpartition('/')
tail = path.split('/')[-1] tail = path.split("/")[-1]
head = path[:-(len(tail) + 1)] head = path[: -(len(tail) + 1)]
return ('/' if head == '' else head, tail) return ("/" if head == "" else head, tail)
# compare fname against pattern. Pattern may contain # compare fname against pattern. Pattern may contain
# the wildcards ? and *. # the wildcards ? and *.
@ -148,15 +157,15 @@ class FTP_client:
pi = 0 pi = 0
si = 0 si = 0
while pi < len(pattern) and si < len(fname): while pi < len(pattern) and si < len(fname):
if (fname[si] == pattern[pi]) or (pattern[pi] == '?'): if (fname[si] == pattern[pi]) or (pattern[pi] == "?"):
si += 1 si += 1
pi += 1 pi += 1
else: else:
if pattern[pi] == '*': # recurse if pattern[pi] == "*": # recurse
if pi == len(pattern.rstrip("*?")): # only wildcards left if pi == len(pattern.rstrip("*?")): # only wildcards left
return True return True
while si < len(fname): while si < len(fname):
if self.fncmp(fname[si:], pattern[pi + 1:]): if self.fncmp(fname[si:], pattern[pi + 1 :]):
return True return True
else: else:
si += 1 si += 1
@ -210,7 +219,7 @@ class FTP_client:
# return # return
command = data.split()[0].upper() command = data.split()[0].upper()
payload = data[len(command):].lstrip() # partition is missing payload = data[len(command) :].lstrip() # partition is missing
path = self.get_absolute_path(self.cwd, payload) path = self.get_absolute_path(self.cwd, payload)
log_msg(1, "Command={}, Payload={}".format(command, payload)) log_msg(1, "Command={}, Payload={}".format(command, payload))
@ -229,9 +238,9 @@ class FTP_client:
elif command == "SYST": elif command == "SYST":
cl.sendall("215 UNIX Type: L8\r\n") cl.sendall("215 UNIX Type: L8\r\n")
elif command in ("TYPE", "NOOP", "ABOR"): # just accept & ignore elif command in ("TYPE", "NOOP", "ABOR"): # just accept & ignore
cl.sendall('200 OK\r\n') cl.sendall("200 OK\r\n")
elif command == "QUIT": elif command == "QUIT":
cl.sendall('221 Bye.\r\n') cl.sendall("221 Bye.\r\n")
close_client(cl) close_client(cl)
elif command == "PWD" or command == "XPWD": elif command == "PWD" or command == "XPWD":
cl.sendall('257 "{}"\r\n'.format(self.cwd)) cl.sendall('257 "{}"\r\n'.format(self.cwd))
@ -239,44 +248,44 @@ class FTP_client:
try: try:
if (uos.stat(path)[0] & 0o170000) == 0o040000: if (uos.stat(path)[0] & 0o170000) == 0o040000:
self.cwd = path self.cwd = path
cl.sendall('250 OK\r\n') cl.sendall("250 OK\r\n")
else: else:
cl.sendall('550 Fail\r\n') cl.sendall("550 Fail\r\n")
except: except:
cl.sendall('550 Fail\r\n') cl.sendall("550 Fail\r\n")
elif command == "PASV": elif command == "PASV":
cl.sendall('227 Entering Passive Mode ({},{},{}).\r\n'.format( cl.sendall(
self.pasv_data_addr.replace('.', ','), "227 Entering Passive Mode ({},{},{}).\r\n".format(
_DATA_PORT >> 8, _DATA_PORT % 256)) self.pasv_data_addr.replace(".", ","), _DATA_PORT >> 8, _DATA_PORT % 256
)
)
self.active = False self.active = False
elif command == "PORT": elif command == "PORT":
items = payload.split(",") items = payload.split(",")
if len(items) >= 6: if len(items) >= 6:
self.act_data_addr = '.'.join(items[:4]) self.act_data_addr = ".".join(items[:4])
if self.act_data_addr == "127.0.1.1": if self.act_data_addr == "127.0.1.1":
# replace by command session addr # replace by command session addr
self.act_data_addr = self.remote_addr self.act_data_addr = self.remote_addr
self.DATA_PORT = int(items[4]) * 256 + int(items[5]) self.DATA_PORT = int(items[4]) * 256 + int(items[5])
cl.sendall('200 OK\r\n') cl.sendall("200 OK\r\n")
self.active = True self.active = True
else: else:
cl.sendall('504 Fail\r\n') cl.sendall("504 Fail\r\n")
elif command == "LIST" or command == "NLST": elif command == "LIST" or command == "NLST":
if payload.startswith("-"): if payload.startswith("-"):
option = payload.split()[0].lower() option = payload.split()[0].lower()
path = self.get_absolute_path( path = self.get_absolute_path(self.cwd, payload[len(option) :].lstrip())
self.cwd, payload[len(option):].lstrip())
else: else:
option = "" option = ""
try: try:
data_client = self.open_dataclient() data_client = self.open_dataclient()
cl.sendall("150 Directory listing:\r\n") cl.sendall("150 Directory listing:\r\n")
self.send_list_data(path, data_client, self.send_list_data(path, data_client, command == "LIST" or "l" in option)
command == "LIST" or 'l' in option)
cl.sendall("226 Done.\r\n") cl.sendall("226 Done.\r\n")
data_client.close() data_client.close()
except: except:
cl.sendall('550 Fail\r\n') cl.sendall("550 Fail\r\n")
if data_client is not None: if data_client is not None:
data_client.close() data_client.close()
elif command == "RETR": elif command == "RETR":
@ -289,43 +298,47 @@ class FTP_client:
data_client = None data_client = None
cl.sendall("226 Done.\r\n") cl.sendall("226 Done.\r\n")
except: except:
cl.sendall('550 Fail\r\n') cl.sendall("550 Fail\r\n")
if data_client is not None: if data_client is not None:
data_client.close() data_client.close()
elif command == "STOR" or command == "APPE": elif command == "STOR" or command == "APPE":
try: try:
data_client = self.open_dataclient() data_client = self.open_dataclient()
cl.sendall("150 Opened data connection.\r\n") cl.sendall("150 Opened data connection.\r\n")
self.save_file_data(path, data_client, self.save_file_data(path, data_client, "wb" if command == "STOR" else "ab")
"wb" if command == "STOR" else "ab")
# if the next statement is reached, # if the next statement is reached,
# the data_client was closed. # the data_client was closed.
data_client = None data_client = None
cl.sendall("226 Done.\r\n") cl.sendall("226 Done.\r\n")
except: except:
cl.sendall('550 Fail\r\n') cl.sendall("550 Fail\r\n")
if data_client is not None: if data_client is not None:
data_client.close() data_client.close()
elif command == "SIZE": elif command == "SIZE":
try: try:
cl.sendall('213 {}\r\n'.format(uos.stat(path)[6])) cl.sendall("213 {}\r\n".format(uos.stat(path)[6]))
except: except:
cl.sendall('550 Fail\r\n') cl.sendall("550 Fail\r\n")
elif command == "MDTM": elif command == "MDTM":
try: try:
tm=localtime(uos.stat(path)[8]) tm = localtime(uos.stat(path)[8])
cl.sendall('213 {:04d}{:02d}{:02d}{:02d}{:02d}{:02d}\r\n'.format(*tm[0:6])) cl.sendall("213 {:04d}{:02d}{:02d}{:02d}{:02d}{:02d}\r\n".format(*tm[0:6]))
except: except:
cl.sendall('550 Fail\r\n') cl.sendall("550 Fail\r\n")
elif command == "STAT": elif command == "STAT":
if payload == "": if payload == "":
cl.sendall("211-Connected to ({})\r\n" cl.sendall(
" Data address ({})\r\n" "211-Connected to ({})\r\n"
" TYPE: Binary STRU: File MODE: Stream\r\n" " Data address ({})\r\n"
" Session timeout {}\r\n" " TYPE: Binary STRU: File MODE: Stream\r\n"
"211 Client count is {}\r\n".format( " Session timeout {}\r\n"
self.remote_addr, self.pasv_data_addr, "211 Client count is {}\r\n".format(
_COMMAND_TIMEOUT, len(client_list))) self.remote_addr,
self.pasv_data_addr,
_COMMAND_TIMEOUT,
len(client_list),
)
)
else: else:
cl.sendall("213-Directory listing:\r\n") cl.sendall("213-Directory listing:\r\n")
self.send_list_data(path, cl, True) self.send_list_data(path, cl, True)
@ -333,9 +346,9 @@ class FTP_client:
elif command == "DELE": elif command == "DELE":
try: try:
uos.remove(path) uos.remove(path)
cl.sendall('250 OK\r\n') cl.sendall("250 OK\r\n")
except: except:
cl.sendall('550 Fail\r\n') cl.sendall("550 Fail\r\n")
elif command == "RNFR": elif command == "RNFR":
try: try:
# just test if the name exists, exception if not # just test if the name exists, exception if not
@ -343,35 +356,35 @@ class FTP_client:
self.fromname = path self.fromname = path
cl.sendall("350 Rename from\r\n") cl.sendall("350 Rename from\r\n")
except: except:
cl.sendall('550 Fail\r\n') cl.sendall("550 Fail\r\n")
elif command == "RNTO": elif command == "RNTO":
try: try:
uos.rename(self.fromname, path) uos.rename(self.fromname, path)
cl.sendall('250 OK\r\n') cl.sendall("250 OK\r\n")
except: except:
cl.sendall('550 Fail\r\n') cl.sendall("550 Fail\r\n")
self.fromname = None self.fromname = None
elif command == "CDUP" or command == "XCUP": elif command == "CDUP" or command == "XCUP":
self.cwd = self.get_absolute_path(self.cwd, "..") self.cwd = self.get_absolute_path(self.cwd, "..")
cl.sendall('250 OK\r\n') cl.sendall("250 OK\r\n")
elif command == "RMD" or command == "XRMD": elif command == "RMD" or command == "XRMD":
try: try:
uos.rmdir(path) uos.rmdir(path)
cl.sendall('250 OK\r\n') cl.sendall("250 OK\r\n")
except: except:
cl.sendall('550 Fail\r\n') cl.sendall("550 Fail\r\n")
elif command == "MKD" or command == "XMKD": elif command == "MKD" or command == "XMKD":
try: try:
uos.mkdir(path) uos.mkdir(path)
cl.sendall('250 OK\r\n') cl.sendall("250 OK\r\n")
except: except:
cl.sendall('550 Fail\r\n') cl.sendall("550 Fail\r\n")
elif command == "SITE": elif command == "SITE":
try: try:
exec(payload.replace('\0','\n')) exec(payload.replace("\0", "\n"))
cl.sendall('250 OK\r\n') cl.sendall("250 OK\r\n")
except: except:
cl.sendall('550 Fail\r\n') cl.sendall("550 Fail\r\n")
else: else:
cl.sendall("502 Unsupported command.\r\n") cl.sendall("502 Unsupported command.\r\n")
# log_msg(2, # log_msg(2,
@ -422,8 +435,7 @@ def accept_ftp_connect(ftpsocket, local_addr):
def num_ip(ip): def num_ip(ip):
items = ip.split(".") items = ip.split(".")
return (int(items[0]) << 24 | int(items[1]) << 16 | return int(items[0]) << 24 | int(items[1]) << 16 | int(items[2]) << 8 | int(items[3])
int(items[2]) << 8 | int(items[3]))
def stop(): def stop():
@ -432,8 +444,7 @@ def stop():
global client_busy global client_busy
for client in client_list: for client in client_list:
client.command_client.setsockopt(socket.SOL_SOCKET, client.command_client.setsockopt(socket.SOL_SOCKET, _SO_REGISTER_HANDLER, None)
_SO_REGISTER_HANDLER, None)
client.command_client.close() client.command_client.close()
del client_list del client_list
client_list = [] client_list = []
@ -470,19 +481,20 @@ def start(port=21, verbose=0, splash=True):
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(addr[0][4]) sock.bind(addr[0][4])
sock.listen(1) sock.listen(1)
sock.setsockopt(socket.SOL_SOCKET, sock.setsockopt(
_SO_REGISTER_HANDLER, socket.SOL_SOCKET, _SO_REGISTER_HANDLER, lambda s: accept_ftp_connect(s, ifconfig[0])
lambda s : accept_ftp_connect(s, ifconfig[0])) )
ftpsockets.append(sock) ftpsockets.append(sock)
if splash: if splash:
print("FTP server started on {}:{}".format(ifconfig[0], port)) print("FTP server started on {}:{}".format(ifconfig[0], port))
datasocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) datasocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
datasocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) datasocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
datasocket.bind(('0.0.0.0', _DATA_PORT)) datasocket.bind(("0.0.0.0", _DATA_PORT))
datasocket.listen(1) datasocket.listen(1)
datasocket.settimeout(10) datasocket.settimeout(10)
def restart(port=21, verbose=0, splash=True): def restart(port=21, verbose=0, splash=True):
stop() stop()
sleep_ms(200) sleep_ms(200)

View File

@ -2,23 +2,25 @@
# copyright (c) 2018 Shawwwn <shawwwn1@gmail.com> # copyright (c) 2018 Shawwwn <shawwwn1@gmail.com>
# License: MIT # License: MIT
# Internet Checksum Algorithm # Internet Checksum Algorithm
# Author: Olav Morken # Author: Olav Morken
# https://github.com/olavmrk/python-ping/blob/master/ping.py # https://github.com/olavmrk/python-ping/blob/master/ping.py
# @data: bytes # @data: bytes
def checksum(data): def checksum(data):
if len(data) & 0x1: # Odd number of bytes if len(data) & 0x1: # Odd number of bytes
data += b'\0' data += b"\0"
cs = 0 cs = 0
for pos in range(0, len(data), 2): for pos in range(0, len(data), 2):
b1 = data[pos] b1 = data[pos]
b2 = data[pos + 1] b2 = data[pos + 1]
cs += (b1 << 8) + b2 cs += (b1 << 8) + b2
while cs >= 0x10000: while cs >= 0x10000:
cs = (cs & 0xffff) + (cs >> 16) cs = (cs & 0xFFFF) + (cs >> 16)
cs = ~cs & 0xffff cs = ~cs & 0xFFFF
return cs return cs
def ping(host, count=4, timeout=5000, interval=10, quiet=False, size=64): def ping(host, count=4, timeout=5000, interval=10, quiet=False, size=64):
import utime import utime
import uselect import uselect
@ -29,7 +31,7 @@ def ping(host, count=4, timeout=5000, interval=10, quiet=False, size=64):
# prepare packet # prepare packet
assert size >= 16, "pkt size too small" assert size >= 16, "pkt size too small"
pkt = b'Q'*size pkt = b"Q" * size
pkt_desc = { pkt_desc = {
"type": uctypes.UINT8 | 0, "type": uctypes.UINT8 | 0,
"code": uctypes.UINT8 | 1, "code": uctypes.UINT8 | 1,
@ -37,9 +39,9 @@ def ping(host, count=4, timeout=5000, interval=10, quiet=False, size=64):
"id": uctypes.UINT16 | 4, "id": uctypes.UINT16 | 4,
"seq": uctypes.INT16 | 6, "seq": uctypes.INT16 | 6,
"timestamp": uctypes.UINT64 | 8, "timestamp": uctypes.UINT64 | 8,
} # packet header descriptor } # packet header descriptor
h = uctypes.struct(uctypes.addressof(pkt), pkt_desc, uctypes.BIG_ENDIAN) h = uctypes.struct(uctypes.addressof(pkt), pkt_desc, uctypes.BIG_ENDIAN)
h.type = 8 # ICMP_ECHO_REQUEST h.type = 8 # ICMP_ECHO_REQUEST
h.code = 0 h.code = 0
h.checksum = 0 h.checksum = 0
h.id = urandom.randint(0, 65535) h.id = urandom.randint(0, 65535)
@ -48,19 +50,19 @@ def ping(host, count=4, timeout=5000, interval=10, quiet=False, size=64):
# init socket # init socket
sock = usocket.socket(usocket.AF_INET, usocket.SOCK_RAW, 1) sock = usocket.socket(usocket.AF_INET, usocket.SOCK_RAW, 1)
sock.setblocking(0) sock.setblocking(0)
sock.settimeout(timeout/1000) sock.settimeout(timeout / 1000)
addr = usocket.getaddrinfo(host, 1)[0][-1][0] # ip address addr = usocket.getaddrinfo(host, 1)[0][-1][0] # ip address
sock.connect((addr, 1)) sock.connect((addr, 1))
not quiet and print("PING %s (%s): %u data bytes" % (host, addr, len(pkt))) not quiet and print("PING %s (%s): %u data bytes" % (host, addr, len(pkt)))
seqs = list(range(1, count+1)) # [1,2,...,count] seqs = list(range(1, count + 1)) # [1,2,...,count]
c = 1 c = 1
t = 0 t = 0
n_trans = 0 n_trans = 0
n_recv = 0 n_recv = 0
finish = False finish = False
while t < timeout: while t < timeout:
if t==interval and c<=count: if t == interval and c <= count:
# send packet # send packet
h.checksum = 0 h.checksum = 0
h.seq = c h.seq = c
@ -68,7 +70,7 @@ def ping(host, count=4, timeout=5000, interval=10, quiet=False, size=64):
h.checksum = checksum(pkt) h.checksum = checksum(pkt)
if sock.send(pkt) == size: if sock.send(pkt) == size:
n_trans += 1 n_trans += 1
t = 0 # reset timeout t = 0 # reset timeout
else: else:
seqs.remove(c) seqs.remove(c)
c += 1 c += 1
@ -82,11 +84,14 @@ def ping(host, count=4, timeout=5000, interval=10, quiet=False, size=64):
h2 = uctypes.struct(uctypes.addressof(resp_mv[20:]), pkt_desc, uctypes.BIG_ENDIAN) h2 = uctypes.struct(uctypes.addressof(resp_mv[20:]), pkt_desc, uctypes.BIG_ENDIAN)
# TODO: validate checksum (optional) # TODO: validate checksum (optional)
seq = h2.seq seq = h2.seq
if h2.type==0 and h2.id==h.id and (seq in seqs): # 0: ICMP_ECHO_REPLY if h2.type == 0 and h2.id == h.id and (seq in seqs): # 0: ICMP_ECHO_REPLY
t_elasped = (utime.ticks_us()-h2.timestamp) / 1000 t_elasped = (utime.ticks_us() - h2.timestamp) / 1000
ttl = ustruct.unpack('!B', resp_mv[8:9])[0] # time-to-live ttl = ustruct.unpack("!B", resp_mv[8:9])[0] # time-to-live
n_recv += 1 n_recv += 1
not quiet and print("%u bytes from %s: icmp_seq=%u, ttl=%u, time=%f ms" % (len(resp), addr, seq, ttl, t_elasped)) not quiet and print(
"%u bytes from %s: icmp_seq=%u, ttl=%u, time=%f ms"
% (len(resp), addr, seq, ttl, t_elasped)
)
seqs.remove(seq) seqs.remove(seq)
if len(seqs) == 0: if len(seqs) == 0:
finish = True finish = True

View File

@ -1,17 +1,17 @@
# The MIT License (MIT) # The MIT License (MIT)
# #
# Copyright (c) 2013, 2014 micropython-lib contributors # Copyright (c) 2013, 2014 micropython-lib contributors
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights # in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is # copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions: # furnished to do so, subject to the following conditions:
# #
# The above copyright notice and this permission notice shall be included in # The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software. # all copies or substantial portions of the Software.
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -20,7 +20,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE. # THE SOFTWARE.
# Source: improved version of https://github.com/micropython/micropython-lib/blob/master/python-ecosys/urequests/urequests.py # Source: improved version of:
# https://github.com/micropython/micropython-lib/blob/master/python-ecosys/urequests/urequests.py
# Some useful links for future updates: # Some useful links for future updates:
# https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4 # https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4
# https://docs.python-requests.org/en/master/ # https://docs.python-requests.org/en/master/
@ -28,6 +29,7 @@
import usocket import usocket
import ubinascii import ubinascii
class Response: class Response:
def __init__(self, code, reason, headers=None, content=None): def __init__(self, code, reason, headers=None, content=None):
self.encoding = "utf-8" self.encoding = "utf-8"
@ -46,23 +48,26 @@ class Response:
def json(self): def json(self):
import ujson import ujson
return ujson.loads(self._content) return ujson.loads(self._content)
def readline(s): def readline(s):
l = bytearray() l = bytearray()
while True: while True:
try: try:
l += s.read(1) l += s.read(1)
if (l[-1] == b'\n'): if l[-1] == b"\n":
break break
except: except:
break break
return l return l
def socket_readall(s): def socket_readall(s):
buf = b'' buf = b""
while True: while True:
recv = b'' recv = b""
try: try:
recv = s.recv(1) recv = s.recv(1)
except: except:
@ -72,6 +77,7 @@ def socket_readall(s):
buf += recv buf += recv
return buf return buf
def request(method, url, data=None, json=None, files=None, headers={}, auth=None, stream=None): def request(method, url, data=None, json=None, files=None, headers={}, auth=None, stream=None):
try: try:
proto, dummy, host, path = url.split("/", 3) proto, dummy, host, path = url.split("/", 3)
@ -82,6 +88,7 @@ def request(method, url, data=None, json=None, files=None, headers={}, auth=None
port = 80 port = 80
elif proto == "https:": elif proto == "https:":
import ussl import ussl
port = 443 port = 443
else: else:
raise ValueError("Unsupported protocol: " + proto) raise ValueError("Unsupported protocol: " + proto)
@ -91,7 +98,9 @@ def request(method, url, data=None, json=None, files=None, headers={}, auth=None
port = int(port) port = int(port)
if auth: if auth:
headers['Authorization'] = b'Basic %s'%(ubinascii.b2a_base64('%s:%s' %(auth[0], auth[1]))[0:-1]) headers["Authorization"] = b"Basic %s" % (
ubinascii.b2a_base64("%s:%s" % (auth[0], auth[1]))[0:-1]
)
resp_code = 0 resp_code = 0
resp_reason = None resp_reason = None
@ -107,7 +116,7 @@ def request(method, url, data=None, json=None, files=None, headers={}, auth=None
s.write(b"%s /%s HTTP/1.0\r\n" % (method, path)) s.write(b"%s /%s HTTP/1.0\r\n" % (method, path))
if not "Host" in headers: if "Host" not in headers:
s.write(b"Host: %s\r\n" % host) s.write(b"Host: %s\r\n" % host)
# Iterate over keys to avoid tuple alloc # Iterate over keys to avoid tuple alloc
@ -119,19 +128,23 @@ def request(method, url, data=None, json=None, files=None, headers={}, auth=None
if json is not None: if json is not None:
import ujson import ujson
data = ujson.dumps(json) data = ujson.dumps(json)
s.write(b"Content-Type: application/json\r\n") s.write(b"Content-Type: application/json\r\n")
if files is not None: if files is not None:
data = bytearray() data = bytearray()
boundary = b"37a4bcce91521f74142f1868e328a6b9" boundary = b"37a4bcce91521f74142f1868e328a6b9"
s.write(b"Content-Type: multipart/form-data; boundary=%s\r\n"%(boundary)) s.write(b"Content-Type: multipart/form-data; boundary=%s\r\n" % (boundary))
for name, fileobj in files.items(): for name, fileobj in files.items():
data += b"--%s\r\n"%(boundary) data += b"--%s\r\n" % (boundary)
data += b'Content-Disposition: form-data; name="%s"; filename="%s"\r\n\r\n' %(name, fileobj[0]) data += b'Content-Disposition: form-data; name="%s"; filename="%s"\r\n\r\n' % (
name,
fileobj[0],
)
data += fileobj[1].read() data += fileobj[1].read()
data += b"\r\n" data += b"\r\n"
data += b"\r\n--%s--\r\n"%(boundary) data += b"\r\n--%s--\r\n" % (boundary)
if data: if data:
s.write(b"Content-Length: %d\r\n\r\n" % len(data)) s.write(b"Content-Length: %d\r\n\r\n" % len(data))
@ -149,35 +162,40 @@ def request(method, url, data=None, json=None, files=None, headers={}, auth=None
raise ValueError("Unsupported " + l) raise ValueError("Unsupported " + l)
elif l.startswith(b"Location:") and not 200 <= status <= 299: elif l.startswith(b"Location:") and not 200 <= status <= 299:
raise NotImplementedError("Redirects not yet supported") raise NotImplementedError("Redirects not yet supported")
if 'HTTPS' in l or 'HTTP' in l: if "HTTPS" in l or "HTTP" in l:
sline = l.split(None, 2) sline = l.split(None, 2)
resp_code = int(sline[1]) resp_code = int(sline[1])
resp_reason = sline[2].decode().rstrip() if len(sline) > 2 else "" resp_reason = sline[2].decode().rstrip() if len(sline) > 2 else ""
continue continue
resp_headers.append(l) resp_headers.append(l)
resp_headers = b'\r\n'.join(resp_headers) resp_headers = b"\r\n".join(resp_headers)
content = b'\r\n'.join(response) content = b"\r\n".join(response)
except OSError: except OSError:
s.close() s.close()
raise raise
return Response(resp_code, resp_reason, resp_headers, content) return Response(resp_code, resp_reason, resp_headers, content)
def head(url, **kw): def head(url, **kw):
return request("HEAD", url, **kw) return request("HEAD", url, **kw)
def get(url, **kw): def get(url, **kw):
return request("GET", url, **kw) return request("GET", url, **kw)
def post(url, **kw): def post(url, **kw):
return request("POST", url, **kw) return request("POST", url, **kw)
def put(url, **kw): def put(url, **kw):
return request("PUT", url, **kw) return request("PUT", url, **kw)
def patch(url, **kw): def patch(url, **kw):
return request("PATCH", url, **kw) return request("PATCH", url, **kw)
def delete(url, **kw): def delete(url, **kw):
return request("DELETE", url, **kw) return request("DELETE", url, **kw)

View File

@ -5,25 +5,26 @@ import socket
import network import network
import uos import uos
import errno import errno
from uio import IOBase from uio import IOBase
last_client_socket = None last_client_socket = None
server_socket = None server_socket = None
# Provide necessary functions for dupterm and replace telnet control characters that come in. # Provide necessary functions for dupterm and replace telnet control characters that come in.
class TelnetWrapper(IOBase): class TelnetWrapper(IOBase):
def __init__(self, socket): def __init__(self, socket):
self.socket = socket self.socket = socket
self.discard_count = 0 self.discard_count = 0
def readinto(self, b): def readinto(self, b):
readbytes = 0 readbytes = 0
for i in range(len(b)): for i in range(len(b)):
try: try:
byte = 0 byte = 0
# discard telnet control characters and # discard telnet control characters and
# null bytes # null bytes
while(byte == 0): while byte == 0:
byte = self.socket.recv(1)[0] byte = self.socket.recv(1)[0]
if byte == 0xFF: if byte == 0xFF:
self.discard_count = 2 self.discard_count = 2
@ -31,9 +32,9 @@ class TelnetWrapper(IOBase):
elif self.discard_count > 0: elif self.discard_count > 0:
self.discard_count -= 1 self.discard_count -= 1
byte = 0 byte = 0
b[i] = byte b[i] = byte
readbytes += 1 readbytes += 1
except (IndexError, OSError) as e: except (IndexError, OSError) as e:
if type(e) == IndexError or len(e.args) > 0 and e.args[0] == errno.EAGAIN: if type(e) == IndexError or len(e.args) > 0 and e.args[0] == errno.EAGAIN:
@ -44,7 +45,7 @@ class TelnetWrapper(IOBase):
else: else:
raise raise
return readbytes return readbytes
def write(self, data): def write(self, data):
# we need to write all the data but it's a non-blocking socket # we need to write all the data but it's a non-blocking socket
# so loop until it's all written eating EAGAIN exceptions # so loop until it's all written eating EAGAIN exceptions
@ -59,32 +60,34 @@ class TelnetWrapper(IOBase):
else: else:
# something else...propagate the exception # something else...propagate the exception
raise raise
def close(self): def close(self):
self.socket.close() self.socket.close()
# Attach new clients to dupterm and
# Attach new clients to dupterm and
# send telnet control characters to disable line mode # send telnet control characters to disable line mode
# and stop local echoing # and stop local echoing
def accept_telnet_connect(telnet_server): def accept_telnet_connect(telnet_server):
global last_client_socket global last_client_socket
if last_client_socket: if last_client_socket:
# close any previous clients # close any previous clients
uos.dupterm(None) uos.dupterm(None)
last_client_socket.close() last_client_socket.close()
last_client_socket, remote_addr = telnet_server.accept() last_client_socket, remote_addr = telnet_server.accept()
print("Telnet connection from:", remote_addr) print("Telnet connection from:", remote_addr)
last_client_socket.setblocking(False) last_client_socket.setblocking(False)
# dupterm_notify() not available under MicroPython v1.1 # dupterm_notify() not available under MicroPython v1.1
# last_client_socket.setsockopt(socket.SOL_SOCKET, 20, uos.dupterm_notify) # last_client_socket.setsockopt(socket.SOL_SOCKET, 20, uos.dupterm_notify)
last_client_socket.sendall(bytes([255, 252, 34])) # dont allow line mode last_client_socket.sendall(bytes([255, 252, 34])) # dont allow line mode
last_client_socket.sendall(bytes([255, 251, 1])) # turn off local echo last_client_socket.sendall(bytes([255, 251, 1])) # turn off local echo
uos.dupterm(TelnetWrapper(last_client_socket)) uos.dupterm(TelnetWrapper(last_client_socket))
def stop(): def stop():
global server_socket, last_client_socket global server_socket, last_client_socket
uos.dupterm(None) uos.dupterm(None)
@ -93,20 +96,21 @@ def stop():
if last_client_socket: if last_client_socket:
last_client_socket.close() last_client_socket.close()
# start listening for telnet connections on port 23 # start listening for telnet connections on port 23
def start(port=23): def start(port=23):
stop() stop()
global server_socket global server_socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ai = socket.getaddrinfo("0.0.0.0", port) ai = socket.getaddrinfo("0.0.0.0", port)
addr = ai[0][4] addr = ai[0][4]
server_socket.bind(addr) server_socket.bind(addr)
server_socket.listen(1) server_socket.listen(1)
server_socket.setsockopt(socket.SOL_SOCKET, 20, accept_telnet_connect) server_socket.setsockopt(socket.SOL_SOCKET, 20, accept_telnet_connect)
for i in (network.AP_IF, network.STA_IF): for i in (network.AP_IF, network.STA_IF):
wlan = network.WLAN(i) wlan = network.WLAN(i)
if wlan.active(): if wlan.active():

View File

@ -1,109 +1,117 @@
import pyb import pyb
VL51L1X_DEFAULT_CONFIGURATION = bytes([ VL51L1X_DEFAULT_CONFIGURATION = bytes(
0x00, # 0x2d : set bit 2 and 5 to 1 for fast plus mode (1MHz I2C), else don't touch */ [
0x00, # 0x2e : bit 0 if I2C pulled up at 1.8V, else set bit 0 to 1 (pull up at AVDD) */ 0x00, # 0x2d : set bit 2 and 5 to 1 for fast plus mode (1MHz I2C), else don't touch
0x00, # 0x2f : bit 0 if GPIO pulled up at 1.8V, else set bit 0 to 1 (pull up at AVDD) */ 0x00, # 0x2e : bit 0 if I2C pulled up at 1.8V, else set bit 0 to 1 (pull up at AVDD)
0x01, # 0x30 : set bit 4 to 0 for active high interrupt and 1 for active low (bits 3:0 must be 0x1), use SetInterruptPolarity() */ 0x00, # 0x2f : bit 0 if GPIO pulled up at 1.8V, else set bit 0 to 1 (pull up at AVDD)
0x02, # 0x31 : bit 1 = interrupt depending on the polarity, use CheckForDataReady() */ 0x01, # 0x30 : set bit 4 to 0 for active high interrupt and 1 for active low
0x00, # 0x32 : not user-modifiable */ # (bits 3:0 must be 0x1), use SetInterruptPolarity()
0x02, # 0x33 : not user-modifiable */ 0x02, # 0x31 : bit 1 = interrupt depending on the polarity, use CheckForDataReady()
0x08, # 0x34 : not user-modifiable */ 0x00, # 0x32 : not user-modifiable
0x00, # 0x35 : not user-modifiable */ 0x02, # 0x33 : not user-modifiable
0x08, # 0x36 : not user-modifiable */ 0x08, # 0x34 : not user-modifiable
0x10, # 0x37 : not user-modifiable */ 0x00, # 0x35 : not user-modifiable
0x01, # 0x38 : not user-modifiable */ 0x08, # 0x36 : not user-modifiable
0x01, # 0x39 : not user-modifiable */ 0x10, # 0x37 : not user-modifiable
0x00, # 0x3a : not user-modifiable */ 0x01, # 0x38 : not user-modifiable
0x00, # 0x3b : not user-modifiable */ 0x01, # 0x39 : not user-modifiable
0x00, # 0x3c : not user-modifiable */ 0x00, # 0x3a : not user-modifiable
0x00, # 0x3d : not user-modifiable */ 0x00, # 0x3b : not user-modifiable
0xff, # 0x3e : not user-modifiable */ 0x00, # 0x3c : not user-modifiable
0x00, # 0x3f : not user-modifiable */ 0x00, # 0x3d : not user-modifiable
0x0F, # 0x40 : not user-modifiable */ 0xFF, # 0x3e : not user-modifiable
0x00, # 0x41 : not user-modifiable */ 0x00, # 0x3f : not user-modifiable
0x00, # 0x42 : not user-modifiable */ 0x0F, # 0x40 : not user-modifiable
0x00, # 0x43 : not user-modifiable */ 0x00, # 0x41 : not user-modifiable
0x00, # 0x44 : not user-modifiable */ 0x00, # 0x42 : not user-modifiable
0x00, # 0x45 : not user-modifiable */ 0x00, # 0x43 : not user-modifiable
0x20, # 0x46 : interrupt configuration 0->level low detection, 1-> level high, 2-> Out of window, 3->In window, 0x20-> New sample ready , TBC */ 0x00, # 0x44 : not user-modifiable
0x0b, # 0x47 : not user-modifiable */ 0x00, # 0x45 : not user-modifiable
0x00, # 0x48 : not user-modifiable */ 0x20, # 0x46 : interrupt configuration 0->level low detection,
0x00, # 0x49 : not user-modifiable */ # 1-> level high, 2-> Out of window, 3->In window, 0x20-> New sample ready , TBC
0x02, # 0x4a : not user-modifiable */ 0x0B, # 0x47 : not user-modifiable
0x0a, # 0x4b : not user-modifiable */ 0x00, # 0x48 : not user-modifiable
0x21, # 0x4c : not user-modifiable */ 0x00, # 0x49 : not user-modifiable
0x00, # 0x4d : not user-modifiable */ 0x02, # 0x4a : not user-modifiable
0x00, # 0x4e : not user-modifiable */ 0x0A, # 0x4b : not user-modifiable
0x05, # 0x4f : not user-modifiable */ 0x21, # 0x4c : not user-modifiable
0x00, # 0x50 : not user-modifiable */ 0x00, # 0x4d : not user-modifiable
0x00, # 0x51 : not user-modifiable */ 0x00, # 0x4e : not user-modifiable
0x00, # 0x52 : not user-modifiable */ 0x05, # 0x4f : not user-modifiable
0x00, # 0x53 : not user-modifiable */ 0x00, # 0x50 : not user-modifiable
0xc8, # 0x54 : not user-modifiable */ 0x00, # 0x51 : not user-modifiable
0x00, # 0x55 : not user-modifiable */ 0x00, # 0x52 : not user-modifiable
0x00, # 0x56 : not user-modifiable */ 0x00, # 0x53 : not user-modifiable
0x38, # 0x57 : not user-modifiable */ 0xC8, # 0x54 : not user-modifiable
0xff, # 0x58 : not user-modifiable */ 0x00, # 0x55 : not user-modifiable
0x01, # 0x59 : not user-modifiable */ 0x00, # 0x56 : not user-modifiable
0x00, # 0x5a : not user-modifiable */ 0x38, # 0x57 : not user-modifiable
0x08, # 0x5b : not user-modifiable */ 0xFF, # 0x58 : not user-modifiable
0x00, # 0x5c : not user-modifiable */ 0x01, # 0x59 : not user-modifiable
0x00, # 0x5d : not user-modifiable */ 0x00, # 0x5a : not user-modifiable
0x01, # 0x5e : not user-modifiable */ 0x08, # 0x5b : not user-modifiable
0xdb, # 0x5f : not user-modifiable */ 0x00, # 0x5c : not user-modifiable
0x0f, # 0x60 : not user-modifiable */ 0x00, # 0x5d : not user-modifiable
0x01, # 0x61 : not user-modifiable */ 0x01, # 0x5e : not user-modifiable
0xf1, # 0x62 : not user-modifiable */ 0xDB, # 0x5f : not user-modifiable
0x0d, # 0x63 : not user-modifiable */ 0x0F, # 0x60 : not user-modifiable
0x01, # 0x64 : Sigma threshold MSB (mm in 14.2 format for MSB+LSB), use SetSigmaThreshold(), default value 90 mm */ 0x01, # 0x61 : not user-modifiable
0x68, # 0x65 : Sigma threshold LSB */ 0xF1, # 0x62 : not user-modifiable
0x00, # 0x66 : Min count Rate MSB (MCPS in 9.7 format for MSB+LSB), use SetSignalThreshold() */ 0x0D, # 0x63 : not user-modifiable
0x80, # 0x67 : Min count Rate LSB */ 0x01, # 0x64 : Sigma threshold MSB (mm in 14.2 format for MSB+LSB),
0x08, # 0x68 : not user-modifiable */ # use SetSigmaThreshold(), default value 90 mm
0xb8, # 0x69 : not user-modifiable */ 0x68, # 0x65 : Sigma threshold LSB
0x00, # 0x6a : not user-modifiable */ 0x00, # 0x66 : Min count Rate MSB (MCPS in 9.7 format for MSB+LSB), use SetSignalThreshold()
0x00, # 0x6b : not user-modifiable */ 0x80, # 0x67 : Min count Rate LSB
0x00, # 0x6c : Intermeasurement period MSB, 32 bits register, use SetIntermeasurementInMs() */ 0x08, # 0x68 : not user-modifiable
0x00, # 0x6d : Intermeasurement period */ 0xB8, # 0x69 : not user-modifiable
0x0f, # 0x6e : Intermeasurement period */ 0x00, # 0x6a : not user-modifiable
0x89, # 0x6f : Intermeasurement period LSB */ 0x00, # 0x6b : not user-modifiable
0x00, # 0x70 : not user-modifiable */ 0x00, # 0x6c : Intermeasurement period MSB, 32 bits register, use SetIntermeasurementInMs()
0x00, # 0x71 : not user-modifiable */ 0x00, # 0x6d : Intermeasurement period
0x00, # 0x72 : distance threshold high MSB (in mm, MSB+LSB), use SetD:tanceThreshold() */ 0x0F, # 0x6e : Intermeasurement period
0x00, # 0x73 : distance threshold high LSB */ 0x89, # 0x6f : Intermeasurement period LSB
0x00, # 0x74 : distance threshold low MSB ( in mm, MSB+LSB), use SetD:tanceThreshold() */ 0x00, # 0x70 : not user-modifiable
0x00, # 0x75 : distance threshold low LSB */ 0x00, # 0x71 : not user-modifiable
0x00, # 0x76 : not user-modifiable */ 0x00, # 0x72 : distance threshold high MSB (in mm, MSB+LSB), use SetD:tanceThreshold()
0x01, # 0x77 : not user-modifiable */ 0x00, # 0x73 : distance threshold high LSB
0x0f, # 0x78 : not user-modifiable */ 0x00, # 0x74 : distance threshold low MSB ( in mm, MSB+LSB), use SetD:tanceThreshold()
0x0d, # 0x79 : not user-modifiable */ 0x00, # 0x75 : distance threshold low LSB
0x0e, # 0x7a : not user-modifiable */ 0x00, # 0x76 : not user-modifiable
0x0e, # 0x7b : not user-modifiable */ 0x01, # 0x77 : not user-modifiable
0x00, # 0x7c : not user-modifiable */ 0x0F, # 0x78 : not user-modifiable
0x00, # 0x7d : not user-modifiable */ 0x0D, # 0x79 : not user-modifiable
0x02, # 0x7e : not user-modifiable */ 0x0E, # 0x7a : not user-modifiable
0xc7, # 0x7f : ROI center, use SetROI() */ 0x0E, # 0x7b : not user-modifiable
0xff, # 0x80 : XY ROI (X=Width, Y=Height), use SetROI() */ 0x00, # 0x7c : not user-modifiable
0x9B, # 0x81 : not user-modifiable */ 0x00, # 0x7d : not user-modifiable
0x00, # 0x82 : not user-modifiable */ 0x02, # 0x7e : not user-modifiable
0x00, # 0x83 : not user-modifiable */ 0xC7, # 0x7f : ROI center, use SetROI()
0x00, # 0x84 : not user-modifiable */ 0xFF, # 0x80 : XY ROI (X=Width, Y=Height), use SetROI()
0x01, # 0x85 : not user-modifiable */ 0x9B, # 0x81 : not user-modifiable
0x01, # 0x86 : clear interrupt, use ClearInterrupt() */ 0x00, # 0x82 : not user-modifiable
0x40 # 0x87 : start ranging, use StartRanging() or StopRanging(), If you want an automatic start after VL53L1X_init() call, put 0x40 in location 0x87 */ 0x00, # 0x83 : not user-modifiable
]) 0x00, # 0x84 : not user-modifiable
0x01, # 0x85 : not user-modifiable
0x01, # 0x86 : clear interrupt, use ClearInterrupt()
0x40, # 0x87 : start ranging, use StartRanging() or StopRanging(), If you want
# an automatic start after VL53L1X_init() call, put 0x40 in location 0x87
]
)
class VL53L1X: class VL53L1X:
def __init__(self,i2c, address=0x29): def __init__(self, i2c, address=0x29):
self.i2c = i2c self.i2c = i2c
self.address = address self.address = address
self.reset() self.reset()
pyb.delay(1) pyb.delay(1)
if self.read_model_id() != 0xEACC: if self.read_model_id() != 0xEACC:
raise RuntimeError('Failed to find expected ID register values. Check wiring!') raise RuntimeError("Failed to find expected ID register values. Check wiring!")
# write default configuration # write default configuration
self.i2c.writeto_mem(self.address, 0x2D, VL51L1X_DEFAULT_CONFIGURATION, addrsize=16) self.i2c.writeto_mem(self.address, 0x2D, VL51L1X_DEFAULT_CONFIGURATION, addrsize=16)
#pyb.delay(100) # pyb.delay(100)
# the API triggers this change in VL53L1_init_and_start_range() once a # the API triggers this change in VL53L1_init_and_start_range() once a
# measurement is started; assumes MM1 and MM2 are disabled # measurement is started; assumes MM1 and MM2 are disabled
self.writeReg16Bit(0x001E, self.readReg16Bit(0x0022) * 4) self.writeReg16Bit(0x001E, self.readReg16Bit(0x0022) * 4)
@ -111,53 +119,61 @@ class VL53L1X:
def writeReg(self, reg, value): def writeReg(self, reg, value):
return self.i2c.writeto_mem(self.address, reg, bytes([value]), addrsize=16) return self.i2c.writeto_mem(self.address, reg, bytes([value]), addrsize=16)
def writeReg16Bit(self, reg, value): def writeReg16Bit(self, reg, value):
return self.i2c.writeto_mem(self.address, reg, bytes([(value >> 8) & 0xFF, value & 0xFF]), addrsize=16) return self.i2c.writeto_mem(
self.address, reg, bytes([(value >> 8) & 0xFF, value & 0xFF]), addrsize=16
)
def readReg(self, reg): def readReg(self, reg):
return self.i2c.readfrom_mem(self.address, reg, 1, addrsize=16)[0] return self.i2c.readfrom_mem(self.address, reg, 1, addrsize=16)[0]
def readReg16Bit(self, reg): def readReg16Bit(self, reg):
data = self.i2c.readfrom_mem(self.address, reg, 2, addrsize=16) data = self.i2c.readfrom_mem(self.address, reg, 2, addrsize=16)
return (data[0]<<8) + data[1] return (data[0] << 8) + data[1]
def read_model_id(self): def read_model_id(self):
return self.readReg16Bit(0x010F) return self.readReg16Bit(0x010F)
def reset(self): def reset(self):
self.writeReg(0x0000, 0x00) self.writeReg(0x0000, 0x00)
pyb.delay(100) pyb.delay(100)
self.writeReg(0x0000, 0x01) self.writeReg(0x0000, 0x01)
def read(self): def read(self):
data = self.i2c.readfrom_mem(self.address, 0x0089, 17, addrsize=16) # RESULT__RANGE_STATUS data = self.i2c.readfrom_mem(self.address, 0x0089, 17, addrsize=16) # RESULT__RANGE_STATUS
range_status = data[0] # range_status = data[0]
# report_status = data[1] # report_status = data[1]
stream_count = data[2] # stream_count = data[2]
dss_actual_effective_spads_sd0 = (data[3]<<8) + data[4] # dss_actual_effective_spads_sd0 = (data[3] << 8) + data[4]
# peak_signal_count_rate_mcps_sd0 = (data[5]<<8) + data[6] # peak_signal_count_rate_mcps_sd0 = (data[5]<<8) + data[6]
ambient_count_rate_mcps_sd0 = (data[7]<<8) + data[8] # ambient_count_rate_mcps_sd0 = (data[7] << 8) + data[8]
# sigma_sd0 = (data[9]<<8) + data[10] # sigma_sd0 = (data[9]<<8) + data[10]
# phase_sd0 = (data[11]<<8) + data[12] # phase_sd0 = (data[11]<<8) + data[12]
final_crosstalk_corrected_range_mm_sd0 = (data[13]<<8) + data[14] final_crosstalk_corrected_range_mm_sd0 = (data[13] << 8) + data[14]
peak_signal_count_rate_crosstalk_corrected_mcps_sd0 = (data[15]<<8) + data[16] # peak_signal_count_rate_crosstalk_corrected_mcps_sd0 = (data[15] << 8) + data[16]
#status = None # status = None
#if range_status in (17, 2, 1, 3): # if range_status in (17, 2, 1, 3):
#status = "HardwareFail" # status = "HardwareFail"
#elif range_status == 13: # elif range_status == 13:
#status = "MinRangeFail" # status = "MinRangeFail"
#elif range_status == 18: # elif range_status == 18:
#status = "SynchronizationInt" # status = "SynchronizationInt"
#elif range_status == 5: # elif range_status == 5:
#status = "OutOfBoundsFail" # status = "OutOfBoundsFail"
#elif range_status == 4: # elif range_status == 4:
#status = "SignalFail" # status = "SignalFail"
#elif range_status == 6: # elif range_status == 6:
#status = "SignalFail" # status = "SignalFail"
#elif range_status == 7: # elif range_status == 7:
#status = "WrapTargetFail" # status = "WrapTargetFail"
#elif range_status == 12: # elif range_status == 12:
#status = "XtalkSignalFail" # status = "XtalkSignalFail"
#elif range_status == 8: # elif range_status == 8:
#status = "RangeValidMinRangeClipped" # status = "RangeValidMinRangeClipped"
#elif range_status == 9: # elif range_status == 9:
#if stream_count == 0: # if stream_count == 0:
#status = "RangeValidNoWrapCheckFail" # status = "RangeValidNoWrapCheckFail"
#else: # else:
#status = "OK" # status = "OK"
return final_crosstalk_corrected_range_mm_sd0 return final_crosstalk_corrected_range_mm_sd0