Browse Source

cleaning and updated readme

master
Ondřej Hruška 4 years ago
parent
commit
3a5ac05d9d
Signed by: MightyPork GPG Key ID: 2C5FD5035250423D
  1. 0
      Client.py
  2. 0
      PayloadBuilder.py
  3. 0
      PayloadParser.py
  4. 82
      README.md
  5. 0
      TinyFrame.py
  6. 0
      Unit.py
  7. 0
      __init__.py
  8. 49
      examples/demo_adc_lightnings.py
  9. 36
      examples/demo_dot_matrix_phat.py
  10. 200
      examples/demo_dot_matrix_phat2.py
  11. 35
      examples/demo_freq_response.py
  12. 23
      examples/demo_i2c_dotmatrix.py
  13. 213
      examples/demo_lora.py
  14. 37
      examples/demo_ndir_leds.py
  15. 18
      examples/demo_ndir_usart.py
  16. 17
      examples/demo_neo2.py
  17. 39
      examples/demo_neo3.py
  18. 11
      examples/demo_neopixel.py
  19. 136
      examples/demo_nrf24.py
  20. 11
      examples/demo_pulse.py
  21. 126
      examples/demo_pymodoro.py
  22. 37
      examples/demo_transient.py
  23. 78
      examples/loratest.txt
  24. 309
      examples/main.py
  25. 24
      examples/mat_sampling.m
  26. 86
      examples/nrf_config.ini
  27. 15
      examples/show_nparray.py
  28. 1109
      examples/sx_fsk.py
  29. 38
      examples/test_adc.py
  30. 17
      examples/test_adc2.py
  31. 49
      examples/test_dac.py
  32. 32
      examples/test_dongle.py
  33. 43
      examples/test_freq_cap.py
  34. 9
      examples/test_ini.py
  35. 81
      examples/test_onewire.py
  36. 7
      examples/test_ping.py
  37. 47
      examples/test_pwmdim_music.py
  38. 17
      examples/test_pwmdim_sweep.py
  39. 21
      examples/test_sipo_omicron.py
  40. 20
      examples/test_touch.py
  41. 128
      gexync.py
  42. 81
      ini_syntax.py
  43. 0
      transport.py
  44. 0
      units/ADC.py
  45. 0
      units/DAC.py
  46. 0
      units/DIn.py
  47. 0
      units/DOut.py
  48. 0
      units/FCAP.py
  49. 0
      units/I2C.py
  50. 0
      units/Neopixel.py
  51. 0
      units/OneWire.py
  52. 0
      units/PWMDim.py
  53. 0
      units/SIPO.py
  54. 0
      units/SPI.py
  55. 0
      units/TOUCH.py
  56. 0
      units/USART.py

0
gex/Client.py → Client.py

0
gex/PayloadBuilder.py → PayloadBuilder.py

0
gex/PayloadParser.py → PayloadParser.py

82
README.md

