A light-emitting diode (LED) is a semiconductor device that emits visible light when an electric current passes through it. The light is not particularly bright, but in most LEDs it is monochromatic, occurring at a single wavelength. The output from an LED can range from red (at a wavelength of approximately 700 nanometers) to blue-violet (about 400 nanometers). Some LEDs emit infrared (IR) energy (830 nanometers or longer); such a device is known as an infrared-emitting diode (IRED).
Schematic
Code
from machine import Pin
from time import sleep
# GPIO16 (D0) is the internal LED for NodeMCU
led = Pin(16, Pin.OUT)
# The internal LED turn on when the pin is LOW
while True:
led.high()
sleep(1)
led.low()
sleep(1)
Here is the reason why the built in led turn on when Pin 16 is Low :)
Toggle a LED
def led_toggle():
led.value(not led.value())
Now we can use this function to blink the LED
while True:
led_toggle()
sleep(1)
Button
Schematic
Code
from machine import Pin
from time import sleep
led = Pin(16, Pin.OUT)
button = Pin(14, Pin.IN, Pin.PULL_UP)
while True:
# The value function returns the current level of the pin,
# either 1 for a high logic level or 0 for a low logic level.
# Notice how the button is at a high level (value returns 1) when
# it's not pressed. This is because the pull-up resistor keeps the pin at
# a high level when it's not connected to ground through the button.
# When the button is pressed then the input pin connects to ground
# and reads a low level (value returns 0).
if not button.value():
# Remember that the internal led turn on
# when the pin is LOW
led.low()
else:
led.high()
sleep(.1)
Pull Up resistor
With a pull-up resistor, the input pin will read a high state when the button is not pressed. In other words, a small amount of current is flowing between VCC and the input pin (not to ground), thus the input pin reads close to VCC. When the button is pressed, it connects the input pin directly to ground. The current flows through the resistor to ground, thus the input pin reads a low state.
def led_toggle():
led.value(not led.value())
while True:
if not button.value():
led_toggle()
time.sleep(.5)
NeoPixels
NeoPixels, also known as WS2812b LEDs, are full-colour LEDs that are connected in serial, are individually addressable, and can have their red, green and blue components set between 0 and 255. They require precise timing to control them and there is a special neopixel module to do just this.
Schematic
NeoPixel basics
To create a NeoPixel object do the following:
>>> from machine import Pin
>>> from neopixel import NeoPixel
>>> np = NeoPixel(Pin(14), 8)
This configures a NeoPixel strip on GPIO14 with 8 pixels. You can adjust the “14” (pin number) and the “8” (number of pixels) to suit your set up.
Then go to the python shell, reboot MicroPython with Ctrl-D (to load the main.py file) and run the function demo(pin, n), where pin is the GPIO number and n is the length of pixels in the neopixels array.
$ bin/shell
[Ctrl-D]
PYB: soft reboot
#21 ets_task(40100164, 3, 3fff8398, 4)
could not open file 'boot.py' for reading
MicroPython v1.8.7-7-gb5a1a20a3 on 2017-01-09; ESP module with ESP8266
Type "help()" for more information.
>>> demo(pin=14, n=8)
$ phant
phant http server running on port 8080
phant telnet server running on port 8081
Go to the browser and open the http://localhost:8080, then click on Create a data stream. Complete the Title with "ESP8266 Weather" and the Description with "ESP8266 Weather Station" for the new data stream and put the following fields: temperature and humidity. Then save and copy the public and private key (or download the keys as a JSON file).
Making http requests with urequests
MicroPython has urequest which is very small port from the Python Requests lib.
Go to the examples/dht11/src/python folder and update the boot.py file with your WiFi essid and password, and the main.py file with the Phant host and keys. Then upload the files with the ampy tool, using the script:
$ bin/put.sh
Then open the Python shell, reboot MicroPython (to load the files) and run the following
$ bin/shell
[Ctrl-D]
...
>>> data = read_sensor()
>>> phant_log(data)
There is a function run() to do this endless
while True:
data = read_sensor()
print("Sending data", data)
phant_log(data)
time.sleep(60)
But you can do OTA with C++ as described in the C++ OTA section of this wiki.
AP
You can put the esp8266 as an Access Point mode with the following lines
import network
ap = network.WLAN(network.AP_IF)
ap.config(essid="ESP-AP", password="12345678")
ap.active(True)
RGB Lamp
PWM (Pulse With Modulation)
Pulse width modulation (PWM) is a way to get an artificial analog output on a digital pin. It achieves this by rapidly toggling the pin from low to high. There are two parameters associated with this: the frequency of the toggling, and the duty cycle.
import socket
import machine
import template
ADDR = ('0.0.0.0', 80)
PIN_R = 12
PIN_G = 13
PIN_B = 14
class RGBLed:
def __init__(self, pin_r, pin_g, pin_b):
self.pin_r = machine.PWM(machine.Pin(pin_r))
self.pin_g = machine.PWM(machine.Pin(pin_g))
self.pin_b = machine.PWM(machine.Pin(pin_b))
self.set(0, 0, 0)
def set(self, r, g, b):
self.r = int(r)
self.g = int(g)
self.b = int(b)
self.duty()
def duty(self):
self.pin_r.duty(self.duty_translate(self.r))
self.pin_g.duty(self.duty_translate(self.g))
self.pin_b.duty(self.duty_translate(self.b))
def duty_translate(self, n):
"""translate values from 0-255 to 0-1023"""
return int((float(n) / 255) * 1023)
def get_url(conn):
conn_file = conn.makefile('rb', 0)
method, url = None, None
while True:
line = conn_file.readline().decode()
if not line or line == '\r\n':
break
if line.startswith('GET'):
method, url, _ = line.split()
return method, url
def parse_url(url):
try:
path, query = url.split('?', 2)
except:
return url, {}
return path, {_.split('=')[0]: _.split('=')[1] for _ in query.split('&')}
def conn_send(conn, response):
total_sent = 0
while total_sent < len(response):
sent = conn.send(response[total_sent:])
if sent == 0:
raise RuntimeError('Socket connection broken')
total_sent = total_sent + sent
led = RGBLed(PIN_R, PIN_G, PIN_B)
s = socket.socket()
s.bind(ADDR)
s.listen(1)
print('RGBLamp daemon started on %s:%s' % ADDR)
while True:
conn, addr = s.accept()
method, url = get_url(conn)
path, query = parse_url(url)
print(addr[0], '-', method, url)
if path == '/':
if [_ for _ in list('rbg') if _ in query.keys()]:
led.set(query.get('r', 0), query.get('g', 0), query.get('b', 0))
response = template.html % (led.r, led.g, led.b)
conn_send(conn, response)
conn.close()
Servo sg90
The sg90 servo has three wires: brown (gnd), red (vcc), and yellow (signal). The vcc is the power source for the servo, and it has to be connected to the vin pin of our board – this way it is connected directly to the USB port, and not powered through the board.
The signal wire tells the servo what position it should move to, using a 50Hz PWM signal. The center is at around 77, and the exact range varies with the servo model, but should be somewhere between 30 and 122, which corresponds to about 180° of movement. Note that if you send the servo a signal that is outside of the range, it will still obediently try to move there – hitting a mechanical stop and buzzing loudly. If you leave it like this for longer, you can damage your servo, your board or your battery, so please be careful.
So now we are ready to try and move it to the center position:
from machine import Pin, PWM
servo = PWM(Pin(14), freq=50, duty=77)
Then we can see where the limits of its movement are:
from machine import Pin, Timer
led = Pin(5, Pin.OUT)
led.value(0)
timer = Timer(-1)
timer.init(period=1000, mode=Timer.PERIODIC, callback=lambda t:led.value(not led.value()))
timers_and_interrupts_interrupt
from machine import Pin
led = Pin(5, Pin.OUT)
button = Pin(4, Pin.IN, Pin.PULL_UP)
button.irq(trigger=Pin.IRQ_FALLING, handler=lambda t:led.value(not led.value()))
timers_and_interrupts_debounce
from machine import Pin
led = Pin(5, Pin.OUT)
button = Pin(4, Pin.IN, Pin.PULL_UP)
def debounce(pin):
prev = None
for _ in range(32):
current_value = pin.value()
if prev != None and prev != current_value:
return None
prev = current_value
return prev
def button_callback(pin):
d = debounce(pin)
if d == None:
return
elif not d:
led.value(not led.value())
button.irq(trigger=Pin.IRQ_FALLING, handler=button_callback)
Pwm and Adc with a Nodemcu and Micropython
https://micronote.tech/2020/02/PWM-and-ADC-with-a-NodeMCU-and-MicroPython/
from machine import Pin, PWM, ADC, Timer
POT_LOWER_EXTREME = 18
POT_UPPER_EXTREME = 1024
pwm_led = PWM(Pin(5), freq=1000)
pot = ADC(0)
def pot_percent(val, lower_limit, upper_limit):
if val > upper_limit:
val = upper_limit
elif val < lower_limit:
val = lower_limit
total_range = upper_limit - lower_limit
return (val-lower_limit)/total_range
def set_dimmer(pin):
pwm_led.duty(int(pot_percent(pot.read(), POT_LOWER_EXTREME, POT_UPPER_EXTREME) * 1023))
timer = Timer(-1)
timer.init(period=100, mode=Timer.PERIODIC, callback=set_dimmer)
6 axis
6axisRobotic-Arm-RaspberryRobotControl
Smoothieboard can be made to work for more than 3 axes.
We are not talking about extruders here ( these are supported separately, see Extruder ), but rotational axes, like those for example used for 4 or 5 axis machining.
This feature is one of the rare ones that requires compiling Smoothie.
Because it increases the size of the movement planning queue significantly, we were unfortunately not able to fit it in the “normal” firmware.
There are two resources that were written about this feature when it was released that you might want to read :
ABC pull request on github.
Smoothie's upgrade notes
Workpiece Coordinate System
NOTE that currently WCS is not supported for ABC axis (G10 L2…).
G92 A0 resets the A axis and does not set the WCS for it (same for B and C).
Extruders
NOTE You cannot define extruders AND ABC axis they are mutually exclusive and smoothie will not run if they are both defined.
Compiling 6 axis.
Before attempting this, please read compiling smoothie, and compile Smoothie “normally” for practice.
Once that is done ( and only once that is done ), you need to do the same process, but instead of doing
make
You now do :
make clean
make AXIS=6 CNC=1
You can change this number to 5 or 4 if you do not need all 6 axes.
It saves memory and allows you to use more of it for other things.
By default, Smoothie calculates distances only on the first three ( XYZ ) axes.
You can change this behavior by setting the PAXIS compilation parameter, for example:
make AXIS=5 PAXIS=4 CNC=1
Means Smoothie is compiled with XYZAB axes, and distances are calculated in the XYZA space.
Primary Axes
NOTE that in most cases you DO NOT need to set PAXIS.
Only do this if you fully understand the difference between a Cartesian (primary) axis and say a rotary axis.
Once your firmware is compiled, you can now flash it to the board and start using it.
Using additional axes
XYZ first
NOTE You MUST fully define the alpha, beta and gamma (XYZ) axis before the other axis.
They MUST have valid pin definitions for the step and dir pins (enable is optional) If you do not define valid pins for these first three axis smoothie will not boot.
You can now add the following to your configuration file :
# A axis
delta_steps_per_mm 100 # may be steps per degree for example
delta_step_pin xx # Pin for delta stepper step signal
delta_dir_pin xx # Pin for delta stepper direction
delta_en_pin xx # Pin for delta enable
delta_current 1.5 # Z stepper motor current
delta_max_rate 300.0 # mm/min
delta_acceleration 500.0 # mm/sec²
# B axis
epsilon_steps_per_mm 100 # may be steps per degree for example
epsilon_step_pin xx # Pin for delta stepper step signal
epsilon_dir_pin xx # Pin for delta stepper direction
epsilon_en_pin xx # Pin for delta enable
epsilon_current 1.5 # Z stepper motor current
epsilon_max_rate 300.0 # mm/min
epsilon_acceleration 500.0 # mm/sec²
# C axis
zeta_steps_per_mm 100 # may be steps per degree for example
zeta_step_pin xx # Pin for delta stepper step signal
zeta_dir_pin xx # Pin for delta stepper direction
zeta_en_pin xx # Pin for delta enable
zeta_current 1.5 # Z stepper motor current
zeta_max_rate 300.0 # mm/min
zeta_acceleration 500.0 # mm/sec²
This configuration is very similar to that of your XYZ axes, and you need to change the values to fit your setup.
Optionally if you are using endstops on the A, B or C axis, you need to replace your endstops section with the following ( it is also found in the snippets example ) :
Only for more than 3 axes
NOTE DO NOT use the following syntax if you only have XYZ axis! use the regular endstop config syntax.
Hand in hand
NOTE If you define a homing axis then there MUST be a defined axis with the same designation (eg B endstop MUST have B axis defined)
NOTE The ABC axis will always home after the XYZ axis home and will home individually, unless homing_order is defined in which case all axis will home individually in the order specified.
## Endstops new syntax (the name is not significant)
# NOTE only a min or a max homing endstop maybe defined
endstop.minx.enable true # enable an endstop
endstop.minx.pin 1.24 # pin
endstop.minx.homing_direction home_to_min # direction it moves to the endstop
endstop.minx.homing_position 0 # the cartesian coordinate this is set to when it homes
endstop.minx.axis X # the axis designator
endstop.minx.max_travel 500 # the maximum travel in mm before it times out
endstop.minx.fast_rate 50 # fast homing rate in mm/sec
endstop.minx.slow_rate 25 # slow homing rate in mm/sec
endstop.minx.retract 5 # bounce off endstop in mm
# uncomment for homing to max and comment the minx above
#endstop.maxx.enable true # enable an endstop
#endstop.maxx.pin 1.25 # pin
#endstop.maxx.homing_direction home_to_max # direction it moves to the endstop
#endstop.maxx.homing_position 200 # the cartesian coordinate this is set to when it homes
#endstop.maxx.axis X # the axis designator
#endstop.maxx.max_travel 500 # the maximum travel in mm before it times out
#endstop.maxx.fast_rate 50 # fast homing rate in mm/sec
#endstop.maxx.slow_rate 25 # slow homing rate in mm/sec
#endstop.maxx.retract 5 # bounce off endstop in mm
endstop.miny.enable true # enable an endstop
endstop.miny.pin 1.26 # pin
endstop.miny.homing_direction home_to_min # direction it moves to the endstop
endstop.miny.homing_position 0 # the cartesian coordinate this is set to when it homes
endstop.miny.axis Y # the axis designator
endstop.miny.max_travel 500 # the maximum travel in mm before it times out
endstop.miny.fast_rate 50 # fast homing rate in mm/sec
endstop.miny.slow_rate 25 # slow homing rate in mm/sec
endstop.miny.retract 5 # bounce off endstop in mm
# uncomment for homing to max and comment the min above
#endstop.maxy.enable true # enable an endstop
#endstop.maxy.pin 1.27 # pin
#endstop.maxy.homing_direction home_to_max # direction it moves to the endstop
#endstop.maxy.homing_position 200 # the cartesian coordinate this is set to when it homes
#endstop.maxy.axis Y # the axis designator
#endstop.maxy.max_travel 500 # the maximum travel in mm before it times out
#endstop.maxy.fast_rate 50 # fast homing rate in mm/sec
#endstop.maxy.slow_rate 25 # slow homing rate in mm/sec
#endstop.maxy.retract 5 # bounce off endstop in mm
endstop.minz.enable true # enable an endstop
endstop.minz.pin 1.28 # pin
endstop.minz.homing_direction home_to_min # direction it moves to the endstop
endstop.minz.homing_position 0 # the cartesian coordinate this is set to when it homes
endstop.minz.axis Z # the axis designator
endstop.minz.max_travel 100 # the maximum travel in mm before it times out
endstop.minz.fast_rate 10 # fast homing rate in mm/sec
endstop.minz.slow_rate 2 # slow homing rate in mm/sec
endstop.minz.retract 5 # bounce off endstop in mm
# uncomment for homing to max and comment the min above
#endstop.maxz.enable true # enable an endstop
#endstop.maxz.pin 1.29 # pin
#endstop.maxz.homing_direction home_to_max # direction it moves to the endstop
#endstop.maxz.homing_position 200 # the cartesian coordinate this is set to when it homes
#endstop.maxz.axis Z # the axis designator
#endstop.maxz.max_travel 100 # the maximum travel in mm before it times out
#endstop.maxz.fast_rate 10 # fast homing rate in mm/sec
#endstop.maxz.slow_rate 2 # slow homing rate in mm/sec
#endstop.maxz.retract 5 # bounce off endstop in mm
# optional enable limit switches, actions will stop if any enabled limit switch is triggered
#endstop.minx.limit_enable false # set to true to enable the limit on this endstop
#endstop.miny.limit_enable false # set to true to enable the limit on this endstop
#endstop.minz.limit_enable false # set to true to enable the limit on this endstop
# also define the pin needed and the Axis designator if limit on a max pin not defined above
#endstop.maxx.enable true # enable an endstop
#endstop.maxx.pin 1.25 # pin
#endstop.maxx.limit_enable true # set to true to enable the limit on this endstop
#endstop.maxx.axis X # the axis designator
# OPTIONAL uncomment for homing the A axis to min
#endstop.mina.enable true # enable an endstop
#endstop.mina.pin 1.29 # pin
#endstop.mina.homing_direction home_to_min # direction it moves to the endstop
#endstop.mina.homing_position 200 # the cartesian coordinate this is set to when it homes
#endstop.mina.axis A # the axis designator
#endstop.mina.max_travel 100 # the maximum travel in mm before it times out
#endstop.mina.fast_rate 10 # fast homing rate in mm/sec
#endstop.mina.slow_rate 2 # slow homing rate in mm/sec
#endstop.mina.retract 5 # bounce off endstop in mm
# type of machine
#corexy_homing false # set to true if homing on a hbot or corexy
# optional order in which axis will home, default is they all home at the same time,
# if this is set it will force each axis to home one at a time in the specified order
#homing_order XYZ # x axis followed by y then z last
#move_to_origin_after_home false # move XY to 0,0 after homing
#endstop_debounce_count 100 # uncomment if you get noise on your endstops, default is 100
#endstop_debounce_ms 1 # uncomment if you get noise on your endstops, default is 1 millisecond debounce
#home_z_first true # uncomment and set to true to home the Z first, otherwise Z homes after XY
motorsPyboard 6V DC motorsServo Motor control using MicroPython