I wrote a page summarizing my HackMIT 2016 Project, check it out here!
I wrote a page summarizing my HackMIT 2016 Project, check it out here!
Edit: I don’t really make a point of saying it, but the common ground between the modules isn’t nesesarry.
Here’s a video of the software running on the boards.
So for this example, there are 4 bytes being transmitted from the master to the slaves.
A transmission to set the pwm value of the green led on the slave with the ID 1 to 100 would be:
1 |
0xDB 0x01 0x01 0x64 |
This is the schematic of the circuit for this example:
This is the code running on the master Arduino:
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
#include <SoftwareSerial.h> int MAX485_Receiver_Output_PIN = 10; int MAX485_Driver_Input_PIN = 11; int MAX485_Driver_Output_Enable_PIN = 12; #define NUMSWITCHES 3 int ID_switches[NUMSWITCHES] = {2, 3, 4}; int mode_switch = 5; byte IDs[NUMSWITCHES] = {1, 2, 3}; #define TXSIZE 4 SoftwareSerial software_serial (MAX485_Receiver_Output_PIN, MAX485_Driver_Input_PIN); // RX, TX byte target_node_ID; struct transmission { byte start = 0xDB; byte ID; byte type; byte message; const int tx_size = TXSIZE; }; void setup() { software_serial.begin(9600); // begin software serial pinMode(MAX485_Driver_Output_Enable_PIN, OUTPUT); digitalWrite(MAX485_Driver_Output_Enable_PIN, HIGH); // this disables Receiver Output Enable and enables Driver Output Enable for (int index = 0; index < NUMSWITCHES; index++) { pinMode(ID_switches[index], INPUT); } pinMode(mode_switch, INPUT); target_node_ID = 0; } void loop() { for (int index = 0; index < NUMSWITCHES; index++) { if ((byte)digitalRead(ID_switches[index])) { target_node_ID = IDs[index]; break; } } transmission t; t.ID = target_node_ID; t.type = digitalRead(mode_switch); t.message = (byte)map(analogRead(0), 0, 1023, 0x00, 0xFF); byte message[TXSIZE] = {t.start, t.ID, t.type, t.message}; software_serial.write(message, t.tx_size); } |
This is the code running on the slave Arduinos:
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
#include <SoftwareSerial.h> int MAX485_Receiver_Output_PIN = 10; int MAX485_Driver_Input_PIN = 11; int MAX485_Driver_Output_Enable_PIN = 12; int led_1_PIN = 5; int led_2_PIN = 6; unsigned long time = 0; unsigned long oldtime = 0; int state = 0; int next_ID_button_PIN = 2; #define NUMSTATES 3 int states[NUMSTATES] = {7, 8, 9}; byte IDs[NUMSTATES] = {1, 2, 3}; #define MESSAGELENGTH 3 SoftwareSerial software_serial (MAX485_Receiver_Output_PIN, MAX485_Driver_Input_PIN); // RX, TX void setup() { software_serial.begin(9600); // begin software serial pinMode(MAX485_Driver_Output_Enable_PIN, OUTPUT); digitalWrite(MAX485_Driver_Output_Enable_PIN, LOW); pinMode(led_1_PIN, OUTPUT); pinMode(led_2_PIN, OUTPUT); pinMode(next_ID_button_PIN, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(next_ID_button_PIN), next_ID, RISING); set_LEDs(); } void loop() // run over and over { byte start_of_message = 0xDB; byte incoming_byte; byte message[MESSAGELENGTH]; while (true) { if (software_serial.available()) { incoming_byte = software_serial.read(); if (incoming_byte == start_of_message) { software_serial.readBytes(message, MESSAGELENGTH); process_message(message); } } } } void process_message(byte message[]) { byte system_ID = IDs[state]; byte incoming_ID = message[0]; byte incoming_mode = message[1]; byte LED_brightness = message[2]; if (incoming_ID == system_ID) { byte pin; if (incoming_mode == 0) { pin = led_1_PIN; } else if (incoming_mode == 1) { pin = led_2_PIN; } analogWrite(pin, LED_brightness); } } void next_ID() { time = millis(); //soft debounce if (time - oldtime > 200) { oldtime = time; state++; if (state >= (NUMSTATES)) { state = 0; } } set_LEDs(); } void set_LEDs() { for (int index = 0; index < NUMSTATES; index++) { if (index == state) { digitalWrite(states[index], HIGH); } else { digitalWrite(states[index], LOW); } } } |
For another project I’m currently working on, I need a way to control multiple microcontrollers in a multi-point, multi-drop network configuration. Luckily, we live in amazing times. As I write this, you you can buy a fully assembled breakout board for the MAX485 chip from Maxim Integrated for a mere $0.45 USD shipped from China.
I bought a 5 pack, here are some of the boards:
RS485 is an old protocol, but is the logical next step for devices I’m already communicating with via RS232. For this example, I’m using 4 Arduino boards of various types.
Here is a video of the setup:
The schematic is really straightforward as well. The only tricky bit is that I’m using a software serial port on each of the Arduinos for ease of debugging. Here’s a schematic:
The code to acomplish this is really intuitive as well.
Here is the code for the master Arduino:
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 32 33 34 35 36 37 38 39 40 41 42 43 |
#include <SoftwareSerial.h> int MAX485_Receiver_Output_PIN = 10; int MAX485_Driver_Input_PIN = 11; int MAX485_Driver_Output_Enable_PIN = 12; int debug_led = 13; SoftwareSerial software_serial (MAX485_Receiver_Output_PIN, MAX485_Driver_Input_PIN); // RX, TX void setup() { Serial.begin(9600); // begin hardware serial software_serial.begin(9600); // begin software serial pinMode(MAX485_Driver_Output_Enable_PIN, OUTPUT); digitalWrite(MAX485_Driver_Output_Enable_PIN, HIGH); // this disables Receiver Output Enable and enables Driver Output Enable } void loop() { byte to_send = 0; // declare the byte to be sent to the slaves though, init as 0 int rate; while (true) { // invert the byte to be sent if (to_send == 1) to_send = 0; else if (to_send == 0) to_send = 1; Serial.print("Sending: "); Serial.println(to_send); digitalWrite(debug_led, to_send); rate = map(analogRead(5), 0, 1023, 0, 1000); software_serial.write(to_send); // send our byte out to the MAX485 delay(rate); } } |
This is the code for the slave Arduinos:
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 |
#include <SoftwareSerial.h> int MAX485_Receiver_Output_PIN = 10; int MAX485_Driver_Input_PIN = 11; int MAX485_Driver_Output_Enable_PIN = 12; int debug_led_PIN = 9; SoftwareSerial software_serial (MAX485_Receiver_Output_PIN, MAX485_Driver_Input_PIN); // RX, TX void setup() { software_serial.begin(9600); // begin software serial pinMode(MAX485_Driver_Output_Enable_PIN, OUTPUT); digitalWrite(MAX485_Driver_Output_Enable_PIN, LOW); pinMode(debug_led_PIN, OUTPUT); } void loop() // run over and over { byte k; if (software_serial.available() > 0) // make sure there is something to read { k = software_serial.read(); // read in a single byte from the serial port digitalWrite(debug_led_PIN, k); } } |
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 |
#include <SoftwareSerial.h> int MAX485_Receiver_Output_PIN = 10; int MAX485_Driver_Input_PIN = 11; int MAX485_Driver_Output_Enable_PIN = 12; int debug_led_PIN = 9; SoftwareSerial software_serial (MAX485_Receiver_Output_PIN, MAX485_Driver_Input_PIN); // RX, TX void setup() { software_serial.begin(9600); // begin software serial pinMode(MAX485_Driver_Output_Enable_PIN, OUTPUT); digitalWrite(MAX485_Driver_Output_Enable_PIN, LOW); pinMode(debug_led_PIN, OUTPUT); } void loop() // run over and over { byte k; if (software_serial .available() > 0) // make sure there is something to read { k = software_serial.read(); // read in a single byte from the serial port digitalWrite(debug_led_PIN, k); } } |
In subsequent posts, things will start getting more advanced. For now however this should be enough to start from scratch.
Thanks for reading.
Trying to get the most out of a day has been big theme of my life lately, as I’m sure it is for many people. I’ve found that I always manage my time better when things are urgent; I’m considerably more productive when I have to be.
I want an ascetically pleasing way to be able to represent how much time is left in the day at a granular scale, like an hourglass. Watching individual seconds disappear will look cool and (hopefully) create that sense of urgency that I want to induce.
Technically, this is a really simple thing to accomplish thanks to python and pygame. Here’s a video of a proof of concept running on my laptop:
At the start of each day, the display is filled with squares at random locations, with a random color. As each second elapses, a square will vanish.
To make it easier to see for the video, I’ve made the squares much bigger than they will actually be for the final build. This is what the display looks like with the squares at their actual size:
The code really really simple, like less than 100 lines simple. Here’s how it works:
Here’s the version of the code running on my computer in the video:
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
import pygame from random import randint from apscheduler.schedulers.blocking import BlockingScheduler import datetime class random_square(object): def __init__(self, max_x_location, max_y_location): self.x_loc = randint(0, max_x_location) self.y_loc = randint(0, max_y_location) max_color_value = 255 red = randint(0, max_color_value) green = randint(0, max_color_value) blue = randint(0, max_color_value) self.color = [red, green, blue] class clock(object): def __init__(self, initial_count, max_count, screen_w, screen_h): self.max_count = max_count self.screen_w = screen_w self.screen_h = screen_h # create the screen object, force pygame fullscreen mode self.screen = pygame.display.set_mode([screen_w, screen_h], pygame.FULLSCREEN) # the screen's width in pixels is stored in the 0th element of the array self.square_size = screen_w / 200 # create the list of squares, initially as empty self.squares = [] # fill the squares with the inital seconds until midnight for second in range(initial_count): self.squares.append(random_square(screen_w, screen_h)) # starts ticking the clock def start(self): scheduler = BlockingScheduler() scheduler.add_job(self.tick, 'interval', seconds=1) try: scheduler.start() except (KeyboardInterrupt, SystemExit): pass # this occurs once every time a unit of time elapses def tick(self): # this will happen once per "day" if len(self.squares) == 0: # fill the list of squares to be drawn for second in range(self.max_count): self.squares.append(random_square(self.screen_w, self.screen_h)) # draw a blank screen self.screen.fill([0, 0, 0]) # draw the squares for square in self.squares: rect = (square.x_loc, square.y_loc, self.square_size, self.square_size) pygame.draw.rect(self.screen, square.color, rect, 0) pygame.display.update() # remove a single square from the list as one tick has elapsed self.squares.pop() # initialize pygame pygame.init() # figure out the parameters of the display we're connected to screen_width = pygame.display.Info().current_w screen_height = pygame.display.Info().current_h screen_size = screen_width, screen_height # determine the number of seconds until midnight seconds_in_a_day = 86400 now = datetime.datetime.now() midnight = now.replace(hour=0, minute=0, second=0, microsecond=0) seconds_until_midnight = seconds_in_a_day - (now - midnight).seconds # create and start the clock! cl = clock(seconds_until_midnight, seconds_in_a_day, screen_width, screen_height) cl.start() |
Let’s walk through some of the design decisions of this code. The first thing that’s worth talking about is how the data for the squares is handled:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class random_square(object): def __init__(self, max_x_location, max_y_location): self.x_loc = randint(0, max_x_location) self.y_loc = randint(0, max_y_location) max_color_value = 255 red = randint(0, max_color_value) green = randint(0, max_color_value) blue = randint(0, max_color_value) self.color = [red, green, blue] |
It’s just an object with no methods, and on initialization, all the parameters of the square (location and color) are generated randomly as opposed to just floating the raw numbers in arrays around (even though that’s basically what is happening). This let’s us fill the squares array very easily later on in the file here:
1 2 3 |
# fill the squares with the inital seconds until midnight for second in range(initial_count): self.squares.append(random_square(screen_w, screen_h)) |
and here:
1 2 3 4 5 6 |
# this will happen once per "day" if len(self.squares) == 0: # fill the list of squares to be drawn for second in range(self.max_count): self.squares.append(random_square(self.screen_w, self.screen_h)) |
When it comes time to draw these squares, it also makes that pretty intuitive:
1 2 3 4 |
# draw the squares for square in self.squares: rect = (square.x_loc, square.y_loc, self.square_size, self.square_size) pygame.draw.rect(self.screen, square.color, rect, 0) |
Again, very simple stuff, but worth it to talk about.
I’ll be back at my place that has the Raspberry Pi and display I would like to use for this project, so more on this then.
Thanks for reading!
Here’s a demo of the finished system:
In the end, it all turned out really well. Painting it white and using a white print stand was a good insight, the light reflects around the box pretty well for how few LEDs are in use.
The software flow chart has changed slightly. I removed the speaker as it wasn’t loud enough and added software debouching for the pushbutton interrupt service routine. Here’s that most recent version:
The interesting parts of the code are the cookResin function as well as the main loop of the Arduino:
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
void loop() { digitalWrite(BUTTONLED_pin, HIGH); displayStaticState(0); if (BPRESSED == true) { cookResin(14400); } } void cookResin(unsigned long MAXTIMER){ unsigned long SECTIMER = 0; writeLCDLines("CURING TIME REM:", "H:MM:SS -:--:--"); fadeLEDs(1,1); while ((SECTIMER < MAXTIMER) && (BPRESSED)) { digitalWrite(BUTTONLED_pin, HIGH); String part0 = String("H:MM:SS "); String part1 = secToHuman(MAXTIMER - SECTIMER); String line1 = part0 + part1; lcd.setCursor(0, 1); lcd.print(line1); int DMILSECTIMER = 0; while ((DMILSECTIMER < 10) && (BPRESSED)){ digitalWrite(BUTTONLED_pin, HIGH); Alarm.delay(100); //Alarm.delay(1); DMILSECTIMER++; } SECTIMER++; } if (BPRESSED == false){ //start of cancelled cure end displayStaticState(2); fadeLEDs(0,1); delay(1000); BPRESSED = false; //end of cancelled cure end } else { //start of successful cure end displayStaticState(1); fadeLEDs(0,1); delay(1000); BPRESSED = false; //end of sec cure end } return; } |
Again, this all should all be explained by the flow chart. The full source can be found at the bottom of this post.
The circuit schematic hasn’t changed at all since this post, here’s a fritzing of what’s going on:
Super simple, basically a screen and a button. The parts to make this are here:
Assembly is super straight forward, if you’re trying to build one and have any questions, let me know!
Thanks for reading!
This past school year I too several classes related to 3D modeling. One class in particular, a class based around SolidWorks. I hadn’t really been able to use the software again, not having the tools to actually execute. MADE@MassChallenge really has the whole kit, 3D Printers, a 40W Laser Cutter etc. All the tools of a hackerspace as a part of my job. Here’s a “finished” model of the system:
The frame is built out of:
There were a couple 3D modeled components as well:
I ended up gluing these down with hot glue even though they have cuts for screws. In the end, it wasn’t worth it to use more screws and add more complexity.
There is a frosted acrylic sheet inserted in the top. One of the goals of this project was to show off the tech, and I think this does that quite nicely.
The knob has a stem that comes of the back and forces the hinge back, keeping the door closed. I wanted to try and keep things as simple as possible. The threads I modeled weren’t within tolerance. So I just glued the nut in place so the knob could rotate freely.
It doesn’t make sense to have the prints just sit on the bottom of the frame. I also cut inserts that fit the inside of the print stand. This is so resin doesn’t cure to the print stand so it can be used many times while only needing to change the cardstock inserts.
Here are some more photos of the build process:
I’ll include the plans to build this whole assembly in the final post for this project once it’s all finalized.
This project is the first of what I hope to be many in collaboration with the MADE@MassChallenge Hardware lab. The primary goal of this project is to speed up the time to delivery on prints coming out of the Formlabs Form 1+ SLA 3D printer using UV LEDS. Here’s a proof of concept of my circuit:
One of my tasks during my internship at MassChallenge was managing the queue of incoming models to be 3D printed on our 3D printers. Turnaround is often a pressing issue when doing this. It was often the case that teams had a deadlines or presentations that they needed parts for. Shaving even minutes off of the time from submission to receiving a fully processed part mattered quite a bit.
The Form 1+ is an amazing printer. If used correctly, the print quality can be much higher than the other 3D printer in MADE, a uPrint SE Plus by Stratysis; a printer almost 5 times the cost.
The post processing involved with the Formlabs has a steeper of a learning curve and leaves a lot of room for possibly destroying a part in the process.
The problem is not a fault of Formlabs, but rather a problem in the chemistry behind the resins used to create the parts. They are photopolymers, and need UV light to be cured. It is suggested that this be done through exposure to sunlight, but that takes quite a long time. I also have a sneaking suspicion that there are adverse effects of doing this, but I can’t prove any of that as of now but hopefully more on that later.
As this is a project that will be used by people other than myself, it is worth it to commit time and effort into the user experience. Atheistic should also be taken into account as this has to stand up next to the beautiful design of the Form1+. In short, a UV LED strand, a 3A switch, a power supply and a Light tight box could functionally do the trick, but in this case a polished design is as important as the functionality.
At this point, a push button switch, a rocker switch and a 16×2 Character LCD will be the UI. The software flow is as follows:
I’ll post the final code when I finish, but this chart is basically what the code running in the above video looks like.
Thanks for reading, more on the physical construction in the next post.
So I’ve been working a lot in the past day in ironing out part of the night side loop (loop 3 in this diagram). Basically, it starts recording based on an input from a sensor and continues to record until these inputs stop occurring.
My test code looks like this
1 2 3 4 5 6 7 8 9 10 11 12 13 |
v1 = CameraModuleVideo("/home/pi/CreatureCapture/", "video1") try: v1.startRecording() except ValueError as e: print(e) FilmDurationTrigger(5) try: v1.stopRecording() except ValueError as e: print(e) |
The interesting functions at work here are the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def FilmDurationTrigger(time): t = CameraTimer(time) while True: continueFlag = False print "Filming For " + str(time) + " Seconds" t.run() while (t.isExpired() != True): if (GetContinueTrigger() == True): continueFlag = True print "Trigger Found, Continuing" print "Time Has Expired, Continue Flag Is Set To " + str(continueFlag) if (continueFlag == False): break |
FilmDurationTrigger()
Takes the period of time that will be filmed, in this example, it’s 5 seconds just to conserve time, but in application it will be 20 seconds. This code will pause for the input time, and continue to be paused upon inputs from GetContinueTrigger()
. This delay allows the code to continue filming until there are no inputs.
In this example, GetContinueTrigger()
returns a Boolean if a random event occurs, but in application it will return a Boolean based on the status of a motion detector.
1 2 3 4 |
def GetContinueTrigger(): z = randint(0,10000) k = ((z == 115)) return k |
I ran two tests, both of them produced separate results. The first one created a 10 second long video:
1 2 3 4 5 6 7 |
pi@raspberrypi ~/CreatureCapture $ python CreatureCaptureTest2.py Filming For 5 Seconds Trigger Found, Continuing Time Has Expired, Continue Flag Is Set To True Filming For 5 Seconds Time Has Expired, Continue Flag Is Set To False Terminated |
And the second created a 15 second long video:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
pi@raspberrypi ~/CreatureCapture $ python CreatureCaptureTest2.py Filming For 5 Seconds Trigger Found, Continuing Trigger Found, Continuing Trigger Found, Continuing Trigger Found, Continuing Time Has Expired, Continue Flag Is Set To True Filming For 5 Seconds Trigger Found, Continuing Time Has Expired, Continue Flag Is Set To True Filming For 5 Seconds Time Has Expired, Continue Flag Is Set To False Terminated |
These two test shows that variable capture length functionality works! As a note, the actual times on the output video varies from the amount of time that it’s designed to record for. This is because the variable frame rate nature of the video coming out of the camera module, it causes the videos to come out a little short, but they still contain all the frames of the amount of time desired to record, just scaled slightly by frame rate error.
One of the biggest problems with the built in commands for using the Raspberry Pi Camera module is that you can’t stop a recording after an unknown time. You can record for a given number of seconds and that’s it. I have attempted to solve this problem by backgrounding the initial record process with a time of 27777.8 hours (99999999 seconds) when it’s time to stop recording, the process is manually killed using pkill.
Here is a test of my code, which I’ve called CameraModulePlus (written in python) which takes two videos, one for five seconds, and one for ten seconds, with a 10 second delay in between.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
from CameraModulePlus import CameraModuleVideo import subprocess from time import sleep import time v1 = CameraModuleVideo("/home/pi/CreatureCapture/", "video1") v2 = CameraModuleVideo("/home/pi/CreatureCapture/", "video2") try: v1.startRecording() time.sleep(5) v1.stopRecording() time.sleep(10) v2.startRecording() time.sleep(10) v2.stopRecording() except ValueError as e: print(e) |
Here is a result of the 5 second duration test:
Here is a result of the 10 second duration test:
As you can see, it works pretty good for how barbaric it is. The full class for CameraModuleVideo can be found here. In the future, I’d like to encode a lot more data into the CameraModuleVideo class, things about time etc. Also I would like to monitor available space on the device to make sure there is enough space to record.
I’ve decided to embark on a video surveillance project! My family lives in a very rural part of the US, and constantly hear and see evidence of animals going crazy outside of my home at night. The goal of this project is to hopefully provide some kind of insight as to what animals actually live in my backyard.
Ideally, I want to monitor the yard using some kind if infrared motion detector. Upon a motion detection, an IR camera assisted by some IR spotlights would begin filming until it has been determined that there isn’t any more movement going on in yard. These clips would then be filed into a directory, and at the end of the night, they would be compiled and uploaded to YouTube. This video would then be sent to the user via email.
I’ve created the following flowchart to develop against as I begin implementing this idea.
I’ll be using a Raspberry Pi to implement this idea, a few months back I bought the IR camera module and haven’t used it for anything, this would be a good project to test it out.
There are a few hurtles that I’ll have to cross in order to make this project a success, like most groups of problems I deal with, they can be separated into hardware and software components.
Hardware
Software
I’ve actually come up with a routine for solving the first software problem I’ve listed, hopefully I’ll have an example of my solution in action later tonight.
Ideally, this project will have a working implementation completed by May 21, which is 7 days from now.