@ -1,9 +1,52 @@
# Python client for GEX
This is the primary GEX front-end for user scripting.
This is the primary GEX front-end for user scripting. It may be used natively with python3,
or integrated in MATLAB through the Python call API.
GEX configuration can be persisted on-chip or loaded dynamically using
the client from a INI file or string.
## Installation
Add this library as a git submodule 'gex' to your project (or simply copy it there).
### Linux
You may want to create this file in `/etc/udev/rules.d/98-gex.rules` and add your user to
the `plugdev` group.
```
SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="4c60", GROUP="plugdev", MODE="0660"
SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="4c61", GROUP="plugdev", MODE="0660"
```
### BSD
Not tested, may be similar to Linux
### Mac
Not tested
### Windows 8.1 and older
You need to attach the [STM32 Virtual COM Port Driver](http://www.st.com/en/development-tools/stsw-stm32102.html)
to the GEX device using the Device Manager, if they want to use the Virtual COM port attachment
method (the Serial transport options, see below).
Additionally, they may have to configure the Mass Storage endpoint to use the Mass Storage system driver,
because older Windows are not smart enough to figure it out (we're using the standard device class and
everything, but still).
The Python scripts can be run with Cygwin, or the Windows Python port.
### Windows 10
It should Just Work™ without any special configuration needed.
## Example
A sample GEX script could look like this:
@ -13,16 +56,35 @@ A sample GEX script could look like this:
import time
import gex
client = gex.Client()
led = gex.Pin(client, 'LED')
for i in range(0,10):
with gex.Client() as client:
led = gex.DOut(client, 'led')
for i in range(0,10):
led.toggle()
time.sleep(.1)
```
The client instance can be used to send control commands directly, bypassing the unit drivers.
Writing new unit drivers is simple and straightforward. See any of the existing units for reference.
The client object can be used to send control commands directly, bypassing unit drivers.
Writing new unit drivers is simple, just extend the Unit class and add the unit-specific
methods and logic.
## Transports
The CLient class takes a Transport instance as a constructor parameter. There are three
transports defined:
- `TrxSerialSync` - VCOM, blocking (with a polling loop)
- `TrxSerialThread` - VCOM, asynchronous
- `TrxRawUSB` - PyUSB, the default and usually the best transport
Pass the device serial ID as am argument to match it if you have multiple
GEX modules attached at once.
Additionally, either transport can be wrapped in `DongleAdapter` when the _Wireless Gateway_
dongle is used. The `TrxRawUSB` further needs the second argument, `remote`, set to true in
this case, to look for devices with the Gateway vid:pid value 1209:4c61.
GEX modules themselves have 1209:4c60.

0
gex/TinyFrame.py → TinyFrame.py

0
gex/Unit.py → Unit.py

0
gex/__init__.py → __init__.py

49
examples/demo_adc_lightnings.py

@ -1,49 +0,0 @@
#!/bin/env python3
import time
import gex
import numpy as np
from matplotlib import pyplot as plt
import datetime
from scipy.io import wavfile
# this script captures lightnings and stores them to npy files
# ADC channel 1 -> 100n -> o -> long wire (antenna)
# |
# '-> 10k -> GND
led = None
def capture(tr):
now=datetime.datetime.now()
now.isoformat()
data = tr.data
print("Capture! ")
print(data)
np.save("lightning-%s"%now.isoformat(), data)
led.pulse_ms(1000, confirm=False)
with gex.Client(gex.TrxRawUSB()) as client:
adc = gex.ADC(client, 'adc')
led = gex.DOut(client, 'led')
adc.set_sample_rate(60000)
adc.on_trigger(capture)
adc.setup_trigger(1,
level=2600,
count=5000,
edge='rising',
pretrigger=500,
holdoff=500,
auto=True)
adc.arm()
sec = 0
while True:
print('%d s' % sec)
sec += 1
time.sleep(1)

36
examples/demo_dot_matrix_phat.py

@ -1,36 +0,0 @@
#!/bin/env python3
import gex
import time
# simple demo with the dot matrix phat
ADDR = 0x61
MODE = 0b00011000
OPTS = 0b00001110 # 1110 = 35mA, 0000 = 40mA
CMD_BRIGHTNESS = 0x19
CMD_MODE = 0x00
CMD_UPDATE = 0x0C
CMD_OPTIONS = 0x0D
CMD_MATRIX_1 = 0x01
CMD_MATRIX_2 = 0x0E
with gex.Client(gex.TrxRawUSB()) as client:
bus = gex.I2C(client, 'i2c')
addr = 0x61
bus.write_reg(addr, CMD_MODE, MODE)
bus.write_reg(addr, CMD_OPTIONS, OPTS)
bus.write_reg(addr, CMD_BRIGHTNESS, 64)
bus.write(addr, [CMD_MATRIX_1,
0xAA,0x55,0xAA,0x55,
0xAA,0x55,0xAA,0x55,
])
bus.write(addr, [CMD_MATRIX_2,
0xFF, 0, 0xFF, 0,
0xFF, 0, 0xFF, 0,
])
bus.write_reg(addr, CMD_UPDATE, 0x01)

200
examples/demo_dot_matrix_phat2.py

@ -1,200 +0,0 @@
#!/bin/env python3
import random
import gex
import time
# This is an adaptation of the micro dot phat library
# - the only change needed was replacing the smbus class with the GEX unit driver
ADDR = 0x61
MODE = 0b00011000
OPTS = 0b00001110 # 1110 = 35mA, 0000 = 40mA
CMD_BRIGHTNESS = 0x19
CMD_MODE = 0x00
CMD_UPDATE = 0x0C
CMD_OPTIONS = 0x0D
CMD_MATRIX_1 = 0x01
CMD_MATRIX_2 = 0x0E
MATRIX_1 = 0
MATRIX_2 = 1
class NanoMatrix:
'''
_BUF_MATRIX_1 = [ # Green
#Col 1 2 3 4 5
0b00000000, # Row 1
0b00000000, # Row 2
0b00000000, # Row 3
0b00000000, # Row 4
0b00000000, # Row 5
0b00000000, # Row 6
0b10000000, # Row 7, bit 8 = decimal place
0b00000000
]
_BUF_MATRIX_2 = [ # Red
#Row 8 7 6 5 4 3 2 1
0b01111111, # Col 1, bottom to top
0b01111111, # Col 2
0b01111111, # Col 3
0b01111111, # Col 4
0b01111111, # Col 5
0b00000000,
0b00000000,
0b01000000 # bit 7, decimal place
]
_BUF_MATRIX_1 = [0] * 8
_BUF_MATRIX_2 = [0] * 8
'''
def __init__(self, bus:gex.I2C, address=ADDR):
self.address = address
self._brightness = 127
self.bus = bus
self.bus.write_byte_data(self.address, CMD_MODE, MODE)
self.bus.write_byte_data(self.address, CMD_OPTIONS, OPTS)
self.bus.write_byte_data(self.address, CMD_BRIGHTNESS, self._brightness)
self._BUF_MATRIX_1 = [0] * 8
self._BUF_MATRIX_2 = [0] * 8
def set_brightness(self, brightness):
self._brightness = int(brightness * 127)
if self._brightness > 127: self._brightness = 127
self.bus.write_byte_data(self.address, CMD_BRIGHTNESS, self._brightness)
def set_decimal(self, m, c):
if m == MATRIX_1:
if c == 1:
self._BUF_MATRIX_1[6] |= 0b10000000
else:
self._BUF_MATRIX_1[6] &= 0b01111111
elif m == MATRIX_2:
if c == 1:
self._BUF_MATRIX_2[7] |= 0b01000000
else:
self._BUF_MATRIX_2[7] &= 0b10111111
#self.update()
def set(self, m, data):
for y in range(7):
self.set_row(m, y, data[y])
def set_row(self, m, r, data):
for x in range(5):
self.set_pixel(m, x, r, (data & (1 << (4-x))) > 0)
def set_col(self, m, c, data):
for y in range(7):
self.set_pixel(m, c, y, (data & (1 << y)) > 0)
def set_pixel(self, m, x, y, c):
if m == MATRIX_1:
if c == 1:
self._BUF_MATRIX_1[y] |= (0b1 << x)
else:
self._BUF_MATRIX_1[y] &= ~(0b1 << x)
elif m == MATRIX_2:
if c == 1:
self._BUF_MATRIX_2[x] |= (0b1 << y)
else:
self._BUF_MATRIX_2[x] &= ~(0b1 << y)
#self.update()
def clear(self, m):
if m == MATRIX_1:
self._BUF_MATRIX_1 = [0] * 8
elif m == MATRIX_2:
self._BUF_MATRIX_2 = [0] * 8
self.update()
def update(self):
for x in range(10):
try:
self.bus.write_i2c_block_data(self.address, CMD_MATRIX_1, self._BUF_MATRIX_1)
self.bus.write_i2c_block_data(self.address, CMD_MATRIX_2, self._BUF_MATRIX_2)
self.bus.write_byte_data(self.address, CMD_UPDATE, 0x01)
break
except IOError:
print("IO Error")
with gex.Client(gex.TrxRawUSB()) as client:
bus = gex.I2C(client, 'i2c')
n1 = NanoMatrix(bus, 0x61)
n2 = NanoMatrix(bus, 0x62)
n3 = NanoMatrix(bus, 0x63)
n1.set_pixel(0, 0, 0, 1)
n1.set_pixel(0, 4, 0, 1)
n1.set_pixel(0, 0, 6, 1)
n1.set_pixel(0, 4, 6, 1)
n1.set_pixel(1, 0, 0, 1)
n1.set_pixel(1, 4, 0, 1)
n1.set_pixel(1, 0, 3, 1)
n1.set_pixel(1, 4, 3, 1)
n2.set_pixel(0, 0, 2, 1)
n2.set_pixel(0, 4, 2, 1)
n2.set_pixel(0, 0, 5, 1)
n2.set_pixel(0, 4, 5, 1)
n2.set_pixel(1, 0, 0, 1)
n2.set_pixel(1, 4, 0, 1)
n2.set_pixel(1, 0, 6, 1)
n2.set_pixel(1, 4, 6, 1)
n3.set_pixel(0, 1, 0, 1)
n3.set_pixel(0, 3, 0, 1)
n3.set_pixel(0, 1, 6, 1)
n3.set_pixel(0, 3, 6, 1)
n3.set_pixel(1, 1, 1, 1)
n3.set_pixel(1, 3, 1, 1)
n3.set_pixel(1, 1, 5, 1)
n3.set_pixel(1, 3, 5, 1)
n1.update()
n2.update()
n3.update()
b1 = 64
b2 = 64
b3 = 64
while True:
b1 += random.randint(-20, 15)
b2 += random.randint(-20, 18)
b3 += random.randint(-15, 13)
if b1 < 0: b1 = 0
if b2 < 0: b2 = 0
if b3 < 0: b3 = 0
if b1 > 127: b1 = 127
if b2 > 127: b2 = 127
if b3 > 127: b3 = 127
n1.set_brightness(b1)
n2.set_brightness(b2)
n3.set_brightness(b3)
time.sleep(0.05)

35
examples/demo_freq_response.py

@ -1,35 +0,0 @@
#!/bin/env python3
import gex
import numpy as np
from matplotlib import pyplot as plt
# frequency response measurement
with gex.Client(gex.TrxRawUSB()) as client:
dac = gex.DAC(client, 'dac')
adc = gex.ADC(client, 'adc')
dac.waveform(1, 'SINE')
adc.set_sample_rate(50000)
table = []
for i in range(100, 10000, 100):
dac.set_frequency(1, i)
data = adc.capture(10000)
# convert to floats
samples = data.astype(float)
amplitude = np.max(samples) - np.min(samples)
print("%d Hz ... rms %d" % (i, amplitude))
table.append(i)
table.append(amplitude)
dac.dc(1, 0)
t = np.reshape(np.array(table), [int(len(table)/2),2])
hz = t[:,0]
am = t[:,1]
plt.plot(hz, am, 'r-', lw=1)
plt.grid()
plt.show()

23
examples/demo_i2c_dotmatrix.py

@ -1,23 +0,0 @@
#!/bin/env python3
import gex
# experiment with the dot matrix driver
with gex.Client(gex.TrxRawUSB()) as client:
bus = gex.I2C(client, 'i2c')
addr = 0x61
bus.write_reg(addr, 0x00, 0b00011000) # dual matrix
bus.write_reg(addr, 0x0D, 0b00001110) # 34 mA
bus.write_reg(addr, 0x19, 64) # set brightness
# matrix 1
bus.write_reg(addr, 0x01, [
0xAA, 0x55, 0xAA, 0x55,
0xAA, 0x55, 0xAA, 0x55
])
# matrix 2
bus.write_reg(addr, 0x0E, [
0xFF, 0x00, 0xFF, 0x00,
0xFF, 0x00, 0xFF, 0x00
])
# update display
bus.write_reg(addr, 0x0C, 0x01)

213
examples/demo_lora.py

@ -1,213 +0,0 @@
import time
import gex
import sx_fsk as sx
# we're demonstrating the use of the GFSK mode of the SX1278
# this is an example of how GEX can be used to control a peripheral module - in this case evaluating
# it for use in GEX remote
class LoRa:
def __init__(self,
rst: gex.DOut,
spi: gex.SPI, ssnum):
self.ss = ssnum
self.rst = rst
self.spi = spi
def reset(self):
self.rst.pulse_us(100, active=False)
time.sleep(0.005)
def rd(self, addr):
return self.spi.query(self.ss, [addr], 1)[0]
def wr(self, addr, value):
self.spi.write(self.ss, [addr | 0x80, value])
def rds(self, start, count=1):
return self.spi.query(self.ss, [start], count)
def wrs(self, start, values):
ba = bytearray()
ba.append(start | 0x80)
ba.extend(values)
self.spi.write(self.ss, ba)
def rmw(self, addr, keep, set):
""" rmw, first and-ing the register with mask and then oring with set """
val = self.rd(addr)
self.wr(addr, (val & keep) | set)
def waitModeSwitch(self):
while 0 == (self.rd(sx.REG_IRQFLAGS1) & sx.RF_IRQFLAGS1_MODEREADY):
time.sleep(0.001)
def waitSent(self):
while 0 == (self.rd(sx.REG_IRQFLAGS2) & sx.RF_IRQFLAGS2_PACKETSENT):
time.sleep(0.001)
def fsk_set_defaults(self):
# Set default values (semtech patches: * in DS)
self.rmw(sx.REG_RXCONFIG,
sx.RF_RXCONFIG_RXTRIGER_MASK,
sx.RF_RXCONFIG_RXTRIGER_PREAMBLEDETECT)
self.wr(sx.REG_PREAMBLEDETECT,
sx.RF_PREAMBLEDETECT_DETECTOR_ON |
sx.RF_PREAMBLEDETECT_DETECTORSIZE_2 |
sx.RF_PREAMBLEDETECT_DETECTORTOL_10)
self.rmw(sx.REG_OSC, sx.RF_OSC_CLKOUT_MASK, sx.RF_OSC_CLKOUT_OFF)
self.rmw(sx.REG_FIFOTHRESH,
sx.RF_FIFOTHRESH_TXSTARTCONDITION_MASK,
sx.RF_FIFOTHRESH_TXSTARTCONDITION_FIFONOTEMPTY)
self.rmw(sx.REG_IMAGECAL,
sx.RF_IMAGECAL_AUTOIMAGECAL_MASK,
sx.RF_IMAGECAL_AUTOIMAGECAL_OFF)
def configure_for_fsk(self, address):
self.rmw(sx.REG_OPMODE,
sx.RF_OPMODE_LONGRANGEMODE_MASK & sx.RF_OPMODE_MODULATIONTYPE_MASK & sx.RF_OPMODE_MASK,
sx.RF_OPMODE_LONGRANGEMODE_OFF |
sx.RF_OPMODE_MODULATIONTYPE_FSK |
sx.RF_OPMODE_STANDBY)
self.waitModeSwitch()
self.fsk_set_defaults()
self.wr(sx.REG_NODEADRS, address)
self.wr(sx.REG_BROADCASTADRS, 0xFF)
# use whitening and force address matching
self.rmw(sx.REG_PACKETCONFIG1,
sx.RF_PACKETCONFIG1_DCFREE_MASK & sx.RF_PACKETCONFIG1_ADDRSFILTERING_MASK,
sx.RF_PACKETCONFIG1_DCFREE_WHITENING |
sx.RF_PACKETCONFIG1_ADDRSFILTERING_NODEBROADCAST)
self.wr(sx.REG_RXCONFIG,
sx.RF_RXCONFIG_AFCAUTO_ON |
sx.RF_RXCONFIG_AGCAUTO_ON |
sx.RF_RXCONFIG_RXTRIGER_PREAMBLEDETECT)
XTAL_FREQ = 32000000
FREQ_STEP = 61.03515625
FSK_FDEV = 60000 # Hz NOTE: originally: 25000, seems to help increasing
FSK_DATARATE = 200000 # bps - originally 50000
MAX_RFPOWER = 0 # 0-7 boost - this doesnt seem to have a huge impact
FSK_PREAMBLE_LENGTH = 5 # Same for Tx and Rx
fdev = round(FSK_FDEV / FREQ_STEP)
self.wr(sx.REG_FDEVMSB, fdev >> 8)
self.wr(sx.REG_FDEVLSB, fdev & 0xFF)
datarate = round(XTAL_FREQ / FSK_DATARATE)
print("dr=%d"%datarate)
self.wr(sx.REG_BITRATEMSB, datarate >> 8)
self.wr(sx.REG_BITRATELSB, datarate & 0xFF)
preamblelen = FSK_PREAMBLE_LENGTH
self.wr(sx.REG_PREAMBLEMSB, preamblelen >> 8)
self.wr(sx.REG_PREAMBLELSB, preamblelen & 0xFF)
# bandwidths - 1 MHz
self.wr(sx.REG_RXBW, 0x0A) # FSK_BANDWIDTH
self.wr(sx.REG_AFCBW, 0x0A) # FSK_AFC_BANDWIDTH
# max payload len to rx
self.wr(sx.REG_PAYLOADLENGTH, 0xFF)
self.rmw(sx.REG_PARAMP, 0x9F, 0x40) # enable gauss 0.5
# pick the sync word size
self.rmw(sx.REG_SYNCCONFIG,
sx.RF_SYNCCONFIG_SYNCSIZE_MASK,
sx.RF_SYNCCONFIG_SYNCSIZE_3)
# sync word (network ID)
self.wrs(sx.REG_SYNCVALUE1, [
0xe7, 0x3d, 0xfa, 0x01, 0x5e, 0xa1, 0xc9, 0x98 # something random
])
# enable LNA boost (?)
self.rmw(sx.REG_LNA,
sx.RF_LNA_BOOST_MASK,
sx.RF_LNA_BOOST_ON)
# experiments with the pa config
self.rmw(sx.REG_PACONFIG,
sx.RF_PACONFIG_PASELECT_MASK|0x8F, # max power mask
sx.RF_PACONFIG_PASELECT_PABOOST | MAX_RFPOWER<<4)
# we could also possibly adjust the Tx power
with gex.Client(gex.TrxRawUSB()) as client:
spi = gex.SPI(client, 'spi')
rst1 = gex.DOut(client, 'rst1')
rst2 = gex.DOut(client, 'rst2')
a = LoRa(rst1, spi, 0)
b = LoRa(rst2, spi, 1)
# reset the two transceivers to ensure they start in a defined state
a.reset()
b.reset()
# go to sleep mode, select FSK
a.configure_for_fsk(0x33)
b.configure_for_fsk(0x44)
print("Devices configured")
for j in range(0, 240):
if(j>0 and j%60==0):
print()
# --- Send a message from 1 to 2 ---
msg = [
51, # len
0x44, # address
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
0, 1, 2, 3, 4, 5, 6, 7, 8, j
]
a.wrs(sx.REG_FIFO, msg)
b.rmw(sx.REG_OPMODE, sx.RF_OPMODE_MASK, sx.RF_OPMODE_RECEIVER)
# time.sleep(0.005)
# trigger A
a.rmw(sx.REG_OPMODE, sx.RF_OPMODE_MASK, sx.RF_OPMODE_TRANSMITTER)
a.waitModeSwitch()
a.waitSent()
# time.sleep(0.02)
# print("a irq flags = ", ["0x%02x"%x for x in a.rds(sx.REG_IRQFLAGS1, 2)])
# print("b irq flags = ", ["0x%02x"%x for x in b.rds(sx.REG_IRQFLAGS1, 2)])
rxd = [b.rd(sx.REG_FIFO) for x in range(0,len(msg))]
if rxd == msg:
print("\x1b[32;1m+\x1b[0m", end="", flush=True)
else:
print("\x1b[31m-\x1b[0m", end="", flush=True)
#
# for i in range(0,8):
# print("0x%02x" % rxd[i],end=" ")
# print()
# print()
print()
# good night
a.rmw(sx.REG_OPMODE, sx.RF_OPMODE_MASK, sx.RF_OPMODE_SLEEP)
b.rmw(sx.REG_OPMODE, sx.RF_OPMODE_MASK, sx.RF_OPMODE_SLEEP)

37
examples/demo_ndir_leds.py

@ -1,37 +0,0 @@
import time
import gex
# NDIR CO2 sensor showing the concentration on a SIPO-based LED display
with gex.Client(gex.TrxRawUSB()) as client:
ser = gex.USART(client, 'ser')
leds = gex.SIPO(client, 'leds')
while True:
ser.clear_buffer()
ser.write([0xFF, 0x01, 0x86, 0, 0, 0, 0, 0, 0x79])
data = ser.receive(9, decode=None)
pp = gex.PayloadParser(data, endian="big").skip(2)
ppm = pp.u16()
# The LEDs are connected to two 595's, interleaved R,G,R,G...
nl = (ppm-300)/1700.0
print("%d ppm CO₂, numleds %f" % (ppm, nl*8))
numb = 0
for i in range(0,8):
if nl >= i*0.125:
if i < 3:
numb |= 2<<(i*2)
elif i < 6:
numb |= 3<<(i*2)
else:
numb |= 1<<(i*2)
leds.load([(numb&0xFF00)>>8,numb&0xFF])
time.sleep(1)

18
examples/demo_ndir_usart.py

@ -1,18 +0,0 @@
import time
import gex
# basic NDIR CO2 sensor readout
with gex.Client(gex.TrxRawUSB()) as client:
ser = gex.USART(client, 'ser')
while True:
ser.clear_buffer()
ser.write([0xFF, 0x01, 0x86, 0, 0, 0, 0, 0, 0x79])
data = ser.receive(9, decode=None)
pp = gex.PayloadParser(data, endian="big").skip(2)
print("%d ppm CO₂" % pp.u16())
time.sleep(1)

17
examples/demo_neo2.py

@ -1,17 +0,0 @@
#!/bin/env python3
import gex
import time
# play a little neopixel animation as a demo
with gex.Client(gex.TrxRawUSB()) as client:
# Neopixel strip
strip = gex.Neopixel(client, 'npx')
# Load RGB to the strip
strip.load([0xFF0000, 0xFFFF00, 0x00FF00, 0x0000FF, 0xFF00FF])
for i in range(0,255):
strip.load([0xFF0000+i, 0xFFFF00, 0x00FF00, 0x0000FF, 0xFF00FF])
time.sleep(0.01)
strip.clear()

39
examples/demo_neo3.py

@ -1,39 +0,0 @@
#!/bin/env python3
import gex
import time
# this shows a spinny animation on a 30-pixel strip forming a circle
def draw_comet(buf, index):
r = 0xFF
g = 0x22
b = 0x00
steps = 5
fades = [0.05, 1, 0.5, 0.1, 0.05]
for i in range(steps):
fade = fades[i]
buf[(len(buf) + index - i)%len(buf)] = \
round(r * fade)<<16 | \
round(g * fade)<<8 | \
round(b * fade)
with gex.Client(gex.TrxRawUSB()) as client:
# Neopixel strip
strip = gex.Neopixel(client, 'npx')
markers = [0, 15]
for i in range(1000):
buf = [0]*30
for j in range(len(markers)):
n = markers[j]
draw_comet(buf, n)
n = n + 1 if n < len(buf)-1 else 0
markers[j] = n
strip.load(buf)
time.sleep(0.02)
strip.clear()

11
examples/demo_neopixel.py

@ -1,11 +0,0 @@
#!/bin/env python3
import gex
# the most basic neopixel demo
with gex.Client(gex.TrxRawUSB()) as client:
# Neopixel strip
strip = gex.Neopixel(client, 'npx')
# Load RGB to the strip
strip.load([0xFF0000, 0x00FF00, 0x0000FF, 0xFF00FF])

136
examples/demo_nrf24.py

@ -1,136 +0,0 @@
import time
import gex
import sx_fsk as sx
# this is a demo with two NRF24L01+ modules connected to SPI and some GPIO.
class Nrf:
def __init__(self, ce: gex.DOut, irq: gex.DIn, spi: gex.SPI, num):
self.snum = num
self.ce = ce
self.irq = irq
self.spi = spi
def rd(self, addr, count=1):
# addr 0-31
return self.spi.query(self.snum, [addr&0x1F], rlen=count)
def wr(self, addr, vals):
if type(vals) == int:
vals = [vals]
# addr 0-31
ba = bytearray()
ba.append(addr | 0x20)
ba.extend(vals)
self.spi.write(self.snum, ba)
def rd_payload(self, count):
""" Read a received payload """
return self.spi.query(self.snum, [0x61], rlen=count)
def wr_payload(self, pld):
""" Write a payload """
ba = bytearray()
ba.append(0xA0)
ba.extend(pld)
self.spi.write(self.snum, ba)
def flush_tx(self):
self.spi.write(self.snum, [0xE1])
def flush_rx(self):
self.spi.write(self.snum, [0xE2])
def reuse_tx_pld(self):
self.spi.write(self.snum, [0xE3])
def get_pld_len(self):
""" Read length of the first Rx payload in the FIFO - available only if dyn len enabled """
return self.spi.query(self.snum, [0x60], rlen=1)[0]
def wr_ack_payload(self, pipe, pld):
""" Write a payload to be attached to the next sent ACK """
ba = bytearray()
ba.append(0xA8|(pipe&0x7))
ba.extend(pld)
self.spi.write(self.snum, ba)
def wr_payload_noack(self, pld):
""" Send a payload without ACK """
ba = bytearray()
ba.append(0xB0)
ba.extend(pld)
self.spi.write(self.snum, ba)
def status(self):
""" Send a payload without ACK """
return self.spi.query(self.snum, [0xFF], rlen=1, rskip=0)[0]
with gex.Client(gex.TrxRawUSB()) as client:
spi = gex.SPI(client, 'spi')
ce = gex.DOut(client, 'ce')
irq = gex.DIn(client, 'irq')
a = Nrf(ce, irq, spi, 0)
b = Nrf(ce, irq, spi, 1)
a.flush_tx()
a.flush_rx()
b.flush_tx()
b.flush_rx()
# transmit demo
# a is PTX, b is PRX
ce.clear(0b11)
# a_adr = [0xA1,0xA2,0xA3,0xA4,0x01]
b_pipe0_adr = [0xA1, 0xA2, 0xA3, 0xA4, 0x02]
# --- Configure A for Tx of a 32-long payload ---
# PWR DN - simulate reset
a.wr(0x00, 0)
b.wr(0x00, 0)
time.sleep(0.001)
a.wr(0x00, 0b00001010) # set PWR_ON=1, EN_CRC=1, all irq enabled
a.wr_payload([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31])
chnl = 5
# Set B's address as target and also pipe 0 Rx (used for ack)
a.wr(0x10, b_pipe0_adr) # target addr
a.wr(0x0A, b_pipe0_adr) # pipe 0 rx addr for ACK
a.wr(0x04, 0b00101111) # configure retransmit
a.wr(0x05, chnl)
# --- Configure B for Rx ---
b.wr(0x00, 0b00001011) # set PWR_ON=1, PRIM_RX=1, EN_CRC=1, all irq enabled
b.wr(0x02, 0b000001) # enable pipe 0
b.wr(0x11, 32) # set pipe 0 len to 11
b.wr(0x0A, b_pipe0_adr) # set pipe 0's address
b.wr(0x05, chnl)
ce.set(0b10) # CE high for B
time.sleep(0.01)
ce.pulse_us(us=10, pins=0b01) # Pulse A's CE
#
# testing B's FIFO by sending another payload...
a.wr_payload([0xFF, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30])
ce.pulse_us(us=20, pins=0b01) # Pulse A's CE
time.sleep(0.01)
print("A's status after Tx: %02x" % a.status())
print("B's status after Tx: %02x" % b.status())
ce.clear(0b11)
# read the two payloads
print(b.rd_payload(32))
print(b.rd_payload(32))

11
examples/demo_pulse.py

@ -1,11 +0,0 @@
#!/bin/env python3
import gex
import time
# generating a pulse on gpio, test of the unit
with gex.Client(gex.TrxRawUSB()) as client:
out = gex.DOut(client, 'out')
out.pulse_us([0], 20)
out.pulse_us([3], 10)

126
examples/demo_pymodoro.py

@ -1,126 +0,0 @@
import time
import gex
# GEX pomodoro timer
# button btn
# neopixel neo
# this is an example of using GEX as a user interface.
# for practical use it would be better to make this into a standalone device with a custom firmware.
WK_TIME = 25
BK_TIME = 5
LIGHT_CNT = 30
PH_BREAK = 'Break'
PH_BREAK_OVER = 'BreakOver'
PH_WORK = 'Work'
PH_WORK_OVER = 'WorkOver'
class Pymodoro:
def __init__(self):
self.phase = PH_BREAK_OVER
self.work_s = 0
self.break_s = 0
self.color = 0x000000
self.colors = [0x000000 for _ in range(0, LIGHT_CNT)]
self.client = gex.Client(gex.TrxRawUSB())
self.btn = gex.DIn(self.client, 'btn')
self.neo = gex.Neopixel(self.client, 'neo')
self.btn.on_trigger([0], self.on_btn)
self.switch(PH_BREAK_OVER)
self.display()
def display(self):
self.neo.load(self.colors)
def on_btn(self, snapshot, timestamp):
if self.phase == PH_BREAK_OVER:
self.switch(PH_WORK)
elif self.phase == PH_WORK:
self.switch(PH_WORK) # restart
elif self.phase == PH_WORK_OVER:
self.switch(PH_BREAK)
def switch(self, phase):
print("Switch to %s" % phase)
if phase == PH_BREAK:
self.color = 0x009900
self.break_s = BK_TIME * 60
elif phase == PH_BREAK_OVER:
self.color = 0x662200
elif phase == PH_WORK:
self.color = 0x990000
self.work_s = WK_TIME * 60
elif phase == PH_WORK_OVER:
self.color = 0x113300
self.colors = [self.color for _ in range(0, LIGHT_CNT)]
self.phase = phase
def show_progress(self, dark, total):
per_light = total / LIGHT_CNT
lights = dark / per_light
lights /= 2
remainder = float(lights - int(lights))
if remainder == 0:
remainder = 1
# print("lights %f, remainder %f" % (lights, remainder))
for i in range(0, int(LIGHT_CNT/2)):
if i < int((LIGHT_CNT/2)-lights):
c = 0x000000
elif i == int((LIGHT_CNT/2)-lights):
r = (self.color&0xFF0000)>>16
g = (self.color&0xFF00)>>8
b = self.color&0xFF
c = (int(r*remainder))<<16 | (int(g*remainder))<<8 | (int(b*remainder))
else:
c = self.color
self.colors[i] = c
self.colors[LIGHT_CNT - 1 - i] = c
def tick(self, elapsed):
if self.phase == PH_BREAK:
self.break_s -= elapsed
# print("Break remain: %d s" % self.break_s)
self.show_progress(self.break_s, BK_TIME * 60)
if self.break_s <= 0:
self.switch(PH_BREAK_OVER)
elif self.phase == PH_WORK:
self.work_s -= elapsed
# print("Work remain: %d s" % self.work_s)
self.show_progress(self.work_s, WK_TIME * 60)
if self.work_s <= 0:
self.switch(PH_WORK_OVER)
self.display()
def run(self):
step=0.5
try:
while True:
time.sleep(step)
self.tick(step)
except KeyboardInterrupt:
self.client.close()
print() # this puts the ^C on its own line
a = Pymodoro()
a.run()

37
examples/demo_transient.py

@ -1,37 +0,0 @@
#!/bin/env python3
import time
import gex
import numpy as np
from matplotlib import pyplot as plt
from scipy.io import wavfile
# catching a transient
with gex.Client(gex.TrxRawUSB()) as client:
adc = gex.ADC(client, 'adc')
rate=50000
fs = adc.set_sample_rate(rate)
d = None
def x(report):
global d
print("capt")
d = report
adc.on_trigger(x)
adc.setup_trigger(0, 50, 600, edge='rising', pretrigger=100)
adc.arm()
time.sleep(2)
if d is not None:
plt.plot(d.data, 'r-', lw=1)
plt.grid()
plt.show()
else:
print("Nothing rx")

78
examples/loratest.txt

@ -1,78 +0,0 @@
## UNITS.INI
## GEX v0.0.1 on STM32F072-HUB
## built Mar 17 2018 at 17:53:15
[UNITS]
# Create units by adding their names next to a type (e.g. DO=A,B),
# remove the same way. Reload to update the unit sections below.
# Digital output
DO=rst1,rst2
# Digital input with triggers
DI=
# Neopixel RGB LED strip
NPX=
# I2C master
I2C=
# SPI master
SPI=spi
# Serial port
USART=
# 1-Wire master
1WIRE=
# Analog/digital converter
ADC=
# Shift register driver (595, 4094)
SIPO=
# Frequency and pulse measurement
FCAP=
# Capacitive touch sensing
TOUCH=
# Simple PWM output
PWMDIM=
# Two-channel analog output with waveforms
DAC=
[DO:rst1@2]
# Port name
port=B
# Pins (comma separated, supports ranges)
pins=2
# Initially high pins
initial=2
# Open-drain pins
open-drain=
[DO:rst2@3]
# Port name
port=B
# Pins (comma separated, supports ranges)
pins=6
# Initially high pins
initial=6
# Open-drain pins
open-drain=
[SPI:spi@1]
# Peripheral number (SPIx)
device=1
# Pin mappings (SCK,MISO,MOSI)
# SPI1: (0) A5,A6,A7 (1) B3,B4,B5
# SPI2: (0) B13,B14,B15
remap=1
# Prescaller: 2,4,8,...,256
prescaller=64
# Clock polarity: 0,1 (clock idle level)
cpol=0
# Clock phase: 0,1 (active edge, 0-first, 1-second)
cpha=0
# Transmit only, disable MISO
tx-only=N
# Bit order (LSB or MSB first)
first-bit=MSB
# SS port name
port=B
# SS pins (comma separated, supports ranges)
pins=1-0

309
examples/main.py

@ -1,309 +0,0 @@
#!/bin/env python3
import time
import numpy as np
from matplotlib import pyplot as plt
import gex
transport = gex.TrxRawUSB(sn='0029002F-42365711-32353530')
#transport = gex.TrxSerialSync(port='/dev/ttyACM0')
with gex.Client(transport) as client:
#
# if True:
# s = client.ini_read()
# print(s)
# client.ini_write(s)
if True:
sipo = gex.SIPO(client, 'sipo')
sipo.load([[0xA5], [0xFF]])
if False:
adc = gex.ADC(client, 'adc')
print("Enabled channels:", adc.get_channels())
adc.set_smoothing_factor(0.9)
while True:
raw = adc.read_raw()
smooth = adc.read_smooth()
print("IN1 = %d (%.2f), Tsens = %d (%.2f), Vrefint = %d (%.2f)" % (raw[1], smooth[1],
raw[16], smooth[16],
raw[17], smooth[17]))
time.sleep(0.5)
if False:
adc = gex.ADC(client, 'adc')
adc.set_active_channels([1])
fs = adc.set_sample_rate(1000)
data = adc.capture(1000)
if data is not None:
plt.plot(data, 'r-', lw=1)
plt.show()
else:
print("Nothing rx")
# for r in range(0,8):
# adc.set_sample_time(r)
# data = adc.capture(10000)
# print("sr = %d" % r)
# std = np.std(data)
# print(std)
#
# global data
# data = None
#
# def capture(rpt):
# global data
# print("trig'd, %s" % rpt)
# data = rpt.data
# #
# # adc.setup_trigger(channel=1,
# # level=700,
# # count=20000,
# # pretrigger=100,
# # auto=False,
# # edge="falling",
# # holdoff=200,
# # handler=capture)
#
# # adc.arm()
#
# data = adc.capture(1000)
#
# if data is not None:
# plt.plot(data, 'r.', lw=1)
# plt.show()
# else:
# print("Nothing rx")
# plt.magnitude_spectrum(data[:,0], Fs=fs, scale='dB', color='C1')
# plt.show()
# def lst(data):
# if data is not None:
# print("Rx OK") #data
# else:
# print("Closed.")
# adc.stream_start(lst)
# time.sleep(3)
# adc.stream_stop()
# print("Done.")
# time.sleep(.1)
# print(adc.get_sample_rate())
# time.sleep(.1)
# adc.stream_stop()
# time.sleep(5)
# print(adc.capture(200, 5))
# adc.setup_trigger(channel=1,
# level=700,
# count=100,
# pretrigger=15,
# auto=True,
# edge="falling",
# holdoff=200,
# handler=lambda rpt: print("Report: %s" % rpt))
#
# print("Armed")
# adc.arm()
# print("Sleep...")
# # adc.force()
# #
# # # adc.disarm()
# time.sleep(5)
# adc.disarm()
# print(adc.capture(200, 50))
# adc.stream_start(lambda data: print(data))
# time.sleep(20)
# adc.stream_stop()
# print(adc.read_raw())
# time.sleep(1)
# print("Rx: ", resp)
# adc.abort()
if False:
s = client.ini_read()
print(s)
client.ini_write(s)
# search the bus
if False:
ow = gex.OneWire(client, 'ow')
print("Devices:", ow.search())
# search the bus for alarm
if False:
ow = gex.OneWire(client, 'ow')
print("Presence: ", ow.test_presence())
print("Devices w alarm:", ow.search(alarm=True))
# simple 1w check
if False:
ow = gex.OneWire(client, 'ow')
print("Presence: ", ow.test_presence())
print("ROM: 0x%016x" % ow.read_address())
print("Scratch:", ow.query([0xBE], rcount=9, addr=0x7100080104c77610, as_array=True))
# testing ds1820 temp meas without polling
if False:
ow = gex.OneWire(client, 'ow')
print("Presence: ", ow.test_presence())
print("Starting measure...")
ow.write([0x44])
time.sleep(1)
print("Scratch:", ow.query([0xBE], 9))
# testing ds1820 temp meas with polling
if False:
ow = gex.OneWire(client, 'ow')
print("Presence: ", ow.test_presence())
print("Starting measure...")
ow.write([0x44])
ow.wait_ready()
data = ow.query([0xBE], 9)
pp = gex.PayloadParser(data)
temp = pp.i16()/2.0
th = pp.i8()
tl = pp.i8()
reserved = pp.i16()
remain = float(pp.u8())
perc = float(pp.u8())
realtemp = temp - 0.25+(perc-remain)/perc
print("Temperature = %f °C (th %d, tl %d)" % (realtemp, th, tl))
if False:
buf = client.bulk_read(gex.MSG_INI_READ)
print(buf.decode('utf-8'))
pb = gex.PayloadBuilder()
pb.u32(len(buf))
client.bulk_write(gex.MSG_INI_WRITE, pld=pb.close(), bulk=buf)
if False:
leds = gex.DOut(client, 'strip')
nn = 3
for i in range(0,20):
leds.write(nn)
time.sleep(.05)
nn<<=1
nn|=(nn&0x40)>>6
nn=nn&0x3F
leds.clear(0xFF)
if False:
leds = gex.DOut(client, 'bargraph')
for i in range(0,0x41):
leds.write(i&0x3F)
time.sleep(.1)
if False:
leds = gex.DOut(client, 'TST')
for i in range(0, 0x41):
#leds.write(i & 0x3F)
leds.toggle(0xFF)
time.sleep(.1)
if False:
btn = gex.DIn(client, 'btn')
strip = gex.DOut(client, 'strip')
for i in range(0, 10000):
b = btn.read()
strip.write((b << 2) | ((~b) & 1))
time.sleep(.02)
if False:
neo = gex.Neopixel(client, 'npx')
print('We have %d neopixels.\n' % neo.get_len())
#neo.load([0xF0F0F0,0,0,0xFF0000])
# generate a little animation...
for i in range(0,512):
j = i if i < 256 else 255-(i-256)
neo.load([0x660000+j, 0x3300FF-j, 0xFFFF00-(j<<8), 0x0000FF+(j<<8)-j])
time.sleep(.001)
neo.load([0,0,0,0])
if False:
i2c = gex.I2C(client, 'i2c')
# i2c.write(0x76, payload=[0xD0])
# print(i2c.read(0x76, count=1))
print(i2c.read_reg(0x76, 0xD0))
print("%x" % i2c.read_reg(0x76, 0xF9, width=3, endian='big'))
i2c.write_reg(0x76, 0xF4, 0xFA)
print(i2c.read_reg(0x76, 0xF4))
if False:
spi = gex.SPI(client, 'spi')
spi.multicast(1, [0xDE, 0xAD, 0xBE, 0xEF])
print(spi.query(0, [0xDE, 0xAD, 0xBE, 0xEF], rlen=4, rskip=1))#
if False:
usart = gex.USART(client, 'serial')
usart.listen(lambda x: print("RX >%s<" % x))
for i in range(0,100):
# Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque ac bibendum lectus, ut pellentesque sem. Suspendisse ultrices felis eu laoreet luctus. Nam sollicitudin ultrices leo, ac condimentum enim vulputate quis. Suspendisse cursus tortor nibh, ac consectetur eros dapibus quis. Aliquam erat volutpat. Duis sagittis eget nunc nec condimentum. Aliquam erat volutpat. Phasellus molestie sem vitae quam semper convallis.
usart.write("""_.-"_.-"_.-"_.-"_.-"_.-"_.-"_.\r\n_.-"_.-"_.-"_.-"_.-"_.-"_.-"_.\r\n_.-"_.-"_.-"_.-"_.-"_.-"_.-"_.\r\n_.-"_.-"_.-"_.-"_.-"_.-"_.-"_.\r\n_.-"_.-"_.-"_.-"_.-"_.-"_.-"_.\r\n_.-"_.-"_.-"_.-"_.-"_.-"_.-"_.\r\n_.-"_.-"_.-"_.-"_.-"_.-"_.-"_.\r\n_.-"_.-"_.-"_.-"_.-"_.-"_.-"_.\r\n_.-"_.-"_.-"_.-"_.-"_.-"_.-"_.\r\n_.-"_.-"_.-"_.-"_.-"_.-"_.-"_.\r\n""".encode(), sync=True)
# time.sleep(.001)
if False:
usart = gex.USART(client, 'serial')
usart.listen(lambda x: print(x, end='',flush=True))
while True:
client.poll()
if False:
print(client.ini_read())
trig = gex.DIn(client, 'trig')
print(trig.read())
# Two pins are defined, PA10 and PA7. PA10 is the trigger, in the order from smallest to highest number 1
trig.arm(0b10)
trig.on_trigger(0b10, lambda snap,ts: print("snap 0x%X, ts %d" % (snap,ts)))
while True:
client.poll()
#
# for n in range(0,100):
# print(n)
# s = client.ini_read()
# client.ini_write(s)

24
examples/mat_sampling.m

@ -1,24 +0,0 @@
% this is an example of sampling the ADC from Matlab and then plotting a
% FFT graph. The ADC unit called 'adc' is configured to use PA1 as Ch. 0
%transport = py.gex.TrxSerialThread(pyargs('port', '/dev/ttyUSB1', 'baud', 57600));
transport = py.gex.TrxRawUSB();
client = py.gex.Client(transport);
adc = py.gex.ADC(client, 'adc');
L=1000;
Fs=1000;
adc.set_sample_rate(uint32(Fs));
data = adc.capture(uint32(L));
data = double(py.array.array('f',data));
Y = fft(data);
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1);
f = Fs*(0:(L/2))/L;
plot(f,P1)
client.close()

86
examples/nrf_config.ini

@ -1,86 +0,0 @@
## UNITS.INI
## GEX v0.1.0 on STM32F072-HUB
## built Mar 28 2018 at 00:42:03
[UNITS]
# Create units by adding their names next to a type (e.g. DO=A,B),
# remove the same way. Reload to update the unit sections below.
# Digital output
DO=ce
# Digital input with triggers
DI=irq
# Neopixel RGB LED strip
NPX=
# I2C master
I2C=