la béchamel part deux

Update:

  1. I can output HDMI video now
  2. I can now program the FPGA with raspberry pi
  3. I have finalized the all-logic, two board version of the béchamel and just need a Mouser order to put together the final version.
  4. After this béchamel 1 experience I have concluded that running a video synth operation developing with discrete logic chips would be so incredibly impractical (expensive, time consuming, physically large, inflexible) compared to working with reprogrammable logic like the FPGA.
  5. I want to finish this chapter of my project and then take a moment, to stop working, to think and to reflect. I either want to shift my attention to the preparedinstruments site, and trying to manage the making and selling of this board, or to take a break from electronics/programming research to read and explore elsewhere for a bit. I also want to show my project to people, like Vincent Rioux, and get their honest feedback about the meaning of this thing outside of my own interests and learning. I want to revisit my strategies and practices, how I work with lead solder and how I decide what boards to make. For instance, it was cool to design and produce lots of boards but it is inefficient when a single board that is just partially populated can do the work of various boards. Another thing : I have spent all this time testing stuff and have ended up with a board that is the same as everyone else’s : FPGA w/ SRAM, single board, rpi programmable. Should I start by doing things that other people are doing instead of trying to do everything else first ?

Update 2 :

Getting the SD card interfaced with FPGA isn’t a gimmick (like having an integrated screen, for instance) as it would make turn this object into a fully fledged “product” which isn’t reliant on another consumer product (like Arduino or rpi) to function. It makes sense to use an FPGA as Arduino cannot output HDMI, and is limited in terms of its ability to read an SD card, manage an SRAM, listen for buttons, and modify video simoultaneously. The system would be kind of complete with both large memory stockage, fast memory, input management, video output. But would it be fun to sell a product ? I would have to do do customer service, repairs, answer questions on the forum, handle shipping and customs. I think I would rather do art installations and kits / workshops ?

Recent Wintergatan youtube video suggested making explicit these :

  • Problem definition : There is a lack of inexpensive, HDMI-compatible, live-playable, lo-fi hardware video synthesis devices on the synth market. But to be honest, this thing is not a response to a problem so much as a gratuitous problem-generating project (a.k.a a creative project).
  • Design requirements : HDMI out, video in via SD card, USB-C power, tactile interface, (audio in ?). Live-playable, relatively small and light (to be easily shipped), inexpensive (~25 euros). Minimilist in design : no bloated software, everything implemented as simply and as fundamental/low-level as possible and everything using only 1,200 LUTs and 2MB of SRAM. On the other hand the device should be fun, quirky, expressive and playful in it’s output and physical incarnation. Everything on the board should be solderable by hand, nothing so small or specialized that you need special tools apart from a fine soldering iron.

I am working on a low-cost, NO SCHOOL NEVERS prototype that is just the Villette Makers PEW model with the addition of a small SRAM. This will be fun because I’ll be able to test the HDMI synth with memory and buttons. Before sending this design off to be made, I want to experiment more with SRAM and FPGA (to figure out what the smallest size memory I can get away with and if I should consider adding anything else to this board based on what I learn from these experiments).

  1. How can I record and playback with sync so images don’t jump around (taking H SYNC or Display Enable information in, presumably) ?
  2. How can I reduce the resolution of recordings (with clock divisions)?
  3. How can I record video to a single channel and mix several channels at output (put them on different colors is the easiest) ?

****

I mentioned Nosedrip, I also want to be able to make art like You Are Listening To. There is radio communication from various city systems like the train network, airports, waterways, etc. combined with ambient sounds.

****

Working on slides for NO SCHOOL to explain what an FPGA is. Looking at some possible analogies. I can’t yet totally understand the difference between control flow programming (like in C) which performs one operation at a time (they digest?) and working with data flow pipelines that just kind of react (like a mirror or a lens) because of their form. I can’t tell if microfluidics and parallel marble machines are like FPGAs if you say that fluid moves super fast…

 

***

Trick to read analog signals with FPGA ( basically time how long it takes to charge a capacitor ) is genius : https://web.archive.org/web/20190615204217/https://hamsterworks.co.nz/mediawiki/index.php/Cheap_Analogue_Input * TO ADD TO NEXT BOARD*

***

In order to test SRAM recording, I’ve got the 4x16MB board back up and running. Here’s how I modified the board to get a test setup running :

  • I took out the SD card from the previous RPI that was no longer appearing to output video from DPI. I have inserted it into a fresh raspberry pi and soldered a thinner profile 20×2 header “pi hat” (which makes the board less vulnerable to bending and losing connections).
  • I desoldered the third and fourth SRAM chips (C&D) as they were causing issues for mysterious reasons I couldn’t debug. In any case I want to see what I can do with very little memory for the next board so not an issue.
  • I replaced the PFET as I noticed before that it was getting hot and also that the power was fading after being initially plugged in.
  • I removed the 50MHz oscillator on the board because it was unreliable and my attempts to resolder it just made it worse. So now it works with either rpi pixel clock or an external 25MHz from the function generator.
  • I desoldered the two jumper wires I had soldered to access rpi_DEN & another non-connected rpi pin. All references to DEN have also been removed in the verilog.

 

There are two verilog codes which I’ve named vga_sync.v and vga_sync_test.v that need to be added :

/*
640x480 VGA singal generator
============================

- Creates h_sync,v_sync signals
- Creates display enable signal and horizontal, vertical
pixel position in display (h,v)
*/

`default_nettype none


module vga_sync(
input wire clk_in,
input wire reset,
output reg h_sync,
output reg v_sync,
output wire clk_sys,
output reg [11:0] h_count,
output reg [11:0] v_count,
output reg display_en
);


// Pixel counters
reg [11:0] h_counter = 0;
reg [11:0] v_counter = 0;

/*

//FOR 100MHz

localparam h_pixel_total = 3200;
localparam h_pixel_display = 2560;
localparam h_pixel_front_porch_amount = 64;
localparam h_pixel_sync_amount = 384;
localparam h_pixel_back_porch_amount = 192;

localparam v_pixel_total = 2100;
localparam v_pixel_display = 1920;
localparam v_pixel_front_porch_amount = 40;
localparam v_pixel_sync_amount = 8;
localparam v_pixel_back_porch_amount = 132;

*/

// FOR 50MHz

localparam h_pixel_total = 1600;
localparam h_pixel_display = 1280;
localparam h_pixel_front_porch_amount = 32;
localparam h_pixel_sync_amount = 192;
localparam h_pixel_back_porch_amount = 96;

localparam v_pixel_total = 1050;
localparam v_pixel_display = 960;
localparam v_pixel_front_porch_amount = 20;
localparam v_pixel_sync_amount = 4;
localparam v_pixel_back_porch_amount = 66;


/*

//FOR 25MHz

localparam h_pixel_total = 800;
localparam h_pixel_display = 640;
localparam h_pixel_front_porch_amount = 16;
localparam h_pixel_sync_amount = 96;
localparam h_pixel_back_porch_amount = 48;

localparam v_pixel_total = 525;
localparam v_pixel_display = 480;
localparam v_pixel_front_porch_amount = 10;
localparam v_pixel_sync_amount = 2;
localparam v_pixel_back_porch_amount = 33;

*/

always @(posedge clk_in) begin

if (reset) begin
//Reset counter values
h_counter <= 0;
v_counter <= 0;
display_en <= 0;
end
else
begin
// Generate display enable signal
if (h_counter < h_pixel_display && v_counter < v_pixel_display)
display_en <= 1;
else
display_en <= 0;

//Check if horizontal has arrived to the end
if (h_counter >= h_pixel_total)
begin
h_counter <= 0;
v_counter <= v_counter + 1;
end
else
//horizontal increment pixel value
h_counter <= h_counter + 1;
// check if vertical has arrived to the end
if (v_counter >= v_pixel_total)
v_counter <= 0;
end
end

always @(posedge clk_in) begin
// Check if sync_pulse needs to be created
if (h_counter >= (h_pixel_display + h_pixel_front_porch_amount)
&& h_counter < (h_pixel_display + h_pixel_front_porch_amount + h_pixel_sync_amount) )
h_sync <= 0;
else
h_sync <= 1;
// Check if sync_pulse needs to be created
if (v_counter >= (v_pixel_display + v_pixel_front_porch_amount)
&& v_counter < (v_pixel_display + v_pixel_front_porch_amount + v_pixel_sync_amount) )
v_sync <= 0;
else
v_sync <= 1;
end

// Route h_/v_counter to out
always @ (posedge clk_in) begin
h_count <= h_counter;
v_count <= v_counter;
end

endmodule

*****************************************************************************************************************************

// vga_sync_test verilog code 

`default_nettype none

module vga_sync_test(

input wire clk_in,
input wire reset,

input wire rec, // Direction of io, 1 = set output, 0 = read input

//RASPBERRY PI
input wire [3:0] r_in,
input wire [3:0] b_in,
input wire [3:0] g_in,

//VGA OUT
output reg [3:0] r_out,
output reg [3:0] b_out,
output reg [3:0] g_out,

output wire h_sync,
output wire v_sync,

//SRAM

output reg [20:0] addr,
inout wire [7:0] io, // inout must be type wire

output wire cs_1,
output wire cs_0,

output reg we_1,
output reg we_0

);

wire [7:0] data_in;
wire [7:0] data_out;

reg toggle;

reg [7:0] a, b;

assign io = rec ? a : 8'bzzzzzzzz;

assign data_out = b;

assign data_in[1:0] = r_in[3:2];
assign data_in[3:2] = b_in[3:2];
assign data_in[5:4] = g_in[3:2];
assign data_in[7:6] = 2'b00;


wire display_en;
wire [11:0] h_count;
wire [11:0] v_count;

localparam h_pixel_max = 1280;
localparam v_pixel_max = 960;
localparam h_pixel_half = 640;
localparam v_pixel_half = 480;

// CS: low to select, high to deselect

assign cs_0 = toggle ? 1 : 0;
assign cs_1 = toggle ? 0 : 1;

//SRAM address counter

always @(posedge clk_in) begin

if (addr == 0) begin
toggle <= toggle+1;
end

if (reset) begin
addr <= 0;
end else begin
addr <= addr+1;
end
end

//REC control

always @(posedge clk_in) begin

b <= io;
a <= data_in;
if (rec) begin
we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
end
else begin
we_0 <= 1;
end
end

//VGA COLOR OUT

always @(posedge clk_in) begin
if (display_en) begin

r_out[3:2] <= data_out[1:0];
r_out[1:0] <= data_out[1:0];
g_out[3:2] <= data_out[3:2];
g_out[1:0] <= data_out[3:2];
b_out[3:2] <= data_out[5:4];
b_out[1:0] <= data_out[5:4];

end else begin
r_out <= 4'b0000;
g_out <= 4'b0000;
b_out <= 4'b0000;
end
end

vga_sync vga_s(
.clk_in(clk_in),
.reset(reset),
.h_sync(h_sync),
.v_sync(v_sync),
.h_count(h_count),
.v_count(v_count),
.display_en(display_en) // '1' => pixel region
);

endmodule
Here is the pcf :
set_io v_sync 87
set_io h_sync 88
set_io clk_in 64
set_io reset 66
set_io io[0] 1
set_io io[1] 2
set_io io[2] 3
set_io io[3] 4
set_io io[4] 122
set_io io[5] 121
set_io io[6] 120
set_io io[7] 119
set_io addr[0] 138
set_io addr[1] 139
set_io addr[2] 141
set_io addr[3] 142
set_io addr[4] 143
set_io addr[5] 8
set_io addr[6] 9
set_io addr[7] 10
set_io addr[8] 11
set_io addr[9] 12
set_io addr[10] 136
set_io addr[11] 135
set_io addr[12] 134
set_io addr[13] 129
set_io addr[14] 128
set_io addr[15] 117
set_io addr[16] 116
set_io addr[17] 115
set_io addr[18] 114
set_io addr[19] 137
set_io addr[20] 113
set_io cs_0 144
set_io cs_1 71
set_io we_0 7
set_io we_1 50
set_io rec 62
set_io r_in[0] 107
set_io r_in[1] 106
set_io r_in[2] 105
set_io r_in[3] 104
set_io g_in[0] 97
set_io g_in[1] 96
set_io g_in[2] 95
set_io g_in[3] 112
set_io b_in[0] 102
set_io b_in[1] 101
set_io b_in[2] 99
set_io b_in[3] 98
set_io r_out[0] 81
set_io r_out[1] 80
set_io r_out[2] 79
set_io r_out[3] 78
set_io g_out[0] 94
set_io g_out[1] 93
set_io g_out[2] 91
set_io g_out[3] 90
set_io b_out[0] 76
set_io b_out[1] 75
set_io b_out[2] 74
set_io b_out[3] 73




**************************************************************************************************************************
I describe here how I loaded videos to the rpi and setup the .desktop autorun code : 

"Setting up another rpi to loop VLC videos" from https://www.marrs.io/la-bechamel/

Here is the raspberry pi config file I've used to get DPI working: 
# For more options and information see
# http://rpf.io/configtxt
# Some settings may impact device functionality. See link above for details

# uncomment if you get no picture on HDMI for a default "safe" mode
#hdmi_safe=1

# uncomment the following to adjust overscan. Use positive numbers if console
# goes off screen, and negative if there is too much border
#overscan_left=16
#overscan_right=16
#overscan_top=16
#overscan_bottom=16

# uncomment to force a console size. By default it will be display's size minus
# overscan.
#framebuffer_width=1280
#framebuffer_height=720

# uncomment if hdmi display is not detected and composite is being output
#hdmi_force_hotplug=1

# uncomment to force a specific HDMI mode (this will force VGA)
#hdmi_group=1
#hdmi_mode=1

# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
#hdmi_drive=2

# uncomment to increase signal to HDMI, if you have interference, blanking, or
# no display
#config_hdmi_boost=4

# uncomment for composite PAL
#sdtv_mode=2

#uncomment to overclock the arm. 700 MHz is the default.
#arm_freq=800

# Uncomment some or all of these to enable the optional hardware interfaces
dtparam=i2c_arm=off
#dtparam=i2s=on
dtparam=spi=off

# Uncomment this to enable infrared communication.
#dtoverlay=gpio-ir,gpio_pin=17
#dtoverlay=gpio-ir-tx,gpio_pin=18

# Additional overlays and parameters are documented /boot/overlays/README

# Enable audio (loads snd_bcm2835)
dtparam=audio=on

# Automatically load overlays for detected cameras
camera_auto_detect=1

# Automatically load overlays for detected DSI displays
#display_auto_detect=1

# Enable DRM VC4 V3D driver
#dtoverlay=vc4-kms-v3d
max_framebuffers=2

# Disable compensation for displays with overscan
disable_overscan=1

[cm4]
# Enable host mode on the 2711 built-in XHCI USB controller.
# This line should be removed if the legacy DWC2 controller is required
# (e.g. for USB device mode) or if USB support is not required.
otg_mode=1

[all]

[pi4]
# Run as fast as firmware / board allows
arm_boost=1

[all]

gpio=0-9=a2
gpio=12-17=a2
gpio=20-25=a2
gpio=26-27=a2

dtoverlay=dpi24

enable_dpi_lcd=1
display_default_lcd=1

dpi_group=2
dpi_mode=87

extra_transpose_buffer=2

dpi_output_format=0x7f216
dpi_timings=720 0 40 48 128 720 0 13 3 15 0 0 0 60 0 50000000 1

******

OK so the challenge with this old board that is now up and running is again :
  1. How can I record and playback with sync so images don’t jump around (taking H SYNC or Display Enable information in, presumably) ?
  2. How can I reduce the resolution of recordings (with clock divisions)?
  3. How can I record video to a single channel and mix several channels at output (put them on different colors is the easiest) ?

SYNC

I have tried using Data Enable (DEN) from the rpi and inputting that, and also using H_SYNC and V_SYNC from the r_pi. So far the best result is taking V_SYNC, but I am still getting drifting. Testing to see if both H_SYNC and V_SYNC are high isn’t more stable for some reason. Here is that code :

`default_nettype none

module vga_sync_test(

input wire clk_in,
input wire reset,

input wire rec, // Direction of io, 1 = set output, 0 = read input

//RASPBERRY PI
input wire [3:0] r_in,
input wire [3:0] b_in,
input wire [3:0] g_in,

input wire DEN,


//VGA OUT
output reg [3:0] r_out,
output reg [3:0] b_out,
output reg [3:0] g_out,

output wire h_sync,
output wire v_sync,

//SRAM

output reg [20:0] addr,
inout wire [7:0] io, // inout must be type wire

output wire cs_1,
output wire cs_0,

output reg we_1,
output reg we_0

);

wire [7:0] data_in;
wire [7:0] data_out;

reg toggle;

reg [7:0] a, b;

assign io = rec ? a : 8'bzzzzzzzz;

assign data_out = b;

assign data_in[1:0] = DEN ? r_in[3:2] : 0;

assign data_in[1:0] = DEN ? r_in[3:2] : 0;
assign data_in[3:2] = DEN ? b_in[3:2] : 0;
assign data_in[5:4] = DEN ? g_in[3:2] : 0;
assign data_in[7:6] = 2'b00;


wire display_en;
wire [11:0] h_count;
wire [11:0] v_count;

localparam h_pixel_max = 1280;
localparam v_pixel_max = 960;
localparam h_pixel_half = 640;
localparam v_pixel_half = 480;

// CS: low to select, high to deselect

assign cs_0 = toggle ? 1 : 0;
assign cs_1 = toggle ? 0 : 1;

//SRAM address counter

always @(posedge clk_in) begin

if (addr == 0) begin
toggle <= toggle+1;
end

if (reset) begin
addr <= 0;
end else begin

//only increment address is DEN high
if (DEN) begin
addr <= addr+1;
end
end
end

//REC control

always @(posedge clk_in) begin

b <= io;
a <= data_in;
if (rec) begin

//only enable WR if DEN high
if (DEN) begin
we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
end
end
else begin
we_0 <= 1;
end
end

//VGA COLOR OUT

always @(posedge clk_in) begin

//only display pixels if DEN high
if (DEN) begin

r_out[3:2] <= data_out[1:0];
r_out[1:0] <= data_out[1:0];
g_out[3:2] <= data_out[3:2];
g_out[1:0] <= data_out[3:2];
b_out[3:2] <= data_out[5:4];
b_out[1:0] <= data_out[5:4];

end else begin
r_out <= 4'b0000;
g_out <= 4'b0000;
b_out <= 4'b0000;
end
end

vga_sync vga_s(
.clk_in(clk_in),
.reset(reset),
.h_sync(h_sync),
.v_sync(v_sync),
.h_count(h_count),
.v_count(v_count),
.display_en(display_en) // '1' => pixel region
);

endmodule

 

I am going to try to count V_SYNCs and use this to vary SRAM address incrementing.

I can get an image to freeze pretty well now, I just needed to find the right size of the V_SYNC_COUNT counter to make it work. Here is the code :

`default_nettype none

module vga_sync_test(

input wire clk_in,
input wire reset,

input wire rec, // Direction of io, 1 = set output, 0 = read input

//RASPBERRY PI
input wire [3:0] r_in,
input wire [3:0] b_in,
input wire [3:0] g_in,

input wire DEN,
output reg [2:0] V_SYNC_COUNT, // the size of this counter impact how many V_SYNC signals it takes to reset the SRAM ADD counter


//VGA OUT
output reg [3:0] r_out,
output reg [3:0] b_out,
output reg [3:0] g_out,

output wire h_sync,
output wire v_sync,

//SRAM

output reg [20:0] addr,
inout wire [7:0] io, // inout must be type wire

output wire cs_1,
output wire cs_0,

output reg we_1,
output reg we_0

);

wire [7:0] data_in;
wire [7:0] data_out;

reg toggle;

reg [7:0] a, b;

assign io = rec ? a : 8'bzzzzzzzz;

assign data_out = b;

assign data_in[1:0] = DEN ? r_in[3:2] : 0;

assign data_in[1:0] = DEN ? r_in[3:2] : 0;
assign data_in[3:2] = DEN ? b_in[3:2] : 0;
assign data_in[5:4] = DEN ? g_in[3:2] : 0;
assign data_in[7:6] = 2'b00;


wire display_en;
wire [11:0] h_count;
wire [11:0] v_count;

localparam h_pixel_max = 1280;
localparam v_pixel_max = 960;
localparam h_pixel_half = 640;
localparam v_pixel_half = 480;

// CS: low to select, high to deselect

assign cs_0 = toggle ? 1 : 0;
assign cs_1 = toggle ? 0 : 1;

//V SYNC counter

always @(posedge DEN) begin
V_SYNC_COUNT <= V_SYNC_COUNT+1;
if(reset == 1) begin
V_SYNC_COUNT <=0;
end
end


//SRAM address counter

always @(posedge clk_in) begin

if (addr == 0) begin
toggle <= toggle+1;
end

// reset the address once we've rolled over V_SYNC_COUNT

if (reset | V_SYNC_COUNT == 0 ) begin
addr <= 0;
end else begin

//only increment address is DEN high
if (DEN) begin
addr <= addr+1;
end
end
end

//REC control

always @(posedge clk_in) begin

b <= io;
a <= data_in;
if (rec) begin

//only enable WR if DEN high
if (DEN) begin
we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
end
end
else begin
we_0 <= 1;
end
end

//VGA COLOR OUT

always @(posedge clk_in) begin

//only display pixels if DEN high
if (DEN) begin

r_out[3:2] <= data_out[1:0];
r_out[1:0] <= data_out[1:0];
g_out[3:2] <= data_out[3:2];
g_out[1:0] <= data_out[3:2];
b_out[3:2] <= data_out[5:4];
b_out[1:0] <= data_out[5:4];

end else begin
r_out <= 4'b0000;
g_out <= 4'b0000;
b_out <= 4'b0000;
end
end

vga_sync vga_s(
.clk_in(clk_in),
.reset(reset),
.h_sync(h_sync),
.v_sync(v_sync),
.h_count(h_count),
.v_count(v_count),
.display_en(display_en) // '1' => pixel region
);

endmodule

I just realized I could also take the AND of various bits of the V_SYNC_COUNTER like so :

if (reset | ((V_SYNC_COUNT[0] == 1) & (V_SYNC_COUNT[2] == 1) & (V_SYNC_COUNT[3] == 1))) begin

addr <= 0;

end 

 

RESOLUTION

This is cool, just change clk_in with a divided clock and you get bigger pixels !

`default_nettype none

module vga_sync_test(

input wire clk_in,

output reg [2:0] clk_div, // remember to change clk_div[MSB] also below ! Changing the size of this register changes how big the pixels are :D !

input wire reset,

input wire rec, // Direction of io, 1 = set output, 0 = read input

//RASPBERRY PI
input wire [3:0] r_in,
input wire [3:0] b_in,
input wire [3:0] g_in,

input wire DEN,
output reg [1:0] V_SYNC_COUNT,


//VGA OUT
output reg [3:0] r_out,
output reg [3:0] b_out,
output reg [3:0] g_out,

output wire h_sync,
output wire v_sync,

//SRAM

output reg [20:0] addr,
inout wire [7:0] io, // inout must be type wire

output wire cs_1,
output wire cs_0,

output reg we_1,
output reg we_0

);

wire [7:0] data_in;
wire [7:0] data_out;

reg toggle;

reg [7:0] a, b;

assign io = rec ? a : 8'bzzzzzzzz;

assign data_out = b;

assign data_in[1:0] = DEN ? r_in[3:2] : 0;

assign data_in[1:0] = DEN ? r_in[3:2] : 0;
assign data_in[3:2] = DEN ? b_in[3:2] : 0;
assign data_in[5:4] = DEN ? g_in[3:2] : 0;
assign data_in[7:6] = 2'b00;


wire display_en;
wire [11:0] h_count;
wire [11:0] v_count;

localparam h_pixel_max = 1280;
localparam v_pixel_max = 960;
localparam h_pixel_half = 640;
localparam v_pixel_half = 480;

// CS: low to select, high to deselect

assign cs_0 = toggle ? 1 : 0;
assign cs_1 = toggle ? 0 : 1;

//V SYNC counter

always @(posedge DEN) begin
V_SYNC_COUNT <= V_SYNC_COUNT+1;
if(reset == 1) begin
V_SYNC_COUNT <=0;
end
end

//increment our clock divider

always @(posedge clk_in) begin
clk_div <= clk_div+1;
if(reset == 1) begin
clk_div <=0;
end
end

//SRAM address counter

always @(posedge clk_div[1]) begin

if (addr == 0) begin
toggle <= toggle+1;
end

// reset the address once we've rolled over V_SYNC_COUNT

if (reset | V_SYNC_COUNT == 0 ) begin
addr <= 0;
end else begin

//only increment address if DEN high
if (DEN) begin
addr <= addr+1;
end
end
end

//REC control

always @(posedge clk_div[1]) begin

b <= io;
a <= data_in;
if (rec) begin

//only enable WR if DEN high
if (DEN) begin
we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
end
end
else begin
we_0 <= 1;
end
end

//VGA COLOR OUT

always @(posedge clk_div[1]) begin

//only display pixels if DEN high
if (DEN) begin

r_out[3:2] <= data_out[1:0];
r_out[1:0] <= data_out[1:0];
g_out[3:2] <= data_out[3:2];
g_out[1:0] <= data_out[3:2];
b_out[3:2] <= data_out[5:4];
b_out[1:0] <= data_out[5:4];

end else begin
r_out <= 4'b0000;
g_out <= 4'b0000;
b_out <= 4'b0000;
end
end

vga_sync vga_s(
.clk_in(clk_in),
.reset(reset),
.h_sync(h_sync),
.v_sync(v_sync),
.h_count(h_count),
.v_count(v_count),
.display_en(display_en) // '1' => pixel region
);

endmodule

The V_SYNC rollover and the clock division need to change in relation to one another.
*********
I’m trying to clear up the recordings, they get super messy with bits everywhere. But the pixelation looks awesome :

Considering removing the amps at the output, as the NO SCHOOL version won’t have these either.

I should definitely change the shape of the pixels to make them rectangular in both X and Y.

**********

I might be entering a new career phase as a prototyper. I am currently working to make an e-ink QR code device, a rotating clock mechanism, and may be tasked with making permanent versions of the FLV DSAA prototypes we proposed !

I can imagine a transition where I do more prototyping professionally and spend my time doing more fine art work – less design of circuits and more experimental video making with my existing boards + new code. Routing circuits is so meticulous, I don’t know how I’ve spent so many hours doing this.

**************

I have finalized the next iteration of the PEW board with a small inexpensive 2MB SRAM, an rpi header (which is also hard-wired to the FLASH programming pins), and an SD card set up to work in either 1 or 4 bit mode (not SPI0) to eventually take in uncompressed video directly, and a test of the potentiometer reading capabilities of the FPGA. The music in + comparator has been removed from this version. I also haven’t been able to learn quickly enough a minimal setup for the FTDI chip so that has not been included either.

**************

UPDATE : It appears that SPI mode, not SD 1-bit mode, is the more thoroughly documented way of interfacing SD cards.

Here is where the SD 1-bit mode communication protocol is explained : https://www.sdcard.org/downloads/pls/ and select Physical Layer Simplified Specification where chapter 4 describes in detail.

I think I should aim for the Multiple Block Read Operation (CMD18) which keeps going until you send a stop command. 1 bit mode seems the simplest, everything is just in serial.

Here are some sample diagrams :

*************************

General stuff : CMD is used for sending messages, DAT0 for sending data. 3.3V is the standard. Default clock speed is 400KHz, default RCA is 0x0000. Phase 1 is Card ID mode then Data TX mode. 512 bytes sent at a time. According to this post (http://forum.6502.org/viewtopic.php?f=4&t=7300) data is read on the low-to-high transition, and starts with the highest bit (47, for example) going to the lowest bit. Here is a site to calculate CRC (https://www.lddgo.net/en/encrypt/crc)

Here is the start-up procedure :

**********************

Card ID mode: After power on, the Host sends to the SD card first :

   0. CMD0 (enter IDLE mode). (And now pull CMD high and tick the CLK a series of times ?)

  1. CMD8  (operating condition validation)

2. ACMD41 (SD_SEND_OP_COND)

3. CMD2 (ALL_SEND_CID) : CID is a unique card ID number.

4. CMD3 (SEND_RELATIVE_ADDR) which asks the card to send it’s relative card address (RCA).

*********************************

Now to enter Data TX mode :

5. CMD7 : Puts the card into TX mode.

6. CMD18 : Multiple Block Read (continues until CMD12 STOP_TRANSMISSION)

7. DAT0 bus sends first 1 of 4 bits LOW followed by a continuous data stream synchronized with the CLK.

**************************

FTDI

I am just now thinking that if I am making a product I don’t necessarily need to worry about making it USB programmable. I can just leave the programming header and a link to how to program with Arduino/raspberry pi.

Looking in to FTDI replacements. This post (https://forum.arduino.cc/t/explanation-of-ftdi/267058/4) suggests these :

  • Sil Labs CP2102
  • Prolific PL2303
  • WCH CH340 (Olimex has a board here : https://www.olimex.com/Products/Breadboarding/BB-CH340T/open-source-hardware)

Fabien says that these chips only do UART however.

There are some open JTAG techniques. Another option he suggests is to have a bootloader as is done with the following boards :

  • tinyFPGA-BX (Uses the FPGA itself to program the flash chip : https://github.com/tinyfpga/TinyFPGA-Bootloader)
  • Fomu (Uses the FPGA itself to program the flash chip : https://github.com/im-tomu/foboot)
  • ICE40HX1K-EVB (Uses an Arduino board – olimexino-32u4 – to program the flash chip)

****

Saw on a blog that controlling a USB flash drive with SPI might be easier than an SD card for some reason ? It looks like a state machine is how you make an FPGA behave like a microcontroller for this type of situation.

Perhaps the usual SPI for SD card is the correct choice. Some things I’m looking at :

  • https://hackaday.io/project/160928/logs
  • https://www.fpga4fun.com/SPI2.html
  • https://hackaday.io/project/119133-rops/log/144622-starting-with-verilog-and-spi
  • SPI slave : https://github.com/joeferner/fpga-spi/blob/master/spi.v
  • ***NANDLAND SPI Master : https://github.com/nandland/spi-master/blob/master/Verilog/source/SPI_Master.v***
  • Optimized SPI Master: https://github.com/freecores/tiny_spi/blob/master/rtl/verilog/tiny_spi.v
  • https://alchitry.com/serial-peripheral-interface-spi-verilog

The Nandland SPI Master compiles. Looking at these documents now explaining SD card protocol with SPI :

  • http://www.dejazzer.com/ee379/lecture_notes/lec12_sd_card.pdf
  • https://users.ece.utexas.edu/~valvano/EE345M/view12_SPI_SDC.pdf

SD interfacing with SPI :

HxD is a tool for raw SD card editing. It could help finding the start address of the first file for instance.

I think step 1 would be to try to do this manually (using an SPI library ?) with Arduino and a logic analyzer / oscilloscope to confirm that what I’m trying to do works with the particular card I am using. With screenshots of the communication I could then attempt to replicate this with the FTDI FPGA which has an SD card and LEDs for indicators.

Explaining how to use Arduino SPI library to send individual bytes :

https://docs.arduino.cc/tutorials/generic/introduction-to-the-serial-peripheral-interface/

https://docs.arduino.cc/learn/communication/spi/?_gl=1*4f5308*_ga*MTk5MjE1MjA0My4xNzA1NzQyNDIx*_ga_NEXN8H46L5*MTcxMTEzOTU0Ny4zOS4wLjE3MTExMzk1NTIuMC4wLjY5OTM2NjkyOQ..*_fplc*SVhlSkEzOXkwVlFqc2MlMkZ6ZlU5NTlzYTJteWJnTWZ5eUVlSUFDSlRZSTl3bVJHd3lMWVZiWTNFenpMUjhOOTVFR2drR3lPdU1NR0pSQWluMGRWYWZRM3htUWFBd3JYeFFiViUyQndybE1SZk9VU1JFT0Y0cXpldG5HVnZXbE1RUSUzRCUzRA..

.

****

Looking at enclosures and found these resources :

https://www.protocase.com/blog/2020/02/20/how-to-choose-between-enclosure-styles/

L-Shape Enclosure with printed circuit board installed.

SBm49_SandwichEnclosure-1

image3

 

 

 

ponoko.com/blog/how-to-make/making-enclosures-for-electronics-with-ponoko/

Electronics Project Boxes 8 - 2x4 Wood Enclosure

 

 

 

Cardboard and Interactive Tech | Aylesbury Box Company

 

Here are some experiments with enclosures :

****

Here’s a more precise model of the 3D print option :

****

Trying to output low resolution images from film using ffmpeg (https://www.bannerbear.com/blog/how-to-extract-images-from-a-video-using-ffmpeg/):

ffmpeg -i input.mp4 -s 100x100 -vf fps=1,extractplanes=y %04d.bmp
This outputs 1 frame a second super small in black and white. The input video needs to be in the same directory of the ffmpeg.exe file.
Super easy ! 

I renamed one of the .bmp as a .raw and opened it in photoshop at 100x100 :


***
A couple tests using the screen itself as a display with text overlay :


 

la béchamel

For my first finished product for prepared instruments (preparedinstruments.com), the goal is to remake the hardware 7ch palimpsest.OS SRAM but with :

  • a square keyboard grid interface like the PEW design (knobs and switches replaced with keys)
  • an aluminium PCB substrate (with a single-sided board) ?
  • a simpler amp in path with only one pot
  • all 0805 and SOIC packages

I’ve been thinking about it and I can’t say that this board will be inexpensive. If you want to explore video synthesis inexpensively, you should do things for free in code on the computer. This is a physical digital logic / analog device so it will cost money.

Because it’s the first in a series, I would like this board to set the format of the others. I’m imagining them all like Ritter Sport square bars. Like the eurorack format, I would like to have a standard way of powering the boards and I need to decide on a way of jumping between the boards (VGA cables?).

I’d also like the boards and the packaging to be ecologically responsible. I don’t know if this means using an aluminium substrate (which can be easily recyclable in theory) or a flexible circuit (with either a thin plastic or stained steel stiffener – in theory recyclable).

Looking back on the synths from this year, I never really wanted to code/configure the things I made. Doing hardware synths makes sense as it puts me in a different spot in the market place, makes the designs harder to just copy, and it allows me to savour the suffering in the design process. The analog function board was ultimately underwhelming. Going the direction of installation pieces isn’t satisfying because they are too site-specific and local for my project I feel (though perhaps making films is the solution here). The gameboy design made me feel bad about the waste of buying a mini screen and the software challenge of communicating with it seems not crucial to my project (but I will finish at least the physical side of the project to test the layout). The memory boards were the coolest, and of them the layering board is the most elegant design and the most interesting in what it can do.

Here’s the new site where the béchamel will appear:

Thoughts from Vincent :

  • Connect with other people making video synths !
  • Try shooting some of your own film to put through the machines !
  • What is it you are trying to SAY ? Hmm

What is it you are trying to SAY:

I think I’m trying to say with this project is : Look at what I like doing, isn’t it fun and cool/silly/odd what is to be found ?!  More specifically, to share the preliminary results of my creative research project method. The project I guess is trying to say that it is possible and fun to a embark on an explorative creative process with technology at a low level (and not just use pre-packaged tech things) with some of the wrestling with constraints that that requires. I like that the objects are idiosyncratic, playful compositions but also that they are ornately routed and articulated. I like the message that machines can look different, and be more human and odd ? I guess I’m also trying to have fun with the ideas of usefulness and futility, so it is partially supposed to be humorous? I like the détournement of industrial objects for playful means, the implication that old things can be exciting and that deconstructing a system reveals unexpected inner worlds. I like that the deconstructing of machines is also the deconstructing of how they interact with our world, like the data they save and how they represent our information.

Thinking that Alterity, oddity, idiosyncracy and animism in objects and people and culture I think are totally up there. This was a fascination of my dad’s.

To echo some artists’ quotes that resonate :  “I don’t know what I’m looking for until I see it.” (Cindy Sherman)

“The reason I play so many sounds, maybe it sounds angry, is because I’m trying so many things at one time, you see? I haven’t sorted them out. I have a whole bag of things that I’m trying to work through and get the one essential.” (John Coltrane)

 I want to learn things all the time, I want to understand the patterns behind things. I experience science through craft. With craft I try to cultivate sensitivity in a practiced, purposeful way. If you work with marbling you know just as much about viscosity and flow than a scientist, through your fingertips in a different way. (Tauba Auerbach)

 

Try shooting some of your own film to put through the machines?

I started thinking about what I would want to film and how. I thought about the beautiful cinematography I appreciate in special films like The Souvenir I & II. I think it would be cool to shoot some video because I spend a lot of time watching videos (not the case for writing because I don’t do enough reading). I thought about recording things I see on commutes, and about the forest walk in Saclay. This has made me consider that what I find interesting about the electronics side is memory, more than just modifying things with math. How the computer represents our world is mainly impacted by its ability to remember it. I’m starting to think that this project could be about memory, putting my own memories into the computer memory. I’m thinking about Iain and Brian’s film Lifesaver and the impact it’s had on me. Also Iain’s photography in general, especially his black and white stage.

I think DRAM board could be a cool project because of how inexpensive large memory is and the memory degradation which could be really poetic in a project about memory. This goes back to Paolo’s suggestion to make a project around an image put into UV erasable EEPROM and watching it slowly degrade in the sun.

I’ve been thinking about how to record as well, with a gopro discretely or with my G2 Cannon more deliberately. I also think that humor should be the primary part of my shooting, not some wanna-be poetic stuff.

**********

Trying to design the interface :

Might need latched buttons like this :

Some all metal heavy duty toggle switches look cool in an array but I’m worried toggle switches will require too much force to turn on and will therefore be annoying all arrayed together on a tiny board.

The other option is to stick with tactile buttons and to convert them into on/off buttons with a JK flipflop. When J and K are pulled high, the outputs Q and !Q toggle every time the clock rises (once per push). This would mean I could have an LED turn on when you push too (helps in the dark for performances!) but I wouldn’t have the pushed-in versus fully extended visual feedback from the button and it would add ICs to my packed board.

I am considering doing this on two boards, the top one would be a single-sided aluminium board which would look cool but also make the top interface more durable. This board would have the leds, keys, JK flip flops. The board below could have lots of space to be laid out in a relaxed, rational way without having to dodge buttons. I could enclose it in a little box or I could leave the two boards exposed on one side or all around.

This follows from the constraint that this will be a real product. I don’t want to make a super difficult to solder and debug highly compact circuit. I want to keep things easy to repair. It also makes the device modular, I could switch a new keyboard and keep the base circuit or vice versa. I would like to keep this device affordable and not using rare / exotic components. I’m more in the robust, standard design world now that Tommy is good at working with perhaps.

*******

I just cleaned by apartment and went through my lab equipment. I will be sending tens of boards and plastic bags to the recycling which makes me sad. I would like to avoid finding myself in this situation in the future.

Check out this Soviet-developed visual programming language DRAKON (https://en.wikipedia.org/wiki/DRAKON)

It would be cool to make a flow diagram like this for the FPGA code I’ve already made.

Also this automation representation called GRAFCET (https://fr.wikipedia.org/wiki/Grafcet)

*******

Check out these user interface principles (https://en.wikipedia.org/wiki/User_interface_design):

  • Clarity: the information content is conveyed quickly and accurately.
  • Discriminability: the displayed information can be distinguished accurately.
  • Conciseness: users are not overloaded with extraneous information.
  • Consistency: a unique design, conformity with user’s expectation.
  • Detectability: the user’s attention is directed towards information required.
  • Legibility: information is easy to read.
  • Comprehensibility: the meaning is clearly understandable, unambiguous, interpretable, and recognizable.

********

Just watched a Youtube video by Code Aesthetic called Don’t Write Comments. The implication is that the code should be so readable that you don’t need to comment it. (He organizes code in such a way that the high level architecture comes through and if you want to get into the details you can check out individual functions). I’m trying to do something similar with the interface, have it be directly readable without text just based on color, button size and location.

I like the idea of being more pragmatic with the design and less obsessive about composition. The cut away in the top board could be nice because it show which color bits are going from rpi to the memory.

The question of the colors is tough – RED is for recording ? Should colors follow RGB ? What colors for clock division, and for selecting between MSB and LSB ? What about the LEDs versus the buttons ? EDIT : I’ve sinced moved to greyscale…

****

Being creative in different ways : Apollonian (rational thinking, order, logic, prudence and purity) vs. Dionysian (irrationality, chaos, passion, emotion and instinct). Electronics and logic circuits are clearly Apollonian, and the effects they create on screen are more Dionysian.

****

Looking into code bloat I learned the Unix philosophy of “do one thing and do it well”. Other programming credos :

  • less functionality (“worse”) is a preferable option (“better”)’https://en.wikipedia.org/wiki/Worse_is_better)
  • “Keep it simple, stupid!” (https://en.wikipedia.org/wiki/KISS_principle)
  • rule of least power is a design principle that “suggests choosing the least powerful [computer] language suitable for a given purpose” (https://en.wikipedia.org/wiki/Rule_of_least_power)
  • a simple (low complexity) solution to a problem of high complexity is seen as elegant. (https://en.wikipedia.org/wiki/Elegance)

**********

New Prepared Instruments github : https://github.com/preparedinstruments/bechamel

OK, here’s the final version of the boards. I will finally be able to test the one-sided aluminum surface reversed with buttons going through-hole. This is (my first) double-board design, keyboard on one and memory on the other with a ribbon jumping between them. This sampler will have two bits of data for the videos which will be new. Layers will now have different colors associated with them. The gen lock turned out to not be what I thought it was so I’m trying to sync by using V and H syncs (and their inverses) connected to the 590 freq division somehow (either to OE, CLR, or RCLK). I like this new setup to select different bits of the RPI. Cool things here are the double use of the buttons in momentary and latched mode with led indicators. New in this version will also be the triple diode packages for the VGA out.

I’ve taken my favorite interface and mixed it with my favorite circuit, let’s see how it works out ! At least I got to use up a bunch of components I already had lying around. Though, I have to say that no one could pay me enough money to assemble one of these myself, and despite my best efforts it is not minimal and easy to put together. Another issue is the fact that I don’t have the skill of imagining what “customers” want, I seem to be more focused on what I want. Does that make me a wanne-be artist as opposed to a wanna-be designer ?

And some renderings :

 

**********

Having a think about what my hardware synths could do better than computers. There is the tangible interface, and the speed and fixed-low latency of using hardware to manipulate data instead of software. While I was once again looking into what I can do with a simple FPGA I came across :

  • this Elphel NC393 camera device board for handling multiple video streams using an FPGA https://wiki.elphel.com/wiki/File:Nc393-dev-sm.jpeg. Combining multiple video streams, or overlaying, sounds like an application? But in VGA you can do this with just a pot !
  • User Jeremy from eevblog’s comment about FLIR explaining that they use FPGAs in order to do “spatial (across pixels) and temporal (across time) filtering simultaneously”.
  • video transcoding from one resolution ‘https://en.wikipedia.org/wiki/Transcoding)
  • real-time edge detection
  • Mister FPGA video game console has a bunch of retro CRT scanline filters here https://boogermann.github.io/Bible_MiSTer/getting-started/extras/video-filters/
  • FPGA student projects from this Cornell class : http://people.ece.cornell.edu/land/courses/ece5760/FinalProjects/

********************

I think that this project is becoming about memory, and possibly the parallels between human memory and computer memory. I wonder what role the FPGA would have here. Would it be positioned in between the video input and the memory to act as a kind of variable filter ? I can imagine an interface that would allow you to select between many different filters by moving your hand like on a theremin or an array of trackballs?

*****

Check out this visualization of randomness :

****

First feedback : it needs HDMI – Sam

This Texas Instruments TFP410PAP is back in stock at Mouser and seems easy enough to integrate onto the board at 8euros. It can be controlled without I2C so it could be connected directly to the raspberry pi DEN, HSYNC, VSYNC, color data.  Black Mesa Labs did a board using this IC here : https://blackmesalabs.wordpress.com/2017/12/15/bml-hdmi-video-for-fpgas-over-pmod/

I also found a VGA to HDMI converter on amazon : FOINNEX Adaptateur VGA vers HDMI avec Audio, Convertisseur VGA a HDMI Vieux PC vers TV Moniteur, 1080P VGA to HDMI Adapter pour Anciens Ordinateur 

TI also makes the TFP401x TI PanelBus™ Digital Receiver so I could have HDMI IN and HDMI OUT with the addition of 15 euros of chips…The alternative is to learn to do this with the FPGA.

*****

Check out these snazy metal container for this synth circuit :

Division 6 Business Card Synthesizer PCB and IC

https://store.synthrotek.com/Division-6-Business-Card-Synthesizer-PCB-and-IC_p_829.html

Check out this beautiful project Sam sent my by Jurg Lehni which compares different screen technologies : https://juerglehni.com/works/four-transitions

******

Thinking about modularity. If I wanted to have a signal chain with filters etc before the memory. I would need a way of daisy-chaining the power, and a way of patching cables between modules. I want it such that the thing functions with just one module but also can with multiple modules too.

Would the video in/out have to be HDMI to be in line with modern VJ practice ? Found some Chinese chips that do HDMI to VGA like the Lontium LT8511A and the Quintile CH7101B. I guess I would need one of these plus an HDMI out chip for each board for them to be truly modular.

I am thinking right now of a simple custom filter like the one in Photoshop (Nice explanation here : https://ian-albert.com/old/custom_filters/) accomplished with an FPGA and some kind of cool interface (track ball matrix ? capacitive touch array ?) :

Photoshop's custom filter dialog

pixel(x,y) = (sum(i=1..5)(sum(j=1..5)(pixel(i+x,j+y)*weight(i,j))))/scale + offset

***************

Check out this company OKW that makes cool modular knobs just outside of Paris : https://www.okw.fr/fr/Boutons-de-commande

 

**************

Working with the array of simple, robust, electronic components (switches, knobs, linear pots, track balls, etc.). Trying to think about how they could be daisy-chained together.

 

****************

Received and soldrerd PCBs !

  • The aluminum layer can be engraved ! I could put “bechamel” on it !
  • The standoff holes are too small for standard size.
  • The boards are not perfectly the same dimensions.
  • Perhaps I should have chosen white for the FR4 board instead of green
  • No caps on the keyboard board
  • Blurg, soldering components on the aluminum board without a heated plate is near impossible. I could have at least selected bigger format ICs.
  • The latched keys which are connected to the output buffer enables aren’t really useful. They should be constantly on as the amp can always be just turned down to turn off the channel.

 

ERRATA MOTHERBOARD :

  • From now on I think I will solder with a stencil and an oven, it’s too much work to solder manually.
  • The latched keys don’t activate the same channels via buffer that they activate via AND gate to analog switch SEL.
  • Also the text BLU, RED etc next to the knobs isn’t really accurate
  • Should have VCC and GND on the silkscreen next to keyboard jumper
  • Pin 13 and 11 of the 590 should by default be connected together on the next board
  • I replaced the caps at the VGA out with 0ohm jumpers because I couldn’t see anything passing through on the oscilloscope
  • I should add the turn off switch for rpi
  • I am considering getting rid of the integrated rpi, despite its advantages, for a more straightforward HDMI IN or VGA IN. It adds considerable thickness to the board with the bulky 40 pin jumper situation and the fact that it is headless makes debugging a real pain.

ERRATA KEYPAD :

  • I’m not sure the idea of using the aluminium sided boards really works, it’s all a bit too risky in terms of short circuits and it’s a real pain to solder (I need a heated plate, a nice microscope, loads of flux and a soldering iron). Fabien’s suggestion is to make the holes larger, so there is little risk of the pins touching the aluminium substrate, and then wiring flexible wire to make the necessary connections. Perhaps add some glue to keep the keys in place and move all the circuitry to the reverse side of the motherboard.
  • Fabien had the idea of using the square holes of the mechanical switches possibly to mount them in the metal, perhaps they would even clip into place ?
  • I roasted the ICs during soldering I think, the LEDs and JK latches are not working as expected.
  • The added height for the two female ribbon cable connectors butting up against each other is insane. If it’s possible to just plug one board directly into the other you can achieve some nice compact sandwich board.
  • Once again the 2mm standoff holes are too tiny
  • The slot is perfect and the LEDs passing through the holes works as expected (though I should really use insulation on the two legs).

**************

Could I use coils from floppy disk player in a matrix as capacitive touch array ?

PCB mounted motor coils in a floppy drive : r/electronics

Could I make a super low resolution LED monitor to replace the HDMI IN and HDMI OUT DSLR field monitor ? (https://www.amazon.fr/Feelworld-Moniteur-Monitor-1920×1080-Peaking/dp/B07J5C98NW/ref=sr_1_1_sspa?crid=2SJEJTOJ1C6HB&keywords=Cam%C3%A9ra+Moniteur+DSLR&qid=1707557998&s=electronics&sprefix=cam%C3%A9ra+moniteur+dslr+%2Celectronics%2C89&sr=1-1-spons&sp_csd=d2lkZ2V0TmFtZT1zcF9hdGY&psc=1) :

I could also just use the simple rpi 5″ HDMI screen.

Other connection options if I had HDMI out :

Would it be smart to keep a VGA out as a plan B if ever the HDMI stopped working ?

It would be super useful to have a micro usb out to power the portable screen / pico projector. If so not much space left, would need to maybe bring the USB C power cable to the rpi side.

*************

Check out Jim Campbell’s low res display :!

Image may contain Rug Light Text and Lighting

Jim Campbell

Jim Campbell

References to reconstructing memories from the mind too !

***************

Setting up another rpi to loop VLC videos. I used primarily this tutorial: https://forums.raspberrypi.com/viewtopic.php?t=17051

First off, I kept the language as english to simplify things (despite having a french keyboard). I believe I did the rpi Lite installation (and then downloaded VLC?) but I’m not 100% sure.

In the place of

nano /home/pi  I have my user name :

nano /home/marrs

I also needed to go to View > Show Hidden to see the .config file then autostart then autovlc.desktop

nano /home/marrs/films/.config/autostart/autovlc.desktop

NOTE : This can’t be a text file, easiest way is to create the file directly in the terminal to avoid any problems as explained here :https://learn.sparkfun.com/tutorials/how-to-run-a-raspberry-pi-program-on-startup/method-2-autostart

I put this in the file (there is nothing else there)

[Desktop Entry] 
Type=Application
Exec=vlc --loop --fullscreen /home/marrs/playlist.m3u

Then control+O to save the file
 I put the m3u playlist here : home/marrs/playlist.m3u
The playlist file looks like this :
/home/marrs/Desktop/films/2001.mp4
etc. and avoid spaces !!
I created a folder called films on my desktop with the films.autostart
To share videos from another computer via the SD card, you can create a folder in the part of the SD card that is accessible, and whatever folder you make there can be found under bootfs / firmware
Finally, AFTER DOING THE FILM FILE TRANSFER WITH THE HDMI SCREEN, I modified the config.txt file to output DPI and not respond to HDMI:

# For more options and information see
# http://rpf.io/configtxt
# Some settings may impact device functionality. See link above for details

# uncomment if you get no picture on HDMI for a default "safe" mode
#hdmi_safe=1

# uncomment the following to adjust overscan. Use positive numbers if console
# goes off screen, and negative if there is too much border
#overscan_left=16
#overscan_right=16
#overscan_top=16
#overscan_bottom=16

# uncomment to force a console size. By default it will be display's size minus
# overscan.
#framebuffer_width=1280
#framebuffer_height=720

# uncomment if hdmi display is not detected and composite is being output
#hdmi_force_hotplug=1

# uncomment to force a specific HDMI mode (this will force VGA)
#hdmi_group=1
#hdmi_mode=1

# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
#hdmi_drive=2

# uncomment to increase signal to HDMI, if you have interference, blanking, or
# no display
#config_hdmi_boost=4

# uncomment for composite PAL
#sdtv_mode=2

#uncomment to overclock the arm. 700 MHz is the default.
#arm_freq=800

# Uncomment some or all of these to enable the optional hardware interfaces
dtparam=i2c_arm=off
#dtparam=i2s=on
dtparam=spi=off

# Uncomment this to enable infrared communication.
#dtoverlay=gpio-ir,gpio_pin=17
#dtoverlay=gpio-ir-tx,gpio_pin=18

# Additional overlays and parameters are documented /boot/overlays/README

# Enable audio (loads snd_bcm2835)
dtparam=audio=on

# Automatically load overlays for detected cameras
camera_auto_detect=1

# Automatically load overlays for detected DSI displays
#display_auto_detect=1

# Enable DRM VC4 V3D driver
#dtoverlay=vc4-kms-v3d
max_framebuffers=2

# Disable compensation for displays with overscan
disable_overscan=1

[cm4]
# Enable host mode on the 2711 built-in XHCI USB controller.
# This line should be removed if the legacy DWC2 controller is required
# (e.g. for USB device mode) or if USB support is not required.
otg_mode=1

[all]

[pi4]
# Run as fast as firmware / board allows
arm_boost=1

[all]

gpio=0-9=a2
gpio=12-17=a2
gpio=20-25=a2
gpio=26-27=a2

dtoverlay=dpi24

enable_dpi_lcd=1
display_default_lcd=1

dpi_group=2
dpi_mode=87

extra_transpose_buffer=2

dpi_output_format=0x7f216
dpi_timings=720 0 40 48 128 720 0 13 3 15 0 0 0 60 0 50000000 1

*************

A new vision for this project : it’s a lo-fi device that takes HDMI and outputs HDMI but allows all kinds of very low res transformations like super low resolution GIFs, and saving every Nth frame of a film.

****TESTING ***

  • Get sync sorted on this board. **EDIT** Unfortunately taking HSYNC, VSYNC and their inverses, and sending them to the 590 on various pins is not syncing the video. Thinking back, I think I managed this only when I was ANDing a bunch of freq divisions and then inverting the output to record images only every nth frame. *EDIT* OK I’ve got something taking VSYNC as the freq and the AND’ind the outputs to be used to reset the 4040 counters.
  • Test the keyboard interface experience. ***EDIT** oof this is painful to do with jumpers and breadboards. Everything seems to be very sensitive to wierdness. It makes me want to eliminate the double board concept and just stick with hard soldered stuff on a single board. In the moments when things were briefly working it was super cool to control things with the keyboard.
  • See what other video distortion is possible with the circuit
  • I tried out the new HDMI converter, it works well.
  • I figured out that you can save a multicolor multichannel image by recording on several channels at once !
  • I like the idea of avoiding mission creep – not going into the screen or video codec business but instead staying with the theme of the project : video memory recording and playback.
  • I like the idea of emphasizing how little memory there is here (4MB), and how I am trying to use every part of the bison rather than going for more memory.
  • the new board should have usb micro female plugs to power an rpi, a VGA to HDMI converter, or the 5″ HDMI screen.
  • No more connectors between boards that are mirrored… Thinking of a single-sided board where the buttons relate the the function (latched push button for things that need to be latched like the freq sel) keyboard keys for selecting layers to record to. I thought that having an array of the same button would make things clean but it ends up being confusing what button does what. I like the idiosyncratic and non orthogonal matrix approach.
  • Just realized that I only need the single pole analog switch and don’t need the double pole anymore.
  • I have higher quality pots I could use for smoother action

Super cool to see everything on the tiny screen actually (but when I plugged in everything from the v synth I think it went above the fuse limit so I’ll have to FIX THIS IN THE NEXT VERSION if I want all to be powered by the board):

*

Trying to make a list of cool new video distortions possible with this board :

  • RCK and CLK of 590 could be connected to different frequencies
  • could have two different counter pairs for two memories that could be desynchronized

**********

A quick redesign :

  • Different components for different functions that they control (flat pots for inputs)
  • Added DIP switches to turn on and off diff V SYNC divisions going to 4040 counter reset
  • Only two speed buttons now, and they are physically latched
  • Nicer quality top facing pots
  • HDMI in and out with TI chips
  • got rid of some replaceable components
  • two micro USB connections to allow for these options :

 

*****

Trying out some hyper symmetrical design here.

Some considerations :

  • Leaving enough room to be able to comfortably twist the knobs
  • having mechanic keys close to one another so that they can be easily tapped with adjacent fingers
  • associating each key with it’s respective knob spatially and clustering controls that are thematically related (like the dip switch and the two frequency setting pushbuttons which are both temporal)
  • components that extend beyond the board on the top and bottom leaving room for hands on the keys and knobs
  • Tommy thinks it should be hand held.

 

And a more controller-like option :

*******

Check out Vincent Rioux’s muq machine and how he show the grid of screen shots :

 

***

I want to make videos like this guy makes experimental sound sets :

****

I’m going to remake the keyboard board (but on FR4, with connector aligned to motherboard, no latched colors to activate channels) and add the sync lock functionality. This will allow me to finish that project and test it easily.

The videos I have already of the board functioning are cool : diff colors allow for multiple images to share the same frame and be legible. The freeze frame(s) is very cool and being able to freeze different chunks of time I think will add a lot of things to test.

After this board I’ll move back to FPGA as they represent the future of the project I think (also the NO SCHOOL workshop this summer). Before I make I new board I should play more with the FPGA SRAM boards I have already made. Can I , for instance, reproduce the bechamel board with an FPGA? Perhaps it is time to upgrade to an FPGA with more LUTs, even if it is harder to solder in Ball Grid Array packaging (and then why not SRAM in BGA also), and the HDMI helper chips.

The dream would be able to throw 20 of the new boards in the oven and have them all work at the end. I could then send them to friends around the world ! Would be clever to use capacitive touch in this case to avoid needing a physical interface like so : https://github.com/stnolting/captouch.

TTP226 8-way touch module Capacitive touch switch digital touch sensor - Calcutta Electronics

I could also design a silicone ruber keypad for it ! https://en.wikipedia.org/wiki/Silicone_rubber_keypad

Looks like an ESP32 with SD card can generate video as input, that or a rpi integrated into the board.

PCB Design for FPGA SoMs and Carrier Boards | NWES Blog

This might be one of those “already solved” problems which isn’t that much fun to explore…

***

Next steps for the HX8K board :

  1. FFMPEG can output into monob or monow 1 bit pixel format and different dimensions. Test making a low resolution video and putting it on an SD card and having Arduino read it and transfer it to FPGA on the gameboy console board I made. (This will test if can replace rpi or HDMI IN with SD card in on next board)
  2. Try again to see if HDMI out is possible with FPGA using the two boards I made to test this. (This will decide if need an HDMI helper chip or not on next board)
  3. Try to use internal FPGA BRAM to save a screenshot on the Villette Makerz board. (This will test if need SRAM or not on the next board).
  4. Replace mechanical keys with capacitive touch so everything is as light as possible.
  5. Plan to get JLC PCB to assemble it ?

****

New top board, no longer aluminum, no more LEDs, which has the V SYNC ADD circuitry, debounced keys, and a female header to connect directly to the motherboard.

 

*****

Realized that I haven’t used the BRAM on the FPGA and that I could store low res recordings there. *EDIT* But it’s only 64K in total for the HX1K which is very little space. Probably more useful to store a mini buffer before sending it along to the SRAM. I’ve tried to create BRAM primitives in IceCube but it’s not working so far. This code from TinLethax appears to work in IceCube :

module BRAM(
input R_CLK,
input W_CLK,

input [7:0]BRAM_IN,
output reg [7:0]BRAM_OUT,

input [11:0] BRAM_ADDR_R,
input [11:0] BRAM_ADDR_W,

input B_CE_W,
input B_CE_R);

reg [7:0] mem [3003:0];

always@(posedge R_CLK) begin// reading from RAM sync with system clock 
if(!B_CE_R)
    BRAM_OUT <= mem[BRAM_ADDR_R];   
end 

always@(posedge W_CLK) begin// writing to RAM sync with Slave SPI clock.
if(!B_CE_W)
    mem[BRAM_ADDR_W] <= BRAM_IN;
end

endmodule

 

 

This code from the Lattice Memory Guide (file:///C:/Users/jm225306/Downloads/MemoryUsageGuideforiCE40Devices.pdf) also works :

 module ram (din, addr, write_en, clk, dout);// 512x8
parameter addr_width = 9;
parameter data_width = 8;
input [addr_width-1:0] addr;
input [data_width-1:0] din;
input write_en, clk;
output [data_width-1:0] dout;
reg [data_width-1:0] dout; // Register for output.
reg [data_width-1:0] mem [(1<<addr_width)-1:0];

always @(posedge clk)
begin
if (write_en)
mem[(addr)] <= din;
dout = mem[addr]; // Output register controlled by clock.
end
endmodule 

After sorting out the parameters BRAM_ADDR_WIDTH and BRAM_DATA_WIDTH, this code also works : https://github.com/Megamemnon/bram/blob/master/bram.v

****

I am swinging back to the SD card and a very simple video codec begin read/or initialized by Arduino and sent along to the FPGA. I think that this could lead to some new kinds of images and animations, and I think it could move away from having a computer generate video which kind of seems like a cop out for an experimental low-fi video device.

I am also swinging back in the direction of mechanical keys and, if I stick with discrete logic chips at least, by extension the two board option. Not having a real physical interface, and replacing with capacitive touch sensing, seems like losing the plot of an instrument like this. However, I am still interested in learning if I can do everything simpler with an FPGA and eventually add cool functionality like digital filtering that is impossible at the moment.

If I could output VGA and HDMI it would be more robust. It would basically have power in and video out only.

So basically, I am imagining a new corrected/optimized version of the bechamel (higher quality pots, streamlining components, etc.) that takes SD video in with an atmega 328p, outputs in HDMI and VGA, but basically has the same look as before with a 4×4 keyboard and motherboard. This new version could conceivably replace logic with an FPGA also but that might be too much of a jump ?

***

Downloaded ffmpeg windows build here : https://www.gyan.dev/ffmpeg/builds/

I tried the following command as a test and it worked nicely :

C:\ffmpeg-2024-02-29-git-4a134eb14a-full_build\bin>ffmpeg -i C:\ffmpeg-2024-02-29-git-4a134eb14a-full_build\bin\input\input.mp4 output.avi

To see the list of formats I could output to I typed in ffmpeg -pix_fmts

There is monow and monob each at 1 bit depth and 1 bits per pixel.

But now I am realizing that I probably want to output just a series of simple bmp images, rather than a video format perhaps.

****

Trying to debug Ice40HXIK HDMI :

  • I soldered pin 19 to 5V. No change. I could also solder the HDMI metal case to GND with the jumper.
  • It appears that the HDMI signals only appear when some keys are activated, I guess an imperfect connection somewhere ?
  • Tested on two diff HDMI screens, not being recognized.
  • Looked a the signals with the ‘scope and I’m not seeing differential signals, they are the same signal twice.
  • I’m getting various warnings from IceCube when compiling the code (minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation by Author: Mike Field <hamster@snap.net.nz>).
  • It is kind of working now after I bitwise (~) inverted the hdmi n pins 🙂

These are the differential signals before inverting properly :

These are the same differential signals after inverting properly :

Here is the code for the Villette Makers Paris Electronic Week board with modifications :

The PCF :

set_io hdmi_p[0] 139 -io_std SB_LVCMOS
set_io hdmi_p[2] 78 -io_std SB_LVCMOS
set_io hdmi_p[1] 80 -io_std SB_LVCMOS
set_io hdmi_p[3] 137 -io_std SB_LVCMOS

set_io hdmi_n[0] 138 -io_std SB_LVCMOS
set_io hdmi_n[2] 79 -io_std SB_LVCMOS
set_io hdmi_n[1] 81 -io_std SB_LVCMOS
set_io hdmi_n[3] 136 -io_std SB_LVCMOS

set_io clk100 49

Mike Field’s minimal HDMI verilog with just the addition of inverted hdmi n pins :

`default_nettype none // disable implicit definitions by Verilog
//-----------------------------------------------------------------
// minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation
//
// Author: Mike Field <hamster@snap.net.nz>
//
// DVI-D uses TMDS as the 'on the wire' protocol, where each 8-bit
// value is mapped to one or two 10-bit symbols, depending on how
// many 1s or 0s have been sent. This makes it a DC balanced protocol,
// as a correctly implemented stream will have (almost) an equal
// number of 1s and 0s.
//
// Because of this implementation quite complex. By restricting the
// symbols to a subset of eight symbols, all of which having have
// five ones (and therefore five zeros) this complexity drops away
// leaving a simple implementation. Combined with a DDR register to
// send the symbols the complexity is kept very low.
//-----------------------------------------------------------------

module top(
clk100, hdmi_p, hdmi_n
);

input clk100;
output [3:0] hdmi_p;
output [3:0] hdmi_n;

// For holding the outward bound TMDS symbols in the slow and fast domain
reg [9:0] c0_symbol; reg [9:0] c0_high_speed;
reg [9:0] c1_symbol; reg [9:0] c1_high_speed;
reg [9:0] c2_symbol; reg [9:0] c2_high_speed;
reg [9:0] clk_high_speed;

reg [1:0] c2_output_bits;
reg [1:0] c1_output_bits;
reg [1:0] c0_output_bits;
reg [1:0] clk_output_bits;

wire clk_x5;
reg [2:0] latch_high_speed = 3'b100; // Controlling the transfers into the high speed domain

wire vsync, hsync;
wire [1:0] syncs; // To glue the HSYNC and VSYNC into the control character
assign syncs = {vsync, hsync};

// video structure constants
parameter hpixels = 800; // horizontal pixels per line
parameter vlines = 525; // vertical lines per frame
parameter hpulse = 96; // hsync pulse length
parameter vpulse = 2; // vsync pulse length
parameter hbp = 144; // end of horizontal back porch (96 + 48)
parameter hfp = 784; // beginning of horizontal front porch (800 - 16)
parameter vbp = 35; // end of vertical back porch (2 + 33)
parameter vfp = 515; // beginning of vertical front porch (525 - 10)

// registers for storing the horizontal & vertical counters
reg [9:0] vc;
reg [9:0] hc;
// generate sync pulses (active high)
assign vsync = (vc < vpulse);
assign hsync = (hc < hpulse);

always @(posedge clk_x5) begin
//-------------------------------------------------------------
// Now take the 10-bit words and take it into the high-speed
// clock domain once every five cycles.
//
// Then send out two bits every clock cycle using DDR output
// registers.
//-------------------------------------------------------------
c0_output_bits <= c0_high_speed[1:0];
c1_output_bits <= c1_high_speed[1:0];
c2_output_bits <= c2_high_speed[1:0];
clk_output_bits <= clk_high_speed[1:0];
if (latch_high_speed[2]) begin // pixel clock 25MHz
c0_high_speed <= c0_symbol;
c1_high_speed <= c1_symbol;
c2_high_speed <= c2_symbol;
clk_high_speed <= 10'b0000011111;
latch_high_speed <= 3'b000;
if (hc < hpixels)
hc <= hc + 1;
else
begin
hc <= 0;
if (vc < vlines)
vc <= vc + 1;
else
vc <= 0;
end
end
else begin
c0_high_speed <= {2'b00, c0_high_speed[9:2]};
c1_high_speed <= {2'b00, c1_high_speed[9:2]};
c2_high_speed <= {2'b00, c2_high_speed[9:2]};
clk_high_speed <= {2'b00, clk_high_speed[9:2]};
latch_high_speed <= latch_high_speed + 1'b1;
end
end

always @(*) // display 100% saturation colourbars
begin
// first check if we're within vertical active video range
if (vc >= vbp && vc < vfp)
begin
// now display different colours every 80 pixels
// while we're within the active horizontal range
// -----------------
// display white bar
if (hc >= hbp && hc < (hbp+80))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display yellow bar
else if (hc >= (hbp+80) && hc < (hbp+160))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b0111110000; // blue
end
// display cyan bar
else if (hc >= (hbp+160) && hc < (hbp+240))
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display green bar
else if (hc >= (hbp+240) && hc < (hbp+320))
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b0111110000; // blue
end
// display magenta bar
else if (hc >= (hbp+320) && hc < (hbp+400))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display red bar
else if (hc >= (hbp+400) && hc < (hbp+480))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b0111110000; // blue
end
// display blue bar
else if (hc >= (hbp+480) && hc < (hbp+560))
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display black bar
else if (hc >= (hbp+560) && hc < hfp)
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b0111110000; // blue
end
// we're outside active horizontal range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end
// we're outside active vertical range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end

// red N
defparam hdmin2.PIN_TYPE = 6'b010000;
defparam hdmin2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin2 (
.PACKAGE_PIN (hdmi_n[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c2_output_bits[1]),
.D_OUT_1 (~c2_output_bits[0])
);

// red P
defparam hdmip2.PIN_TYPE = 6'b010000;
defparam hdmip2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip2 (
.PACKAGE_PIN (hdmi_p[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c2_output_bits[1]),
.D_OUT_1 (c2_output_bits[0])
);

// green N
defparam hdmin1.PIN_TYPE = 6'b010000;
defparam hdmin1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin1 (
.PACKAGE_PIN (hdmi_n[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c1_output_bits[1]),
.D_OUT_1 (~c1_output_bits[0])
);

// green P
defparam hdmip1.PIN_TYPE = 6'b010000;
defparam hdmip1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip1 (
.PACKAGE_PIN (hdmi_p[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c1_output_bits[1]),
.D_OUT_1 (c1_output_bits[0])
);


// blue N
defparam hdmin0.PIN_TYPE = 6'b010000;
defparam hdmin0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin0 (
.PACKAGE_PIN (hdmi_n[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c0_output_bits[1]),
.D_OUT_1 (~c0_output_bits[0])
);

// blue P
defparam hdmip0.PIN_TYPE = 6'b010000;
defparam hdmip0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip0 (
.PACKAGE_PIN (hdmi_p[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c0_output_bits[1]),
.D_OUT_1 (c0_output_bits[0])
);

// clock N
defparam hdmin3.PIN_TYPE = 6'b010000;
defparam hdmin3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin3 (
.PACKAGE_PIN (hdmi_n[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~clk_output_bits[1]),
.D_OUT_1 (~clk_output_bits[0])
);


// clock P
defparam hdmip3.PIN_TYPE = 6'b010000;
defparam hdmip3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip3 (
.PACKAGE_PIN (hdmi_p[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (clk_output_bits[1]),
.D_OUT_1 (clk_output_bits[0])
);
// D_OUT_0 and D_OUT_1 swapped?
// https://github.com/YosysHQ/yosys/issues/330


SB_PLL40_PAD #(
.FEEDBACK_PATH ("SIMPLE"),
.DIVR (4'b0000),
.DIVF (7'b0001001),
.DIVQ (3'b011),
.FILTER_RANGE (3'b101)
) uut (
.RESETB (1'b1),
.BYPASS (1'b0),
.PACKAGEPIN (clk100),
.PLLOUTGLOBAL (clk_x5) // DVI clock 125MHz
);

endmodule

Proof 🙂 !

After moving my figure around the board it seemed like the amplifier was creating noise. I removed it and now things seem to work better, however, nothing happens unless I push a button. Could it be because the buttons pass right next to the 100MHz Clock and input pin 49 ? I wonder if I should also make the differential signals closer to the HDMI out. Not sure how I’ll make this work with a noisy board having multiple op amps.

Best thing that has improved reliability has been to push hard on the oscillator one time. It still isn’t solid though, pushing buttons can make it lose connection. I’m going to have to be very careful how I reduce noise in future versions, possibly dividing up the board and having distinct zones. Maybe the double board is more noise resistant also ?

*EDIT* I resoldered the 100MHz clock and now everything is 100% working even when I mash buttons and lay hands on all parts of the board. It also works on the mini HDMI without any issues. YAY !

***

Trying to debug the Ice40HX4K on the gameboy board. I can now upload code, and the clock looks fine, but it’s not outputting anything to the test led pin other than being pulled constantly high.

***

Thought : They (the people who may one day own this kit?) will need an rpi to program the FPGA so I can assume they will have one to produce video. The FPGA would just need to be conected to the programming pins and be able to switch between programming mode and sending pixels. The rpi could actually connect to the same output screen as the video device if there were a switch. It would be more like a marriage of rpi and device, and with a wireless keyboard it could be nice ? At the same time I feel like it complicates the object, and the lo-fi-ness. It also begs the question : why not do everything on the rpi ?

Another thought: if it is HDMI out there are no outputs amps, it’s all digital. I could make a new version without the amps but with the keys. I don’t think it makes sense to make the board with both VGA and HDMI in this case. So, next version could be my first HDMI FPGA synth that replicates the functionality of the bechamel (diff I/O pins as video channels). Not sure if one or two layers.

Might be smart to reuse this board for the NO SCHOOL ? And therefore chose a cheap SRAM. And if this is the case, I can maybe select an inexpensive SRAM (like 1MB or 2MB that runs at 3.3V) and show them how to record incoming video. Perhaps keeping HDMI and VGA out would be smart. Also, perhaps a single sided board too, to minimize cost and weight of this thing. (So basically add small SRAM to the villette makerz 3×3 board and make one key a double if there isn’t enough room). So, forget about SD card video, higher LUT FPGAs and BGA soldering.

  • In order to prep for this I could do some more experiments with SRAM FPGA board reproducing béchamel functionality and doing some other low LUT requiring FPGA things.
  • I could also program FPGA with rpi to prep

Just one other thought : if FPGA is too challenging for beginners to program, should I make a board with Arduino that commands FPGA to do stuff ?

Another thought : One reason why I have had to keep on ordering new boards is because I always make boards that don’t have every element on it. For example I’ve made boards with SRAM and VGA but not SRAM and HDMI. Perhaps I should include in the following board an atmega 328 and an SD card if I ever get around to doing that in the future. I won’t need to populate it now but basically I can transition into coming up with cool codes and can kind of freeze the board developement.

I can also start by resoldering just the parts of SRAM card that I need to go further in testing bechamel functionality : FPGA, 16MB SRAM, VGA out. This would help understand how important having 16MB of RAM is, because 2MB would be a lot cheaper.

***

Oof, just tried to get the SRAM FPGA back up and running and the complexity of the board, plus the amount of different code versions on the website. The fact that I never sorted out the faulty SRAM pin, the board needing to be bent just so for the counting to start, the hand soldered rpi in colors (can’t remember why I did that), and that the rpi is now restarting itself everytime for some reason. All of it makes me think that I should KEEP THE NEXT BOARD SIMPLE. I also need a proper file structure to store verilog code versions as IceCube loads files and doesn’t just keep the text that you have written in the program.

Tried getting the FTDI board working with HDMI despite not having the differntial resistors. Confirmed that there is differential signals, but it doesn’t appear to work for an HDMI screen.

The mini projector is awesome though :

Also works on the small screen :

****

A thought : In order to save time, I think I should do the exercise of assuming that I can manage something technically, and doing the thought experiment of trying to understand what would be possible if I can indeed make the thing. For instance, with the HDMI FPGA, I can assume right now that I can do several colors only, but cannot change the intensity of the colors. Having four channels would reaquire having solid colors that overlap.

*****

TLDR : Follow this (https://github.com/anse1/olimex-ice40-notes) but in the place of -w dump put your file name and manually make sure it is exactly 4194304 bytes long (don’t try their -dd code which I can’t get to work).

The long painful version :

Some other research around programming FPGA with rpi :

  • https://github.com/squarewavedot/iceprog-rpispi
  • https://j-marjanovic.io/lattice-ice40-configuration-using-raspberry-pi.html
  • https://github.com/plex1/raspice40
  • https://www.olimex.com/wiki/ICE40HX1K-EVB#Get_started_under_Linux  IF SCROLL TO THE RPI SECTION

I am following the OLIMEX tutorials because it seems like the best documented and least in progress. Starting with this page (https://github.com/plex1/raspice40) I began by downloading rapice40 and uploading putting an FPGA .bin file I had generated with IceCube2. I activated SSH and SPI on the rpi. I cd’d to the raspice40-master directory and typed sh install.sh. After this I tried to program with program.sh programming_file.bin but it said it didn’t have flashrom. So I started following this website instead (https://www.olimex.com/wiki/ICE40HX1K-EVB#Get_started_under_Linux) and I typed:

git clone https://github.com/flashrom/flashrom.git

Then :

cd flashrommake

sudo CONFIG_ENABLE_LIBPCI_PROGRAMMERS=no CONFIG_ENABLE_LIBUSB0_PROGRAMMERS=no CONFIG_ENABLE_LIBUSB1_PROGRAMMERS=no install

I made the connections between my board and the raspberry pi Zero pins (not the same as the rpi 2+ pins as far as I can see) as per this image :

And then :

claim GPIO24 for sysfs-control

echo 24 > /sys/class/gpio/export

Pull GPIO24 low to put the ice40 into reset. The cdone-LED on the board should turn off.

echo out > /sys/class/gpio/gpio24/direction

Read the flash chip at 20MHz (for short cabling)

flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=20000 -r dump

I tried this but when I looked inside the dump file it was empty.

Simply swap -r for -w to write the dump back

flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=20000 -w dump

As generated bitstreams are smaller than size of the flash chip, you need to add padding for flashrom to accept them as image. I used the follwing commands to do that:

tr '\0' '\377' < /dev/zero | dd bs=2M count=1 of=image
dd if=my_bitstream conv=notrunc of=image

*EDIT*

This is where I am getting stuck. It can even read the flash chip^on the FPGA board and identify it correctly ! It just keeps on saying that the binary fie is too small to upload.

I can now use the dd command to change the size of the file, and I know the file needs to be 4194304b in size. But it wants that in another number format, but when I try 4194kB it makes it only 31.5 something. I will try asking it for 4M next time and see if that works.

Looking in to padding with dd in different ways to get it up to the required 4194304b size but so far stuck here.

I eventually just used the following command in windows cmd  fsutil file createnew <filename> <length> :

fsutil file createnew FPGA.bin 4194304

I then opened this file in hexed.it, and pasted the binary file from IceCube2 into this file (make sure to replace the data and not add it or else you will need to move to the address 4194304 and erase what comes after). I exported it as a bin file, and passed this file (which was precisely the right size) NOT to the program.sh (which I now understand was automating the padding of the input file) but with the following commands in sequence (from https://github.com/anse1/olimex-ice40-notes) :

echo 24 > /sys/class/gpio/export

echo out > /sys/class/gpio/gpio24/direction

flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=20000 -w FPGA.bin

And I get this success message :

However when I tried it again it could not see the FLASH chip. But to fix this I manually plugged RESET to GND thanks to Fabien’s advice.

*EDIT*

Deassert creset to let the ice40 read the configuration from the bus:

echo in > /sys/class/gpio/gpio24/direction

 

****

Tried an HDMI splitter and it didn’t split. Also tried to send HDMI to USB C using my adapter, no dice ! I need an HDMI recorder like this :

****

Messed with the HDMI color symbols and can generate many different colors !

// A nice Azure blue for example :
c2_symbol = 10'b1111010000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b1011110000; // blue

If you send it something that isn’t balanced colorwise it goes haywire :

c2_symbol = 10'b1111111111; // red
c1_symbol = 10'b1111111111; // green
c0_symbol = 10'b1111111111; // blue

c2_symbol = 10'b0000000000; // red
c1_symbol = 10'b0000000000; // green
c0_symbol = 10'b0000000000; // blue

Informatique préparée / Assemblages informatiques

I have an idea for a next “chapter” in my project : prepared computers or assemblages.

Each computer would be a hodge-podge of different tech from different eras, but would each include some kind of I/O, memory, ALU, and display, cooling. I could return to the e-waste side of things, but still make circuits to control them.

I could use a corps exquis method to combine different types of screens/displays :

  • mini CRT
  • LCD
  • projector
  • printer
  • e-ink
  • galvo lasers

Types of memory :

  • floppy
  • SRAM
  • DDR
  • CD
  • Tape

ALU :

  • FPGA
  • microcontroller
  • raspberry pi
  • mechanical

I/O :

  • mouse
  • keyboard
  • trackball
  • touchpad
  • speakers
  • internet

Cooling

  • Peltier
  • Fan
  • Radiator

The actual functions of the computers would be unclear, they would generate mysterious images and sequences and react to the interface. They would allow me to very clearly be in the practice of making art.

They would highlight what I’ve come to find interesting about the world of computers, the hand-madeness and social dimension of the objects. Each would be unique, idiosyncratic, and I would no longer be in the situation of working with screens and not knowing what exactly the “object” is. The object here would be clear, it would be the computer assemblage. I could revisit my obsession with miniatureness, and oddity in these hybrid “monsters”. They would also require lots of technical challenges to make work, and would not produce waste like the mass production of circuit boards does when making a kit.

This is building on Vivien’s idea to be inspired by full computer builds to emphasize materiality :

https://cdn.cnx-software.com/wp-content/uploads/2022/12/LapPi-2.0-DIY-Raspberry-Pi-laptop.jpg?lossy=0&strip=none&ssl=1

Clockwork's uConsole is a modular portable computer & "fantasy console" for $139 and up - Liliputing

It would be really cool to make everything from e-waste, but also to keep the miniature dimensions. It looks like phone screens from Nokia can be controlled by Arduino, but that others are undocumented and hard to control without finding a datasheet :

Nokia 5510 84*48 LCD and the Arduino Nano – thesolaruniverse

Some ressources for controlling a TFT display directly with Arduino :

https://community.element14.com/members-area/personalblogs/b/blog/posts/screen-success-hack-like-heck-build-update

Looks like it is doable with a raspberry pi and therefore doable with an FPGA.

None of these will be possible if I don’t have an autonomous board though – one that produces it’s own videos with data (instead of relying on video from a computer or rapsberry pi). So I will have to make this work first, and why not try to have this done for the expo next month in Germany ?

******

This Autonomous board could play videos from memory and display it all on the same board, without any wires !

 

I am now seeing this as a two board construction. The keyboard is too dense to pack any electronics into really, so it is better just as an interface with a ribbon cable going to the board behind the screen which has the FPGA and microcontroller. The bottom could be in aluminium which would make it stable and could have tabs which bend on the edges. This would provide a solid mounting place for the micrometer knob and the laptop hinge. It would also give a different look to the board as it would be all metal, combining with the micrometer, the screen and the screen hinge to make an all metal looking assemblage.

Not yet sure how to convert the linear motion of the micrometer knob, and I feel that the screen + board might look and be a little top heavy / too thick for the effect.

Check out these beautiful machines from Love Hulten : https://www.lovehulten.com/ Thanks Martin De bie for the ref !

Now I’m thinking it should be flat in fact :

Looking in to TFT LCDs, here’s what I’m finding :

  • 40 pins seems to be a kind of standard
  • There seems to be at least two modes : ‘RGB’ mode and SPI mode.
  • rectangular screens with 800×480 in 4.3″, 5″ or 7″ seems to be standard
  • TFTs seem to come with a driver chip like the ILI9341 or the ST7701S
  • Some seem to require being initialized by SPI

*****

Kristy has helped me select a nice sized screen, 2.8″ seems to be cute but not trinket-like. Adafruit’s screen sells on Mouser : https://www.adafruit.com/product/1774

It appears to have a simple 8 bit parallel mode by default with a straight-forward timing diagram :

And here’s another explanation of the same thing showing two different options :

The datasheet is detailed and I have Adafruit code to base things on as well.

****

The screen also appears to also have a “raw” Parallel RGB Interface mode which seems to involve the HSYNC, VSYNC, DOTCLOCK, DE, while sending color data to the D pins…..but it looks like first bits (RCM, RIM, and DPI) must be set from the SPI mode ? Also, it looks like you have to calculate yourself the timings of the screen based on the clock and screen dimensions (and also set these bits (DIV, RTN, etc.) ? But it looks super simple afterward :

 

*************

PROJECT UPDATE :

This project seems to be unlucky :

  • My Mouser order was stolen from inside my building or was never actually delivered.
  • The idea of having doublesided PCB + aluminum went out the window when I saw the cost (approx. 10x a normal PCB)
  • The replacement screen I ordered from Amazon has only 20 connections (instead of 50) and only allows SPI, which is very difficult for the FPGA to do and so would require a microchip + FPGA teamwork which seems technically a real headache.
  • Currently the HX4K is not starting up, despite having both PLLs connected now. I’m not sure what I’m missing but it is different enough from the HX1K to merit more research before using I guess.

    On the other hand I have been preparing for my ESDAC creative coding class and enjoying learning about the different kinds of P5JS codes that exist. I made an image to show the typologies I found so far :

    I should also have a class on Glitching !!! And on WEBGL Shader, check out this awesome book : https://thebookofshaders.com/03/

    Moiré

    Field

    Random walk

    Spirograph

    Cavegen

    Radial

    Hyper-realistic face generation

    **********

    Trying to put together an analysis of the kind of hardware effects I have been producing so far :

    Here is a slide trying to show the process of designing a board :

    And some sketches of circuits I’ve made over the course of the project :

    ****

    Older board I never photographed :

    ****

    I finally have access to opencores.org ! Some things of interest so far :

    • Math – https://opencores.org/projects/verilog_fixed_point_math_library
    • Noise and PRNG  – https://opencores.org/projects/gng
    • CORDIC core (for trig functions) – https://opencores.org/projects/verilog_cordic_core
    • ATTINY core – https://opencores.org/projects/attiny_atmega_xmega_core

    There is plenty more, like H.264 and JPEG decoding, SoC, sorting algorithms, DDR controllers, Real-time clocks, PCI bus controllers, DSP Filters, SD card controllers, SPI/UART/USB/I2C etc interfaces, etc.

     

    ****

    Some aesthetic references I was reminded of this week :

    • The fractal, “trippy”, spaceship-like camera movements, and kaleidescopic winamp visuals : https://www.youtube.com/watch?v=RBkhUg1oVIE

    Winamp Milkdrop Visualization Plugin - YouTube

    • The black and white retro dithering from Return Of The Obra Dinn https://www.youtube.com/watch?v=k2y5qH2Jn14

    *************

    Post Darmstadt workshop thoughts :

    • CNC building workshops are hard. In the Fab Academy it takes an entire team two weeks. My two attempts, a while back at CMU and in Germany this week were too ambitious. If I want to have everyone leave with a working machine I need to do a lot of prep before hand. This weekend I could have 3D printed parts before hand that clipped together in the spirit of my linear actuators for the Fab Ac (https://www.marrs.io/medium-sized-cnc-foam-cutter-with-leon-reboul/). Alternatively I could have bought a cheap drawing kit from Aliexpress for around 100 euros if I had gotten myself organized early enough. I think threaded rods may also be a better solution than belt drives which require more skill to design with. The linear rails were cool but they required special low-profile screws so the students struggled a bit with this. It also seems to be the case that servos can simply replace all the challenges of stepper motors requiring drivers for everyone but experts. Essentially : Don’t overthink it, do the most obvious tech thing that is the shortest path for beginners. This will be my strategy for the Beaux Arts workshop coming up where I’ll focus on servos only.
    • I should probably keep in mind the challenge of the workshop. The higher the challenge for the participants, the more work I need to do to prepare everything. More plug and play = less challenge.
    • Learning to be a free lancer will take time but I need to consider myself as a professional, not doing things on a shoestring budget and exhausting myself. As I move around and workshop in different places, I need to keep in mind my audience – engineering students are different from artists, designers, enthusiasts and kids.
    • It might be cool to learn to make video PDFs for future presentations when talking about video synthesis. Also maybe live coding with toy shader / P5JS to illustrate things ?
    • For the expo, I was happy to have a plug and play FPGA solution that just generated images. The boards I have made that record video all need heavy intervention on the part of the player and are not intuitive at all. I am still hoping to find a way to order the remaining parts from Mouser soon so I can test SD card reading and FPGA screen displaying.
    • For my talk, I still don’t have a way of directly connecting my earlier projects with the new video synth project.

    Next steps :

    I am feeling positively about just following my interests, and not necessarily knowing where I am going with this project. I want to :

    • make abstract generating form code with FPGA using the new trig functions, multiplication and division from opencores !
    • experiment with more filters using the FPGA.
    • move beyond my current aesthetic, which will require making the recorded image not jump around. I should be able to do this pretty easily when the FPGA is sending the sync signals.

    *************

    Post BA workshop thoughts :

    • The students this time were really into following a more formal series of prepared slides with exercises, and seemed less interested in building their own drawing machines. The Arduino exercises were well received, as well as the glitch exercise, and also the soldering workshop. I like that they bring their own projects and integrate them.
    • I tried to be extra professional this time, staying late with students to continue helping them, being extra careful to put everything back in its proper place, and generally being as uncompromising as possible in the quality I’m trying to bring to the workshop.
    • My little polyphonic synth, portable speaker, and VGA arduino board were a good thing to bring. I should have something interactive that puts images on the screen I think and a lamp to show off the solar sunflower too.
    • I had too many projects to manage in parallel at one point and was rushing about everywhere. Not sure what the solution is there, there were around 8/9 people for the first two days.
    • We need some basic boards like mic amp, speaker amp, easy stepper motor drivers, relay boards, etc. that are useful at the BD.
    • Vincent was suggesting that we think of ways of creating a virtual expo space for digital works somehow in relation to his radio station at the school which is appreciated.

    *************

    Post website being down, looking at previous pages now and having some perspective thoughts :

    • pure p5js coding I find far less exciting than hardware visual effects, it’s all too immediate and mediated. It’s not raw and unstable like analogue. I think I’m more convinced that the painful process of doing simple things the hardest way possible is actually productive creatively for the friction it affords.
    • Putting a series of bitmaps on an SD card (possibly with a custom FFMPEG running on raspberry pi) and then onto the screen would be cool. The idea for a custom FFMPEG comes from this tutorial http://dranger.com/ffmpeg/tutorial01.html which I found here https://github.com/codecrafters-io/build-your-own-x which I in turn found on reddit FREE MEDIA HECK YES 
    • I need to get the sync working to test some other (less jarring) visual experiences.
    • The Real Time Corrupter methods (piping, etc.) are brilliant and I have to copy them to experiment with FPGA memory filters. Beyond the RTC it would be very cool to experiment with DCT compression filter spectra.
    • I don’t think I need to look under each hole and check under each rock. Getting one type of memory working, and one video protocol, is good enough and I don’t need to suffer with DDR and HDMI in this way of working I’m assuming currently.
    • For marketing video synths, there is the website php like Marc did (https://muca.cc/fr) or the instagram/facebook marketplace option. Alternatively there are services that host the payment side of things like gumroad, prestashop, and wordpress https://wordpress.com/fr/ecommerce/
    • I’m doing NO SCHOOL this summer !! Planning on getting a simple FPGA v synth programmable with Arduino / RPI in python and made on an aluminum-backed board (with SMD VGA).

    *************

    Learned about live coding with hydra :

    hydra >” jsname=”kn3ccd” aria-hidden=”false”></p>
<p>Kind of like Processing, I find it too easy to make wacky stuff and unsatisfying to not have the friction of analogue.</p>
<p class=I will be VJing this month, we’ll see what that experience is like. Perhaps it will help me design boards that VJs could use. Here’s what I learned :

    • The stress of doing a live performance when everything isn’t set up and rehearsed many times before hand is high. One would need to have a bunch of backups, and a very rehearsed performance to lower stress.
    • Having a small lamp is helpful to see what connections and knobs you are messing with
    • One question is how to react to music, does a beat equal a varying of a parameter ? How to react to song changes ?
    • How should “effects” be introduced ? I defaulted towards starting simple and then going towards the more complex effects
    • Clearly having performance grade equipment means everything is super robust, easy to use, etc. This makes me think that it would make sense to make a really reliable simple filter, for instance. This also connects with a larger issue for me : I want to do things differently but there are often good reasons why things are done a particular way
    • Sending my crazy VGA to a machine that digitizes it and sends it on as HDMI is probably safer than directly sending things to the projector…

    I want to use an older media device (cassette tape, floppy, mini-VHS?) in my next board. I realized that if my practice is about “doing things the hard way”, that should be visually communicated in the design (no one understands if the electronics are done the hard way or not…). This would also signal that I am not trying to make cutting edge new things but that my interests are elsewhere.

    Here is a first look at some video synth companies and their designs :

    Trying again with the floppy drive :

    Check out this video extracting motion from video by just inverting and sliding videos sideways :

    ****************

    I finally bought preparedinstruments.com ! Here are some potential designs for the page :

     

    Paris Electronic Week Synth Workshop

    Making a simple FPGA video synth for PEW 2023 at Villette Makerz. It takes audio in and generates patterns based on the voltage threshold and the 9 keys being pressed.

    The code is here : https://github.com/merlinmarrs/iCE40HX-verilog-video-patterns

    This time the top interface side has only buttons and no electronics. I have a simplified VGA out and HDMI with the correct LVDS resistor setup this time. I am relying on raspberry pi or arduino for programming.

    Trying a grid ground pour on the Stop layer :

     

    Here’s the final soldered version :

    Nicer photos :

    Found a site called OpenCores (https://opencores.org) that has free FPGA IP like math libraries.

    Here are some tests with cool looking patterns :

    Because dividing, trig functions, etc. aren’t supported by default, the most promising is the bitwise boolean logic operating on the entire vector :

    wire[11:0] bit_or;
    assign bit_or = h_count[11:0] | v_count[11:0];
    
    [...]
    
    if (bit_or[5] == 1 | bit_or[3] == 0) begin
    wire[11:0] bit_xor;
    assign bit_xor = h_count[11:0]^v_count[11:0];
    
    [...]
    
    if (bit_xor[2] == 1 | bit_xor[4] == 1) begin
    wire[11:0] bit_and;
    assign bit_and = h_count[11:0]&v_count[11:0];
    
    ...
    
    if (bit_and[6] == 1 | bit_xor[7] == 1) begin

    I could imagine having the keys vary which logic operations are in play and having the music threshold change the color of the pixels for instance.

    ***

    This site has a module for a quasi random number generator : https://simplefpga.blogspot.com/2013/02/random-number-generator-in-verilog-fpga.html

    This site has some good Processing examples : http://www.generative-gestaltung.de/2/

    ***

    If I do this workshop again I’d like to design it to be made on aluminum as, it is at least in principle, recyclable.

    PCBWay’s diagram of a double layer board (https://www.pcbway.fr/pcb_prototype/General_introduction_of_Aluminum_PCB.html) :

    Two-layer_single_side.png

    ***

    Simplest imaginable keypad switch (and audio in post comparator) test works :

    module top (
    input hwclk,
    output led1,
    input keypad_c1
    );
    
    assign led1=keypad_c1;
    
    endmodule
    Modified and restructured the random code from this site : https://simplefpga.blogspot.com/2013/02/random-number-generator-in-verilog-fpga.html and it appears to work :
    `default_nettype none
    
    module LFSR (
    input clock,
    input reset,
    output [12:0] rnd
    );
    
    wire feedback;
    reg [12:0] random, random_done;
    reg [3:0] count; //to keep track of the shifts
    
    assign feedback = random[12] ^ random[3] ^ random[2] ^ random[0];
    assign rnd = random_done;
    always @ (posedge clock)
    begin
    if (reset)
    begin
    random <= 13'hF; //An LFSR cannot have an all 0 state, thus reset to FF
    count <= 0;
    end
    
    else
    begin
    if (count == 13)
    begin
    count <= 0;
    random_done <= random; //assign the random number to output after 13 shifts
    end
    
    else
    begin
    random <= {random[11:0], feedback}; //shift left the xor'd every posedge clock
    count <= count + 1;
    end
    end
    end
    endmodule

     

    I am using it in the top code like this :

    wire [12:0] random_number;
    
    [...]
    
    LFSR random_s(
    .clock(clk_in),
    .reset(reset),
    .rnd(random_number)
    );

    ***

    Sine gen :

    • https://miscircuitos.com/sinus-wave-generation-with-verilog-using-vivado-for-a-fpga/
    • https://zipcpu.com/dsp/2017/07/11/simplest-sinewave-generator.html
    • https://verilogcodes.blogspot.com/2015/11/verilog-code-for-simple-sine-wave.html

    I added a time delay module I modified from the led_rotation example :

    `default_nettype none
    module tempo(
    input wire clk,
    output reg half_sec_pulse
    );
    
    reg[25:0] div_cntr1;
    reg[25:0] div_cntr2;
    always@(posedge clk)
    begin
    div_cntr1 <= div_cntr1 + 1;
    if (div_cntr1 == 0)
    if (div_cntr2 == 0)
    begin
    div_cntr2 <= 0;
    half_sec_pulse <= ~half_sec_pulse;
    end
    
    else
    div_cntr2 <= div_cntr2 + 1;
    else
    half_sec_pulse <= half_sec_pulse;
    end
    
    endmodule

     

    I can’t figure out why this makes 1/2 second ish though. Google says it should last 130.312488 days…Perhaps the compiler is deciding it doesn’t have enough space to store it and it truncating it?

    From the led_rotation code I get 2^16 = 65536, this happens 91 times so 65536x91, then multiplied by the period of a 12MHz clock (83.3ns) gets me to around 0.49 seconds.

    I’m guessing it is getting clipped because the register is too big ?

    *****

    Fabien showed me that on AliExpress you can buy things like this tracking ball for cents :

    Blackberry Trackball

    https://fr.aliexpress.com/item/1005004324983317.html?spm=a2g0o.detail.0.0.3f40hagUhagUgw&gps-id=pcDetailTopMoreOtherSeller&scm=1007.40000.327270.0&scm_id=1007.40000.327270.0&scm-url=1007.40000.327270.0&pvid=83e84367-84b9-424f-b42a-31b87563ff5e&_t=gps-id:pcDetailTopMoreOtherSeller,scm-url:1007.40000.327270.0,pvid:83e84367-84b9-424f-b42a-31b87563ff5e,tpp_buckets:668%232846%238114%231999&pdp_npi=4%40dis%21EUR%210.80%210.72%21%21%216.14%21%21%40211b801516946910696348428ea8cb%2112000030031307832%21rec%21FR%21%21ABS&search_p4p_id=202309140431096642081558243384177015_4

    Each one requires four little hall effect sensors which output HI-LOW pulses. The one made by Sparkfun is the AN48841B : high sensitivity CMOS Hall IC Operates on Alternating Magnetic Field (low-speed rotation for lock detection). Trying to find an alternative for not too much on Mouser.

    https://www.mouser.fr/ProductDetail/Infineon-Technologies/TLI49632MXTSA1?qs=0DP5yvOrqYlmAP5lA6MTRA%3D%3D

    I am also thinking about an HDMI mini – not micro – (if I can get HDMI working, if not get a helper chip), an atmega2560 to handle the SD card communication. Possibly a USB charger and a battery too – so that there can really be a minimum of cables when the battery is charged up.

    A dumb-proof programming connector with an automatically controlled bus to disconnect the programmer from the FPGA when reset not asserted so I don’t need to unplug and replug every time ?

    *****

    NEXT BOARD THOUGHTS :

    Maybe I don’t need to rebuild the FPGA etc each time, but should just make interfaces that plug into it ?

    The only thing left to try in hardware is the SD card fetching video data and then putting it on screen, and perhaps test having way more bits for each color and using the 4K LUT version of the iCE40.

    I could also try using the specialized HDMI chip ? This would allow me to try the mini screen.

    VGA with like 64 bit color !!!

    put the remaining video ICs that I haven’t played with yet (video differentiator, video sync module, etc.) on the board !

    Combine the possibility of reading a video from an SD card, having memory to store it in, and having a bunch of keys to press, AND coming up with code that makes meaningful patterns.

    My leftover video ICs :

    • ADV7125 8 bit Triple HS Video – https://www.mouser.fr/pdfdocs/ADI_ADV7125_Datasheet.PDF
    • LT1251CN Video Fader – https://www.mouser.fr/datasheet/2/609/12516fa-3123106.pdf
    • 660GILFT Digital Video Clock – https://www.mouser.fr/datasheet/2/698/REN_660_DST_20100514-1995937.pdf
    • NJM2274R Low Power CI video w/ Y-C mixer – https://www.mouser.com/datasheet/2/294/NJM2274_E-1917180.pdf
    • FMS6143CSX Video Filter Driver – https://www.mouser.fr/datasheet/2/308/1/FMS6143_D-2313627.pdf
    • AD724JRZ – https://www.mouser.fr/datasheet/2/609/AD724-3118543.pdf

    *****

    PATTERN CODE THAT WORKS :

    Got a code that works :

    `default_nettype none
    
    module vga_sync_test(
    
    input wire clk_in,
    input wire reset,
    input wire key[8:0],
    input wire adc_in,
    output reg led_red,
    output reg led_green,
    
    //VGA OUT
    
    output reg [3:0] r_out,
    output reg [3:0] b_out,
    output reg [3:0] g_out,
    output wire h_sync,
    output wire v_sync
    
    );
    
    wire half_sec;
    wire [12:0] random_number_0;
    wire [12:0] random_number_1;
    wire [12:0] random_number_2;
    wire [12:0] random_number_3;
    wire [12:0] random_number_4;
    wire [12:0] random_number_5;
    wire [12:0] random_number_6;
    wire [12:0] random_number_7;
    wire [12:0] random_number_8;
    wire [12:0] random_number_9;
    wire [7:0] sine_wave;
    wire display_en;
    wire [11:0] h_count;
    wire [11:0] v_count;
    
    localparam h_pixel_max = 1280;
    localparam v_pixel_max = 960;
    localparam h_pixel_half = 640;
    localparam v_pixel_half = 480;
    localparam h_pixel_25 = 320;
    localparam v_pixel_25 = 240;
    localparam h_pixel_75 = 960;
    localparam v_pixel_75 = 700;
    
    //VGA COLOR OUT
    
    always @(posedge clk_in) begin
    
    if (display_en) begin
    
    if ( key[0] == 0 &&
    
    h_count < h_pixel_25 + random_number_1[4:0] && v_count < v_pixel_25 + random_number_7[8:5] &&
    h_count > h_pixel_25 - random_number_2[4:0] && v_count > v_pixel_25 - random_number_2[8:5]
    
    ) begin
    
    r_out <= random_number_1[3:0];
    g_out <= random_number_5[3:0];
    b_out <= random_number_9[6:4];
    
    led_green <= 1;
    
    end
    
    else if ( key[1] == 0 &&
    
    h_count < h_pixel_half + random_number_7[5:0] && v_count < v_pixel_half + random_number_7[10:5] &&
    h_count > h_pixel_25 - random_number_6[5:0] && v_count > v_pixel_25 - random_number_6[10:5]
    
    ) begin
    
    r_out <= random_number_3[7:5];
    g_out <= random_number_3[3:0];
    b_out <= random_number_6[6:4];
    
    led_green <= 1;
    
    end
    
    else if ( key[2] == 0 &&
    
    h_count < h_pixel_25 + random_number_8[6:0] && v_count < v_pixel_25 + random_number_8[12:6] &&
    h_count > h_pixel_half - random_number_9[6:0] && v_count > v_pixel_half - random_number_9[11:5]
    
    ) begin
    
    r_out <= random_number_4[7:5];
    g_out <= random_number_5[3:0];
    b_out <= random_number_7[6:4];
    led_green <= 1;
    
    end
    
    else if ( key[3] == 0 &&
    
    h_count < h_pixel_half + random_number_0[7:0] && v_count < v_pixel_half + random_number_0[7:0] &&
    h_count > h_pixel_half - random_number_3[7:0] && v_count > v_pixel_half - random_number_3[11:4]
    
    ) begin
    
    r_out <= random_number_8[7:5];
    g_out <= random_number_8[3:0];
    b_out <= random_number_8[6:4];
    led_green <= 1;
    
    end
    
    else if ( key[4] == 0 &&
    
    h_count < h_pixel_75 + random_number_2[8:0] && v_count < v_pixel_half + random_number_2[11:3] &&
    h_count > h_pixel_75 - random_number_4[8:0] && v_count > v_pixel_half - random_number_4[11:3]
    
    ) begin
    
    r_out <= random_number_7[7:5];
    g_out <= random_number_7[3:0];
    b_out <= random_number_7[6:4];
    
    led_green <= 1;
    
    end
    
    else if ( key[5] == 0 &&
    h_count < h_pixel_half + random_number_3[9:0] && v_count < v_pixel_75 + random_number_3[11:2] &&
    h_count > h_pixel_half - random_number_8[9:0] && v_count > v_pixel_75 - random_number_8[11:2]
    
    ) begin
    
    r_out <= random_number_5[7:5];
    g_out <= random_number_5[3:0];
    b_out <= random_number_5[6:4];
    led_green <= 1;
    
    end
    
    else if ( key[6] == 0 &&
    
    h_count < h_pixel_75 + random_number_4[10:0] && v_count < v_pixel_75 + random_number_4[11:1] &&
    h_count > h_pixel_75 - random_number_6[10:0] && v_count > v_pixel_75 - random_number_6[11:1]
    
    ) begin
    r_out <= random_number_4[7:5];
    g_out <= random_number_4[3:0];
    b_out <= random_number_4[6:4];
    led_green <= 1;
    end
    else begin
    r_out <= random_number_2[3:0];
    g_out <= random_number_3[6:4];
    b_out <= random_number_4[3:0];
    led_green <= 0;
    end
    end
    else begin
    r_out <= 4'b0000;
    g_out <= 4'b0000;
    b_out <= 4'b0000;
    led_green <= 0;
    end
    end
    
    vga_sync vga_s(
    
    .clk_in(clk_in),
    .reset(reset),
    .h_sync(h_sync),
    .v_sync(v_sync),
    .h_count(h_count),
    .v_count(v_count),
    
    .display_en(display_en) // '1' => pixel region
    
    );
    
    LFSR random_s(
    
    .clock(clk_in),
    .reset(reset),
    .half_sec_pulse(half_sec),
    .rnd_0(random_number_0),
    .rnd_1(random_number_1),
    .rnd_2(random_number_2),
    .rnd_3(random_number_3),
    .rnd_4(random_number_4),
    .rnd_5(random_number_5),
    .rnd_6(random_number_6),
    .rnd_7(random_number_7),
    .rnd_8(random_number_8),
    .rnd_9(random_number_9)
    );
    
    sine_wave_gen sine_wave_s(
    .clk(clk_in),
    .data_out(sine_wave)
    );
    
    tempo tempo_s(
    .clk(clk_in),
    .half_sec_pulse(half_sec)
    );
    
    endmodule

    and here is the PCF file :

    set_io v_sync 97
    set_io h_sync 76
    set_io clk_in 49
    set_io reset 66
    
    set_io r_out[0] 91
    set_io r_out[1] 95
    set_io r_out[2] 96
    
    set_io g_out[0] 75
    set_io g_out[1] 74
    set_io g_out[2] 73
    
    set_io b_out[0] 87
    set_io b_out[1] 88
    set_io b_out[2] 90
    
    set_io key[0] 37
    set_io key[1] 38
    set_io key[2] 39
    set_io key[3] 41
    set_io key[4] 42
    set_io key[5] 43
    set_io key[6] 44
    set_io key[7] 45
    set_io key[8] 47
    set_io led_red 23
    set_io led_green 24
    set_io adc_in 56

     

    ***************

    TAKEAWAYS FROM VILLETTE MAKERZ

    Getting feedback on my circuits has been really cool, getting “users” to try my boards and tell me what they would like to be able to do has to become part of my process of design and iteration if I am ever to make something people want to buy.

    Some feedback so far :

    • If people have hard time understanding the interest in “doing something the hard way” even if it is not obvious for the outcome, then you have to show them what your process is. You need to share your workspace and tools with them so they can see the advantages of doing something the hard way.
    • Using hand to modulate speaker for sequencer is cool
    • The 7 layer board is fun to let people play with. Difficult to see different layers though, would be cool if could have diff colors.
    • People like the idea of deforming an image of themselves like from a webcam and seeing it projected on the big screen
    • Kids like colors and the 9 key board board is colorful. Would be cool if once you select a pattern is stays though
    • The Arduino boards are less cool because they are not interactive or colorful
    • It would be cool for the FPGA 9 key things to have more of a relationship with sound 
    • putting circuits ontop of a screen is cool.
    • Projections kind of get lost if not in total darkness.
    • Curious to try a tiny screen, or a really big screen, or a tiling of screens ?
    • the combo of the oscilloscope and the function generator + speaker is cool to understand the basics of electronics signals.

    Aliexpress has components that are insanely inexpensive compared to Mouser. If I were ever to sell something for profit I would need to source the components from China entirely.

    I think I also need to address the environmental question too : how is continuing to manufacture circuits responsible ? Could I demonstrate how to recycle an aluminum PCB ? Obviously using e-waste is cool and should be emphasized more in my projects perhaps

    *****************

    SD CARD EXPERIMENTS

    Modified this code to write the contents of a file on the SD card as a byte to PORT D.

    Need to find a way to convert an image into a collection of bytes and store it. Then I need to iterate through the file and output the values to a port.

    /*
     * Created by ArduinoGetStarted.com
     *
     * This example code is in the public domain
     *
     * Tutorial page: https://arduinogetstarted.com/reference/library/arduino-file.readbytes
     */
    
    
    #include <SD.h>
    
    
    #define PIN_SPI_CS 4
    
    
    File file;
    char buf[200];
    
    
    void setup() {
      Serial.begin(9600);
      DDRD = B11111111;
    
    
      if (!SD.begin(PIN_SPI_CS)) {
        Serial.println(F("SD CARD FAILED, OR NOT PRESENT!"));
        while (1); // don't do anything more:
      }
    
    
      // open file for reading
      file = SD.open("albers.hex", FILE_READ);
      if (file) {
        int avail_len = file.available();
        int read_len = file.readBytes(buf, avail_len); // read all to buffer to buffer
    
    
        Serial.print("Length:");
        Serial.println(read_len);
        Serial.println("Content:");
    
    
                  for (int i=0; i<200; i++) {
       
        Serial.print(byte(buf[i]));
        PORTD = byte(buf[i]);
              }
       
    
    
        file.close();
      } else {
        Serial.print(F("SD Card: error on opening file"));
      }
    }
    
    
    void loop() {
    }

    I consulted this site : https://www.dcode.fr/binary-image to convert an image into binary (not sure yet how I will do this for video but I’ll cross that bridge later). I chose resolution 200 for this image :

    For the video I am looking into FFMPEG. It can export still images every X seconds (ffmpeg -i input.mp4 frame%04d.png) which I could then turn into bits with a simple python or processing code.

    Processing also has code to take video and convert it to ASCII : https://github.com/processing/processing-video/blob/main/examples/Capture/AsciiVideo/AsciiVideo.pde

    Maybe the best setup would be to have Processing create a series of ASCII text files based on frames from a video, and then put that on the SD card.

    Here is a schematic of how it could work with two SRAM banks in dual banking mode, writing to one while reading from the other and then switching :

    ******

    PROGRAMMING FPGA WITH ARDUINO OR RPI

    I had been trying to open the OLIMEX demo code pilot incorrectly. It does indeed work from the command line. I’ll have to test with the MEGA next.

    ****

    An attempt at mixing the pattern generation with the recording to memory functionality :

    `default_nettype none
    
    module vga_sync_test(
    
    input wire clk_in,
    input wire reset,
    
    input wire rec, // Direction of io, 1 = set output, 0 = read input
    
    //RASPBERRY PI
    input wire [3:0] r_in,
    input wire [3:0] b_in,
    input wire [3:0] g_in,
    
    //VGA OUT
    output reg [3:0] r_out,
    output reg [3:0] b_out,
    output reg [3:0] g_out,
    
    output wire h_sync,
    output wire v_sync,
    
    //SRAM
    
    output reg [20:0] addr,
    inout wire [7:0] io, // inout must be type wire
    
    output wire cs_1,
    output wire cs_0,
    output reg we_0
    
    );
    
    
    wire half_sec;
    
    wire [12:0] random_number_0;
    wire [12:0] random_number_1;
    wire [12:0] random_number_2;
    wire [12:0] random_number_3;
    wire [12:0] random_number_4;
    wire [12:0] random_number_5;
    wire [12:0] random_number_6;
    wire [12:0] random_number_7;
    wire [12:0] random_number_8;
    wire [12:0] random_number_9;
    wire [7:0] sine_wave;
    
    wire [7:0] data_in;
    wire [7:0] data_out;
    
    reg toggle;
    
    reg [7:0] a, b;
    
    assign io = rec ? a : 8'bzzzzzzzz;
    
    assign data_out = b;
    
    assign data_in[1:0] = r_in[3:2];
    assign data_in[3:2] = b_in[3:2];
    assign data_in[5:4] = g_in[3:2];
    assign data_in[7:6] = 2'b00;
    
    wire display_en;
    wire [11:0] h_count;
    wire [11:0] v_count;
    
    localparam h_pixel_max = 1280;
    localparam v_pixel_max = 960;
    localparam h_pixel_half = 640;
    localparam v_pixel_half = 480;
    
    
    reg [1:0] count2; //to keep track of the shifts
    
    wire [11:0] bit_or;
    assign bit_or = h_count[11:0]|v_count[11:0];
    
    wire [11:0] bit_xor;
    assign bit_xor = h_count[11:0]^v_count[11:0];
    
    wire [11:0] bit_and;
    assign bit_and = h_count[11:0]&v_count[11:0];
    
    wire [11:0] bit_nand;
    assign bit_nand = h_count[11:0]& ~v_count[11:0];
    
    wire [11:0] bit_xnor;
    assign bit_xnor = h_count[11:0]^ ~v_count[11:0];
    
    reg [11:0] out_bit ;
    
    
    // CS: low to select, high to deselect
    
    assign cs_0 = toggle ? 1 : 0;
    assign cs_1 = toggle ? 0 : 1;
    
    //SRAM address counter
    
    always @(posedge clk_in) begin
    
    if (addr == 0) begin
    toggle <= toggle+1;
    end
    
    if (reset) begin
    addr <= 0;
    end else begin
    addr <= addr+1;
    
    end
    end
    
    
    //REC control
    
    always @(posedge clk_in) begin
    
    b <= io;
    a <= data_in;
    
    if (rec) begin
    we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
    end
    else begin
    we_0 <= 1;
    end
    end
    
    //VGA COLOR OUT
    
    always @(posedge clk_in) begin
    
    case(count2)
    2'b00: out_bit = bit_or;
    2'b01: out_bit = bit_xor;
    2'b10: out_bit = bit_and;
    2'b11: out_bit = bit_nand;
    
    default: out_bit = bit_xnor;
    endcase
    count2 <= count2 + 1;
    end
    
    //VGA COLOR OUT
    
    always @(posedge clk_in) begin
    if (display_en) begin
    
    if (out_bit[random_number_2[3:0]] == 1 | out_bit[random_number_6[3:0]] == 0) begin
    
    r_out <= random_number_2[3:0];
    g_out <= random_number_3[3:0];
    b_out <= random_number_4[3:0];
    
    end
    
    else begin
    
    r_out[3:2] <= data_out[1:0];
    r_out[1:0] <= data_out[1:0];
    g_out[3:2] <= data_out[3:2];
    g_out[1:0] <= data_out[3:2];
    b_out[3:2] <= data_out[5:4];
    b_out[1:0] <= data_out[5:4];
    
    end
    
    end else begin
    
    r_out <= 4'b0000;
    g_out <= 4'b0000;
    b_out <= 4'b0000;
    
    end
    end
    
    vga_sync vga_s(
    .clk_in(clk_in),
    .reset(reset),
    .h_sync(h_sync),
    .v_sync(v_sync),
    .h_count(h_count),
    .v_count(v_count),
    .display_en(display_en) // '1' => pixel region
    );
    
    LFSR random_s(
    .clock(clk_in),
    .reset(reset),
    .half_sec_pulse(half_sec),
    .rnd_0(random_number_0),
    .rnd_1(random_number_1),
    .rnd_2(random_number_2),
    .rnd_3(random_number_3),
    .rnd_4(random_number_4),
    .rnd_5(random_number_5),
    .rnd_6(random_number_6),
    .rnd_7(random_number_7),
    .rnd_8(random_number_8),
    .rnd_9(random_number_9)
    );
    
    sine_wave_gen sine_wave_s(
    .clk(clk_in),
    .data_out(sine_wave)
    );
    
    tempo tempo_s(
    .clk(clk_in),
    .half_sec_pulse(half_sec)
    );
    
    endmodule
    

     

    I just realized that I could connect the rpi pixel clock to the FPGA to have a totally synchronized clock between the videos and the graphics ! I can also connect the DEN pin. Not really working currently though.

    *****

    Miniature discovery, if you just take a single bit of information from raspberry pi sending a video, it looks really blocky and cool like a badly compressed video :

    Check out this artist Ni Hao’s  broken screens ( https://haoni.art/spin-III) :

    FPGA SRAM board

    FPGA SRAM board

     

    Shadertoy is the future of this project, so what am I doing ? This new board is a reprogrammable FPGA 2D pixel shader (aka fragment shader) which can control the color of pixels and know only of the coordinate of the pixel being drawn. Pixel shaders can also sample the values of nearby pixels. As a result, they can do 2D post-processing (https://en.wikipedia.org/wiki/Video_post-processing) or filtering for a video streams. Why do this on an FPGA rather than in-brower ? You are super low level, and you have constraints based on the number of LUTs, it has a live-playable interface. In any case, I think this is my last board for a while, unless I can make an AI board somehow.

    On the other hand, I’m not sure how profound it is to make photoshop like filters. (You can test a 5×5 grid if you go under Filters>Custom in PS). They are all kind of superficial…

    I am starting to realize that I spend loads of time debugging soldering errors (yes, I get the learning that comes with that but still). If I made boards and had them assembled elsewhere I would not have this issue. Should I also, in the same spirit, just jump ahead to what everyone else is doing, with the thinking that they are not wasting time on problems that have already been solved and are exploring an interesting space.

    I also spend a lot of time designing and building boards, interfaces, and less time programming them: the SCARA robot arm that I didn’t even plug in, the covid temperature interface board, the drone, and the MIT version of the mini swarm robot. On the other side are the projects I’ve perhaps spent too must time making and incrementally remaking perhaps : 3105 power harvester + radio transciever, the Ben Eater style memory. A nice balance was perhaps the solar sunflower protytpes which were experiments with form and function.

    *EDIT* I am now thinking that I must work on things that would be of interest to other people, like Machine Learning for instance. I must be humble, just because something is interesting to me doesn’t mean anyone else cares ! Turns our that FPGAs consume a lot less current than GPUs so they are not bad for implementing ML once the training is done. Here is a info about ML with FPGAs :

    • https://www.youtube.com/@marcowinzker3682
    • https://www.mdpi.com/2076-3417/12/1/89
    • https://github.com/verimake-team/MLonFPGA
    • https://qengineering.eu/deep-learning-with-fpga-aka-bnn.html
    • https://qengineering.eu/embedded-vision.html#FPGA_stitching

    Great post about the marble machine project : https://www.reddit.com/r/MarbleMachineX/comments/pzh5kw/an_open_letter_to_martin_on_the_state_of_the/

    • The machine is not about reliability and precision, it is about imperfection and the journey
    • It’s an art project, it is a project for the project’s sake and the documentation.
    • The project shouldn’t run like a start-up, and we are not the sum of what we produce for capitalism
    • Not specializing in one thing is what makes this project cool.

    ERRATA :

    • I think the 590 is not clock dividing as expected…Is it not 3.3V compatible ? I think so, even though unclear on Mouser website.
    • Currently only 2 bits of each raspberry pi color input are appearing despite having selected dpi24 in the rpi config file.
    • The fine pots are the wrong footprint for the pots I have.
    • I could have used DEN (display enable) from the raspi.
    • sometimes the rpi starts up and then restarts after playing only a few seconds of video…not sure why.

    To change the PLL, there is a tool in IceCube2 under Tool>Configure>Configure PLL Module

    Here is the user guide explaining how to use it : https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/IK2/FPGA-TN-02052-1-4-iCE40-sysCLOCK-PLL-Design-User-Guide.ashx?document_id=47778

    I want the following options :

    • General Purpose I/O Pad or Core Logic
    • Using a feedback path internal to the PLL
    • Input Frequency (MHz) : 100
    • Output Frequency (MHz) : 25

    Here is the new PCF file :

    set_io v_sync 87
    set_io h_sync 88
    set_io clk_in 64
    set_io reset 66
    set_io r0 81
    set_io r1 80
    set_io r2 79
    set_io g0 94
    set_io g1 93
    set_io g2 91
    set_io b0 76
    set_io b1 75
    set_io b2 74
    set_io led 45
    set_io locked_led 37
    
    Tested with a 12MHz external clock, it works to output VGA.
    No luck so far with 100MHz clock and multiplying all the pixel drawing, syncing and front and back porches by 4. Thinking of just switching out for a 12MHz clock like on the first version.
    *EDIT* Also working with 25MHz external clock and removing the PLL.
    *EDIT* Very strange but working with 100MHz now, except the code should only work with 50MHz…Here are the two files being used to generate the signal :
    
    module vga_sync_test(
    input wire clk_in,
    input wire reset,
    output reg r0,
    output reg r1,
    output reg r2,
    output reg b0,
    output reg b1,
    output reg b2,
    output reg g0,
    output reg g1,
    output reg g2,
    output wire h_sync,
    output wire v_sync
    );
    
    wire display_en;
    //reg [9:0] h_count;
    wire [11:0] h_count;
    //reg [9:0] v_count;
    wire [11:0] v_count;
    
    /*
    //for 100MHZ
    localparam h_pixel_max = 2560;
    localparam v_pixel_max = 1920;
    localparam h_pixel_half = 1280;
    localparam v_pixel_half = 960;
    */
    
    
    //for 50MHZ
    
    localparam h_pixel_max = 1280;
    localparam v_pixel_max = 960;
    localparam h_pixel_half = 640;
    localparam v_pixel_half = 480;
    
    /*
    //for 25MHZ
    
    localparam h_pixel_max = 640;
    localparam v_pixel_max = 480;
    localparam h_pixel_half = 320;
    localparam v_pixel_half = 240;
    */
    
    //Check if we can create RGB colors
    always @(posedge clk_in) begin
    if (display_en) begin
    if (h_count < h_pixel_half
    && v_count < v_pixel_half) begin
    //Assign here your test color
    r0 <= 1'b0;
    r1 <= 1'b0;
    r2 <= 1'b0;
    g0 <= 1'b0;
    g1 <= 1'b0;
    g2 <= 1'b0;
    b0 <= 1'b1;
    b1 <= 1'b1;
    b2 <= 1'b1;
    end else if (h_count > h_pixel_half
    && v_count < v_pixel_half) begin
    //Assign here your test color
    r0 <= 1'b0;
    r1 <= 1'b0;
    r2 <= 1'b0;
    g0 <= 1'b1;
    g1 <= 1'b1;
    g2 <= 1'b1;
    b0 <= 1'b0;
    b1 <= 1'b0;
    b2 <= 1'b0;
    end else if (h_count < h_pixel_half
    && v_count > v_pixel_half) begin
    //Assign here your test color
    r0 <= 1'b1;
    r1 <= 1'b1;
    r2 <= 1'b1;
    g0 <= 1'b0;
    g1 <= 1'b0;
    g2 <= 1'b0;
    b0 <= 1'b0;
    b1 <= 1'b0;
    b2 <= 1'b0;
    end else begin
    //Assign here your test color
    r0 <= 1'b1;
    r1 <= 1'b1;
    r2 <= 1'b1;
    g0 <= 1'b1;
    g1 <= 1'b1;
    g2 <= 1'b1;
    b0 <= 1'b1;
    b1 <= 1'b1;
    b2 <= 1'b1;
    end
    end else begin
    r0 <= 1'b0;
    r1 <= 1'b0;
    r2 <= 1'b0;
    g0 <= 1'b0;
    g1 <= 1'b0;
    g2 <= 1'b0;
    b0 <= 1'b0;
    b1 <= 1'b0;
    b2 <= 1'b0;
    end
    end
    
    vga_sync vga_s(
    .clk_in(clk_in), //100,50, OR 25MHz clock input
    .reset(reset), // RST assigned to SW1
    .h_sync(h_sync),
    .v_sync(v_sync),
    .h_count(h_count),
    .v_count(v_count),
    .display_en(display_en) // '1' => pixel region
    );
    
    endmodule
    /*
    640x480 VGA singal generator
    ============================
    
    - Creates h_sync,v_sync signals
    - Creates display enable signal and horizontal, vertical
    pixel position in display (h,v)
    */
    
    `default_nettype none
    
    
    module vga_sync(
    input wire clk_in,
    input wire reset,
    output reg h_sync,
    output reg v_sync,
    output wire clk_sys,
    output reg [11:0] h_count,
    output reg [11:0] v_count,
    output reg display_en
    );
    
    
    // Pixel counters
    reg [11:0] h_counter = 0;
    reg [11:0] v_counter = 0;
    
    /*
    
    //FOR 100MHz
    
    localparam h_pixel_total = 3200;
    localparam h_pixel_display = 2560;
    localparam h_pixel_front_porch_amount = 64;
    localparam h_pixel_sync_amount = 384;
    localparam h_pixel_back_porch_amount = 192;
    
    localparam v_pixel_total = 2100;
    localparam v_pixel_display = 1920;
    localparam v_pixel_front_porch_amount = 40;
    localparam v_pixel_sync_amount = 8;
    localparam v_pixel_back_porch_amount = 132;
    
    */
    
    // FOR 50MHz
    
    localparam h_pixel_total = 1600;
    localparam h_pixel_display = 1280;
    localparam h_pixel_front_porch_amount = 32;
    localparam h_pixel_sync_amount = 192;
    localparam h_pixel_back_porch_amount = 96;
    
    localparam v_pixel_total = 1050;
    localparam v_pixel_display = 960;
    localparam v_pixel_front_porch_amount = 20;
    localparam v_pixel_sync_amount = 4;
    localparam v_pixel_back_porch_amount = 66;
    
    
    /*
    
    //FOR 25MHz
    
    localparam h_pixel_total = 800;
    localparam h_pixel_display = 640;
    localparam h_pixel_front_porch_amount = 16;
    localparam h_pixel_sync_amount = 96;
    localparam h_pixel_back_porch_amount = 48;
    
    localparam v_pixel_total = 525;
    localparam v_pixel_display = 480;
    localparam v_pixel_front_porch_amount = 10;
    localparam v_pixel_sync_amount = 2;
    localparam v_pixel_back_porch_amount = 33;
    
    */
    
    always @(posedge clk_in) begin
    
    if (reset) begin
    //Reset counter values
    h_counter <= 0;
    v_counter <= 0;
    display_en <= 0;
    end
    else
    begin
    // Generate display enable signal
    if (h_counter < h_pixel_display && v_counter < v_pixel_display)
    display_en <= 1;
    else
    display_en <= 0;
    
    //Check if horizontal has arrived to the end
    if (h_counter >= h_pixel_total)
    begin
    h_counter <= 0;
    v_counter <= v_counter + 1;
    end
    else
    //horizontal increment pixel value
    h_counter <= h_counter + 1;
    // check if vertical has arrived to the end
    if (v_counter >= v_pixel_total)
    v_counter <= 0;
    end
    end
    
    always @(posedge clk_in) begin
    // Check if sync_pulse needs to be created
    if (h_counter >= (h_pixel_display + h_pixel_front_porch_amount)
    && h_counter < (h_pixel_display + h_pixel_front_porch_amount + h_pixel_sync_amount) )
    h_sync <= 0;
    else
    h_sync <= 1;
    // Check if sync_pulse needs to be created
    if (v_counter >= (v_pixel_display + v_pixel_front_porch_amount)
    && v_counter < (v_pixel_display + v_pixel_front_porch_amount + v_pixel_sync_amount) )
    v_sync <= 0;
    else
    v_sync <= 1;
    end
    
    // Route h_/v_counter to out
    always @ (posedge clk_in) begin
    h_count <= h_counter;
    v_count <= v_counter;
    end
    
    endmodule

    ***
    Trying to reconfigure the raspberry pi now for DPI output based on this document : https://www.raspberrypi.com/documentation/computers/raspberry-pi.html
    Here’s what I did :
    dtparam=spi=off
    dtparam=i2c_arm=off
    #display_auto_detect=1 // comment this
    #dtoverlay=vc4-kms-v3d // comment this
    gpio=0-9=a2
    gpio=12-17=a2
    gpio=20-25=a2
    gpio=26-27=a2
    dtoverlay=dpi24
    enable_dpi_lcd=1
    display_default_lcd=1
    extra_transpose_buffer=2
    dpi_group=2
    dpi_mode=87
    dpi_output_format=0x7f216
    dpi_timings=720 0 40 48 128 720 0 13 3 15 0 0 0 60 0 41000000 4
    Will now try to get the raspberry pi going at 50000000. Doesn’t seem to help FPGA with FPGA H&V to record or play anything back.
    Testing to see if I can very simply just let the raspberry pi signals pass through the FPGA :
    module vga_sync(
    input wire R7IN,
    input wire R6IN,
    input wire R5IN,
    input wire R4IN,
    input wire G7IN,
    input wire G6IN,
    input wire G5IN,
    input wire G4IN,
    input wire B7IN,
    input wire B6IN,
    input wire B5IN,
    input wire B4IN,
    output wire R7OUT,
    output wire R6OUT,
    output wire R5OUT,
    output wire R4OUT,
    output wire G7OUT,
    output wire G6OUT,
    output wire G5OUT,
    output wire G4OUT,
    output wire B7OUT,
    output wire B6OUT,
    output wire B5OUT,
    output wire B4OUT
    );
    
    assign R7OUT = R7IN;
    assign R6OUT = R6IN;
    assign R5OUT = R5IN;
    assign R4OUT = R4IN;
    assign G7OUT = G7IN;
    assign G6OUT = G6IN;
    assign G5OUT = G5IN;
    assign G4OUT = G4IN;
    assign B7OUT = B7IN;
    assign B6OUT = B6IN;
    assign B5OUT = B5IN;
    assign B4OUT = B4IN;
    

    I made a pcf to go with this test code :

    set_io R7IN 107
    set_io R6IN 106
    set_io R5IN 105
    set_io R4IN 104
    set_io G7IN 97
    set_io G6IN 96
    set_io G5IN 95
    set_io G4IN 112
    set_io B7IN 102
    set_io B6IN 101
    set_io B5IN 99
    set_io B4IN 98
    set_io R7OUT 81
    set_io R6OUT 80
    set_io R5OUT 79
    set_io R4OUT 78
    set_io G7OUT 94
    set_io G6OUT 93
    set_io G5OUT 91
    set_io G4OUT 90
    set_io B7OUT 76
    set_io B6OUT 75
    set_io B5OUT 74
    set_io B4OUT 73

    Memory soldering done, added to the pcf :

    set_io v_sync 87
    set_io h_sync 88
    set_io clk_in 64
    set_io reset 66
    set_io r0 81
    set_io r1 80
    set_io r2 79
    set_io g0 94
    set_io g1 93
    set_io g2 91
    set_io b0 76
    set_io b1 75
    set_io b2 74
    
    set_io io0 1
    set_io io1 2
    set_io io2 3
    set_io io3 4
    set_io io4 122
    set_io io5 121
    set_io io6 120
    set_io io7 119
    
    set_io a0 138
    set_io a1 139
    set_io a2 141
    set_io a3 142
    set_io a4 143
    set_io a5 8
    set_io a6 9
    set_io a7 10
    set_io a8 11
    set_io a9 12
    set_io a10 136
    set_io a11 135
    set_io a12 134
    set_io a13 129
    set_io a14 128
    set_io a15 117
    set_io a16 116
    set_io a17 115
    set_io a18 114
    set_io a19 137
    set_io a20 113
    
    set_io cs1 71
    set_io we0 7

    *********************

    Refreshing my verilog learning from HDLbits :

    A note on wire vs. reg: The right-hand-side of an assign statement must be a net type (e.g., wire), while the left-hand-side of a procedural assignment (in an always block) must be a variable type (e.g., reg).

                                                                                                      *******

    you can do assign w = a; outside of an always block

    assign continuously drives a into w

    assign out = a&b; makes out equal to a AND’ed with b

    it’s a combinational (i.e., memory-less, with no hidden state) function

    using assign is the same as using always @(*)

                                                                                                *****

    if you don’t declare it, output [3:0] a; will be a vector of type wire

    to declare a vector : type [upper:lower] vector_name;

                                                                                                  *****

    A bitwise operation between two N-bit vectors replicates the operation for each bit of the vector and produces a N-bit output, while a logical operation treats the entire vector as a boolean value (true = non-zero, false = zero) and produces a 1-bit output.

    Eg.

    output [2:0] out_or_bitwise,
    output out_or_logical,

    assign out_or_bitwise = a | b;
    assign out_or_logical = a || b;

    Beware the different symbols for bitwise or logical !
                                                                                                    ****

    module mod_a ( input in1, input in2, output out );
    // Module body
    endmodule

    When instantiating mod_a inside top_module :

    module top_module (
    input a,
    input b,
    output out
    );

    // Create an instance of “mod_a” named “inst1”, and connect ports by name:
    mod_a inst1 (
    .in1(a), // Port”in1″connects to wire “a”
    .in2(b), // Port “in2” connects to wire “b”
    .out(out) // Port “out” connects to wire “out”
    // (Note: mod_a’s port “out” is not related to top_module’s wire “out”.
    // It is simply coincidence that they have the same name)
    );

                                                                                                    ***

    ALWAYS BLOCKS :

    Clocked: always @(posedge clk)

    Procedural blocks have a richer set of statements (e.g., if-then, case), cannot contain continuous assignments,

    Clocked always blocks create a blob of combinational logic just like combinational always blocks, but also creates a set of flip-flops (or “registers”) at the output of the blob of combinational logic. Instead of the outputs of the blob of logic being visible immediately, the outputs are visible only immediately after the next (posedge clk).

    In a locked always block, only use procedural non-blocking assignment: (x <= y;)

                                                                                                ***

    Combinational circuits must have a value assigned to all outputs under all conditions. This usually means you always need else clauses or a default value assigned to the outputs.

    This can cause the Warning (10240): … inferring latch(es) error message

                                                                                                ***

    Saw some inspiring machines in Munich :

    Barrels would be cool to incorporate into my next prototype.

    I wonder if I could make an analog feedback set up with linear actuators, a screen and camera…

    I love the scientific instrument aspect. The monitor hovering on the side could be cool to incorporate.

     

    *****

    Check out this analog video feedback setup… could I do a miniature version ? :

     

    ****

    One reaction to the museum visit it to think of what makes something look like a machine.

    • an agglomeration of mainly metal (especially machined aluminum and folded steel) and plastic modules
    • rotation and linear motion, symmetry about a central axis or a pile of modules facing the user

    If I wanted to focus on the technical object itself, I could work on a custom cooling setup, with thin curving pipes, knobs that allow for various kinds of custom fine control, screens and cameras on various linear axes for feedback loop control.

    A DIY water cooler :

    Amazing! DIY Mini Liquid Cooling System - YouTube

    Importance apprenti Gonfle diy cpu water block la toux mosaïque sans rapport

    A custom keyboard from https://keyboards.tanebox.com/a/blog/939/ :

    I like the exposed keyboard that extends into a circuit board with chips on the top. Could it be possible to make a kind of reprogrammable FPGA video synth computer ? Perhaps I could also learn things about video by taking a video file from a an SD card and then moving it into a dual port DRAM memory to be displayed.

    This gets at something that feels important : I am always trying to get myself to some harder to get to place, with the help of DIY tech, before making artsy stuff. It’s almost like I feel I’m not competitive enough starting from the most accessible starting point (classical art like drawing, painting, sculpture). Sam suggested that I combine my interest in hardware with software. Can I make a kind of custom programmable computer that is my own and then program it in a low level language (like assembly or verilog)?

    MechBoardsのPlaid // ThroughHole – コトコト

    I love the windy wire plug found on the mechanical keyboard subreddit :

    r/CustomKeyboards - I Can't Get No

    r/CustomKeyboards - My totally customized split keyboard

    From https://spectrum.ieee.org/yugoslavia-diy-microcomputer :

    Keyboard buttons from Mouser : https://www.mouser.fr/ProductDetail/E-Switch/KS1100OA1AF060?qs=gt1LBUVyoHkJwqhwHM2wRw%3D%3D

    ***

    Or, in the totally opposite direction (not going for gimmicks or flashiness), super inexpensive DRAM chips (1 euro for 16Mbit) :

    https://www.mouser.fr/ProductDetail/ISSI/IS42S16100H-7TL?qs=yfIbTn1BQ2BEUPfKEYbGCA%3D%3D

    ****

    Had a thought : I am basically trying to “play” the VGA format itself like an instrument.

    ****

    So making a pin that is both input and output for an FPGA is not so simple ! This post helped : https://electronics.stackexchange.com/questions/33144/birectional-i-o-pin-in-verilog

    My attempts to make diagrams :

     

    I also cleaned up the code a bit, making vectors with indices and following naming conventions :

    I can see evidence of something being recorded and played back out of sync (only when rpi input provides H&V and the FPGA used the rpi 41MHz pixel clock or the 100MHz clock) but it’s far from working completely.

    I can send external clocks that are divisions of 41MHz clock (20.5MHz, 10.25MHz) when rpi is supplying the H&V and it just barely works.

    The FPGA works with external 25MHz clock also, but I can’t see any recordings.

    *EDIT* I can get pretty clear recordings with the RPI clock and H&V after fiddling. Still no luck with recording RPI color information and then showing it at FPGA H&V with either pixel clock. I have gotten rid of the test colors and now am showing full screen FPGA. Twiddled 4 versus 3 bits on the output.

    `default_nettype none
    
    module vga_sync_test(
    
    input wire clk_in,
    input wire reset,
    
    input wire rec, // Direction of io, 1 = set output, 0 = read input
    
    //RASPBERRY PI
    input wire [3:0] r_in,
    input wire [3:0] b_in,
    input wire [3:0] g_in,
    
    //VGA OUT
    output reg [3:0] r_out,
    output reg [3:0] b_out,
    output reg [3:0] g_out,
    
    output wire h_sync,
    output wire v_sync,
    
    //SRAM
    
    output reg [20:0] addr,
    inout wire [7:0] io, // inout must be type wire
    
    output wire cs_1,
    output reg we_0
    
    );
    
    wire [7:0] data_in;
    wire [7:0] data_out;
    
    reg [7:0] a, b;
    
    assign io = rec ? a : 8'bzzzzzzzz;
    
    assign data_out = b;
    
    assign data_in[1:0] = r_in[3:2];
    assign data_in[3:2] = b_in[3:2];
    assign data_in[5:4] = g_in[3:2];
    assign data_in[7:6] = 2'b00;
    
    wire display_en;
    wire [11:0] h_count;
    wire [11:0] v_count;
    
    assign cs_1 = 0; // low to select, high to deselect
    
    localparam h_pixel_max = 1280;
    localparam v_pixel_max = 960;
    localparam h_pixel_half = 640;
    localparam v_pixel_half = 480;
    
    
    //SRAM address counter
    
    always @(posedge clk_in) begin
        if (reset)
            addr <= 0;
        else
            addr <= addr+1;
        end
    
    
    //REC control
    
    always @(posedge clk_in) begin
          b <= io;
          a <= data_in;
         if (rec) begin
              we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
         end
         else begin
              we_0 <= 1;
         end
    end
    
    //VGA COLOR OUT
    
    always @(posedge clk_in) begin
    if (display_en) begin
    if (h_count < h_pixel_half
    && v_count < v_pixel_half) begin
    
    r_out[1:0] <= data_out[1:0];
    r_out[2] <= 1'b0;
    g_out[1:0] <= data_out[3:2];
    g_out[2] <= 1'b0;
    b_out[1:0] <= data_out[5:4];
    b_out[2] <= 1'b0;
    
    end else if (h_count > h_pixel_half
    && v_count < v_pixel_half) begin
    
    
    r_out <= 3'b000;
    g_out <= 3'b111;
    b_out <= 3'b000;
    
    end else if (h_count < h_pixel_half
    && v_count > v_pixel_half) begin
    
    
    r_out <= 3'b111;
    g_out <= 3'b000;
    b_out <= 3'b000;
    
    end else begin
    
    
    r_out <= 3'b111;
    g_out <= 3'b111;
    b_out <= 3'b111;
    
    end
    end else begin
    
    r_out <= 3'b000;
    g_out <= 3'b000;
    b_out <= 3'b000;
    end
    end
    
    vga_sync vga_s(
    .clk_in(clk_in),
    .reset(reset),
    .h_sync(h_sync),
    .v_sync(v_sync),
    .h_count(h_count),
    .v_count(v_count),
    .display_en(display_en) // '1' => pixel region
    );
    
    endmodule
    
    

     

    Here is the complete pcf :

    set_io v_sync 87
    set_io h_sync 88
    set_io clk_in 64
    set_io reset 66
    
    set_io io[0] 1
    set_io io[1] 2
    set_io io[2] 3
    set_io io[3] 4
    set_io io[4] 122
    set_io io[5] 121
    set_io io[6] 120
    set_io io[7] 119
    
    set_io io[8] 45
    set_io io[9] 47
    set_io io[10] 48
    set_io io[11] 49
    set_io io[12] 28
    set_io io[13] 26
    set_io io[14] 25
    set_io io[15] 24
    
    set_io addr[0] 138
    set_io addr[1] 139
    set_io addr[2] 141
    set_io addr[3] 142
    set_io addr[4] 143
    set_io addr[5] 8
    set_io addr[6] 9
    set_io addr[7] 10
    set_io addr[8] 11
    set_io addr[9] 12
    set_io addr[10] 136
    set_io addr[11] 135
    set_io addr[12] 134
    set_io addr[13] 129
    set_io addr[14] 128
    set_io addr[15] 117
    set_io addr[16] 116
    set_io addr[17] 115
    set_io addr[18] 114
    set_io addr[19] 137
    set_io addr[20] 113
    
    set_io addr[21] 38
    set_io addr[22] 39
    set_io addr[23] 41
    set_io addr[24] 42
    set_io addr[25] 43
    set_io addr[26] 52
    set_io addr[27] 56
    set_io addr[28] 58
    set_io addr[29] 60
    set_io addr[30] 61
    set_io addr[31] 34
    set_io addr[32] 33
    set_io addr[33] 32
    set_io addr[34] 31
    set_io addr[35] 29
    set_io addr[36] 23
    set_io addr[37] 22
    set_io addr[38] 21
    set_io addr[39] 20
    set_io addr[40] 37
    set_io addr[41] 19
    
    set_io cs_0 144
    set_io cs_1 71
    set_io cs_2 44
    set_io cs_3 63
    
    set_io we_0 7
    set_io we_1 50
    
    set_io rec 62
    
    set_io r_in[0] 107
    set_io r_in[1] 106
    set_io r_in[2] 105
    set_io r_in[3] 104
    set_io g_in[0] 97
    set_io g_in[1] 96
    set_io g_in[2] 95
    set_io g_in[3] 112
    set_io b_in[0] 102
    set_io b_in[1] 101
    set_io b_in[2] 99
    set_io b_in[3] 98
    
    set_io r_out[0] 81
    set_io r_out[1] 80
    set_io r_out[2] 79
    set_io r_out[3] 78
    set_io g_out[0] 94
    set_io g_out[1] 93
    set_io g_out[2] 91
    set_io g_out[3] 90
    set_io b_out[0] 76
    set_io b_out[1] 75
    set_io b_out[2] 74
    set_io b_out[3] 73

    ****

    Added the third SRAM and having issues, commented out one new pin at a time and it seems like address 18 (on pin 20) is problematic. When I remove this pin everything else functions well enough for a memory with a missing pin however.

    `default_nettype none
    
    module vga_sync_test(
    
    input wire clk_in,
    input wire reset,
    
    input wire rec, // Direction of io, 1 = set output, 0 = read input
    
    //RASPBERRY PI
    input wire [3:0] r_in,
    input wire [3:0] b_in,
    input wire [3:0] g_in,
    
    //VGA OUT
    output reg [3:0] r_out,
    output reg [3:0] b_out,
    output reg [3:0] g_out,
    
    output wire h_sync,
    output wire v_sync,
    
    //SRAM
    
    output reg [20:0] addr,
    inout wire [7:0] io, // inout must be type wire
    
    output wire cs_1,
    output wire cs_0,
    output reg we_0
    
    );
    
    wire [7:0] data_in;
    wire [7:0] data_out;
    
    reg toggle;
    
    reg [7:0] a, b;
    
    assign io = rec ? a : 8'bzzzzzzzz;
    
    assign data_out = b;
    
    assign data_in[1:0] = r_in[3:2];
    assign data_in[3:2] = b_in[3:2];
    assign data_in[5:4] = g_in[3:2];
    assign data_in[7:6] = 2'b00;
    
    wire display_en;
    wire [11:0] h_count;
    wire [11:0] v_count;
    
    localparam h_pixel_max = 1280;
    localparam v_pixel_max = 960;
    localparam h_pixel_half = 640;
    localparam v_pixel_half = 480;
    
    // CS: low to select, high to deselect
    
    assign cs_0 = toggle ? 1 : 0;
    assign cs_1 = toggle ? 0 : 1;
    
    //SRAM address counter
    
    always @(posedge clk_in) begin
    
    if (addr == 0) begin
    toggle <= toggle+1;
    end
    
    if (reset) begin
    addr <= 0;
    end else begin
    addr <= addr+1;
    
    end
    end
    
    
    //REC control
    
    always @(posedge clk_in) begin
    
    b <= io;
    a <= data_in;
    
    if (rec) begin
    we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
    end
    else begin
    we_0 <= 1;
    end
    end
    
    //VGA COLOR OUT
    
    always @(posedge clk_in) begin
    if (display_en) begin
    
    r_out[3:2] <= data_out[1:0];
    r_out[1:0] <= data_out[1:0];
    g_out[3:2] <= data_out[3:2];
    g_out[1:0] <= data_out[3:2];
    b_out[3:2] <= data_out[5:4];
    b_out[1:0] <= data_out[5:4];
    
    end else begin
    
    r_out <= 4'b0000;
    g_out <= 4'b0000;
    b_out <= 4'b0000;
    end
    end
    
    vga_sync vga_s(
    .clk_in(clk_in),
    .reset(reset),
    .h_sync(h_sync),
    .v_sync(v_sync),
    .h_count(h_count),
    .v_count(v_count),
    .display_en(display_en) // '1' => pixel region
    );
    
    endmodule

    ***

    I made an animated video of the assembly of the board :

    When I make the next board I’ll  record the eagle CAD and make another video.

    Here are some images of what a new device could look like !

    What about an FPGA chip holder ? And a snazzy pivotable SD card holder ? Using meanders this time? With its own fictional programming language like in that assembly video game ? With a mini PCI-E connector somewhere ?

    To pre-digest the videos for the FPGA before putting them on a gigantic SD card, I could use ffmpeg to output a less compressed format like .avi or an .mkv and then erase the header and footer ?

    Have a name (little Lebowski urban achiever is too long), and a format 100×80 (free Eagle max), and a rough layout (HDMI super close, USB next to FTDI and to flash memory, all the memory to the right equidistant to the chip). AND, some kind of transparent cover, a first for me, make it seem more like a legitimate product and would reduce the risk of short circuits from something conductive falling on the board. For the symbols on the keyboard, there is the Comodore 64 keyboard symbol set which is cool. Or some ancient symbols ??

    I am now moving away from SDRAM, challenging and I’m not sure why I’m putting myself through it. The real interest is going to be decoding videos stored on the SD card and putting them on screen. I want to try to do it like the pros – drawing to the screen in the blanking period and using only one SRAM.

    ****

    Still trying to resolve the A18 issue.

    • I tried soldering another IO pin to pin 20, but this led to total chaos, none of the address pins working. I could have tried severing the link between the pin 20 and the SRAM but I don’t want to damage the board before I identify the problem (it may be in the code?).
    • Trying to lower the size of the address counter to stop at 17 instead of going to 18.
    • I connected to the rpi’s DEN and changed the code to work with it instead of the FPGAs display en (it doesn’t work anyways). It is cool as it is more likely to capture the image on the rpi if I understand correctly.

    ****

    I am now trying some of the code I was dreaming about – modifying the input and output video stream based on other parallel data.

    Here is one strategy based on two bits of the same color having an impact on recording and playback.

    if (b[3]==b[2]) begin
    a <= data_in;
    end
    else begin
    a <= 8'b00000000;
    end

    I have the two memories working (excepted A18 on the second SRAM), and also tried reading DEN from the rpi. It makes some pretty funky glitches by touching the RAM pins with my hands !

    Also check out the bitmap to program the FPGA :

     

    `default_nettype none
    
    module vga_sync_test(
    
    input wire clk_in,
    input wire reset,
    
    input wire rec, // Direction of io, 1 = set output, 0 = read input
    
    //RASPBERRY PI
    input wire [3:0] r_in,
    input wire [3:0] b_in,
    input wire [3:0] g_in,
    input wire DEN, // I SOLDERED A WIRE HERE TO ACCESS DEN ON THE RPI !!
    
    //VGA OUT
    output reg [3:0] r_out,
    output reg [3:0] b_out,
    output reg [3:0] g_out,
    
    output wire h_sync,
    output wire v_sync,
    
    //SRAM
    
    output reg [20:0] addr,
    output reg [20:0] addr_copy,
    inout wire [15:0] io, // inout must be type wire
    
    output wire cs_3,
    output wire cs_2,
    output wire cs_1,
    output wire cs_0,
    
    output reg we_1,
    output reg we_0
    
    );
    
    wire [15:0] data_in;
    wire [15:0] data_out;
    
    reg [1:0] toggle;
    
    reg [15:0] a, b;
    
    assign io = rec ? a : 16'bzzzzzzzzzzzzzzzz;
    
    assign data_out = b;
    
    assign data_in[1:0] = DEN ? r_in[3:2] : 0; // I SOLDERED A WIRE HERE TO ACCESS DEN ON THE RPI !!
    assign data_in[3:2] = DEN ? b_in[3:2] : 0; // I SOLDERED A WIRE HERE TO ACCESS DEN ON THE RPI !!
    assign data_in[5:4] = DEN ? g_in[3:2] : 0; // I SOLDERED A WIRE HERE TO ACCESS DEN ON THE RPI !!
    assign data_in[7:6] = 2'b00;
    
    assign data_in[9:8] = DEN ? r_in[3:2] : 0;
    assign data_in[11:10] = DEN ? b_in[3:2] : 0;
    assign data_in[13:12] = DEN ? g_in[3:2] : 0;
    assign data_in[15:14] = 2'b00;
    
    wire display_en;
    wire [11:0] h_count;
    wire [11:0] v_count;
    
    localparam h_pixel_max = 1280;
    localparam v_pixel_max = 960;
    localparam h_pixel_half = 640;
    localparam v_pixel_half = 480;
    
    // CS: low to select, high to deselect
    
    assign cs_0 = toggle == 2'b00 ? 1 : 0;
    assign cs_1 = toggle == 2'b01 ? 1 : 0;
    assign cs_2 = toggle == 2'b10 ? 1 : 0;
    assign cs_3 = toggle == 2'b11 ? 1 : 0;
    
    //SRAM address counter
    
    always @(posedge clk_in) begin
    
    if (addr == 0) begin
       toggle <= toggle+1;
    end
    
    if (reset) begin
       addr <= 0;
    end else begin
       addr <= addr+1;
       addr_copy <= addr_copy+1;
    
    end
    end
    
    //REC control
    
    always @(posedge clk_in) begin
    
       b <= io;
       a <= data_in;
    if (rec) begin
       we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
       we_1 <= addr[0];
    end
    else begin
       we_0 <= 1;
       we_1 <= 1;
    end
    end
    
    //VGA COLOR OUT
    
    always @(posedge clk_in) begin
    if (DEN) begin // I SOLDERED A WIRE HERE TO ACCESS DEN ON THE RPI !!
    if (toggle==2'b00 || toggle==2'b01 ) begin
       r_out[3:2] <= data_out[1:0];
       r_out[1:0] <= data_out[1:0];
       g_out[3:2] <= data_out[3:2];
       g_out[1:0] <= data_out[3:2];
       b_out[3:2] <= data_out[5:4];
       b_out[1:0] <= data_out[5:4];
    
    end else begin
       r_out[3:2]<= data_out[9:8];
       r_out[1:0]<= data_out[9:8];
       g_out[3:2]<= data_out[11:10];
       g_out[1:0]<= data_out[11:10];
       b_out[3:2]<= data_out[13:12];
       b_out[1:0]<= data_out[13:12];
    end
    end else begin
       r_out <= 4'b0000;
       g_out <= 4'b0000;
       b_out <= 4'b0000;
    end
    end
    
    vga_sync vga_s(
    .clk_in(clk_in),
    .reset(reset),
    .h_sync(h_sync),
    .v_sync(v_sync),
    .h_count(h_count),
    .v_count(v_count),
    .display_en(display_en) // '1' => pixel region
    );
    
    endmodule

    From https://www.fpga4fun.com/VerilogTips.html:

    • I need to initialize verilog counters !!
    • Another way of selecting 4 bits in a vector : wire [3:0] thisis4also = myvalue[16+:4];
    • It might be smarter to use a PLL than to take the external clock directly.
    • I need to debounce all input buttons !

    ****

    Check out shaders that take video as input :

    https://www.shadertoy.com/view/ctsyzN

    https://www.shadertoy.com/view/XtcSRs

    ****

    Tried to make some vignettes of the new board to show the impedence matching :

    I think I will order it in white with the meanders exposed like on the VGA spaghettini.

    Color options (I’m leaning towards white or maybe green):

    ***************************

    I am now thinking about how I will be able to share all that I am trying to learn about verilog and FPGAs in workshops. The bottleneck is programming the FPGA without special hardware.

    OLIMEX has a link to a rasberry pi utility called flashrom that can program and read FLASH memory documented here : https://github.com/OLIMEX/iCE40HX1K-EVB/tree/master/programmer/olimexino-32u4%20firmware

    These raspberry pi examples program the ice40 and looks simple :

    • https://j-marjanovic.io/lattice-ice40-configuration-using-raspberry-pi.html
    • https://github.com/plex1/raspice40

    The steps have been automated here : https://notabug.org/sagaracharya/swarajya/src/master/hdl_to_hx8k/on_pi

    Also for the rpi Pico : https://github.com/dan-rodrigues/pico-hx

    I could knock off the 2232 FTDI and have made something like this: https://shop.trenz-electronic.de/en/TEM0009-02-FPGA-USB-programmer-JTAG-for-development-with-Microchip-FPGAs

    Most interestingly, it seems like Arduino Uno could program an FPGA using a library like SPIMemory by Prajwal Bhattaram : https://github.com/Marzogh/SPIMemory/tree/v2.2.0

    Finally, I could make a board like the gameduino that has an API for the Arduino to use to talk to the FPGA which handles fast screen drawing. This would mean everything could be done within the Arduino IDE.

    EDIT* Come to think of it, even if the FTDI adds cost, it would make everything so much easier for a workshop – one piece of software and no extra hardware to program after for the participants. But when I plug it in it says USB device malfunctioned…(I also realized that I think I can forgo the RS232 connections between the FTDI and the FPGA and just connect it to the EEPROM and FLASH.)

    EDIT* The OLIMEX code for Arduino is also not compiling and winIceProgDuino.exe (to program FPGA by sending serial commands to Arduino firmware I suppose) is not opening either.

    EDIT*2 The Arduino sketch compiles but only for MEGA, Leonardo, and Micro so far (no UNO or NANO or MINI) but only after removing the iceprog.cpp file.

    ******************

    Some thoughts on low-level versus high-level programming :

    Something unsatisfying about making something from a pre-digested meta language (fast.ai for instance). Lost the feeling of ownership of the thing you’re making.

    Obviously you can do much more when you stand atop of the shoulders of giants, but you are less connected with the surface of the ground because you are working with abstractions. If something goes wrong, you have no recourse because you are just a customer.
    Low level languages make the architecture of the machine visible by laying it bear. There is agency. Then again, to do something very simple can be hard and require lots of code which provides many opportunities for silly mistakes.
    Yet for workshops it seems good to avoid low level stuff. It’s not impressive, it’s discouraging, and it’s hard to debug.
    *******************
    I have soldered the new board, I love the keys !! But HDMI is still not working.

    Lots of fun could be had rearranging the keys using the same row heights  :

    I wonder if I should just transition to making interfaces directly – I am a designer after all !

    The FPGA4fun.com code is not synthesizing, I think because it requires clock dividing modules that I don’t have.
    I tried this code and it compiles but I don’t see anything on the ‘scope: https://gist.github.com/uXeBoy/0d46e2f1560f73dd573d83e78309bfa0
    This code eventually produced signals on HDMI out, will have to wait to see if it is acceptable HDMI once I have the proper resistor LVDS setup.
    `default_nettype none // disable implicit definitions by Verilog
    //-----------------------------------------------------------------
    // minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation
    //
    // Author: Mike Field <hamster@snap.net.nz>
    //
    // DVI-D uses TMDS as the 'on the wire' protocol, where each 8-bit
    // value is mapped to one or two 10-bit symbols, depending on how
    // many 1s or 0s have been sent. This makes it a DC balanced protocol,
    // as a correctly implemented stream will have (almost) an equal
    // number of 1s and 0s.
    //
    // Because of this implementation quite complex. By restricting the
    // symbols to a subset of eight symbols, all of which having have
    // five ones (and therefore five zeros) this complexity drops away
    // leaving a simple implementation. Combined with a DDR register to
    // send the symbols the complexity is kept very low.
    //-----------------------------------------------------------------
    
    module top(
    clk100, hdmi_p, hdmi_n
    );
    
    input clk100;
    output [3:0] hdmi_p;
    output [3:0] hdmi_n;
    
    // For holding the outward bound TMDS symbols in the slow and fast domain
    reg [9:0] c0_symbol; reg [9:0] c0_high_speed;
    reg [9:0] c1_symbol; reg [9:0] c1_high_speed;
    reg [9:0] c2_symbol; reg [9:0] c2_high_speed;
    reg [9:0] clk_high_speed;
    
    reg [1:0] c2_output_bits;
    reg [1:0] c1_output_bits;
    reg [1:0] c0_output_bits;
    reg [1:0] clk_output_bits;
    
    wire clk_x5;
    reg [2:0] latch_high_speed = 3'b100; // Controlling the transfers into the high speed domain
    
    wire vsync, hsync;
    wire [1:0] syncs; // To glue the HSYNC and VSYNC into the control character
    assign syncs = {vsync, hsync};
    
    // video structure constants
    parameter hpixels = 800; // horizontal pixels per line
    parameter vlines = 525; // vertical lines per frame
    parameter hpulse = 96; // hsync pulse length
    parameter vpulse = 2; // vsync pulse length
    parameter hbp = 144; // end of horizontal back porch (96 + 48)
    parameter hfp = 784; // beginning of horizontal front porch (800 - 16)
    parameter vbp = 35; // end of vertical back porch (2 + 33)
    parameter vfp = 515; // beginning of vertical front porch (525 - 10)
    
    // registers for storing the horizontal & vertical counters
    reg [9:0] vc;
    reg [9:0] hc;
    // generate sync pulses (active high)
    assign vsync = (vc < vpulse);
    assign hsync = (hc < hpulse);
    
    always @(posedge clk_x5) begin
    //-------------------------------------------------------------
    // Now take the 10-bit words and take it into the high-speed
    // clock domain once every five cycles.
    //
    // Then send out two bits every clock cycle using DDR output
    // registers.
    //-------------------------------------------------------------
    c0_output_bits <= c0_high_speed[1:0];
    c1_output_bits <= c1_high_speed[1:0];
    c2_output_bits <= c2_high_speed[1:0];
    clk_output_bits <= clk_high_speed[1:0];
    if (latch_high_speed[2]) begin // pixel clock 25MHz
    c0_high_speed <= c0_symbol;
    c1_high_speed <= c1_symbol;
    c2_high_speed <= c2_symbol;
    clk_high_speed <= 10'b0000011111;
    latch_high_speed <= 3'b000;
    if (hc < hpixels)
    hc <= hc + 1;
    else
    begin
    hc <= 0;
    if (vc < vlines)
    vc <= vc + 1;
    else
    vc <= 0;
    end
    end
    else begin
    c0_high_speed <= {2'b00, c0_high_speed[9:2]};
    c1_high_speed <= {2'b00, c1_high_speed[9:2]};
    c2_high_speed <= {2'b00, c2_high_speed[9:2]};
    clk_high_speed <= {2'b00, clk_high_speed[9:2]};
    latch_high_speed <= latch_high_speed + 1'b1;
    end
    end
    
    always @(*) // display 100% saturation colourbars
    begin
    // first check if we're within vertical active video range
    if (vc >= vbp && vc < vfp)
    begin
    // now display different colours every 80 pixels
    // while we're within the active horizontal range
    // -----------------
    // display white bar
    if (hc >= hbp && hc < (hbp+80))
    begin
    c2_symbol = 10'b1011110000; // red
    c1_symbol = 10'b1011110000; // green
    c0_symbol = 10'b1011110000; // blue
    end
    // display yellow bar
    else if (hc >= (hbp+80) && hc < (hbp+160))
    begin
    c2_symbol = 10'b1011110000; // red
    c1_symbol = 10'b1011110000; // green
    c0_symbol = 10'b0111110000; // blue
    end
    // display cyan bar
    else if (hc >= (hbp+160) && hc < (hbp+240))
    begin
    c2_symbol = 10'b0111110000; // red
    c1_symbol = 10'b1011110000; // green
    c0_symbol = 10'b1011110000; // blue
    end
    // display green bar
    else if (hc >= (hbp+240) && hc < (hbp+320))
    begin
    c2_symbol = 10'b0111110000; // red
    c1_symbol = 10'b1011110000; // green
    c0_symbol = 10'b0111110000; // blue
    end
    // display magenta bar
    else if (hc >= (hbp+320) && hc < (hbp+400))
    begin
    c2_symbol = 10'b1011110000; // red
    c1_symbol = 10'b0111110000; // green
    c0_symbol = 10'b1011110000; // blue
    end
    // display red bar
    else if (hc >= (hbp+400) && hc < (hbp+480))
    begin
    c2_symbol = 10'b1011110000; // red
    c1_symbol = 10'b0111110000; // green
    c0_symbol = 10'b0111110000; // blue
    end
    // display blue bar
    else if (hc >= (hbp+480) && hc < (hbp+560))
    begin
    c2_symbol = 10'b0111110000; // red
    c1_symbol = 10'b0111110000; // green
    c0_symbol = 10'b1011110000; // blue
    end
    // display black bar
    else if (hc >= (hbp+560) && hc < hfp)
    begin
    c2_symbol = 10'b0111110000; // red
    c1_symbol = 10'b0111110000; // green
    c0_symbol = 10'b0111110000; // blue
    end
    // we're outside active horizontal range
    else
    begin
    c2_symbol = 10'b1101010100; // red
    c1_symbol = 10'b1101010100; // green
    //---------------------------------------------
    // Channel 0 carries the blue pixels, and also
    // includes the HSYNC and VSYNCs during
    // the CTL (blanking) periods.
    //---------------------------------------------
    case (syncs)
    2'b00 : c0_symbol = 10'b1101010100;
    2'b01 : c0_symbol = 10'b0010101011;
    2'b10 : c0_symbol = 10'b0101010100;
    default : c0_symbol = 10'b1010101011;
    endcase
    end
    end
    // we're outside active vertical range
    else
    begin
    c2_symbol = 10'b1101010100; // red
    c1_symbol = 10'b1101010100; // green
    //---------------------------------------------
    // Channel 0 carries the blue pixels, and also
    // includes the HSYNC and VSYNCs during
    // the CTL (blanking) periods.
    //---------------------------------------------
    case (syncs)
    2'b00 : c0_symbol = 10'b1101010100;
    2'b01 : c0_symbol = 10'b0010101011;
    2'b10 : c0_symbol = 10'b0101010100;
    default : c0_symbol = 10'b1010101011;
    endcase
    end
    end
    
    // red N
    defparam hdmin2.PIN_TYPE = 6'b010000;
    defparam hdmin2.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmin2 (
    .PACKAGE_PIN (hdmi_n[2]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (c2_output_bits[1]),
    .D_OUT_1 (c2_output_bits[0])
    );
    
    // red P
    defparam hdmip2.PIN_TYPE = 6'b010000;
    defparam hdmip2.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmip2 (
    .PACKAGE_PIN (hdmi_p[2]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (c2_output_bits[1]),
    .D_OUT_1 (c2_output_bits[0])
    );
    
    // green N
    defparam hdmin1.PIN_TYPE = 6'b010000;
    defparam hdmin1.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmin1 (
    .PACKAGE_PIN (hdmi_n[1]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (c1_output_bits[1]),
    .D_OUT_1 (c1_output_bits[0])
    );
    
    // green P
    defparam hdmip1.PIN_TYPE = 6'b010000;
    defparam hdmip1.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmip1 (
    .PACKAGE_PIN (hdmi_p[1]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (c1_output_bits[1]),
    .D_OUT_1 (c1_output_bits[0])
    );
    
    
    // blue N
    defparam hdmin0.PIN_TYPE = 6'b010000;
    defparam hdmin0.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmin0 (
    .PACKAGE_PIN (hdmi_n[0]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (c0_output_bits[1]),
    .D_OUT_1 (c0_output_bits[0])
    );
    
    // blue P
    defparam hdmip0.PIN_TYPE = 6'b010000;
    defparam hdmip0.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmip0 (
    .PACKAGE_PIN (hdmi_p[0]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (c0_output_bits[1]),
    .D_OUT_1 (c0_output_bits[0])
    );
    
    // clock N
    defparam hdmin3.PIN_TYPE = 6'b010000;
    defparam hdmin3.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmin3 (
    .PACKAGE_PIN (hdmi_n[3]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (clk_output_bits[1]),
    .D_OUT_1 (clk_output_bits[0])
    );
    
    
    // clock P
    defparam hdmip3.PIN_TYPE = 6'b010000;
    defparam hdmip3.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmip3 (
    .PACKAGE_PIN (hdmi_p[3]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (clk_output_bits[1]),
    .D_OUT_1 (clk_output_bits[0])
    );
    // D_OUT_0 and D_OUT_1 swapped?
    // https://github.com/YosysHQ/yosys/issues/330
    
    
    SB_PLL40_PAD #(
    .FEEDBACK_PATH ("SIMPLE"),
    .DIVR (4'b0000),
    .DIVF (7'b0001001),
    .DIVQ (3'b011),
    .FILTER_RANGE (3'b101)
    ) uut (
    .RESETB (1'b1),
    .BYPASS (1'b0),
    .PACKAGEPIN (clk100),
    .PLLOUTGLOBAL (clk_x5) // DVI clock 125MHz
    );
    
    endmodule
    
    I learned a bit about how HDMI is encoded to reduce strings of 1s or Os and how transitions are minimized. I currently suspect that the PLL is not working but I’m not sure why yet. I will turn on the PLL locked LED feature to check and then mess with other parameters (see https://zipcpu.com/blog/2017/09/14/even-i-get-stuck.html) if it isn’t working.
    *EDIT 1 Apparently IceCube2 doesn’t want me to attach the CLK to any pin other than 49 (GBIN5) or 129 (GBIN0) ! This explains why nothing is happening, the clock wasn’t even entering the FPGA.
    *EDIT 2 I connected the clock pin to 49, and made the original connection to pin 64 high Z, along with the accidental connection to pin 48 nextdoor, and now I see signals being output to the HDMI. (It’s too tight to try and solder in resistors to test the proper LVDS simulation as described in the Using Differential I/O (LVDS, Sub-LVDS) in iCE40 LP/HX Devices annex.) I used the pin constraints editor in IceCube and it added some info to the pcf which may have helped get things working ? :
    set_io hdmi_p[0] 139 -io_std SB_LVCMOS
    set_io hdmi_p[2] 144 -io_std SB_LVCMOS
    set_io hdmi_p[1] 142 -io_std SB_LVCMOS
    set_io hdmi_p[3] 137 -io_std SB_LVCMOS
    set_io clk100 49
    
    For the next version, I think I need to include resistors to simulate the LVDS signals and I should connect the clock at one of the dedicated input pins which seem to be optimized for this.
    Just realizing now that all the code I have been looking at involves using a dedicated chip (the TFP410 @ 8 euros) to convert the FPGA signals into. From the blogs it also seems like this solution works for some TVs but not all, and is better for monitors. Perhaps I should include a VGA + HDMI in case this is just too finicky. Looking at the OLIMEX VGA PMOD board, it looks like I only need a few resistors to get VGA working : https://www.olimex.com/Products/FPGA/iCE40/iCE40-IO/open-source-hardware
    I am looking for more ressources on DVI with Ice40 FPGAs and found these :
    • https://projectf.io/posts/fpga-graphics/
    • https://github.com/lawrie/hdmi_examples/tree/master
    The fact that the code above (before my modifications at least) is being used on another ice40 makes me think it should work in the end.
    **********
     

    Technische Universität München Lecture

    I have a month to work on a 45 minute lecture on my work at the Technische Universität München. I will also be doing a lecture at TU Darmstadt in November. I would like to use this opportunity to try to bring my video experiments into focus and incorporate it into my previous time-lapse projects.

    This could take the form of a video series of me playing the prepared video buffer, possibly with explanations about what is causing what effect and demonstrative images / videos ? It could be contrasted with a “typical” workflow using the constraints of a software platform like photoshop? I could prepare this a similarly focused investigation of how the prepared piano altered sound, how Sonia Sheridan altered the photocopier, Paul Schafer made custom concrete music machines, etc.

    EDIT* : I had a try !

    • It’s kind of long and I also need to get a better top view.
    • Adding text to describe what’s happening ?
    • I also see that when I’m turning knobs the device is kind of hidden. I haven’t really designed the interface to be playable and to look good and be legible while being played.
    • Looked into some other examples of video synth top down videos :

    This is making me think more about the physical presence of my devices, and how I could incorporate more mechanical parts – instead of purely making electronics. I may have to play the game a bit more, make things that look like what they do and help people understand the associations I’m trying to make.

    I could have micrometer heads for the potentiometers :

    I could have fiber optic cables too :

    Fiber Optic Cable Connectors & Assemblies | Clearfield

    I think if I every try to make a product though I would need to team up with someone who has skills I don’t.

    ***

    Why make hardware ?

    • it can be analog and digital
    • it’s faster at doing things with video than software
    • it can be custom
    • it has a live-playable interface

    ***

    What effects am I creating ? (check out Premiere to see what kinds of effects exist).

    • Recording and playing things back at different speeds (video transposition), assigning to different color channels
    • desynchronizing recordings with the screen sync so that they jump around
    • recording videos side by side in memory so that they bleed over one another.
    • Video collage and montage composer (video sampling, video looping), video plastifier, étude II, micro-montage, noise research, play, do and see, polyvideo layering, defamiliarizing of the image + reflecting the mediascape we inhabit, video as “palpable”, “nontheoretical”, and “experiential” (quotes from musique concrete wiki).
    • The structure of the videos is based on the indexicality of the medium (the SRAM and how it stores). So the recording technique is also the synthesis technique.

      Struggling to represent the painterliness of the raster videos without showing them as a video. It is completely different from my experiments with lines and triangles, and is less intuitive for me. An attempt at a kind of chronogram of a video :

      It gives some idea of the range of images I guess?

      Here are some notes I took about a year ago after visiting a painting expo that relate to my painterly video experiments :

      • Layering
      • Contour
      • Surface
      • Saturation
      • Flow vs. Choppiness
      • Roughness, relief 
      • Edges versus centers
      • Movement vs stasis 
      • Enriching of detail
      • Abstracting, cosmic making
      • Fine lines versus coarse
      • Exposure 
      • Color fields
      • Texture
      • Deep friedness
      • Films of cows, ocean. Stills of still life? Flowers, birds, drapes folding, mold, fruit, bones
      • Color mixing vs melting vs meeting
      • Paint viscosity (runny vs clotted)
      • Direction of brush strokes 
      • Contour
      • Focus direction in canvas
      • Translucent flesh, shine vs glow from interior, waxiness 
      • Graininess 
      • Composition, color pairings and form
      • Foreground displaced to background vice versa 
      • Meeting pattern planes 
      • Lighting and shadow
      • Illusion of depth
      • Cloudiness 
      • Turbulence

      Check out this “glitch” video artist Jacques Perconte that Marc shared with me : https://www.youtube.com/watch?v=8_Xhu9Vx5XM

      He works, since the 1990s, with compression and achieves a painterly, impressionistic effects and exposes them on immense projection surfaces.

      Check out also Tauba Auerbach’s work :

      Tauba Auerbach

      Tauba Auerbach at the ICA | Inside LDN

      Jack Whitten :

      Jack Whitten: Artwork Survey: 1970s | Art21

      *************

      Some quotes from Art21 series :

      James Turrel – what you are perceiving is perception itself. We don’t receive the world around us we create it.

      Jeff Wall – Art gives you an experience that alters something, it doesn’t tell you or convince you.

      Jeff Wall – When I started photography there were potential energies in the medium that weren’t being realized.

      Roland Barthes – The punctum points to those features of a photograph that seem to produce or convey a meaning without invoking any recognizable symbolic system.

      Olafur Eliasson – Art provokes a negotiation : why I am seeing this the way that I am seeing it, what does looking mean? Instead of questioning the object you are questioning yourself. Art offers the opportunity for self-evaluation.

      Tauba Auerbach – I want to learn things all the time, I want to understand the patterns behind things. I experience science through craft. With craft I try to cultivate sensitivity in a practiced, purposeful way. If you work with marbling you know just as much about viscosity and flow than a scientist, through your fingertips in a different way.

      Tauba Auerbach – Ideally I want to make an image that has a tiny effect on all the future images that you see.

      Tauba Auerbach – The pursuit of the sweet spot, cultivate the place of boundaries, limits of fraying, not hard edges.

      Cindy Sherman – I don’t know what I’m looking for until I see it.

      Richard Serra – Artists invent strategies, tools, techniques, that allow themselves to see in a way they haven’t seen before to extend their vision beyond the standardized classic reflex actions. These help us to see inside what we’re doing so we don’t get into a lock step notion of how to do what you do.

      Louise Despont – If you’re always making work for someone, and not for yourself, maybe you don’t let yourself make the mistakes that are necessary.

      Louise Despont – Each drawing is a series of tiny discoveries, it reveals/unfolds the drawing from total control. I own at most 1/4 of the drawing. The rest is something else, this is what’s exciting.

      Louise Despont – It’s best explained in the drawings, words are clumsy to describe something like the concrete feeling of spirituality.

      Liz Magor – My Studio is a space to reconcile dissonance, no one knows I’m here. When I’m not in my studio I want to be in my studio working. My tools are rudimentary. I’m here for pleasure but it’s not fun. Everyone should have a studio for mental health. I can filter out the noise and see the ever present under the radar stuff.

      Liz Magor – I’m not an animist but objects have stuff in them that comes out. I try to resurrect these objects from the netherworld.

      Liz Magor – The slowness of the material process matches the slowness of my thinking process.

      Liz Magor – I’m creating an experience for looking.

      Liz Magor – I give myself my own program, I give myself my own assignments. And art is the choices I make.

      Liz Magor – Maintaining the conditions for this production, which is not very logical, uncalled for. No one is asking me to do this, I’m barely asking me to do this. To do this I have to overlook my making with the journey of the things into the world.

      Liz Magor – After the casting I unwrap it like a little gift, there are surprises.

      Elliott Hundley – This photoshoot is so elaborate but not because I want a certain effect, because I want to not control the effects, and have a layer of unexpected results. In relinquishing control, the piece gives me something back I didn’t expect.

      Jack Whitten – I’m not a narrative painter.

      Jack Whitten – I built a device to move large amounts of acrylic paint in one gesture.

      Katherina Gross – Painting is not linear. The synchronicity in painting is compelling for your thought process. I’m trying to grasp some of those fast thoughts that are moving through my brain.

      *************

      Reading about the history of video synthesis.

      From https://museumzero.blogspot.com/2013/12/its-all-baseball-nam-june-paik-starts.html)

      Nam June Paik sees Taoist energy in working with the flow of TV video :

      “To receive simultaneously the parallel flows of many independent movements is, as Paik pointed out at the time, a classical Taoist way of meditating: by becoming aware of everything going on in the present, you discover eternity right now.” 

      “I love Chinese history, surprise, nonlinearity.  You know what judo means?  A way to be soft.  You let other guy do all the work.  I think in some Oriental way.  In TV I try not to be boss, to stay as small as possible.  So that seems to work. “ 

      “So then when I started TV, the best decision I made in life is not really to go into TV, but to do work inside TV.”

      “Imagine everything that exists is flowing, like the tao, in a perpetual motion, with endless waves, like the ocean.  We come from it; we go back to it.  Whatever surges forward falls back, so Lao-tse, who wrote the Tao Te Ching (On the Nature of the Way), urges us not to try too hard.  Relax; be like a baby; follow whatever path presents itself.  That is the way. ”

      Not striving to fit yourself to some preconceived ideal, like a Buddhist or a Confucian; simply being, following breath wherever it blows.  In this sense, the television signal is like the tao; endlessly variable, continually reversing itself, dying, being reborn, it is never idle, yet it is so vast and perpetual that it often seems calm as the sea.

      Beginning with whatever live programs came along, distorting them, then recombining his distortions like waves on the ocean surface, Paik created an indeterminate and endless show for the Galerie Parnass, in Wuppertal, Germany, in 1963.  

      From Scanimate: The Origins of Computer Motion Graphics :

      • This is about the history of television, these were the first ways to put text and make graphics on screen (Media Archaeology!). Nowadays visuals are gratuitous. What did we lose in the transition to digital ? The imperfections, the proximity to the functioning.
      • Had a role in music videos, and news segments, so was part of culture.
      • “Blooming” is the term for the glowing edges in CRT Scanimate.
      • It’s real life artefacts. It’s like exploding scale models instead of GCI.
      • They made cookbooks with the recipes of different effects that created while working with clients.

      From https://wearethemutants.com/2018/01/09/a-sloppy-machine-like-me-the-history-of-video-synthesizers/

      Like its close temporal and conceptual counterpart, the audio synthesizer, the video synthesizer was created iteratively by academics, artists, and tinkerers, then eagerly snatched up by the world’s biggest media producers once prototypes had proved their power and versatility.

      • Started with experimental musicians like John Cage and la musique concrète.
      • Early pirate TV station in NYC.
      • Amiga supplants the Scanimate and analog synths.
      • The 1967 Portapak release and it’s importance for activism and experimental art.
      • Video comes from the root video of Latin video (“I see”).
      • There is a relationship between images and waves  :

      https://upload.wikimedia.org/wikipedia/commons/6/61/P-type-chirplets-for-image-processing.png

      CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=641492

      This is so hard for me to understand but this somehow relates to chirping also :

      Published in IEEE International Conference on Acoustics, Speech, and Signal Processing 1992

      Time-frequency perspectives: the ‘chirplet’ transform

      Steve MannS. Haykin

      From article about DCT for image compression :

      undefined

      By Drummyfish – Own work, CC0, https://commons.wikimedia.org/w/index.php?curid=77608151

      More cool matrix math image modifications here : https://en.wikipedia.org/wiki/Digital_image_processing

      Also this website which describes it all visually ! https://setosa.io/ev/image-kernels/

      From Video Art : An Anthology :

        • The synthesizer let’s you see things that exist only in the mind’s eye.
        • visual ingredients
        • video synthesizers churn out…images based on their own electronic structure.
        • We make the image with our eyes and brain. It’s psycho-visual
        • video as concretized imagination.
        • video is surreal, the images are ephemeral products of micro electronic pulses.
        • “electronic imagery”
        • video is a medium which works with time

      Au commencement était le bruit, la poésie électronique de Steina et Woody Vasulka | sonore visuel

      The Vasulkas

      *************

      Here is an attempt to describe what could be a model of a potential “practice” :

      *EDIT* This should include me blogging ! Maybe add a RESEARCH and/or DOCUMENTATION phase ?

      *******

      Check out this pyramid of types of work :

      Bloom's revised taxonomy organized as a pyramid of learning levels with explanations of each

      I feel like my project is about analysing (and curating), I’m not sure if I take a real position.

      *******

      Check out this amazing NIME article called The Concentric Sampler: A musical instrument from a repurposed floppy disk drive by Timothy Tate about “the redundancy and physicality of magnetic recording media” which utilizes “time-based granular synthesis” and lo-fi:

      https://nime.pubpub.org/pub/uh76shf0/release/1

      Some quotes :

      • Physical media offer an opportunity to “foreground the physical medium as a tool for musical expression.”
      • “The deterioration of old magnetic tape as it is looped, defining a … musical structure.” The “failure of the medium is in focus, rather than the mechanism.”
      • “as soon as something’s on tape, it becomes a substance which is malleable and mutable and cuttable and reversible in ways that discs aren’t.”
      • “This study emphasises working within a medium’s limitation, rather than prescribing a compositional framework or sonic agenda upon it”
      • Motivation to “foreground musical and technical possibilities…and generative sonic potentials…and novel, unique expressive and
      • compositional possibilities”…to be “discoveredexplored”
      • The device is a “performable instrument and a generative sonic tool”
      • “Rather than focusing on the authenticity of the sound reproduction, the Concentric Sampler draws focus to the byproducts and the resultant indexicality and generative sonic possibilities of the floppy disk as a medium.”
      • “the term ‘musical expression’ here places value on the ability to shape and define a musical grammar from the indexicality of themedium and mechanism.”
      • subverting traditional modes ofexpression
      • “Each section is accompanied by a demonstrative video” !!!
      • The live playable instrument can “achieve the dissociation of pitch and time
      • “imperfections in … reproduction”
      • “possibilities within a framework of … making”

      Now following up on some of the references from this article:

      • You can think of an instrument as a framework that has a predisposed range (affordances). Our tools provide a certain range of suggested usages. Some things are easier and some things harder to do with an instrument, this is the instrument’s spectrum. If you stay within those ranges, you accept the silent grain of the instrument. If you make an unusual tool then, do you risk finding yourself in an unusual affordance ? (J. Mooney) Could it be possible to make videos subtractively, instead of additively ?
      • From New Media Reader : The different between representation and simulation. P.18″Digital media process the physical properties of the input data, light and sound waves, are not converted into another object but into numbers; that is, into abstract symlbols rather than analogous objects and physical surfaces. Hence, media processes are brought into the symbolic realm of math rather than physics or chemistry. Once coded numerically, the input data in a digital media production can immediately be subjected to the math processes of addition, subtraction, multiplication and division…” AND compressed AND manipulated more easily than analogue forms because they are more mutable than objects. Also easy access and quick movement of the data of an enormous mass of data. This causes the materiality of the world around us to become unsteady…The televisual
      • Because everything else is also digitized, there is a “convergence of previously discreet media forms, now a new fluid area of media decentralized production and consumption. Remediation, Multilinearity.
      • Hand-made videos. Exploring the boundary between hand tools and large-scale machines which is a distinction Marx makes P.91.

      Check out these Bauhaus era artist’s work with the audio band in video :

      Paul Schaeffer (coiner of the “club d’essai“) designed specific tools like the morphophone and phonogène for modifying audio for musique concrete :

      *******

      Watching ENSCI diploma presentations I’ve thought about how nice the sequence is : first you write something that explores a topic and cites thinkers on the subject, and then you work on a design project that relates to this somehow. Also looking back at some videos, there is already a lot of content I have produced that I could try to learn from ! Like that Amsterdam-based artist told me, I could also focus on using my tools.

      I am also doing a synth workshop in late September at Villette Makerz but I’m not sure if I can connect to that or not here. *EDIT* an FPGA board that generates patterns based on audio input is a GO !*

      I also want to make a next FPGA board which will be a software reconfigurable device so that I can move to verilog for the summer instead of making a new board for every experiment. Not sure if this could also take the form of a simple video synth for Villette Makerz or at least a simplified version perhaps.

      *******

      Some quick looking in to GPUs :

      GPUs are all about direct memory access (DMA) not mediated through the CPU. A thought about the FPGA board – perhaps I should first have Arduino mess with the clock while playing back / recording something on the 16MB board (but I’m limited to the number of pins for address and I/O – it would essentially be a counter that could change in frequency on the fly)? Having memory connected to the screen (a screen buffer) and modifying it is the fundamental computer display setup. From 1951 a memory pattern on CRT :

      It would be cool to experiment with sprites and copying parts of memory from one bank to another too like a blitter https://en.wikipedia.org/wiki/Blitter .

      It looks like what makes shaders cool is that they are programmable and have their own code languages now. Before shaders there were hardware rendering pipelines called fixed-function. Different units would be responsible for specific functions. It seems like these units would take in vertex data then output pixel colors.

      I looked in to how to do matrix multiplication in hardware. It actually seems to be more natural as an analog circuit with resistor arrays. It can also be done by multiplying two 2-bit binary numbers using this kind of logic setup :

      **********

      I am becoming more and more curious about the difference from having a screen buffer that is written to during the blanking period and having a massive bank of memory that can be recorded to like in the 128Kbit. Check out these articles about how writing and reading to video memory is dealt with in different systems :

      https://en.wikipedia.org/wiki/Tiled_rendering

      https://en.wikipedia.org/wiki/Multiple_buffering

      https://en.wikipedia.org/wiki/Vertical_blank_interrupt ,quoted from : 

      During the vertical blanking interval, the driver orders the video card to either rapidly copy the off-screen graphics area into the active display area (double buffering), or treat both memory areas as displayable, and simply switch back and forth between them (page flipping). [The two buffers are called the front and the back buffer] Some graphics systems let the software perform its memory accesses so that they stay at the same time point relative to the display hardware’s refresh cycle, known as raster interrupt or racing the beam.” [i.e. you can also draw things during the horizontal blanking interval]

      **********

      The Real-Time Corrupter is fascinating https://redscientist.com/rtc . The kinds of memory modifications it allows for are fascinating and far more dynamic than static, text-based find-and-replace experiments I’ve done in workshops. Looking at this video I learned some cool stuff (https://www.youtube.com/watch?v=n9HS6zftuSk&list=PLItZ3jvJKD7rnoxmqJJ0B5E1B9_WW9L5E&index=2). For instance, copying values from one address (in video RAM, scrolling backdrops memory space a.k.a. “nametables”, NES RAM, ROM etc.) and then reapplying them to another address every frame (called piping) or just one time, the ability to isolate specific effects with the sanitize function. It can generate noise between certain values, or shift values up or down on (called tilting), listen to a value and then reapply it after a fixed number of frames, freezing the current value somewhere for future frames, replacing values intelligently with values in certain sets (for 3D glitching). It’s like performing brain surgery while the patient is fully conscious and telling a story. All of these memory transformations could be applied with the FPGA board !

      **********

      In a similar spirit, this game called Code War involves creating a program that competes with others to write to all of the memory space. There are competing strategies (quoted from https://corewar-docs.readthedocs.io/en/latest/corewar/strategies/) for warriors :

      Rock - a warrior which rapidly bombs the core with dat instructions
      Paper - a warrior which replicates, creating multiple, parallel copies
      Scissors - a warrior which scans the core looking for other warriors

      The battle is visualized in a memory grid (https://corewar-docs.readthedocs.io/en/latest/corewar/visualisation/)

      https://upload.wikimedia.org/wikipedia/commons/c/c2/Core_War_PMars_Screenshot.png

      You can simulate and battle warrior code here : https://crypto.stanford.edu/~blynn/play/redcode.html

      **********

      Visualizing memory access in time. From https://bling.kapsi.fi/blog/x86-memory-access-visualization.html:

      *********

      Some thoughts on HCI from people at work :

      it’s research if it follows scientific method and has possible applications.

      Find your peers, try to publish in their journals.

      The idea of publishing is to get feedback from your peers.

      If you make a pedagogical tool, then you could test how effective it is at helping people learn.

      Based on this conversation, I thought about how I don’t necessarily want to promote engineering education and that I always want to be on the design/art side of things. I also have learned that I find doing something I already know how to do less exciting, and so making simple kits or small series of artworks that are guaranteed to work for days on end, is not always super rewarding for me intellectually. This leads me to the current conclusion : I want to teach and then do a kind of artistic research on the side. I should then keep building my teaching career and try to get expos of my work. I am still trying to figure out how exactly my artistic research should be communicated (through articles in design magazines, through conferences with expos connected to them?).

      **********

      I am preparing a solar workshop at Villette Makerz based on the simple solar engine with optimized SMD components. The idea is an art bot that will draw, paint, in the sun. I am working on testing the various combinations of solar panels, capacitors and voltage supervisors.

      We are also making a version 2 of the video synth modules which are all Eurorack compatible in terms of dimensions, use 3.5mm audio jacks, the eurorack power supply. The idea is that the modules be both manual and automatable for the purpose of demos and to be didactic for students learning about analog electronics.

       

      Marc is doing a great job reimagining this project – he is good at compromising and imposing the vision of the final thing on each step of the construction. I am, in contrast, driven astray I think by my fascination by what “the machine itself wants to express”…

      ********

      Finally getting around to testing the Arduino-controllable analog switch to activate record or READ mode on the SRAM 16MB board. The idea is to test taking a recorded video and then doing some bit manipulations with it and saving it back to the memory. Predictably though, I’m having issues just writing anything to the thing…

      I/O pins on Arduino 0-7
      CLK on Arduino 12
      REC on Arduino 13 connected to analog switch connecting the WR pin on SRAM to either VCC or !CLK
      
      unsigned long memory; //total number of 8bit words on the 16Mb SRAM = 2048000
      
      const byte IO_0 = 7; 
      const byte IO_1 = 6; 
      const byte IO_2 = 5; 
      const byte IO_3 = 4; 
      const byte IO_4 = 3; 
      const byte IO_5 = 2; 
      const byte IO_6 = 1; 
      const byte IO_7 = 0; 
      
      void setup() {
      //Serial.begin(115200);
      pinMode(13, OUTPUT);
      pinMode(12, OUTPUT);
      
      pinMode(IO_0, OUTPUT);
      pinMode(IO_1, OUTPUT);
      pinMode(IO_2, OUTPUT);
      pinMode(IO_3, OUTPUT);
      pinMode(IO_4, OUTPUT);
      pinMode(IO_5, OUTPUT);
      pinMode(IO_6, OUTPUT);
      pinMode(IO_7, OUTPUT);
      
      //WRITE
      while(memory<= 2048000){
      memory++;
      
      // if (memory%10000==0){
      // Serial.println(memory);
      // }
      
      if (memory%5==0){
      PORTD = B11110000; // the IO pins. simple pattern test.
      
      }
      else{
      PORTD = B00001111;// the IO pins. simple pattern test.
      }
      //WRITE MODE
      PORTB = B11101111; // PIN 12 ARDUINO CLK goes LOW/HIGH
      PORTB = B11111111; // PIN 13 ARDUINO WR STAYS HIGH
      
      }
      // Serial.println("finished writing");
      }
      
      void loop() {
      pinMode(IO_0, INPUT);
      pinMode(IO_1, INPUT);
      pinMode(IO_2, INPUT);
      pinMode(IO_3, INPUT);
      pinMode(IO_4, INPUT);
      pinMode(IO_5, INPUT);
      pinMode(IO_6, INPUT);
      pinMode(IO_7, INPUT);
      
      digitalWrite(IO_0, LOW);
      digitalWrite(IO_1, LOW);
      digitalWrite(IO_2, LOW);
      digitalWrite(IO_3, LOW);
      digitalWrite(IO_4, LOW);
      digitalWrite(IO_5, LOW);
      digitalWrite(IO_6, LOW);
      digitalWrite(IO_7, LOW);
      
      //PIN 13 REC IN LOW READ MODE
      PORTB = B11011111; // PIN 12 ARDUINO CLK goes LOW/HIGH
      PORTB = B11001111; // PIN 12 ARDUINO CLK goes LOW/HIGH
      }
      

      Not working…

      unsigned long memory; //total number of 8bit words = 2048000
      byte cell; // store the word at this address
      
      void setup() {
      
      //PD0 - PD7 are IO
      pinMode(13, OUTPUT); // REC - PB5
      pinMode(12, OUTPUT);// CLK - PB4
      
       while(memory<= 2048000){
          memory++;
      //READ
              DDRD = B00000000; // all IOs in INPUT
              PORTD = B00000000; // no pull-ups
      
              PORTB = B11011111; // WR stays LOW + CLK HIGH
              delayMicroseconds(10);
              cell = PIND; // read the cell at this memory address and store it in the variable cell
              delayMicroseconds(10);
              PORTB = B11001111; // WR stays LOW + CLK LOW
              delayMicroseconds(10);
      
      //WRITE
             
              DDRD = B11111111; // all IOs in OUTPUT
      
              PORTD = ~cell; // write  a transformed cell back to memory
              PORTB = B11111111; // WR stays HIGH + CLK HIGH
              delayMicroseconds(10);
              PORTB = B11101111; // WR stays HIGH + CLK goes LOW
              delayMicroseconds(10);
        }
      }
      
      
      void loop() {
      //READ MODE TO SEE WHAT WE HAVE IN MEMORY
              PORTB = B11011111; // WR stays LOW + CLK HIGH
              delay(500);
              PORTB = B11001111; //  WR stays LOW +  CLK LOW
              delay(500);
      }
      Not working either...

       

      ****

      I reread the manual for the SRAM and found that in the writing mode where OE is held LOW, you need to not force the SRAM IO pins for a duration of 10ns after WE goes low or else the “previously read data will drive the IO buffer”. So I need to put the IO pins into HIZ for this period of time.

      I also found that the SRAM needs a startup time of 150ns after the voltage has settled. This needs to be added in the setup.

      Looking at the 74HC590 datasheet, I am realizing that there is a significant propogation delay for the CLK to convert into a new address on the ADD pins. I can’t figure out the exact delay but the highest delays I see for any step in the pipeline are in the 100s of ns. To be conservative, after the CLK goes HIGH and WR LOW, I’m adding a big delay so that the address lines can catch up.

      *EDIT* I forgot, the SRAM is 3.3V logic level so I need a level shifter or I’ll damage it !

      However, even with that, still no dice ! Very curious…

      *****

      I just tried some different op amps with the Serpentin 128KB board.

      Recorded some different speeds in the same recording which was cool.

      Here are the things I’ve played with :

      1. Recording versus playing
      2. Clock speeds
      3. Amplifying input / output and threshold
      4. Count resetting, memory chunk selection
      5. The memory size
      6. The bit width
      7. combining several recordings

      What I want to play with :

      1. editing tiny parts of memory, possibly based on the contents of the memory
      2. moving through the memory non-linearly, and at different speeds/accelerations
      3. copying parts of one memory to another  

        ********

      FPGA synth

      Heading towards a more reliable, deployable and software-leaning approach (but not so much about automated choreography as with previous atmega boards as augmented live playable machine for manual performances) to working with video now.

      I am also trying to be a bit more strategic and attempt to think ahead a little bit beyond the one board one experiment situation. It takes a long time to trouble shoot a single board and learn all the lessons from it, I have to give each board the time it deserves and I need to be perhaps less productive and more thorough.

      I am also coming to the awareness of what is my scope with this next board :

      • I won’t be doing challenging engineering things (like implementing different ways of programming the same FPGA, interfacing with differential pair protocols like HDMI) that have no impact on the images and take loads of time to do.
      • These are boards for experimenting and live playing, so they also need to have controls on the board.
      • I also need to think about safety, avoiding the possibility of easily making short circuits and damaging the board.
      • I’d also like to move back to color, and away from the intense limits of exclusively 1bit resolution.
      • I want to keep removing superfluous cables and adapters too, this one will have only a VGA out.

      I made a map of my prototypes so far for this project :

      It’s been around two years I’ve been working on this !

      After talking with Zach at work, his idea was to show not just the screen but also me turning knobs etc. Here’s what this could look like :

      Or,

      …or,

       

      It would be nice to have a computer interfaced oscilloscope to record the signals.

      *****

      The most plug and play option would appear to already exist : a tiny raspberry pi zero can already run Processing scripts and control small screens https://learn.adafruit.com/processing-on-the-raspberry-pi-and-pitft/processing !

      Here’s a good intro to making simple patterns with processing : https://processing.org/examples/

      We would be more in the zone of software video synth : https://learn.adafruit.com/feather-rp2040-dvi-video-synth

      Also seem to be companies making just this kind of thing : https://store.excamera.com/

      Arduino talks to the FPGA by SPI as if it were a RAM space. https://excamera.com/files/gameduino/synth/doc/gen/poster.pdf

      Design / Engineer friend Paolo Salvagione pointed me to Configurable mixed-signal ICs. It appears to be like an FPGA but for mixed analog and digital circuitry and is configured in design software.

      There is also the world of ASICs and the google open-source silicon project https://developers.google.com/silicon

      *************

      My project was originally about slowing down computer processes and representing them in space. Now I just seem to order things on Amazon and design increasing numbers of PCBs. It seems that I have so little time to do the art in the end because of how much investment the technical stuff requires. How could I get back to my original project here ?

      My project also had a collection of related techniques :

      • Try to free machines from their corseted, factory settings and reveal their full range of expression that would otherwise just exist in a parallel unseen dimension
      • Try to show our files from the perspective of / through the eyes of the machines that are processing them.
      • Curate series of abstract formal compositions which emerge from figurative, architecturally-themed or culturally iconic ones.
      • Try to describe the behaviour / idiosyncracies of algorithms and make them tangible
      • Isolate specific moments of machine-to-machine interface in a larger system
      • Get one’s hands inside the black box, and make prepared machines which expose their parameters
      • Try to rebuild electronic systems based on help from the DIY internet and then hope to stumble on something unintentional
      • All the while emphasize the materiality of technology
      • Trying to go super low-level and avoiding abstract computer “visualizations”
      • Exploring the link between visual chaos and harmony, that sweet spot of medium entropy.
      • Riffing off of the music synthesizer movement of knobs and playful electronics interfaces and its anti-theory vibe
      • Exploring the surreal, bizarre space of the computational unconscious
      • Do “artistic research” projects that are educational, at least for me
      • play with form versus content

      I wonder if the pixel based screen is not really appropriate for my methodology. It’s the opposite of over-looked and black boxed. It’s an obsession for an entire culture. It also does a pretty good job of “representing” what happens inside the computer already. It doesn’t really need my help !

      ********

      I also need to do a debrief on this leg of the video synth project. What have I learned ?

      Technical stuff :

          • How to make and debug PCBs
          • To work with video + SRAM with counters as well as some op amp circuits. The difference between digital and analog in the video context.
          • To work with the VGA protocols, about resolution and brightness of the image.
          • I learned about oscillation, the difference between KHz and MHz in the context of video and what sampling is.

      Stuff about media representation

          • some aspects of the nature of visual memory, how some of our sensory apparatus work at different speeds
          • the curious experience of “searching” for an image (like when messing with ADC knobs and CLK speeds).
          • what is required to identify / recognize and image or scene, how much resolution and shape
          • Deconstructing the illusion of television by interacting with its materiality and mutability
          • Repeating historical art explorations with television (see Sans Soleil – Chris Marker)
          • synchronizing the data with the frame of the image is a key part of the illusion of video. If you mess with this suddenly the video frame becomes a malleable object. If it is desynchronized, the image is out of frame and will jump around with the SRAM recording. If you change the speed of recording or of playback you can zoom.
          • Layering of different images in a palimpsest can create textures from them.
          • I’ve made some nice abstract pattern discoveries through knob twisting.
          • Exploring the gap between legible figurative image and abstract patterns, contours, fields,
          • the texture and grain of data at various scales
          • the connection between video and abstract field painting

      What does it mean to be trying to make super simple hardware video filter in the age of AI generated video and images ? How is this activity relevant ? I feel like I’m looking for low hanging fruit here but I’m not sure there is any left. Also, the time it takes to make a single experiment in hardware is astronomical compared to the software option…

      I think the project started off about the screen itself, how to send signals and display them. At some point it became clear that the screen just displays data stored in memory.

      In this spirit, I could try to represent memory access patterns : https://en.wikipedia.org/wiki/Memory_access_pattern Or the patterns that memory is refreshed in : https://en.wikipedia.org/wiki/Memory_refresh

      http://www.overbyte.com.au/misc/Lesson3/CacheFun.html

      I could also sample images and then modify them algorithmically? But it kind of ends up looking like simple Photoshop filters. It would have to be modified in some way based on the way the image is stored in the memory ?

      Check out this image stored in DDR ram decaying over time from J. Alex Halderman’s paper : https://jhalderm.com/pub/papers/coldboot-sec08.pdf

      Also it seems like different DDR has different decay rates (from A Trustworthy Key Generation Prototype Based on DDR3 PUF for Wireless Sensor Networks https://www.researchgate.net/figure/DRAM-cells-decay-feature-with-power-switch-interval-time-of-120-s-a-DDR3-device-1-b_fig3_263586301):

       

      For comparison, check out the same Mona Lisa image sent through the air and damaged by the earth’s atmosphere :

      undefined

      ********

      I’ve never thought of this before but I guess I could generate a simple arduino code, and then take the .hex file and glitch it with any of the glitching techniques I’ve been using and see what it puts on screen.

      I could also test overclocking / overheating the atmel and seeing how they collapse !!

      ********

      I should also finish the boards I’ve already designed and had made, they offer learning opportunities and possible discoveries ! The bucket brigade, the digitally-controlled pots (meh), and the band splitter (*EDIT* completed !) are the three that are left from that series. Also the Palimpsest.OS 2 board when controlled by microchip – especially if I can get the sync working ! Of course I have the new 128Kbit memory board (*EDIT* all done !) once I get the parts too and then a final (?) FPGA v2 board.

      ********

      I am still interested in exploring FPGAs and getting my hands in the tech to possibly stumble on cool stuff though so here is a second attempt at a video FPGA board based on the iCE40HX1K-TQ144 to output HDMI, DVI, VGA, and composite video.

      For generating verilog, there is migen for python 3 (https://github.com/m-labs/migen). I’m going to try a super simple thing in Verilog first to get a feeling for it. 

      I am learning verilog here : https://hdlbits.01xz.net/wiki/Main_Page

      It looks like generating HDMI is doable even though there is a difference between LVDS and TMDS (involving possibly 100nF series capacitors ?) :

      Here is the official guide : https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/UZ/FPGA-TN-02213-1-7-Using-Differential-IO-LVDS-Sub-LVDS-iCE40-LP-HX.ashx?document_id=47960

      https://www.fpga4fun.com/HDMI.html

      blackmesalabs.wordpress.com/2017/12/15/bml-hdmi-video-for-fpgas-over-pmod/

      https://github.com/hdl-util/hdmi/

      Different chips being used like the PTN3360D HDMI / DVI Level Shifter (IN STOCK!), a TFP410PAP TFP410 TI PanelBus™ Digital Transmitter (NO STOCK!),

      Question - Pluto IIX GCvideo help needed | BitBuilt - Giving Life to Old Consoles

      According to the datasheet, “The LVDS25E/subLVDSE differential output buffers are available on all banks but the LVDS/subLVDS input buffers are only available on Bank 3 ofiCE40 LP/HXdevices.” ***Those are INPUTS but there are no special OUTPUT pins.***

      I have replaced the LDO with was giving me trouble previously and am making this version USB programmable so it could be a useful for others as a cool video kit.

      *****

      Revisiting FPGA programming, here is where I got to last time :

      Here is my process for the FPGA programming so far :

      1. I signed up for an account at www.latticesemi.com.
      2. I got a licence for iCEcube2, put it somewhere easy to find and took note of the directory, and downloaded iCEcube2 and the Lattice Diamond software from here : https://www.latticesemi.com/en/Products/DesignSoftwareAndIP
      3. I plugged in the iCEstick and it blinked in a circular pattern. I also checked that it had a port under Device Manager (it had 2!). 
      4. I went into iCEcube2 then to Help > Tutorial to open a pdf called “Implementing the Design” and followed the following steps :
        1. Start new project (iCE40, HX1K, ?TQ144?)
        2. Add Design file iCElab.v and Constraint file iCElab.sdc and checked the Device Info.
        3. Run Synplify Pro Synthesis
        4. Select Implementation
        5. Add constraint file iCElab.pcf under Import P&R Input Files.
        6. Run Placer
        7. Check the Floorplan, package view, timing analysis, power estimation
        8. Generate the bitmap (.bit and .hex files) which is saved wherever the project was saved. (For instance C:\Users\Jonah\Desktop\FPGA LATTICE DEMO PROJECTS\quick_start\quick_start\quick_start_Implmnt\sbt\outputs\bitmap).
        9. Here is what everything should look like once finished :
      5. I then transitioned to Youtube for a detailed explaination of using Diamond Programmer with the iCEstick here : https://www.youtube.com/watch?v=Df9k1T0bHmA&ab_channel=Dom
        1. Launch Diamond Programmer and take the default options (or click scan and take those). Important to note that you must launch Lattice Diamond Programmer directly, not through Lattice Diamond, in order to find the iCE40HX1K chip.
        2. It will mistake the board for a JTAG interface and give the following errors : “Failed to scan board”, “Scan Failed – Creating Blank Programmer Project.”
        3. Now change the Device Family to iCE40, the device to iCE40HX1K, and under Program enter the following information :
          1. Change Access mode to SPI Flash Programming and now some other options appear. Select > Vendor: Micron, Device: N25Q032, Package:  8-pin VDFPN8
          2. Now load the bitmap (.bin) file and check that it has a non-zero datasize :
          3. Click OK and then click the green traffic light on the next to top row of icons. You should then see a completion message and have the program on your board :
          4. Presto !

      Just for info :

      For the actual pinout of the ice40 (which you can change in Pin Constraints Editor):

      ***

      And here is my attempt programming my own board with the Lattice Programmer.

      I followed the same steps as above but instead selected the HW-USBN-2B (FTDI) programmer. Important to note that you must launch Lattice Diamond Programmer directly, not through Lattice Diamond, in order to find the iCE40HX1K chip. Also note that you need Lattice iCEcube2™ Software, not just Diamond for some reason.

      Here are the color connections needed from the Lattice Programming Cables User Guide :

      The USB power and Board power lights should be on on the programmer and the Prog blue LED should flash a few times before the Done blue LED will flash and Lattice Programmer will display Operation: successful.

      I am taking the example LED rotation verilog .v code :

      module LED_Rotation(
      
          input  clk,
          output LED1,
          output LED2,
          output LED3,
          output LED4,
          output LED5
      
          );
      
                     reg[15:0] div_cntr1;
                     reg[6:0] div_cntr2;
                     reg[1:0] dec_cntr;
                     reg half_sec_pulse;                         
      
                     always@(posedge clk)
                                    begin
                                    div_cntr1 <= div_cntr1 + 1;
                                    if (div_cntr1 == 0)
                                                  if (div_cntr2 == 91)
      
                                                                 begin
                                                                 div_cntr2 <= 0;
                                                                 half_sec_pulse <= 1; 
                                                                 end
                                                  else
                                                                 div_cntr2 <= div_cntr2 + 1;
                                    else
                                                  half_sec_pulse <= 0;                            
      
                                    if (half_sec_pulse == 1) 
                                                  dec_cntr <= dec_cntr + 1;                                          
                                    end                                                            
                     assign LED1 = (dec_cntr == 0) ;
                     assign LED2 = (dec_cntr == 1) ;
                     assign LED3 = (dec_cntr == 2) ;
                     assign LED4 = (dec_cntr == 3) ;
                     assign LED5 = 1'b1;
                                                           
      endmodule
      

      Under Tool > Pin Constraints Editor, we can change the pins activated in the code but mine is already set up connected to pin 99:

      I am soldering a second FPGA board. I noticed that the clock was not outputing anything, and also that the max temperature for the IC for reflow soldering is around 250°C. I am making sure to use the soldering iron “kissing” technique along with loads of flux applied.

      Reading through the datasheet :

      • Bank 3 additionally supports differential LVDS25 input buffers.
      • Each bank can have it’s own voltage level.
      • I may have to power the VCC, VCCIO_2, VCC_SPI, VCC_PLL, and VPP_2V5 (with a diode) even if I don’t plan on using them all just to get the device to turn on. (There is even a proper sequence to power them up in).
      • GNDPLL must NOT be connected to the board’s ground !
      • Bank 2 has two dedicated Input pins.

      ****

      FPGA BOARD ERRATA / DIARY :

      • 1.2V is connected to 5V throughout because of an EAGLE issue 🙁
      • I tried the voltage regulator 3 times and still no luck. *UPDATE* I tried a second 1.2V LDO that I ordered and it still reads 2.2V or so. No idea why this is happening when I can power the pin directly with 1.2V with my power supply no problem (therefore it can’t be a bad connection somewhere between 1.2V and another trace right?). Positive news is that the FPGA appears to have survived the over-voltage !!
      • Maybe I should give it a higher frequency oscillator, at least 25MHz, as I’m working with video?
      • I connected pin 9 of VGA to GND, it is connected to 5V on the signal side !
      • The GNDPLL should not be connected to GND.
      • The R2R ladder has a 270ohm going to ground instead of a 536 on each ladder.
      • Unclear which pins to plug where for programming – *EDIT* SOLVED
      • I had a hard time at first soldering the FPGA but then discovered a soldering “kiss” technique that was reliable and effective. For the clock, I melted the solder and then placed it gently on top.
      • I plugged it in with 2.2V instead of 1.2V and likely destroyed the chip the first time…
      • 0805 is doable and better for compact designs
      • I can’t find the name of the chip in the Target Device menu of Diamond Programmer. *EDIT* SOLVED : You need to open Diamond Programmer directly.
      • I need to get nicer boards if I am going to be working on FPGA boards, Aisler not inexpensively manufactured.
      • It seems like exploiting the parallelism of the FPGA could be interesting to explore. For this project it would probably be to logically mix several input videos together while transforming ?

      ***

      I’ve got it blinking at different frequencies !

      Now I want to generate some VGA signals as quickly as possible and start creating images by tweaking pre-made code like for VGAX.

      And here is demo code for the OLIMEX ice40 developement board which has RAM, DAC, ADC and VGA (but it needs a 100MHz clock) : https://github.com/OLIMEX/iCE40HX1K-EVB/tree/master/demo/ice40-io-video

      Also super simple (but uses a 25MHz clock) : https://www.fpga4fun.com/PongGame.html

      Here is the one I ended up using (takes a 12MHz clock and turns it into a 25MHz with the PLL) : https://github.com/imuguruza/alhambra_II_test/blob/master/vga/vga_test/

      Here is the key part of the code that actually draws on screen :

      module vga_sync_test(
          input wire clk_in,
          input wire reset,
          output reg r0,
          output reg r1,
          output reg r2,
          output reg b0,
          output reg b1,
          output reg b2,
          output reg g0,
          output reg g1,
          output reg g2,
          output wire h_sync,
          output wire v_sync,
          output wire led,
          output wire locked_led
        );
      
      wire clk_sys;
      wire display_en;
      //reg [9:0] h_count;
      wire [9:0] h_count;
      //reg [9:0] v_count;
      wire [9:0] v_count;
      assign  led = clk_sys;
      
      localparam  h_pixel_max = 640;
      localparam  v_pixel_max = 480;
      localparam  h_pixel_half = 320;
      localparam  v_pixel_half = 240;
      
      //Check if we can create RGB colors
      always @(posedge clk_sys) begin
        if (display_en) begin
          if (h_count < h_pixel_half
              && v_count < v_pixel_half) begin
            //Assign here your test color
            r0 <= 1'b0;
            r1 <= 1'b0;
            r2 <= 1'b0;
            g0 <= 1'b0;
            g1 <= 1'b0;
            g2 <= 1'b0;
            b0 <= 1'b1;
            b1 <= 1'b1;
            b2 <= 1'b1;
          end else if (h_count > h_pixel_half
                  && v_count < v_pixel_half) begin
            //Assign here your test color
            r0 <= 1'b0;
            r1 <= 1'b0;
            r2 <= 1'b0;
            g0 <= 1'b1;
            g1 <= 1'b1;
            g2 <= 1'b1;
            b0 <= 1'b0;
            b1 <= 1'b0;
            b2 <= 1'b0;
           end else if (h_count < h_pixel_half
                  && v_count > v_pixel_half) begin
            //Assign here your test color
            r0 <= 1'b1;
            r1 <= 1'b1;
            r2 <= 1'b1;
            g0 <= 1'b0;
            g1 <= 1'b0;
            g2 <= 1'b0;
            b0 <= 1'b0;
            b1 <= 1'b0;
            b2 <= 1'b0;
          end else begin
            //Assign here your test color
            r0 <= 1'b1;
            r1 <= 1'b1;
            r2 <= 1'b1;
            g0 <= 1'b1;
            g1 <= 1'b1;
            g2 <= 1'b1;
            b0 <= 1'b1;
            b1 <= 1'b1;
            b2 <= 1'b1;
            end
        end else begin
          r0 <= 1'b0;
          r1 <= 1'b0;
          r2 <= 1'b0;
          g0 <= 1'b0;
          g1 <= 1'b0;
          g2 <= 1'b0;
          b0 <= 1'b0;
          b1 <= 1'b0;
          b2 <= 1'b0;
        end
      end
      
      vga_sync vga_s(
            .clk_in(clk_in),         //12MHz clock input
            .reset(reset),           // RST assigned to SW1
            .h_sync(h_sync),
            .v_sync(v_sync),
            .clk_sys(clk_sys),       //25.125 MHz clock generated by PLL
            .h_count(h_count),
            .v_count(v_count),
            .display_en(display_en), // '1' => pixel region
            .locked(locked_led)      // PLL signal, '1' => OK
            );
      
      endmodule
      
       

      I would like to modify this code in the spirit of VGAX bytebeat-inspired video synthesis.

      I made sure to load the three dependent .v files and then changed the pin constraints in the .pcf as follows :

      set_io b0 76
      set_io clk_in 21
      set_io led 99
      set_io r0 81
      set_io b1 75
      set_io g2 91
      set_io r1 80
      set_io v_sync 87
      set_io b2 74
      set_io g1 95
      set_io locked_led 142
      set_io r2 79
      set_io reset 2
      set_io g0 96
      set_io h_sync 88
      
      

       

      It works !

      Just waiting for the LDOs to make it more practical to plug in. I will now test some actual patterns !

      ***

      Here is my first test bench with ModelSim ! Thanks to this tutorial from Nandland : https://nandland.com/tutorial-your-fpga-program-an-led-blinker-part-2/

      I am using ModelSim Lattice FPGA 2020.3 (which downloaded with either Icecube2 or Lattice Programmer?) and followed this PDF called
      Using Mentor ModelSim Simulator with Lattice iCEcube2
      (https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/MP2/Modelsim_AN006_Dec2020.ashx?document_id=50795)

      The steps to follow :

      1. Open a new project
      2. Click Add Existing Files (pick only the test bench verilog file) and then Close
      3. Now Compilation > Compile All
      4. Now Simulation > Start Simulation
      5. In the console type <<view wave>>…
      6. …then <<add wave * >>…
      7. …and a time period for example : <<run 100ms>>. (It takes us, ns, ms, and sec it appears)

      The signals are a little small and it is apparently possible to change this under Tools -> Edit Preferences, Wave Font and Footer Font…but when I try it crashes !

      Looks like printing to the console is going to be useful ! They work by using display like so…

      $display("0.5 seconds");

      …and then in the console area of ModelSim:

      run -all

      Here’s a simpler version of a tb for a 10 ps period of the following module :

      module dut ( input clk ); 
      `timescale 1ps / 1ps
      module top_module ( output reg clk );
      
            dut instance2 (.clk(clk));
      
                 initial begin
                 clk = 1'b0;
                 forever #5 clk = ~clk;
           end
      endmodule

      ***

      Thinking about different ways of generating patterns on a screen :

      1. Screen coordinate based (plotting functions that take an input x and produce an output y)
      2. Time based + screen coordinate based (a counter t increases over time and modifies the function’s behaviour)

      Turns out FPGAs can’t do math like microprocessors ! To implement trig functions you need to build a CORDIC, which is like a computer to calculate functions.

      Then again there are some things FPGAs can do better than microcontrollers like cellular automata (one study I checked out said 36 times faster than a GPU and up to 2,800 times faster than software). HDL Bits has an exercise to build the rule 30, 90, rule 101, rule 184 cellular automata.

      (Similar to cellular automata, it’s possible to make a hardware psuedo random number generator using a linear feedback shift register : https://en.wikipedia.org/wiki/Linear-feedback_shift_register )

      Here was the solution to the rule 90 challenge that I had to look up in the end :

      module top_module(
      input clk,
      input load,
      input [511:0] data,
      output reg [511:0] q);
      
      always @(posedge clk) begin
             if (load)
                 q <= data; // Load the DFFs with a value.
             else begin
                // At each clock, the DFF storing each bit position becomes the XOR of its left neighbour
                // and its right neighbour. Since the operation is the same for every
                // bit position, it can be written as a single operation on vectors.
                // The shifts are accomplished using part select and concatenation operators.
      
               // left                     right
               // neighbour              neighbour
                 q <= q[511:1] ^ {q[510:0], 1'b0} ;
              end
          end
      endmodule

      Check out this processing sketch that is evocative of cellular automata :

      https://openprocessing.org/sketch/1879926 or sketch 1908904

      Could it be possible to do this live to recorded video with the FPGA and be able to vary the number of generations, speed, etc. ?

      Try changing the “rules” live (by clicking “random”) while this cellular automata scrolls : https://devinacker.github.io/celldemo/

      https://en.wikipedia.org/wiki/Cyclic_cellular_automaton

      Makes one think that you could make a video game entirely from these patterns being activated at different times and in different zones…Or a dedicated FPGA board that takes in audio and divides it into different bands, or other control signals directly, to control which rules are being used to generate the pattern + a bunch of knobs to change the speeds etc. It could be super minimal, just some knobs, an FPGA and an HDMI out.

      Hardware version of CA : tinkerings.org/2016/09/24/cellular-automata-explorer/

      Also check out rules that go beyond logic operations with neighboring cells and into modulo values : https://en.wikipedia.org/wiki/Cyclic_cellular_automaton

      I am now trying to use the test bench to better understand why this code isn’t putting any pixels on the screen currently.

      Here is my test bench. An important note is that variables that are not modified in an always or initial block should not be reg but wire instead.

      `include "rule90.v"
      `timescale 1ps / 1ps
      
      
      module rule90_tb();
      reg clk;
      reg load;
      reg [511:0] data;
      wire [511:0] q;
      
      rule90 DUT (
      .clk(clk),
      .load(load),
      .data(data),
      .q(q)
      );
      
      initial begin
      #10
      clk = 1'b1;
      #10;
      clk = 1'b0;
      #10;
      load = 1'b1;
      data = 1'b1;
      clk = 1'b1;
      #10;
      clk = 1'b0;
      #10;
      load = 1'b0;
      
      end
      
      always #10 clk = ~clk;
      endmodule

      This shows that the rule90 circuit is working. Every clock cycle it advances. If I could show one bit per pixel and refresh every clock cycle ?

      ***

       

      I’m getting some cool images :

      ***

      For the next FPGA board, I’m aiming for the following features :

      • It could have a handy mini screen (raspberry pi zero using DPI I/0 pins with video splitter to pass on the VGA to FPGA). I wonder if this isn’t a gimmick and not really in line with the “artistic research” aspect of my project.
      • FTDI chip for USB programming (this is too technical I have decided)
      • 100MHz oscillator with divider
      • 4x SRAM, possibly in two banks as I only have a total of 95 pins to work with
      • HDMI out, (possibly composite and DVI also ?). I wonder if this isn’t a gimmick and not really in line with the “artistic research” aspect of my project.
      • Some kind of basic interface ? (Knobs etc? But this would require an ADC unless I just use dip switches). One solution is to make hardware controls on the peripherals (clock divider, bit selections for incoming and outgoing signals, reset and CS sel on SRAM, etc.).
      • An atmega which can ask the FPGA to do things? Not sure this is necessary.
      • A fan and heat sink for the FPGA?? But this will hide it. Also a gimmick.
      • An FPGA with more Logic Modules – like 4K instead of 1K? But this doesn’t come in the easier to solder format…
      • A reset button ?
      • A fuse and reverse polarity protection !
      • microcontroller-accessed SPI FLASH so we can test moving between different types of memory. Except I haven’t had a lot of luck with SPI memory and video recording so far.

      Reading a bit more about what FPGAs can do well ;

      • they have tons more I/O pins than an Arduino? This means I can control multiple SRAM address and I/0 pins.
      • Architecture can be completely reconfigurable (unlike a microcontroller which keeps the same architecture).
      • low latency applications that take data as a stream in real time from sensors, cameras, etc. This means I can perform fast logic manipulations of saved data.
      • high throughput applications, pipelining
      • encryption and networking
      • digital signal processing (IIR, FIR, FFT, ADC, DAC) like video filtering
      • parallelism, like doing 128 small tasks at the same time, versus microcontrollers doing things in serial
      • algorithms that can be solved with LUTs, tasks with lots of small adding and subtracting or multiplication (?)
      • K-means clustering, Monte Carlo, neural nets, Mandelbrot fractals, cellular automata ?
      • Seems that there are more and more systems that include micrcontrollers + FPGA + DSP, and you can of course put a microchip inside an FPGA. Here the FPGA accelerates certain tasks for the CPU

      *****

      Here’s what I think I could do with an FPGA and two individually-accessible SRAM I/0 banks   banks :

      • Reverse, scramble, or move through memory addressing with different patterns
      • could change the sample rate of recorded or incoming images
      • could perform logic operations on incoming /recorded video
      • could place sprites on screen, or other parts of memory, through blitting I could selectively overwrite memory.
      • It could record 8 tracks in parallel at 1 bit resolution, or record super long 1 bit resolution videos like on previous boards.
      • It could do matrix transformations on memory like shear, rotation
      • Copying values from one address and then reapplying them to another address every frame (called piping) or just one time. I could generate noise between certain values, or shift values up or down on (called tilting), listen to a value and then reapply it after a fixed number of frames, freezing the current value somewhere for future frames, replacing values intelligently with values in certain sets. Could this be called in-memory processing (https://en.wikipedia.org/wiki/In-memory_processing)? I could copy all the kinds of ways memory is accessed : https://en.wikipedia.org/wiki/Memory_access_pattern !

      With this setup, the FPGA can increment the ADD of all memories while individually controlling which SRAM is reading or writing. Additionally, and simultaneously, it can send and receive different data between two connected SRAM banks. Bascially, it can’t simultaneously access two different parts of memory for reading or writing, if it wanted to do that it would need to remember some info in a buffer and then change the address in sequence like a normal microchip (this is all to give a greater color range to the board). So as far as I can tell the following memory reading and writing operations could be accomplished :

      1. while incrementing memory ADD, record incoming video on A, B, C, D in sequence (at various bit depths)
      2. while incrementing memory ADD, record incoming video on A, B, C, D in parallel (make 4 copies)
      3. while incrementing memory ADD, record incoming video on A, B, C, D and playback previously recorded video
      4. while incrementing memory ADD, play back video on A, B, C, D in sequence
      5. while incrementing memory ADD, playback video 4 copies of a video on A, B, C, D in sequence (i.e. loop)
      6. while decrementing memory ADD, playback a video in reverse
      7. access recorded video with different ADD patterns
      8. while incrementing memory ADD, take stored sprites from the top of one SRAM and insert them into over SRAMs at later addresses
      9. while incrementing memory ADD, perform logic operation on incoming video + an SRAM reading while writing it to a second SRAM
      10. while incrementing memory ADD, perform bit level transformation on an SRAM recording while writing it to a second SRAM

      I am starting to think that losing some color information is worth having more flexibility to work in parallel with SRAM and save me headaches later on when I’m doing verilog hardware descriptions. Here is a simpler proposal that would have a single input (possibly record/pb or “function” meaning do something to the incoming video and then show it on a loop) :

      • I lose LEDs, but I replace them with the most honest indicators possible, the signals themselves buffered.
      • Lost the ability to control individually each quarter memory with WE control.
      • No more DIP select buttons…How will I be able to play this thing live once it’s going through its code (apart from being able to mess with the clock, the RGB bias out, VGA pins coming in, and being able to reset SRAMs and the FPGA)? Should I be able to interrupt it’s functioning by directly controlling CS and WE pins ? (This is possibly dangerous as two memories both set to READ sharing the same I/O pins would be bad.) How about a single button, which activates some hardware function? I would need to load a new code if I wanted to test a new hardware function.

      I’m trying to justify doing this thing in hardware by having tons of indicator LEDs, knobs to change parameters etc. I have to acknowledge that there is a certain amount of manipulation in making art, I am pushing it further in the direction I want so that it is easier to explain to people.

      Reminder : Why am I not using an atMega 2580 which has 86 programmable IO lines and 16MHz frequency ? FPGA is parallel, and it is faster, several people in different contexts have suggested FPGAs for my project, and it’s a learning opportunity.

      In general, doing things in harder is (Fabien) Faster than software, (Vivien) emphasizing materiality, and (Marc) pushing against the genericness of software and hardware that we are increasingly using.

      ****

      Using BRAM seems pretty simple to implicity create :

      reg [15:0] memory [start_address : end_address];

      //memory[4] addresses word 4;

      Fabien at work suggested two things : Not using a resettable fuse as they take longer to stop high current destroying things. And possibly having an SD card with the videos which the FPGA can read directly and getting rid of the RPI. This is because, while writing to SD takes time, reading can be fast. This would be a kind of ROM where I could store videos.

      The best mode for an FPGA apparently is one bit SD mode, instead of SPI, as it only requires 3 pins: CMD, CLK and DAT0.

      https://www.fpga4fun.com/SD2.html

      It looks like inside the SD card is just NAND Flash memory, so maybe I could interface directly with this ?

      https://upload.wikimedia.org/wikipedia/commons/f/f0/Pretec_16GB_SDHC_without_cover_20090420.jpg

      Or I could have Arduino doing the interface with an SD card ? I’m not sure why but I feel like this is going in the engineering direction and not the artistic one. The Rasbpi solution works, why not just keep it and save the headache that won’t in any way change the artistic output of the board.

      *****

      ATmega2560 SRAM board with SD card and I2C Flash memory. Raspberry pi as video in.

      But not very interactive. More about messing with different kinds of memory, and as a parallel test as the FPGA but more familiar and constrained in C.

      ***

      Don’t know what this is yet but, some kind of typology of memory modifications ?

      Choreographable video synth (PART III)

      Making things easier, simpler, more plug and play.

      ****

      Workshop
      Organize parts 
      
      Palimpsest.OS
      Get all channels working 
      Get raspberry pi VGA out working 
      Test with raspberry pi VGA put 
      Fix VGA input/output (I know what the problem is at least :)!
      SPI SRAM 
      Get recording as fast as possible 
      Try with 4MB FRAM ?
      New board with several + raspberry pi + snake-like channel overwriting?
      
      FPGA
      Try simple VGA with LatticIce40
      Solder FPGA board 
      ****
      La Generale experience :

      So many cables and wires and machines….something more minimal / smaller and portable possible ?

      Would love to have an integrated thing with raspberry pi / FPGA generating video and the thing directly outputting it.
      Do not rely on computers to output VGA signals ! Try raspberry pi.
      Physical objects is what I should make, not projections, because my project is about materiality ! I could make circuits that go beyond just recording and have moving parts and tiny screens eventually ? Then again this is a dangerous dream as I’ve learned with VHS and Floppy Disk tests…
      Don’t make experimental stuff during residencies, test tools you’ve already built and deploy tested things.
      My project is all about trying to touch video, get my hands into the inside of the machine. It’s also all about making mistakes, and stumbling upon unexpected output as a result.
      Somewhere between a kit/workshop and an object. (It’s not a performance) 
      ****

      I’ve got a Raspberry Pi Zero outputting VGA nicely. On startup it launches VLC in fullscreen and plays a playlist of iconic film excerpts. I activated hotplugging so it keeps looping even if the adapter is unplugged for long periods of time.

      I used Win32DiskImager and downloaded the ISO Image Raspberry Pi OS with desktop (around 1GB) from the officiql website (https://www.raspberrypi.com/software/operating-systems/) and put it onto an 8GB SD Card.

      In terms of connectors I needed a SD Micro > SD card adapter, HDMI mini > HDMI, an HDMI > VGA, a USB A > USB Micro for power, and a USB Micro to USB A female in order to plug in a mouse / keyboard.

      I followed these three tutorials for setting up VLC autoplay and turning off annoying VLC text:

      https://forums.raspberrypi.com/viewtopic.php?t=17051

      Raspberry Pi: Run VLC on startup and play slideshow videos/pictures from folder

      https://www.shellhacks.com/raspberry-pi-force-hdmi-hotplug/

       

      Now adding a power off button (not good to pull the plug while running)

      https://howchoo.com/g/mwnlytk3zmm/how-to-add-a-power-button-to-your-raspberry-pi

      **EDIT The rasbpi turns off when I unplug HDMI or plug into the palimpsestOS. Is it because RGB are mixed with only 100ohm resistors ? Should I have 3 blocking caps and then mix after ?

      Here’s a write up on how to VGA :

      https://chipnetics.com/tutorials/understanding-75-ohm-video-signals/

      Looks like parallel 75ohm resistors going to GND from the input is good practice + a cap and 75 ohm resistor on output?

       

      VGA OUTPUT examples :

      MAX4032 5V, 6dB Video Buffer with Sync-Tip Clamp, Output Sag Correction, and 150nA Shutdown Current | Analog Devices

      If I understand this last one, three parallel 75ohm resistors makes a 25ohm resistor. 97ohms gets 80% of 3.3V gone before the 25ohm dissipates the rest so that there is 0.7V left for the 3 channels. For a 5V supply like mine, I’ll need to remove 86% of 5V to get it down to 0.7V.  A 160 ohm resistor then ?

      VGA INPUT examples :

       

       

      *EDIT Made the changes (RGB colors in to GND through 75ohm, one color through cap to bias + 160ohm and now there is no output…But maybe it was already broken at this point? Can test when new converters arrive.)

      Looked at some VGA in/out boards I had in my possession and saw the following :

      • H and V sync can go to a 74HC125 Line Driver / Buffer.
      • Color returns can be conected together and grounded along with chassis.
      • Examples of color pins going to grounded 75ohm resistors then on to blocking caps. Also colors to gates of transistors.
      • Colors also going to video signal switcher like this : QS4A210 – 2-Channel 4:1 Analog Mux/Demux
      • Definitely DO NOT ground pin 9 (which is 5V), as I have done in all previous boards…

      ****

      An older HDMI to VGA converter broke so I opened it up to see if I could reproduce it. It’s an ALGOLTEK AG6201 which appears to be not accessible anywhere but the manufacturer’s website.

      The alternative is using a video chip which can trasmit HDMI like this project : https://hackaday.com/2019/07/26/hdmi-from-your-arduino/ which uses a CH7035B HDMI encoder.

      Alternatively I use the HDMI to VGA adapter and solder a D Sub Standard Male connector (https://www.mouser.fr/ProductDetail/TE-Connectivity-AMP/2301843-1?qs=rrS6PyfT74crws9wAQVNoA%3D%3D&mgh=1&vip=1&gclid=Cj0KCQjw27mhBhC9ARIsAIFsETE7Rp9JFGXhYFAXh_Z2YbQUB2NfHqO8rM7yPueb-T3yFiKbUMvJkOEaAtjJEALw_wcB) to the board.

      ***

      I will be testing a pico projector and a VGA capture device soon…

      Video Coverter VGA Capture Card,VGA to USB2.0 Converter,Audio and Video Capture Device,Plug-and-Play,USB Drive-Free HD 108...Mini Projector, PVO Portable Projector for Cartoon, Kids Gift, Outdoor Movie Projector, LED Pico Video Projector for Home ...

      ***

      Trying again with SPI SRAM

      This code seems solid to me but I can’t go beyond 100Hz…

      #include <SPI.h>
      #include <SRAM_23LC.h>
      
      // SPI bus can be SPI, SPI1 (if present), etc.
      #define SPI_PERIPHERAL    SPI
      #define CHIP_SELECT_PIN   8
      
      /* Device can be:
       * 128KB: SRAM_23LCV1024, SRAM_23LC1024, SRAM_23A1024
       * 64KB: SRAM_23LCV512, SRAM_23LC512, SRAM_23A512
       * 32KB: SRAM_23A256, SRAM_23K256
       * 8KB: SRAM_23A640, SRAM_23K640
       */
      SRAM_23LC SRAM(&SPI_PERIPHERAL, CHIP_SELECT_PIN, SRAM_23LCV1024);
      
      // Additional SRAM chips
      // SRAM_23LC SRAM1(&SPI_PERIPHERAL1, CHIP_SELECT_PIN1, SRAM_23LC512);
      
      #define START_ADDRESS   0
      
      //uint8_t buffer[BUFFER_SIZE];
      //#define BUFFER_SIZE  320
      
      char buffer[1000];
      #define BUFFER_SIZE  (sizeof(buffer) / sizeof(uint8_t))
      
      void setup(void)
      {
        pinMode(2, OUTPUT);
        /* Without parameters, begin() uses the default speed for this
         * library (12MHz for samd, 14MHz for sam, and 4MHz for avr).
         * Note that SPI transaction support is required.
         */
        SRAM.begin();
       // SRAM.begin(8000000UL);      // or specify speed
        //Serial.begin(9600);
      }
      
      void loop(void)
      {
        //while (!Serial); // Wait for serial monitor to connect
      
      // Record a series of values
        for (size_t i=0; i < BUFFER_SIZE; i++) {
          //buffer[i] = byte(digitalRead(A2));
              if ( (PINC & (1 << PINC2)) == (1 << PINC2) ) { //PC2 is what we're reading
              buffer[i] = 0xFF;// pin is high
              }
              else {
              buffer[i] = 0x00;// pin is low
              }
        }
      /*
        // Print buffer to serial monitor
        Serial.print("Write Block: ");
        for (size_t i=0; i < BUFFER_SIZE; i++) {
          Serial.print(buffer[i], DEC);
        }
        Serial.println();
        */
      
        // Write block
        if (!SRAM.writeBlock(START_ADDRESS, BUFFER_SIZE, buffer)) {
          //Serial.println("Write Block Failure");
        }
      
        // Clear buffer
        //memset(&buffer[0], 0, BUFFER_SIZE);
      
        // Read block
        //Serial.print("Read Block:  ");
        if (!SRAM.readBlock(START_ADDRESS, BUFFER_SIZE, buffer)) {
          //Serial.println("Read Block Failure");
        }
      
        // Print buffer to serial monitor
        for (size_t i=0; i < BUFFER_SIZE; i++) {
          //Serial.print(buffer[i], DEC);
         if(buffer[i] == 0x00){
           //digitalWrite(2, LOW);
            PORTD &= ~(1 << PD2);    // set pin 2 of Port D low
         }  
         else{
          // digitalWrite(2, HIGH);
            PORTD |= (1 << PD2);     // set pin 2 of Port D high
         }
        }
        //Serial.println();
        //delay(1000);
      }

      I just can’t manage to get the same level of control with an SPI device as with parallel SRAM and counters.

      ****

      New board time 😀 ! Test new things but keep the plug and play idea from last board and make even more so. SPEND MORE TIME SANITY CHECKING THE DESIGN and double checking pull ups and downs.

      Goals :

      • How about using a 16Mbit SRAM to store 128MBits of data with only a few added components ?! The plan is a pair of shift registers to convert serial output from a comparator into 8 bits to be sent all at once to the SRAM and the inverse on the output side. I would need the shift register clocks to be 8 times faster than the SRAM I think?
      • Make it so power (5V – with an LDO on board to eliminate possibility for accidents) can be shared with a double power jack. Reverse polarity protection.
      • A fine and coarse threshold selection !
      • proper VGA input and output good practices that will never harm anything sending video signals in or recieving signals sent out (blocking caps, 75 ohm impedence matching, Vref for top side of variable comparator bias!!, buffers for H and V sync!!, two cables close together!! and shielding – guard rings? – on the PCB for these traces!! different grounds for different parts of the VGA cable, use BNC connectors for RGB??)
      • Raspberry pi as input VGA through converter which plugs into a male VGA on the board, add an OFF/ON switch to safely turn off
      • pass thru VGA signal switch !!
      • Have different clock cans to switch between?
      • Extended Display Identification Data chip as a test?
      • Have audio out option ?

      Here’s the preliminary circuit :

      From https://www.cs.unca.edu/~bruce/Fall11/255/Labs/Lab13AnalogInput.html

      comparator

      From https://electronics.stackexchange.com/questions/144530/circuit-for-a-coarse-and-fine-setting-potentiometer

      ****

      As for PCB inspiration, perhaps something that evokes the computer video graphics card :

      Rectangular, with metal bracket (these are called Computer brackets in the Circuit Board Hardware – PCB category of Mouser.fr. They come in low profile or high profile), fan, heatsink, memory bank in a row, name of the board somewhere, PCI connector

      Palit Daytona NVIDIA GeForce2 MX 400 AGP 32MB VGA Vintage Retro Graphics Card | eBay

      16 MB PCI VGA Video Adapter Card - Our video cards and sound cards enable you to add smooth video performance or high-end audio capability to your PC computer, through a motherboard

      143 8MB Graphics Card, VGA PCI 8MB 32Bit Desktop Computer Accessories Multi- Display for ATI Rage XL Supporting All Motherboards with PCI Plug : Amazon.co.uk: Computers & Accessories

      ****

      Here is my first attempt at simulating the logic :

      The only tricky part was activating parallel load of the output register only once per 8 clock cylces.

      This design includes a giant FPGA to control enables all broken out along the PCI-E connector (that could be played like a piano with an alligator going to GND) in time and also to match the aesthetic of the video card. It would feature a stunning 128 Mbits (16MBits SRAM x 8) of memory. Would be cool to have LEDs next to each memory which light up when that block is recording.

      ***

      Building on the idea of making physical artefacts, Vivien Roussel had the idea of moving towards displaying the chassis of the computer along with these boards. There are loads of cool test bench computer cases that could be an inspiration for this :

       

      ****

      Or a DDR SDRAM module as inspiration :

      Diferenças entre as memórias DDR2, DDR3 e DDR4 | Crucial BR

      Here’s what it’s looking like in Eagle so far :

      I still like the idea of a kind of piano controlling :

      • 3x SRAM CS sel bits
      • 3x COUNTER EN + individual RST

      And of course :

      • Threshold control (fine and coarse this time !)
      • Clock DIV
      • REC/PB

      Here’s the board as ordered :

      CIRCUIT DIARY :

      • The fine and coarse pots appear to work very well.
      • The clock divider works as expected. Though it might be interesting to be able to play with different frequencies too as it seems to be important to get the combination of sampling versus pixel frequencies.
      • I checked and the serial clock is indeed being divided by 8 and inverted (albeit super noisy!) :
      • The Overflow and CS signals appear to be working well (though I had a hard time soldering the resistor networks!)
      • The !WR signal is as expected
      • Possibly should have put pull-downs for the SRAM I/0 pins ?
      • I should have made a timing diagram not just the simulation..
      • The option to manually advance the OVERFLOW signals is cool because you can solder one SRAM and test only it without having to wait for its turn to come around !
      • I have reached a problem : the 596 is not shifting the serial into parallel. I can see it trying (little mini signals getting through), but it is as if the I/O pins are being pulled low, but by what ? The SRAM ? I’ve confirmed we’re in write mode so they should be high Z. The Serial to Parallel converter somehow? But the I/O pins are inputs for this IC…Hmmm. Is it something to do with the open drain outputs (or the Schmitt trigger inputs?) on the 596 ? **EDIT : Had a 595 with the same footprint and substituted it, seems to be working now.
      • So, the output is very stripey. I have removed the 75ohm resistor to ground and shorted the 100ohm to the output RGB pins. I’ve tried shorting the blocking cap and it makes things a tiny bit easier to see but overall it is barely legible as an image. I think it has something to do with the output parallel to serial conversion. Here is a sample of a capture :

      • For some reason it can also work much better :

      • I don’t understand how it is possible to see the screen while it is recording as there is no “pass-thru” option…. There is also sometimes quite a difference from what is somehow passing through while in record mode and what plays during PB mode. *EDIT*There is a noise issue, when the VGA input is plugged in there is static that is entering somewhere. Trying to locate it currently. *EDIT* it is coming from the VGA IN. Tried connecting a bunch of GNDs (digital GND, chassis GND, color return RED) but still getting nowhere trying to eliminate the noise.
      • I ordered the wrong type of bracket, this one is for a double row VGA…
      • I had an issue with SRAM D but then replaced it and all is good.
      • I forgot to have a pre-amp before the comparator, this is essential otherwise you don’t get any in screen peaks and basically don’t see anything unless you’re really lucky. I used the Palimpsest.OS preamp and bias setup and it works fine.
      • The tiny trimpots need to be replaced with actual knobs !!
      • I forgot to add loop LEDs ! It would have also been cool to know which SRAM is currently recording.
      • EVERYTHING IS WORKING I CAN’T BELIEVE IT. The solution was to just test everything with a simple ramp using the function generator and look at the play back. Then I checked the pins on the SRAM and saw funky signals for several address pins that looked like they were several signals being added together. Clearly when I soldered in a bunch more SRAM and there were tiny connections between five different address pins and an I/O that I did not notice. This was causing chaos. I found the connections and fixed them, now it’s recording just as expected and it’s so glorious. NOTES : I am using the op amp from Palimpsest.OS AND crucially, am connecting both Analog and Digital Grounds between the two boards and the VGA IN with VGA OUT.

      ***

      While waiting for this board to arrive :

      • Try different function generated patterns of hitting count 0 enable while recording on the 16Mbit version
      • Test the 8 layer board ! *DONE*
        • Remove pin 9 GND and fix other issues with VGA in and OUT with VGA beakout PCB
        • Test on raspberry pi / computer IN *DONE*
        • Test feedback ! *DONE*
      • Try mini projector *DONE*
      • Try VGA capture device *DONE*
      • Put restart button on rasbpi

      *******************

      *EDIT* It works with rasbpi when I connect only a color, GND, H and V syncs !! So much more convenient than previously.

      • It might be cool to be able vary the mixing of the different channels
      • The output image is quite faintweak, I think I should rexamine the resistors I put at the termination and maybe switch to B+W.
      • It’s a bit muddy with several channels, would be cool to be able to filter that
      • I need a path to feedback with a pot !
      • Switched the output resistors to a 150ohm going to R,G,B and it works nicely.
      • Dip switch hard to use without tool.
      • Hard to see which direction switches are pointing in darker light

      More technical ERRATA :

      • Pin 1 of the 40MHz clock should be pulled HIGH to be enabled, not LOW as it is on the board !
      • The THRESHOLD pot isn’t connected to GND on one side…
      • I should have tied the CCLR (pin 10, which resets when pulled LOW) of the 590 to VCC with a 10K instead of leaving it floating connected to VSYNC. This made it malfunction.
      • BIG PROBLEM : I ordered multiplexers (unidirectional) instead of analog switches (bidirectional) ! The ‘157 multiplexers mess with SRAM B’s I/Os when it is in READ mode (when the SRAM is outputing values on the I/O pins). I ordered a replacement that is pin compatible, the 74LVC1G3157 (DATASHEET : https://www.ti.com/lit/ds/symlink/sn74lvc1g3157.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1679316452048&ref_url=https%253A%252F%252Fwww.mouser.de%252F). EDIT* The new components work perfectly – everything is good !
      • The Sync Clock IC is 3.3V not 5V…
      • Image jumps around of course. When I try to record with the Burst AND IC which outputs !CLK, no writing can occur. I’m guessing this gate messes with the timing somehow? As a workaround I’m trying to connect the 4 AND’ed V SYNC counts to either the address counter IC Reset or to another reset somewhere. 
      • Lots of noise on V SYNC, I should maybe have made the input and output VGA jacks right next to one another…
      • Pin 9 of the VGA D-SUB 15 is VCC for the Display ID EEPROM chip – but I have it connected to GND !! In general I need to respect the rules around the different grounds and return pins in the VGA protocol and not just connect them all together. Hoping to fix this in the next version.

      ***

      Thinking about making a board with a small 5″ screen / or a mini projector incorperated in it

      Fabien suggests using an LCD driver from a normal sized computer screen for a smaller screen (but he says it will will in 16:9 format). The other option he suggests is to use an FPGA to control a screen directly with LVDS.

      Artist Andreas Gysin’s LCD 1 from http://lcd.ertdfgcvb.xyz/

      ***

      Just realized that I could have an raspberry pi using VGA directly from I/0 pins : https://fr.pinout.xyz/pinout/dpi#

       

      ****

      Currently thinking the next PCB should be raspberry pi sending video through I/O (not through HDMI) and FPGA controlling SRAM. This would allow messing with the sequence of the memory recording / playback. Could make a compact version of this project (a kind of black box, deployable version) without many external components but with sililar functionality. I could test making the FPGA USB programmable board too. Of course it would also be programmable.

      To get here :

      • make the automated palimpsest.OS board
      • make the simple FPGA VGA board

      ****

      I am revisiting the SPI SRAM code with renewed patience, I actually wrote it first on paper !

      Here is my simple buffer filling code first off :

      const int buffer_size = 2000;  // Only 2K bytes of SRAM on the 328P
      byte buffer[buffer_size];
      void setup() {
        DDRC = 0b00000000;  // set PC2 as input pin
        DDRD = 0b00000100;  // set PD2 as output pin
      }
      
      void loop() {
      
        //  fill the buffer
        for (int i = 0; i < buffer_size; i++) {  // iterate through buffer
          for (byte mask = 0b00000001; mask > 0; mask <<= 1) {  // iterate through a bitmask
            if ((PINC & 0b0000100) == 0b00000100) {  // if input pin high...
              buffer[i] = (buffer[i] | mask);  // set this bit
            }
            else {
              buffer[i] = (buffer[i] & ~(mask));  // clear this bit
            }
          }
        }
        // read buffer back
        for (int i = 0; i < buffer_size; i++) {  // iterate through buffer
          for (byte mask = 0b00000001; mask > 0; mask <<= 1) {  // iterate through mask
            if ((buffer[i] & mask) == mask) {  // if bit in buffer is high...
              PORTD |= 0b00000100;  // ...write output pin high
            }
            else {
              PORTD &= ~(0b00000100);  // else, write output pin low
            }
          }
        }
      }
      
      
      

      Tried to see what this produce on the screen. I’m using a comparator from the previous board to do ADC and then feeding the ouput through a 470ohm resistor to red. It just leaves little comets which don’t appear to relate to the image…

      On the scope it doesn’t look far away though :

      Hmm…

      ****************

      After skimming from Nand2Tetris and checking out the VTECH Video Painter, I realized I could make a really basic screen interface :

      • I could record an image and then have an algorithm go through it and make changes (like photoshop effects like blur or polarize).
      • I could have sprites stored and then copy them into the screen-memory mapped SRAM
      • I could even have a joystick move from pixel to pixel and let you draw, copy and paste and draw circles !
      • It could also be a non uniform mapping from memory to the screen !

      ****************

      The VGA Capture is working and it’s fantastic ! I’m using OBS, I just clicked ADD SOURCE and then Video Capture device. It needs to be plugged in to USB A, not C, for the LED to come on.

       

      I tried the mini projector but it didn’t like the signals I was sending it. I think I have to avoid intelligent interfaces that can decide to send or not along my signals. Wondering about the camcorder CRT viewfinder and if I should head back in that direction.

      Here is a popular 4″ screen :

      Il Mini Monitor CRT da 4 pollici in bianco e nero supporta l'ingresso Video 12V4W tubo per immagini elettronico da 4 pollici - AliExpress

      Choreographable video synth (PART II)

      Inspired by the history of mounting circuits on wood with screws and wire, evoking John Cage’s prepared pianos. 

      This iteration of the project is born of a few realizations :

      1. VGA is an analog signal, it’s not digital.
      2. The coolest effects are the simplest ones : XOR, HP and LP filters, comparators, etc. which can be easily made with discrete components.
      3. A simple low frequency oscillator could control these effects with an LED > LDR voltage controlled resistor set-up. 
      4. Two out of sync oscillators easily make a pattern that doesn’t repeat often and is difficult to predict.

      Pedagogically and aesthetically I like this idea : it goes back to the basics of electronics, the history of electronics, and will show the materiality of video signals. This in contrast to the overly complex use of digital components to do basic modifications…

      The three basic circuits :

        

      *TESTED* This works (powered at 5V, with pull down 10K on input and the rest 1K and two BD135s) at lower frequencies (<100KHz) around 5V.  I added a 5K variable resistor (or an LDR + resistor) to increase the resistance of R1 and it varies the threshold !

      *TESTED* Works using BD136 PNP, 10K for the pull down and 1K for the base protection. General purpose diodes. I replaced the pull down with a 5K variable resistor and got some messed up results in the MHz. Doesn’t need power – just takes it from the input signal ! (But VGA signal would need to be amplified in order to work. 

      *TESTED* With a 104 cap and 5K variable resistor I can vary the filtering of a 5V function generator signal. 

       

      ****

      OK we’re back to the original plan ! Here is the Palimsest.OS Rev.2 :

      WHY ?

      • This board is for the inauguration of the IFT at the DVIC. It has to demonstrate technical proficiency on behalf of the staff, and must run independently as an tech-art installation, modifying video independently day in day out. 
      • Rev.1 didn’t work for microchip automation, this one should work smoothly(?). If everything works I could write code that executes different video manipulations. I also added selection LEDs to show the microchip doing its thing.
      • This tests the 8 channel 1-bit WRITE/REWRITE memory idea (but not the more elegant SRAM + buffer idea of Fabien), with certain effects tied to certain channels
      • Incorporates a simple to control XOR, comparator and HP/LP/BP filter into one board
      • Has a stable clock setup with metal can oscilloscope, a gen lock IC, and V SYNC burst setup
      • Tests an (expensive) programmable filter IC
      • It is a test for a simpler, inexpensive 8 ch recorder board that could be a cool kit
      • Does away with the ADC and all its pesky issues and trades it for a simpler 1bit “ADC”. 
      • I’m also using 5V SRAM to avoid the headache of different logic levels (though this means I only have 4MB of SRAM versus 16MB).
      • For testing purposes it can be soldered as a logic-controlled board or as a physical switch controlled-board. 
      • Instead of having lots of jumpers for wires, I went for rotary switches to make things more plug and play. However this also means that there are fewer experminental tests that are facilitated (like disabling a single counter while recording etc.) that were possible on rev.1
      • Can layer different recorded portions easily in theory
      • Battery switch and edge potentiometers instead of knobs
      • Theoretically could do feedback and echo tests with this board as it can read and write from and into memory simoultaneously 
      • This board should be easier to work with than the previous stacked PCI-E motherboard design which made knob turning and mounting hard. 
      • Instead of using generic octal logic ICs I went for more specific purpose ones.
      • The board will be made at https://aisler.net/ instead of JLCPCB so I’m hoping for a really nice finish

      Here’s a possible minimal installation with it :

      ****

      PALIMPSEST.OS v2 ERRATA :

      • Pin 1 of the 40MHz clock should be pulled HIGH to be enabled, not LOW as it is on the board !
      • The THRESHOLD pot isn’t connected to GND on one side…
      • I should have tied the CCLR (pin 10, which resets when pulled LOW) of the 590 to VCC with a 10K instead of leaving it floating connected to VSYNC. This made it malfunction.
      • BIG PROBLEM : I ordered multiplexers (unidirectional) instead of analog switches (bidirectional) ! The ‘157 multiplexers mess with SRAM B’s I/Os when it is in READ mode (when the SRAM is outputing values on the I/O pins). I ordered a replacement that is pin compatible, the 74LVC1G3157 (DATASHEET : https://www.ti.com/lit/ds/symlink/sn74lvc1g3157.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1679316452048&ref_url=https%253A%252F%252Fwww.mouser.de%252F). EDIT* The new components work perfectly – everything is good !
      • The Sync Clock IC is 3.3V not 5V…
      • Image jumps around of course. When I try to record with the Burst AND IC which outputs !CLK, no writing can occur. I’m guessing this gate messes with the timing somehow? As a workaround I’m trying to connect the 4 AND’ed V SYNC counts to either the address counter IC Reset or to another reset somewhere. 
      • Lots of noise on V SYNC, I should maybe have made the input and output VGA jacks right next to one another…
      • Pin 9 of the VGA D-SUB 15 is VCC for the Display ID EEPROM chip – but I have it connected to GND !! In general I need to respect the rules around the different grounds and return pins in the VGA protocol and not just connect them all together. Hoping to fix this in the next version.

      More Meta errata :

      • It’s a bit muddy with several channels, would be cool to be able to filter that
      • I need a path to feedback with a pot !
      • Switched the output resistors to a 150ohm going to R,G,B and it works nicely.
      • Dip switch hard to use without tool.
      • Hard to see which direction switches are pointing in darker light

      Oy vey ! I realized that I could just use an I2C RAM IC and save about a billion components. Here are the more plug and play simple boards I made in the aftermath :

      Good news: The 328 works. 

      ERRATA : I fried the logic chips with 5V :(. Resoldered new ones and looks good.  I think I need to be feeding in a signal that has already been digitized (like by a comparator). Otherwise I need to mess around with knobs to get the signal right around the threshold of the logic gates with bias setup. 

      Good news: The 328 works. And I can see the effect of selecting different filters. I’m not sure why but it works best when the signal is coming in on A and I’m taking the output on B (or vice versa). I’ve also got a cap going from the output of the filter board before amplifying it again.

      ERRATA: I ordered the wrong length of bussed resistor network. 

      Good news: The 328 works. 

      errata ^ Wrong footprint (and value!) for the digital potentiometer here… Will have to wait for the correct part to test this.

      Good news: The 328 works and at least it produces an image !

      errata ^ H SYNC and V SYNC plugged in to wrong pins on Arduino (should be PD3 and PB1 respectively).

      Good news: The 328 works.

      ERRATA : the PD pin (which enables ADC IOs when low) is pulled HIGH ! I resoldered it and now it’s working great. 

      I chose a non PWM pin on the atmega to be the voltage control pin of the VCO… I soldered it to the nextdoor pin which can do PWM and all is good. 

       

      ERRATA: I tried two chips, they both come up as unidentified FRAM when I have them speak on the serial running the demo codes…I should probably pull Write Protect (WP) HIGH just in case. Still not working…I will try replacing with a 23LC1024 serial SPI SRAM as it has the same pinout. 

      ***EDIT: Just saw this on the Adafruit website : “For the 4Mbit version, you should change this to: fram.begin(3)”…Still doesn’t work though.

      I replaced with the 23LC1024 and am using the SRAMsimple libary which appears to work well. For some reason the digitalRead I’m doing in the setup is always returning 0 even if connected to 5V.

      #include <SRAMsimple.h>
      #defineCSPIN8       // Default Chip Select Line for Uno (change as needed)
      SRAMsimple sram;       //initialize an instance of this class
      /*******  Set up code to define variables and start the SCI and SPI serial interfaces  *****/
      voidsetup()
      {
      uint32_t address = 0;                       // create a 32 bit variable to hold the address (uint32_t=long)
      Serial.begin(9600);                         // set communication speed for the serial monitor
      SPI.begin();                                // start communicating with the memory chip
        // And now the fun begins:
      /**********Write a Single Byte *******************/
      bool data = digitalRead(A0);                        // initialize the data
      for(int i = 0; i <=5; i++){                 // Let's write 5 individual bytes to memory
          address = i;                              // use the loop counter as the address
      sram.WriteByte(address, byte(data));            // now write the data to that address
          data+=2;                                  // increment the data by 2
      }
      /********* Read a single Byte *********************/
      Serial.println("Reading each data byte individually: ");
        byte value;                                 // create variable to hold the data value read
      for(int i = 0; i <=5; i++){                 // start at memory location 0 and end at 5
          address = i;                              // use the loop counter as the memory address
          value = sram.ReadByte(address);           // reads a byte of data at that memory location
      Serial.println(value);                    // Let's see what we got
      }
      }

      Not sure why…

      *EDIT: Got the thing working with Fabien’s help !  

      
      #include <SPI.h>
      #include <SRAM_23LC.h>
      #define SPI_PERIPHERAL SPI
      #define CHIP_SELECT_PIN 8
      
      SRAM_23LC SRAM(&SPI_PERIPHERAL, CHIP_SELECT_PIN, SRAM_23LC1024);
      
      #define START_ADDRESS 0
      
      void setup(void)
      {
      pinMode(2, OUTPUT); // where we write the recorded data to.
      pinMode(A2, INPUT);
      SRAM.begin();
      }
      
      void loop(void)
      {
      for(unsigned long i=0; i<128000; i++)
      {
      byte val;
      if ( (PINC & (1 << PINC2)) == (1 << PINC2) ) {
      val = 255; // pin is high
      }
      else {
      val = 0; // pin is low
      }
      SRAM.writeByte(START_ADDRESS + i, val);
      }
      
      delayMicroseconds(1);
      
      for(unsigned long i=0; i<128000; i++)
      {
      byte ch = SRAM.readByte(START_ADDRESS + i);
      PORTD = ch;
      }
      
      delayMicroseconds(1);
      }

      However in this byte write/read mode the fastest I can get it going is 10KHz…The next step is to use this SPI tutorial (https://docs.arduino.cc/tutorials/generic/introduction-to-the-serial-peripheral-interface) and the SRAM datasheet ( https://ww1.microchip.com/downloads/aemDocuments/documents/MPD/ProductDocuments/DataSheets/23A1024-23LC1024-1-Mbit-SPI-Serial-SRAM-with-SDI-and-SQI-Interface-DS20005142.pdf) to operate in “sequential operation” mode where pages are ignored and its all just one big array. 

      I have tried to do this but haven’t yet succeeded. I can successfully write and read from the memory with the code below..:

      #define DATAOUT 11//MOSI
      #define DATAIN 12//MISO
      #define SPICLOCK 13//sck
      #define CHIPSELECT 8//ss
      
      //opcodes
      #define READ 0x03 // read data
      #define WRITE 0x02 // write data
      #define WRMR 0x01 // write to the Mode Register
      
      //modes
      #define SEQUENTIAL 0x20 // 0100000 in Binary
      
      byte clr;
      
      byte address0 = 0;
      byte address1 = 0;
      byte address2 = 0;
      
      byte data_to_write = 0;
      byte data_to_read = 0;
      
      char spi_transfer(volatile byte data)
      {
      SPDR = data; // Start the transmission
      while (!(SPSR & (1<<SPIF))) // Wait the end of the transmission
      {
      };
      return SPDR; // return the received byte
      }
      
      void setup(void)
      {
      
      pinMode(DATAOUT, OUTPUT);
      pinMode(DATAIN, INPUT);
      pinMode(SPICLOCK,OUTPUT);
      pinMode(CHIPSELECT,OUTPUT);
      digitalWrite(CHIPSELECT,HIGH); //disable device
      pinMode(2, OUTPUT); // where we write the recorded data to.
      //pinMode(10, OUTPUT); // LED
      pinMode(A2, INPUT); // where we read the data from
      // SPCR = 01010000
      //interrupt disabled,spi enabled,msb 1st,controller,clk low when idle,
      //sample on leading edge of clk,system clock/4 rate (fastest)
      SPCR = (1<<SPE)|(1<<MSTR);
      clr=SPSR;
      clr=SPDR;
      
      digitalWrite(CHIPSELECT,LOW);
      spi_transfer(WRMR); // access the Mode Register
      spi_transfer(SEQUENTIAL);//enter Sequential Mode
      digitalWrite(CHIPSELECT,HIGH); //disable device
      }
      void loop(void)
      {
      //WRITE
      digitalWrite(CHIPSELECT,LOW);
      spi_transfer(WRITE);
      spi_transfer(address0);
      spi_transfer(address1);
      spi_transfer(address2);
      
      for(unsigned long i=0; i<128000; i++) // address is automatically incremented internally in the SRAM in this mode
      {
      if ( (PINC & (1 << PINC2)) == (1 << PINC2) ) {
      data_to_write = 255; // pin is high
      } 
      else {
      data_to_write = 0; // pin is low
      }
      spi_transfer(data_to_write);
      }
      digitalWrite(CHIPSELECT,HIGH);
      
      //READ
      digitalWrite(CHIPSELECT,LOW);
      spi_transfer(READ); //transmit read opcode
      spi_transfer(address0);
      spi_transfer(address1);
      spi_transfer(address2); // 24 bit address in three parts (this could be a problem)
      
      for(unsigned long i=0; i<128000; i++)
      {
      data_to_read = spi_transfer(0xFF); //get data byte
      PORTD = data_to_read;
      }
      digitalWrite(CHIPSELECT,HIGH); //release chip, signal end transfer
      }

      …but when I do this in chunks with loops it doesn’t appear to work. I’m not sure if it’s because I’m reading the value of the pin during the writing and if I should perhaps do this before with a buffer array and then just read from it ?

      OVERALL ERRATA : I also need some kind of power board that distributes 5V and 3.3V 

      ***

      On the back of the boards I tried an experiment using the bmp import ULP in Eagle. Not sure what the results will look like :

         An

      **** 

      I also starting thinking that I should probably look at more existing DIY and pro analog video synth circuits…

      A collection of great video synthesizer schematics resources : https://scanlines.xyz/t/diy-resources-file-system/224

      Here are some of the ICs (that are still being manufactured) that I haven’t work with before that appear in several designs :

      • Resettable and retriggerable Monostable Multivibrators : https://assets.nexperia.com/documents/data-sheet/74HC4538.pdf
      • High speed diff comparators : https://www.ti.com/lit/ds/snosbj5c/snosbj5c.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1676232730602&ref_url=https%253A%252F%252Fwww.mouser.fr%252F
      • Video Fader IC: https://www.analog.com/media/en/technical-documentation/data-sheets/12516fa.pdf
      • Graphic EQ display filter : https://www.sparkfun.com/products/10468

      To divide a signal into 8 different bands (if you skip the MUX at the end) :

      Here’s a simple implementation with a 4051 analog mux at the end :

      This worked after I sorted out that the Atmega 328 reset was pulled LOW all the time – so it programmed fine but never did anything after that. I also removed the op amps which were supposed to follow the selected thresholds, I connected the top and bottom directly to the pots in the end. I tried slowly switching between bands at half a second and also faster at 50ms. Both as expected.

      And I’m revisiting all kinds of combined capacitor + switch circuits which move tiny charges around based on signals  :

      • sample and hold 
      • bucket brigade
      • comb filter 
      • switched capacitor circuit

       I have made this bucket brigade “delay” with 50 microchip tunable (variable) 100-200pF capacitors :

      I am now curious about some other capacitor switching circuits like this Comb Filter : https://en.wikipedia.org/wiki/Comb_filter which somehow “provides response” at different multiples of 1KHz depending on 3 bit code. All the caps are the same value.

      Here is another circuit design for the same function :

      Block diagram of comb filter including the CMOS multiplexers and the... | Download Scientific Diagram

      ****

      For the FPGA board, I’ve ordered an iCE40HX1K development board and am currently planning to test some basic VGA signal generation. The next steps could involve basing a design off of the OLIMEX dev board design (https://github.com/OLIMEX/iCE40HX1K-EVB/blob/master/ICE40-1KEVB_Rev_A.pdf) which already has SRAM and VGA OUT along with code on the internet (which I would follow closely for the first iteration). I would alter the board to be able to program directly via USB using the components from the Ice40 stick ( https://www.mouser.fr/new/lattice-semiconductor/lattice-icestick-kit/), like a fancy new FTDI chip, so I could stay within the Lattice software for my first steps. 

      Compared to the VGAX, the FPGA could generate signals much faster (being clocked with a 100MHz crystal versus the 16MHz of the Atmega), it would also be producing signals based on a a different coding paradigm (I have no idea what that would mean in terms of images), and because it has around 90 I/O it could also work with super deep colors using only a R2R ladder. The possibility of using an SDRAM is so cool but it seems hard to implement, with SRAM being much simpler to interface. The OLIMEX has only 512KB of SRAM, so I couldn’t really record a lot with it. 

      ****

      Here is my process for the FPGA programming so far :

      1. I signed up for an account at www.latticesemi.com.
      2. I got a licence for iCEcube2, put it somewhere easy to find and took note of the directory, and downloaded iCEcube2 and the Lattice Diamond software from here : https://www.latticesemi.com/en/Products/DesignSoftwareAndIP
      3. I plugged in the iCEstick and it blinked in a circular pattern. I also checked that it had a port under Device Manager (it had 2!). 
      4. I went into iCEcube2 then to Help > Tutorial to open a pdf called “Implementing the Design” and followed the following steps :
        1. Start new project (iCE40, HX1K, ?TQ144?)
        2. Add Design file iCElab.v and Constraint file iCElab.sdc and checked the Device Info.
        3. Run Synplify Pro Synthesis
        4. Select Implementation
        5. Add constraint file iCElab.pcf under Import P&R Input Files.
        6. Run Placer
        7. Check the Floorplan, package view, timing analysis, power estimation
        8. Generate the bitmap (.bit and .hex files) which is saved wherever the project was saved. (For instance C:\Users\Jonah\Desktop\FPGA LATTICE DEMO PROJECTS\quick_start\quick_start\quick_start_Implmnt\sbt\outputs\bitmap).
        9. Here is what everything should look like once finished :
      5. I then transitioned to Youtube for a detailed explaination of using Diamond Programmer with the iCEstick here : https://www.youtube.com/watch?v=Df9k1T0bHmA&ab_channel=Dom
        1. Launch Diamond Programmer and take the default options (or click scan and take those)
        2. It will mistake the board for a JTAG interface and give the following errors : “Failed to scan board”, “Scan Failed – Creating Blank Programmer Project.”
        3. Now change the Device Family to iCE40, the device to iCE40HX1K, and under Program enter the following information :
          1. Change Access mode to SPI Flash Programming and now some other options appear. Select > Vendor: Micron, Device: N25Q032, Package:  8-pin VDFPN8
          2. Now load the bitmap (.bin) file and check that it has a non-zero datasize :
          3. Click OK and then click the green traffic light on the next to top row of icons. You should then see a completion message and have the program on your board :
          4. Presto !

      Just for info :

      For the actual pinout of the ice40 (which you can change in Pin Constraints Editor):

      Generating your own .v file, you can open Notepad++ and paste :

      module AND( input A, input B, output Y ); assign Y = A&B; endmodule

      ***

       

      Here’s what ended up getting presented :

      Here is the text I made to go with it :

      La préparation d’un piano est l’action expérimentale d’ouvrir le couvercle du piano et de placer des objets (des vis, par exemple) entre les cordes. Ces « préparations » modifient la vibration des cordes et peuvent provoquer de nouveaux sons, parfois des sons de batterie plus du piano. Post-préparation, le pianiste peut composer une partition qui exploite les nouvelles modalités soniques découvertes, entrant en dialogue créatif avec l’instrument. L’acte d’intervenir et de détourner le fonctionnement du piano encourage le pianiste à comprendre le fonctionnement de leur instrument d’une manière concrète et recadre l’instrument en tant que site d’expérimentation créative, menant souvent à des découvertes inopinées, bien que fugaces, dans un processus artistique ouvert et indéterminé.
      Ce projet représente une tentative (ambitieuse) de fabriquer, et ensuite de « jouer » avec, un type d’écran préparé. Les signaux électroniques qui passent entre le laptop et l’écran ici sont comparables aux vibrations qui voyagent dans les cordes du ventre du piano. De la même manière qu’une vis peut modifier le son généré par l’activation d’une touche du piano, nous pouvons atténuer et amplifier sélectivement les voies de signaux électroniques pour produire de nouveaux stimuli (en forme d’images cette fois).
      Notre quête pour répertorier des préparations vidéo possibles, et pour documenter leur fonctionnement, nous a mené à la rencontre de filtres artisanaux montés sur bois, qui évoquent les radios fait maison des années 1950s, ainsi que les technologies moins abordables et plus sophistiquées comme les FPGA (des circuits intégrés de logiques programmables). Ces prototypes sont fixés dans l’arrière-plan et ont vocation de rappeler le caractère expérimental et ludique du projet de recherche.
      En clin d’œil au monde de la synthèse modulaire audio, qui prend souvent une esthétique punk, nous avons fixé un enchainement de modules de préparation vidéo au premier plan. En concaténant des modules, tel que les filtres à capacités commutées, des mémoires vives statiques, des convertisseurs analogique-numérique, des portes logiques combinatoires, et encore d’autres, nous pouvons délaminer les flux vidéo, créer de nouvelles voies électroniques, tordre, caresser et déformer des signaux, les synthétiser, et finalement les tresser ensemble, avec plus ou moins de finesse, et observer les résultats en temps-réel.
                                                                                                  ***
      A prepared piano is the result of opening up a piano and placing objects (often bolts and screws) between the piano strings. “Preparations” modify the sound of a vibrating piano string to create new sounds, sometimes more resembling a drum than a traditional piano. After a piano has been prepared, a score is developed to explore the new qualities of the instrument. Instrument preparation invites a musician to engage with and understand the inner working of their instrument, literally getting their hands inside the body of the instrument. Preparation reframes the instrument itself as a site of creative experimentation, often leading to serendipitous discovery in an exhilarating, and at times painfully anxious making (!), open-ended design process.
      This work is an attempt at making a kind of prepared computer monitor. The electrical signals passing between the laptop and the monitor are analogous to the vibrations inside the strings within the belly of the piano. Just like a bolt can create a new type of sound in the prepared piano, we can similarly dampen and accentuate the video signal pathways of the prepared computer monitor to create new types of image transformations that play out in time for our eyes.
      Our journey to explore a range of possible preparations and document their effects has taken us to different eras of technology, from simple, artisanal high pass filters mounted on nails in wood, to more sophisticated technologies like the Field Programmable Gate Array (FPGA). These exploratory prototypes are visible on the backplane and emphasize the importance of the process in this artistic research project.
      In a nod to the world of modular audio synthesis, we are displaying one of many different possible concatenations, or “patches” as they are known, of our video preparation modules in the foreground. By linking together various microchip-controlled switched capacitor filters, static RAM delay modules, Analog to Digital converters, combinatorial logic boards, and other circuits, we can playfully delaminate a video signal, open up different signal pathways, bend and warp signals, synthesize new ones altogether, and finally braid these all together on screen and observe the results.
      ***

      My takeaways :

      Do something simple, and humble, but try to do it well. Trying to do too many things, or a “universal” thing, or a multi-functional reprogrammable thing is really ambitious. This has happened to me before :

      • At the Barilla Hackathon (the team that did the best made a simple lunch surface, it was simple but really well made and designed). 
      • With my solar robot project (the one that was supposed to run on solar energy and no battery, interact with other robots, and possibly build a structure)
      • With my original solar sunflower design that was supposed to look super intricate.

      Making a machine that works for a demo is the highest level of challenge. It requires you to master the functining of the machine to a high degree to be able to fix it and adapt in changing conditions. It you do something simple, you have a higher chance of being able to do this.

      It would have been important to realize that the place this was to be shown is a corporate environment, and that my interest in the bodge / bricolage would never have found its place here. The corporate mentality is shiny, new, pro-tech, and “impressive”. I should have understood this before taking the project. 

      The simple, distilled, purified, humble and clear idea from this project was the wooden-mounted electronic circuits. This would have been in line with my “design research” interests, my aesthetic.

      If I were to make a complex machine like the one I had in mind for this expo that would have worked, I think I would have to change the following things :

      • Generate my own VGA signals. For plug and play things, generating this in software seems so much easier because you are the one making it and you have control over what is being produced. 
      • Figure out, once and for all, the story around shared grounds and blocking caps, and bias pots, and voltage in and voltage out levels, for different modules. Also, 75ohm resistors and small caps before VGA out ? Should I just get a VGA driver chip that works? (EDIT: Here is an explaination of how to calculate the output impedence for VGA cables : https://hackaday.io/project/18682-pic-graphics-demo/log/49786-vga-interface)
      • I would need to figure out what makes the VGA monitor turn off (is it sending something it doesn’t consider a video, or a voltage level that is too high?)

      I think the alternative, which would be showing pre-recorded videos, is uninteresting for a performance. Another key ingredient I think is having designers / artists to help me review the project regularly (like in design school). It should also be really clear to me if it is my design project or a commission (in which case I’m an engineer I guess?). I should also get clear right from the begining next time what are the conditions of the final thing – where will it be, what are the goals of the person commissioning the work, etc.). I should probably only work with people who share my interests and aesthetic ! 

      I think I have to acknowledge that I have an extremely constrained working process that is highly method driven. I am not focused so much on the end product directly. My friend suggested I was over constraining myself. Basically there is a confusion between whether I’m using technology to stumble upon unexpected things, or whether I’m using it to make something that works. To work efficiently on things would work well would require time developing the things that must work well, and an organized, systematic working practice. I MUST ORGANIZE MY ELECTRONIC PARTS to make this process more efficient.

      Also : all the intense pressure and hardcore work streaks I put in DID NOT HELP THE FINAL PROJECT – I can’t even think straight or debug basic circuits right now :(. It would have been better to perhaps start working on the final thing starting on day 1, with the EASIEST, SIMPLEST, AND MOST LIKELY TO WORK PAINLESSLY solutions. 

      Another key question is what is the output, and who is my audience ? I need a way of mesuring my output and comparing it. 

      • Am I making art for people who consume art ? (then I have to move towards very simple projects and focus on their EXECUTION while photographing everything super well for my PORTFOLIO)
      • Am I making “design research” papers/talks for academics and people who consume this ? (then I need to WRITE about my research)
      • Am I making open-sourced electronics kits for hobbyists ? (Then I need to spend time ENGINEERING working kits that people would be interested in making)
      • Am I making workshops for designers ? (Then I need to come up with simple exercises for workshops?)

      And am I doing several of these things simoultaneously ? I think doing any one of them well first could take a lifetime already ! My friend suggested projecting five years in the future to where I would like to be and building backwards from there. I think I would most like to be a teacher who does art/design research in the form of expos, articles and classes (a bit like Andrew Witt at the GSD).

      ********

      Where I would like to go from here :

      1. Test my basic FPGA VGA board. What can it do with video ? Possibly make another pro version (USB programmale, nicely laid out, SRAM). FPGAs feel like the future because they are good a processing video in parallel, and because coding them is essentially like building 74 series logic circuits but without all the soldering. It could also lead to some cool machine learning experiments later on..
      2. Test my automatable SRAM 4MB board. What kind of automatic looping can it make?
      3. Try to get the SPI SRAM board functioning (it is so simple it would be perfect for a video sampling workshop). It would be cool to see what kinds of images it produces. It works at 20MHz, it would be perfect !
      4. Get Spaghettini working with aluminum board (just avoid going through-hole) – this is the simplest way to generate cool simple memory based video !
      5. Have another stab at presenting my work as an art installation at my show end of march at La Générale. 
      6. Try the bucket brigade and digitally controlled pot boards

      *******

        After my colleague Fabien told me about EDID EEPROMs, I am thinking it could be possible to mess with the memory of this EEPROM that communicates with the computer sending VGA signals and create some interesting glitches this way : https://en.wikipedia.org/wiki/Extended_Display_Identification_Data

        Also check out these types of RAM : 

        • https://en.wikipedia.org/wiki/Dual-ported_RAM
        • https://en.wikipedia.org/wiki/GDDR_SDRAM

        And this keeps coming up when looking into controlling screen rows and columns directly : https://en.wikipedia.org/wiki/Low-voltage_differential_signaling

        Check out no input mixing and electro-acoustic musique concrete, and machine exorcism. 

        Check out universal LCD driver IC RTD2556 on boards like this :

        ***

        It looks like I could build my own HMDI to VGA converter with two AD ICs : https://www.analog.com/en/analog-dialogue/articles/hdmi-made-easy.html

        I could combine this with a new board that uses a bunch of SPI SRAM memory chips controlled by arduino to approximate the 7 channel 4MB recording and mixing board I made. 

        I could also put a rasbpi zero with just power, an SD card containing videos and an HDMI mini out. For the Raspberry Pi I made the HDMI hot pluggable (hdmi_force_hotplug=1) and put a bunch of low res mp4 videos in the boot SD card which are accessible.

        Choreographable Video Synth

         This circuit is a next version from the video synth. 

        Some ideas from how this installation could look like :

        Messing around with my last synths, here are some circuits that I want to be able to reproduce with my new synth :

           

        Here are some ideas of how the circuit could take form :

        A Nintendo gameboy catridge teardown :

        Some sketches of possible configurations :

        *******

        I am currently splitting the project into two parts : 

        PART 1 : A series of independent transforming modules, VGA IN/OUT with screens, and oscillators with patch cables.

        PART 2: A board called palímpsêst.OS which is a rack (or archive) of several 16MB memory modules. 

        ***********

        Palimpsest.OS

        Each memory will share the same power (5V, 3.3V, GND), timing CLK signals, control signals (to enable/disable counters, buffers, RAM, ADC etc. and to RESET things). All this can be automatically controlled by an microchip which can turn on/off these memory banks individually in time. Despite this there will be basically all the components on the board to make it function independently and, most importantly, test and debug it’s functioning. 

        Just like for the previous memory board, the following parameters will be modifiable :

        • the duration of the recording and playback,
        • the sequencing of addressing this memory,
        • the amplification of the incoming and outgoing video data,
        • the sampling rate and sampling pattern of video recording and playback,
        • the bit resolution of the recording and playback,
        • the continuity of the above processes or the frequency of their interruption

        I have corrected the errata from the previous 16MB memory board, removed jumpers (as this board will be made by JLC PCB), added a ground plane, made it more compact (by removing the burst record functionality, level shifting, and having two layers). I also added a battery so that memory is persistent as long as the battery has juice. The main work however has been breaking out pins to a bus which can be controlled by a microcontroller. There is now the possibility to have 3 memory boards A,B and C, and to control their recording and playback, and all the parameters of these two things that can easily be controlled, in time. 

        With these boards it should be possible to take an input, record it, then play it back and mix it with the input through a feedback pot. Because this can be done by three memories, they can also record their respective outputs and layer without any mixing with the VGA . 

        *****

        01/12/2022 UPDATE :

        So far debugging: For some reason the ADC is not spitting out anything, regardless of what I feed it. I changed the input op amp and it had no effect. Previously to soldering the ADC everything seemed to be working…As far as I can tell the memory is working and all the bias pots too.

        ERRATA :

        -The direction pin of the 245 buffer connecting to the R2 ladder is wrong – it should be HIGH instead of LOW.  To temporarily correct this involves jumping the DIR pin to VCC – AND CRUCIALLY – to ALSO disconnect this pin from GND unless you will have a short.****

        -Why on earth did I connect all the pins without dip switches (ADC EN, 1/0 0-7, etc.) from the memory boards together ? This makes it impossible to record on one board and not on the others. I cut the non-DIP’d traces and it seems to work. 

        -the LED needs a current limiting resistor or it just explodes.

        -one switch for master record (WR EN and ADC EN)?

        -I should follow the ADC1175 instructions and isolate the DGND and AGND with caps. 

        -Should have an input pin and 5V pin for testing on board.

        -I should add the right footprint for a big capacitor at the input of the ADC (I forgot and put a 0.1uF which didn’t work).

        -It would be super cool to have a coarse and fine knob (and or a super precise pot) for the VCO for instance on the base board. 

        -I should take the classic gameboy battery footprint.

        -There isn’t an extra pin so I can send V SYNC both to be frequency divided and pass it on to the VGA out. 

        -The populated side of the memory PCB is facing “backwards” with regards to the base board…

        -The LDO pinout on the base board is wrong

        -I can’t plug in standard jumper headers to the PCIe breakout pins because they are a different diameter

        -I tried hard syncing the VGA’s H Sync with the 4046 ( see text in description of video here : https://www.youtube.com/watch?v=S694YY7sJMw&ab_channel=drumasaurusrex ). It only works with the V Sync but that’s too slow. I can’t get a stable signal out, it looks super wobly at high frequencies.  

        *****

        TESTING THE PALIMSEST.OS

        • Not sure why but it seems like I can’t record only on one board when multiple boards are connected. I have to disconnect all but the board I want to record on to get a recording onto it. *EDIT* Because I connected all the non-dipped pins from the boards together via the PCI-E bus…
        • It would be super cool to be able to send different clock signals to each board. I should maybe not have made the same CLK hardwired to each board. 
        • It’s not possible to turn the knobs when then three boards are plugged in at once because the pot shafts are too long. 
        • Not sure exactly how the battery is working, it seems like it can “remember” something for a few minutes at least. 
        • It’s hard to palimpsest various things together – You have to get one board all calibrated, then send it something from another calibrated board, record it, then rewrite to play it back. (I am hoping the new 8 channel 1 bit device makes things easier). 
        • I can’t automatically have things read or write currently, not sure why it’s not working…
        • I can’t echo with this board because I can’t simoultaneously read and write. That said I can mix a recording with live channel and then record that together into a memory and begin layering. 
        • I would like to have the image stay stable instead of always jumping everywhere. 
        • Dephasing the ADC and RAM clocks makesa large variety of nice patterns on the output !
        • It would be nice to be able to count up and down and do that to 4 bits in the middle of memory !
        • remember to connect GNDs if using memory board with analog f(x) for example
        • Trying to hard sync with h sync and cd4046 based on these links (https://www.renesas.com/us/en/document/oth/tb476regenerating-hsync-corrupted-sog-or-csync-during-vsync and https://www.youtube.com/watch?v=5VymS65eefo&ab_channel=drumasaurusrex)… I think the solution might be an oscillator in a can (with 4 not 2 leads), which I can then divide. (I can still have a free style VCO with knob to control the sampling of the ADC though !)
        • Works great with a four lead crystal oscillator can and the f divider – a very clean image that doesn’t warp ! You can still use the knob controlled high speed VCO to trigger the ADC and it’s a nice combo. 
        • *****WOW – feeding in a quartz can 10MHz into a frequency divider (and sending diff divisions to the SRAM clock), and hooking up V Sync directly to the reset, produces a really stable image that doesn’t slide left or right !!!****
        • ***ABOVE ALSO WORKS WITH H SYNC AT LOWER FREQUENCIES – SOOO COOOL : D !!
        • I’m not able to use MOSFETs or transistors to reliably automate the switch from REC to PB for some reason. I’ve even tried using an optocoupler to isolate the arduino and also tried using and AND gate as a buffer with the WR signal but neither work. I think should use mechanical relays to get around this problem or just avoid it all together by having Arduino send the CLK and WR signals in the next version. 
        • Tying differnt oscillator cans (10MHz, 12MHz, and 40MHz) 12 is nice because it doesn’t jump too much on my screen resolution (800×600). At 1280×720, 10MHz is nicest. 
        • I did a proof of concept for the 8 track recording and rerecodording idea, it seems to work though there may be a bit of interference from neighbouring channels on one another ? Not sure how I would try to isolate them from one another but some bleeding might actually be cool ?
        • I tried doing a feedback recording (recording a mix of a play back and a live feed) and had no success. Not sure why this isn’t working.

        ***********

        Realizing that this whole massive multi-module thing could be made with just one memory module having feedback !!! That or a simple echo IC like this one I ordered (BU9253 : https://www.mouser.fr/datasheet/2/348/rohm_semiconductor_rohms16891-1-1742621.pdf ) :

        That said the delay is only 8Kbits and it’s designed for audio.

        *****

        Analog Functions Board :

        Trying to remake the previous analog functions board but at JLC and as pro-style as I can. The general idea is to make a bunch of op-amp circuits that modify video in various ways and that can be combined with the logic modification board to make a completely full suite of electronics video transformations. Not sure yet how they could be automated.

        Fixes from previous esceptionally newbie laser engraved version :

        • +15/-15V supply for all the op-amps using the handy ICL7660 negative voltage generating IC will hopefully solve many issues.
        • Using normal general purpose op amps in addition to super fast video ones 
        • Having bias pots at the inputs and outputs to sort out any different in offsets. 
        • Doing more research to get better circuit designs, notably looking at the Analog Thing, The Hackaday Logic Noise Series, Rod Elliott’s active op-amp series.
        • Making things less noisy with : seperated AGND and DGND, caps near op amps, following suggested board layouts, 2 sided board with GND plane (!)
        • Tying to take into account impedence matching with 75ohm VGA camble at the input and output and testing a specialized video line driver IC
        • Making op amp board with the possibility of automating thier switching / pot levels ?
        • Adding over-amped fuz circuit, phase-shifter, and various filters (LP, HP, BP, Notch), and adding a reset to the integrator circuit. 

        I think the theme of this board, especially seeing as I have 25 of these TL074 op amps, is showing the diversity of flavours (and range of sometimes exotic circuits) of the history of op amp filters, arithmatic, etc. circuits. I plan on highlighting the names (and dates?) somehow.

        The idea has clarified now, it will be a grid of 24 different op amp circuits in a 6 by 4 grid with the name of each circuit and a letter/number identifier. At the top will be all the power, VGA in out and bias amps.

        I collected the circuits from various places (AoE among them) but especially from Rod Elliott’s write up on active filters (which you can access with Internet Archive) : http://sound.westhost.com/articles/active-filters.htm

        I am most excited about the combination of digital and analog – like in the switched cap filter, sample and hold, resettable integrator, and demux amp circuits. Being able to modify the functioning of am amp with digital signals (even with a tunable capacity and voltage controlled potentiometer) seems to offer really cool potential. This being combined with SRAM could be a really cool composite project.

        ERRATA :

        • VCCIO and GND directly overlapping on top layer  (near A2/A3) :/
        • the preamp in and out and misaligned 
        • Should have included blocking capacitors
        • VGA not being recognized by my computer with driver chip 
        • Should have included an inductor circuit
        • awful humming from the negative 12V generator
        • not enough room for 220uF caps for the video driver on board
        • some kind of “pass through” switch would be useful to show what the input looks like
        • the driver IC can only accept one color for some reason…I should not include it next time
        • the female – female jumpers are not reliable and lead to weak contacts…maybe going towards jumpers?
        • The thing is long to set up – it’s not plug and play and is finicky for demos
        • Learn about the safety of hot plugging
        • The Voltage Control works a litte, with a 1k8 resistor and picking the right voltage range. But it is not reliable, I should move to pot ICs for the next version. 

         

        • A0  with a 200uF cap after the final amp has some filtering output
        • A1  just tears the left side of the screen
        • A2  nothing happening
        • A3 varies nicely with different frequencies, though sometimes just looks like it’s “adding” frequency pattern to input
        • A4 I think the original comparator design (with 1K resistor going to VCC and pot with middle shorted to GND going to GND is correct and this one wrong)
        • A5 same note as above, but preamp in and upper seem to work. Perhaps the best way to get abstract forms from input super easily. 
        • B0 Not working…
        • B1 nice filtering !
        • B2 needs blocking cap on output or else produces nothing. Goes from filtering to almost comparator like behaviour.
        • B3 needs a cap on output and >20MHz to produce anything, no relation to input though ?
        • B4 creates a cool zebra repeating effect at one extreme
        • B5 great filter !! Both knobs do things and there is great range.
        • C0 nothing cool happening here…Seems just to pass on a modified version of the digital in signal ?
        • C1 top knob needs resoldering. Only does stuff when top knob fully to one side then behaves a bit like A3 but can go down to low frequencies on digital in.
        • C2 Cool zebra like filtering (like B4 but messier and less repetitive.
        • C3 nothing happening…
        • C4 faintest image at one extreme
        • C5 nice filter (like B5) ! Only top knob seems to do cool stuff though. (Other knob goes haywire at one extreme).
        • D0 barely the faintest image at low frequencies…
        • D1 the HP inverts the input !
        • D2 Does add when the knobs are set right. I put a cap on the function generator input.
        • D3 Does kind of subtract ! Can invert or not.
        • D4 makes a banding across the screen, not sure what effect the 3 binary inputs have…
        • D5 doesn’t really work but with the second multiplier input floating you can touch it and send the thing into an overdrive momentarily. 

        *********

        Notes from Museum visits :

        Composite (wood, porcelaine, gold) in flat, relief, and form of object.

        Could I have both feathery frilly fuzzy video mixed with crisp solid geometric form to create similar layering ?
        Some video noise looks like different types of stone
        Rococo / Baroque video ?
        Decorative video?
        High quality video / image capture ?
        Once I’ve finished with this round of tools, I have to compose more !!

        Can I mirror an image half way to make a Rorschach like ink splot ?

        Wicker chair silhouette series, pumpkins 
        Some format of showing the circuit and the output that can end up in a book
        Shoot my own video to mess with

        Layering

        Contour
        Surface
        Saturation
        Flow vs. Choppiness
        Look at Photoshop for 
        Roughness, relief 
        Edges versus centers 

        Movement vs stasis 

        Enriching of detail possible ?
        Abstracting, cosmic making
        Fine lines versus coarse
        Exposure 
        Color fields
        Texture
        Deep friedness
        Films of cows, ocean. Stills of still life? Flowers, birds, drapes folding, mold, fruit, bones
        Color mixing vs melting vs meeting
        Paint viscosity (runny vs clotted)
        Direction of brush strokes 
        Contour
        Focus direction in canvas
        Translucent flesh, shine vs glow from interior, waxiness 
        Graininess 
        Composition, color pairings and form
        Foreground displaced to background vice versa 
        Meeting pattern planes 
        Lighting and shadow
        Illusion of depth
        Cloudiness 
        *****
        IDEA : I could take a file, put it into the memory, and then “play” it different ways (because the width is completely arbitrary below). A file could thereby become a pattern, or a long abstract video, 

        Windows PE visualization

        Or I could put an image onto a floppy, then damage the floppy, and show how it would modify the image ?

        THINGS TO TRY:

         

        • Palimpsest.OS with arduino controlling WR and COUNTER CLK (and forgetting about CLK) to control the boards recording and playing back. 

         

        • Check out Video Effects list from Adobe Premiere : https://helpx.adobe.com/premiere-pro/using/effects.html
        • test outputting to a projector !! * EDIT * WOW this was a good idea :

        • take HDMI and mess with it digitally (but not analogically?)

        • design easier to understand / less complex board which is more plug and play ? *EDIT* Try 74*8154 16 bit counter + 32K x8 IS61C256A SRAM !
        • Take photos of boards in the light box !  

        This is what a loop sounds like :

        *****

        I’m currently working on a final set of four boards for the Choreographable Video Synth that will finish this project. 

        All the boards will have the following improvements :

        • Plug and play – very little setup time, no need for a function gen, and no fiddling to get an image on the screen for demos quickly.
        • Nice 2mm banana connectors and 30cm multi colored patch cables that make reliable connections and are more modular. (Not sure about edge connecting female plugs and edge actionnable pot knobs or vertical plugs).
        • Only the best bits of the previous boards (Analog f(x), Oc74, Palimpses.OS) in a smaller square format with playful designs.
        • The three f(x) boards will have an onboard audio amp + speaker to hear the signals you are making. A speaker hole will be present.
        • Finished product for inauguration of the IFT in early March, 75mmx75mm ans nice compositions. 
        • Make this version actually be entirely controlled by digital potentiometers and buffers and then also be able to manually control these digital devices on board with hardware switches and buttons. 

        SIGNALS IN/OUT board :

        • Takes VGA, HDMI, DVI-A in, and puts out VGA and HDMI. (Or just VGA in and out?)
        • Has a passthrough switch to see directly what’s on the screen. (Or skip this?)
        • Has a final mixing stage (especially a microcontrolled matrix mixer !!)
        • Acts as a power supply for the other boards (12V because the analog board needs this?)
        • Could take 2 VGA inputs ?
        • Has an atmega 328 to output low res H and V ?

        ANALOG F(X) board:

        • Only the four best of the 24 analog functions board. (Butterworth HP, LP, Analog MUX, ADC)

        OC74 DIGITAL F(X) board :

        • Only the best functions (comparator, DAC, S+H, XOR, Bin Counter)

        MEMORY board (OR MAYBE THIS IS A SEPERATE KIT AND I SHOULD JUST MAKE A 16MB MEMORY BOARD AGAIN?) :

        • 8 channels of 1 bit memory recording on a total of 1MBit of SRAM
        • Comparators acting as 1-bit ADCs (but maybe this belongs on another board ?)
        • A battery system that actually works (where battery and input voltage Diode-OR’d and with an ON-OFF switch to conserve batt)  !
        • Double SRAM so that I can begin messing with echo and delay, and rerecord over non-new signal channels
        • Series of 74AUP1G157GW MUX to prevent any damage to memories when recording fresh signals and to make this process automizable
        • A pulse generation setup which does not need an external function generator… (like by using an oscillator can).

        Some possible playful interface designs (I especially like the gravity effected ones) :

        ***

        Arduino controlled SRAM :

        Not yet working so going to test with two mechanical relays – this HAS to work or else I’m crazy.

        This code can get the SRAM writing :

        void setup() {
        pinMode(13, OUTPUT);
        pinMode(12, OUTPUT);
        }
        
        void loop() {
        //WRITE
        PORTB = B11101111; // CLK goes LOW/HIGH
        PORTB = B11011111; //WR goes HIGH/LOW
        }

        This code seems to work but it’s results are not predictable (I think because the code runs too slowly)… :

        unsigned long startMillis; //some global variables available anywhere in the program
        unsigned long currentMillis;
        const unsigned long period = 1000; //the value is a number of milliseconds
        int WRState = HIGH; // the current state of the WR pin
        
        void setup()
        {
        pinMode(13, OUTPUT); //WR PIN
        pinMode(12, OUTPUT); //CLK PIN
        pinMode(11, OUTPUT); //ADC EN PIN
        startMillis = millis(); //initial start time
        }
        
        void loop()
        {
        
        if(WRState == HIGH)
        {
        currentMillis = millis(); //get the current "time" (actually the number of milliseconds since the program started)
        
        //WRITE TO SRAM
        PORTB = B11101111; // CLK goes LOW/HIGH, ADC_EN HIGH
        PORTB = B11011111; //WR goes HIGH/LOW, ADC_EN HIGH
             if (currentMillis - startMillis >= period) //test whether the period has elapsed
             {
              WRState = !WRState;
              startMillis = currentMillis; //IMPORTANT to save the start time of the current LED state.
             }
        }
        
        if(WRState == LOW)
        {
        currentMillis = millis(); //get the current "time" (actually the number of milliseconds since the program started)
        
        // READ SRAM
        PORTB = B11100111; // CLK goes LOW/HIGH, ADC_EN LOW
        PORTB = B11110111; //WR stays high, ADC_EN LOW
        
             if (currentMillis - startMillis >= period) //test whether the period has elapsed
              {
               WRState = !WRState;
               startMillis = currentMillis; //IMPORTANT to save the start time of the current LED state.
              }
        }
        }
        

        Magnetic Media Video Synthesis

         

        I want to continue messing around with magnetic media (audio cassette players/recorders, floppy disk drives, video camcorders) and try to get them recording and playing back video.

        Here is some inspiration :

        From the timeless Hardware Hacking manual. The suggestion is to use a high gain amplifier to hear the results of the tape pickup. :

        A very cool project putting video onto a cassette :

        https://www.ubergizmo.com/2020/04/audio-cassette-tape-used-to-capture-video/

        From this reply : https://electronics.stackexchange.com/questions/108907/create-input-jack-from-tape-head

        “You can use the continuity tester to check for the ground lead. This should be connected to the metal shield of the head, you can leave it unconnected. You’ll want to connect the other two wires to your two inputs”

        From this site http://stevecoates.net/walkman/ :

        This https://makezine.com/projects/turn-old-walkman-scratchbox/ Make Magazine article connects simply :

        Four-terminal play head

        Some very cool experiments with a disconnected tape head :

        And a custom loop tape :

        **************************

        I began testing a bit, it looks like I can both see and hear the audio signals from the tape (yellow is R?, blue is L?):

        It’s most audible right near the end (the hum is the tape cassette motor spining) :

         

        *****************************************

        The method is just to power the cassette rotating motor with around 3.3V, then hook up the red(R?) or white(left?) to a probe or audio in for an amp, and then the black (GND) – which looks like it’s connected to two pins – to GND.

        The next step would be to try to amplify with a high gain op-amp circuit ? And then to begin trying to write ?

        UPDATE :

        Might be easier to work with the electronics already present on the board, learn how it works, desolder it and then put it on a breakout board to mess around with? Here is one of the simplest ICs I found on one of the dissassembled tape decks in an easy to solder SOP16 package:

        I could order a series of boards like this to make tests with them : SOIC-32, SOP-32 | SMD to DIP Adapter | SchmalzTech

        UPDATE:

        I plugged in the little audio amp into the Audio out jack of the device while I touched different pins on the IC. I found that the large slide switch on the back switched from REC to PLAY. I found that during REC mode the pins that caused the most amplification were the MIC IN and EQ IN. In PLAY mode I found the most important pin for producing output on the AUDIO OUT was LINE IN.

        I also tried connecting the little speaker amp to the REC OUT, LINE OUT while I touched various pins. 

        I was unable to amplify the tape head signals however with any of the various combinations I tried. I will try the other tape cassette circuit boards to see if I have any more luck, and also test the 2.5W power amp from Adafruit. Ideally I would be able to take an input, amplify it, send it to the tape head, then read it back by reversing the sequence after rewinding to the same section. This should also work with a strip of tape taped to a desk that I can manually run the magnet head over (right??). 

        Once I’m able to do this, I want to try the same thing (amplify an analog signal, write it to the disk through the heads directly, then read the analog signal back off it) with the Floppy Circuitry. 

        *************

        A great VCR technical reference : https://www.wikiwand.com/en/VHS

        I got my hands on a VCR, took it apart and broke out the cable coming from the helical read/write head.

         

         

        It looks like there is one little board with all the drivers for the various motors. Might be able to control it. (See this video : https://www.youtube.com/watch?v=4F7xJOb32GI&ab_channel=LifeOfDiy). I’ve got a BA6878EFV 3-phase motor driver for VTR capstan, datasheets available online. It looks like you need to plug in GND (3), 5V (2), and then connect EC(7) and HYS OUT (9) together.

        So far I haven’t been able to pick anything up on the oscilloscope from moving the tape across the print heads while listening in to various output pins.

        https://www.wikiwand.com/en/VHS#Media/File:Medion_MD8910_-_VHS_Helical_scan_tape_head-8601.jpg

        There appear to be two video heads (with two wires each – one wire for each electromagnet) and two audio heads (with one wire each) making a total of 6 wires with 1 dummy. It looks like the pinout is the following :

        1. Video Green 1
        2. Video Red 1
        3. Video Green 2
        4. Video Red 2
        5. No connection (shortened turn)
        6. Audio Red
        7. Audio Green

        It seems like others have two shortened turns to help isolate signals from one another.

        I measured the resistances between the output pins:

        1. grey 
        2. white
        3. blue(1)
        4. green
        5. yellow(1)
        6. orange
        7. red
        8. blue(2)
        9. yellow(2)

        The following are connected :

        Looking up the chip LA71750 ( Video and Audio Signal-Processing IC) – on the right in the image below – and the LA7264B (Audio Signal-Processing IC) – on the left – and checking out the datasheets and inspecting the PCB, looks like the following are the important connections :

        From the LA71750 datasheet :

         

        Would appear possible to make this pinout diagram :

        I have tried connecting pins to the oscilloscope while moving the tape head, but even at 10mV level at various time scales I can’t see anything. 

        UPDATE : Just seeing now that this chip takes control data in the form of I2C. It would be super hard for me to desolder the chip and make my own controller with it as a result.

        ******************

        I have tried amplifying and sending 20V signals at various frequencies. 

        Turning to more research for answers, check out these links :

        Article about audio sampler made from floppy :

        https://nime.pubpub.org/pub/uh76shf0/release/1

        Video made from floppy :

        Freespin is a demo made for the Commodore 1541 – no, not the computer, just the floppy drive

        For more tech info about floppy heads :

        http://forums.openmusiclabs.com/viewtopic.php?f=11&t=92&sid=9a4c3a6763f9bc02426c9d3910187e70

        ******************

        From this reading break I think the following things should be attempted :

        -with a >2W class D amp can I push some signal onto the tape that I can then read ? (Just afraid that it won’t be fast enough for video signals as it is made for audio signals ? Perhaps a high power high speed amp is even better?).

        -I could get the same preamp LA IC used in the concentric sampler paper.

        -Everyone seems to say, “try different combinations with the R/W heads” and “check continuitiy” and apply the signal to continuous wires. 

        -I could try adding the bias oscillator to the signal I’m trying to R/W?

        -I should get a super strong magnet for testing purposes

        ***********

        Looking at my floppy drive, I found the following CX20185 chip which is a Read/Write Amplifier for Floppy Disk Drive.

        It looks like the pinout is the following :

        It looks like I could read the Preamp Out pins to read from the floppy. I’m not sure how I could yet wright to the floppy without giving data to the logic on the driver.

        Here is me figuring out the pinout :

        I turned on the floppy LED I have by connecting the sixth pin next to the stepper head motor to the GND, and turned on the central motor by connecting the 8th pin to GND and plugging in 12V as well :

        When I listen in to READ DUMP A or B I can see voltage when I spin the disk manually (when I plug in the brushless motor it no longer responds to spinning):

        When I listen in to PRE OUT A or B I can see the normally high voltage go down and vary :

        Meh. 

        Some stuff about data degredation :

        https://fr.wikipedia.org/wiki/The_Disintegration_Loops

        Project Kryoflux – Part 3: Recovery in Practice

        ******

        Suggestion from research engineer at DVIC for the previous experiments:

        -For the floppy, look at the input from head (but oscilloscope will probably mess this up), preamp, and digital out (READ OUT – which should be connected directly to the chip I have the datasheet for) at same time. 

        -Basically you’re not going to see anything on the oscilloscope when looking at these electromagnets. It’s all in the very sensitive amp – so all the more reason to use the amp that comes with the device !

        -Is the preamp chip IC I am looking at even enabled ? Check POWER ON.

        -Try the floppy pinout here : https://old.pinouts.ru/HD/InternalDisk_pinout.shtml

        -For the VCR, get the thing plugged in and functioning as it was in the beginning. Observe it first, then try to intervene (you’ll never be able to reinvent the thing from nothing !)

        ****

        Listening to the DATA READ pin 33 while the disk is spinning :

        ****

        UPDATES:

        I learned from watching this (https://www.youtube.com/watch?v=DdMOGvKjrfk&ab_channel=JeffHxC2001) that the way to read is to sync the oscilloscope with the INDEX pulse. The Difference Out pin is the one being listened to in this video.

        I learned from looking more into the floppy interface that the Head Select (32) is HIGH for BOTTOM and LOW for TOP head. I also learned that Floppy Write Enable (24) is HIGH to READ and LOW to WRITE. Finally, the READY pin 34 may need to be tied to GND.

        From a Floppy Maintenance Manual showing the sequence : R/W head > Pre-Amp > Differentiator > Voltage Comparator > Time/Domain Filter > DATA OUT

        UPDATES:

        I think to make this project work, I would first need to *VERY CAREFULLY* observe the correct functioning of a working device without any changes made to it. I would need to be able to confirm completely the working of the machine, and see what signals are supposed to look like at each step. 

        Only after doing this could I start intervening in this process.

        I think Floppy is the best way to go – it’s part of the history of the computer (like all my other projects) which isn’t the case with the video recorder. The floppy is the best documented, easiest to source, and least expensive to work with. It already has a nice interface which is easy to use. It also is more standardized, unlike the many different kind of video recorders that exist. 

        What is the most standard Floppy Drive Read/Write Amplifier ? The Sony CXA series (1360, 1720, 3010, 3071, 20185, etc.) seem to have specs available online. ROHM also have BH6xxx and BA6xxx series. 

        Then again if I’m only going to be recording analog, I don’t need anything but the preamp (especially for reading) ? I could just take the tape preamps I have and put them on a breakout board ? I have BA3110, CX20023, and a CXA1262 to work with. I could try getting them to work amplifying the mic first, and also get the playback working. Then I could test putting analog signals on a floppy.

        Video synth workshop

        This workshop with La Diagonale at UP Saclay is a simple circuit to begin playing with code and screen patterns. 

        Here is the github: https://github.com/merlinmarrs/spaghettini-video-synth

        Arduino Coding and Video Synthesis Workshop

        Lean to code in C with Arduino, solder and assemble a custom printed-circuit board made in the Fablab, and make your own algorithmically-generated video art!

        This workshop is inspired by the improvisational music of Live coding (https://fr.wikipedia.org/wiki/Live_coding) and the burgeoning minimalist Bytebeats movement (https://nightmachines.tv/bytebeats-tutorial).

        Our video synthesis kit plugs in to the Arduino (https://www.arduino.cc/), a reprogrammable embedded micro-controller that can interact with the world through code, and uses the VGAX library (https://github.com/smaffer/vgax) to generate funky video.

        Meet other creative technologists and sharpen your expertise in computer sciences and electronics soldering!

        Here are some initial sketches :

        The functionality that is important is :

        -being able to select between different programs (with button and leds to indicate)

        -to be able to take audio in and have it modify the patterns on screen

        -to be able to change the colors on the go

        -I have since phased out the brightness changeability but I could add an LDR easily ?

        *******************************************************************************************

        Because our lasercutter is not operational, I am trying to make this circuit with the vinyl cutter using 5cm wide copper tape. 

         

        I am worried about the headers getting pulled out when plugging/unplugging. I will try adding some hot glue to fortify them. The other option is potting the circuit in resin but that would get messy and expensive. 

        Errata:

        -button too close to headers when glue is applied

        -remember space for audio in jack plug 

        -the two columns of headers for the Arduino should not be continuous, also unclear what is first and last pin. Also they should be SMD headers. 

        -don’t mess with RX and TX pins on the Arduino. 

        -make a version with VGA plug?

        -add text but have it cut onto plastic not made from metal

        -audio offset thing doesn’t work

        I also want to think about expandability, how could people take this project further by themselves afterwards ?

        -Having a knob  or two knobs / LDR connected to an analog in on the Arduino…

        -Be able to control the program based on voltage control levels at an input. This could mean it could even be attached to the polyphonic synth !

         

        ***************************************************************************

        Here is the next version called the Spaghettini:

        -It respects the height of the 50mm wide copper tape I have on hand.

        -It makes more sense for the audio in cable to plug in at the side and not interfere with the capacitive sensor. 

        -It uses curvy lines just for the capacitive sensing functionality and straight elsewhere which feels honest. 

        -It has the ability to modify the brightness of any of the three channels, one at a time. 

        -It has a potentiometer for analog in in adition the capacitive sensor. 

        -I’m experimenting with different patterns on the left portion.

        -The sensor works with the Arduino library CapacitiveSensor 

        And here’s what it looks like all plugged in ! (But I need to add hot glue to make it really usable)

         

        Memory Experiments (continued)

        I have two goals for this project:

        1. Finish the VGA video recorder board (with op amps, bias pots, bias for ADC) – DONE!
        2. Make a version of the vectorscope image modification series but with a raster system taking the following image as the input – DONE :

        1. Finishing the VGA Video Sampler board

        For the first goal, I need to correct that last board I made:

        Errata:

        -The gain pots for the op-amp don’t work.

        -I mixed up VCC (3.3V) and VCA (5V) in several places. (I just replaced the PFET with the LDO and manually rewired)

        -The “EN” text next to the ADC was in the wrong place. 

        -I forgot to tie down the reset of 74HC590 with a 10K resistor.

        -The 74HC04 is labelled backwards, I put inputs on the right and outputs on the left for some reason.

        Things appear to work with these changes:

        It takes some knob fiddling but I can get an input sine wave digitalized. One definitely needs an oscilloscope to see what’s happening though.

        2. Vectorscope image series

        I can either load the image into memory by playing it on a computer screen and capturing the pixels, OR, loading the image into memory with a microcontroller. I’m not sure how big I should make the image, and what dimensions, if I load the image. 

        Here is the p5.js code to take an image (called p.jpg) and print the red pixel value into a text document (called OUTPUT) with a comma and space seperator :

        let img;
        
        function preload() {
        img = loadImage('p.jpg');
        }
        
        function setup() {
        createCanvas(img.width, img.height);
        background(220);
        image(img, 0, 0);
        let writer = createWriter('OUTPUT.txt');
        
        for(var y = 0; y < height; y++) {
        for(var x = 0; x < width; x++){
        let pix = img.get(x, y);
        writer.write(pix[0] + ', '); // takes RED pixel info, adds a comma and a space
        if(!(y==1-height || x==1-width))
        {
        writer.write(pix[0]); // don't put a comma if it's the last element of the list
        }
        }
        }
        writer.close();
        }

        And here is the Arduino code to take this color information and load it into the memory space (90×90 max as PROGMEM has max 8,192 bytes max):

        byte myChar;
        
        #define IMG_WIDTH 90
        #define IMG_HEIGHT 90
        //data size=900 bytes
        const unsigned char img [IMG_HEIGHT*IMG_WIDTH] PROGMEM={
        17, 19, 19, 19, 19, 20, 20, 20, 20, 20, 21, 23, 23, 22, 21, 21, 22, 22, 21, 22, 21, 22, 21, 20, 21, 21, 23, 23, 23, 23, 23, 23, 24, 23, 25, 23, 24, 23, 25, 25, 25, 25, 25, 24, 25, 26, 24, 23, 25, 24, 23, 24, 25, 21, 20, 24, 24, 23, 20, 20, 21, 23, 22, 22, 22, 22, 22, 23, 23, 24, 23, 23, 23, 25, 23, 24, 24, 23, 24, 25, 23, 25, 25, 23, 25, 24, 24, 25, 28, 28, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 19, 35, 25, 32, 32, 26, 27, 17, 23, 27, 30, 33, 33, 35, 37, 35, 29, 30, 34, 37, 44, 26, 30, 65, 65, 57, 56, 55, 57, 59, 42, 38, 46, 38, 33, 40, 41, 43, 46, 35, 3, 0, 0, 0, 0, 1, 1, 0, 9, 49, 18, 0, 4, 0, 0, 0, 3, 2, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 3, 29, 231, 255, 245, 252, 250, 247, 248, 240, 245, 249, 255, 255, 255, 255, 255, 254, 254, 255, 255, 255, 255, 252, 252, 255, 254, 255, 254, 255, 255, 255, 254, 255, 255, 254, 253, 255, 255, 255, 255, 255, 71, 1, 1, 1, 1, 0, 15, 189, 244, 255, 223, 205, 216, 183, 191, 207, 210, 221, 234, 240, 233, 220, 215, 113, 138, 251, 225, 219, 223, 224, 169, 166, 201, 208, 217, 220, 196, 190, 181, 189, 181, 188, 193, 148, 149, 207, 171, 25, 0, 50, 255, 255, 255, 255, 255, 255, 255, 255, 254, 255, 250, 245, 244, 253, 255, 255, 246, 240, 239, 231, 239, 234, 255, 255, 255, 249, 238, 234, 247, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 255, 90, 0, 1, 0, 1, 1, 43, 245, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 241, 229, 249, 255, 255, 255, 254, 249, 249, 248, 255, 251, 254, 254, 255, 242, 235, 241, 253, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 77, 0, 31, 240, 254, 249, 189, 170, 192, 190, 179, 202, 131, 12, 4, 0, 107, 226, 192, 54, 0, 11, 8, 2, 2, 146, 225, 204, 71, 0, 0, 65, 200, 206, 192, 194, 197, 185, 200, 255, 255, 255, 211, 24, 0, 1, 0, 1, 0, 8, 178, 255, 255, 243, 204, 194, 197, 198, 189, 209, 181, 25, 0, 0, 106, 217, 213, 96, 0, 6, 5, 7, 0, 126, 236, 199, 38, 0, 0, 84, 220, 208, 203, 205, 198, 210, 200, 225, 255, 255, 68, 0, 19, 226, 254, 219, 10, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 190, 255, 255, 95, 0, 3, 0, 2, 0, 3, 0, 48, 253, 255, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 144, 255, 255, 80, 0, 62, 255, 255, 235, 24, 0, 0, 0, 0, 0, 3, 2, 0, 1, 2, 0, 1, 5, 3, 1, 1, 2, 4, 1, 0, 0, 1, 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 189, 255, 255, 105, 0, 1, 1, 1, 0, 3, 0, 65, 255, 255, 124, 0, 1, 0, 0, 0, 0, 0, 3, 6, 2, 1, 0, 0, 4, 3, 0, 3, 1, 1, 2, 1, 0, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 157, 255, 255, 85, 0, 45, 252, 255, 241, 31, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 2, 1, 0, 0, 187, 255, 255, 102, 0, 2, 1, 0, 0, 2, 0, 94, 255, 254, 143, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 2, 3, 1, 0, 0, 0, 134, 255, 255, 69, 0, 48, 253, 255, 227, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 178, 255, 255, 119, 0, 1, 0, 0, 1, 2, 0, 66, 255, 255, 138, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 145, 255, 255, 84, 0, 49, 254, 255, 229, 19, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 0, 0, 0, 1, 1, 2, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 196, 255, 255, 110, 0, 0, 0, 0, 0, 2, 1, 54, 255, 255, 138, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 130, 254, 255, 76, 0, 39, 245, 255, 225, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 2, 1, 23, 233, 255, 255, 82, 0, 2, 1, 0, 1, 1, 0, 61, 255, 255, 124, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 142, 255, 254, 74, 0, 55, 255, 254, 246, 31, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 6, 207, 255, 255, 116, 0, 2, 0, 0, 0, 1, 1, 99, 255, 255, 137, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 92, 255, 255, 83, 0, 32, 254, 255, 107, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 1, 0, 0, 2, 1, 0, 71, 231, 255, 79, 0, 3, 0, 0, 0, 5, 0, 68, 241, 193, 48, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 159, 255, 89, 0, 55, 255, 182, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 1, 1, 0, 4, 1, 0, 0, 5, 6, 0, 0, 0, 2, 3, 1, 1, 0, 1, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 3, 3, 0, 137, 255, 95, 0, 52, 255, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 3, 0, 0, 2, 1, 1, 1, 1, 14, 41, 8, 0, 1, 1, 0, 0, 2, 7, 4, 0, 1, 2, 5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 2, 0, 0, 173, 255, 80, 0, 40, 254, 238, 34, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 7, 5, 0, 0, 0, 1, 1, 0, 29, 124, 222, 255, 113, 0, 0, 0, 0, 0, 4, 0, 44, 174, 80, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 114, 255, 255, 75, 0, 44, 250, 255, 225, 17, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 3, 1, 5, 1, 2, 5, 6, 3, 1, 2, 4, 4, 4, 2, 0, 0, 0, 1, 0, 93, 255, 255, 254, 191, 1, 0, 0, 0, 0, 2, 0, 60, 255, 255, 101, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 1, 0, 0, 2, 0, 113, 255, 255, 87, 0, 47, 251, 255, 249, 36, 1, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 1, 1, 2, 1, 1, 3, 3, 1, 1, 1, 3, 0, 1, 1, 0, 0, 0, 0, 1, 89, 255, 255, 255, 178, 0, 0, 0, 0, 1, 2, 0, 66, 241, 255, 139, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 67, 255, 255, 87, 0, 51, 245, 254, 235, 24, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 86, 255, 255, 254, 197, 0, 0, 0, 0, 0, 3, 0, 97, 163, 192, 142, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 115, 255, 255, 77, 0, 68, 254, 255, 241, 29, 1, 2, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 95, 255, 255, 255, 192, 0, 1, 0, 1, 0, 3, 0, 78, 237, 254, 114, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 118, 255, 255, 84, 0, 72, 255, 255, 251, 41, 0, 2, 0, 0, 1, 3, 1, 1, 0, 0, 3, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 2, 1, 0, 1, 2, 0, 71, 255, 254, 255, 160, 0, 0, 0, 0, 1, 3, 0, 26, 233, 255, 132, 0, 0, 2, 0, 1, 1, 2, 2, 2, 2, 2, 0, 1, 0, 1, 2, 0, 1, 2, 3, 3, 2, 1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 165, 255, 255, 83, 0, 77, 255, 254, 227, 17, 0, 0, 0, 0, 0, 1, 4, 2, 2, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 51, 253, 255, 255, 158, 0, 0, 0, 0, 2, 0, 1, 54, 255, 255, 98, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 3, 3, 4, 2, 0, 0, 0, 0, 0, 0, 0, 139, 255, 255, 72, 0, 71, 255, 255, 229, 89, 66, 87, 83, 78, 90, 46, 0, 0, 0, 103, 142, 136, 145, 128, 162, 157, 122, 126, 127, 128, 127, 128, 130, 128, 128, 127, 127, 127, 126, 125, 106, 172, 255, 255, 255, 224, 30, 0, 0, 0, 0, 3, 0, 92, 255, 254, 135, 0, 46, 44, 8, 11, 39, 30, 25, 26, 34, 54, 45, 22, 39, 32, 28, 28, 26, 28, 27, 38, 14, 0, 4, 0, 5, 32, 37, 29, 27, 32, 25, 9, 169, 254, 255, 88, 0, 26, 227, 255, 255, 255, 255, 255, 255, 255, 255, 231, 36, 0, 61, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 254, 255, 255, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 116, 0, 0, 0, 1, 0, 20, 190, 255, 254, 250, 239, 255, 255, 181, 196, 255, 245, 240, 241, 248, 255, 247, 239, 253, 246, 245, 245, 242, 243, 243, 255, 173, 0, 0, 0, 100, 254, 255, 245, 243, 247, 246, 246, 250, 255, 255, 79, 0, 46, 245, 255, 255, 254, 255, 255, 255, 255, 255, 255, 190, 0, 163, 255, 255, 254, 255, 255, 255, 255, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 254, 255, 254, 255, 255, 255, 138, 0, 0, 0, 1, 0, 61, 255, 255, 255, 254, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 119, 0, 12, 222, 255, 255, 255, 255, 255, 254, 255, 255, 255, 249, 48, 0, 80, 255, 255, 237, 139, 113, 121, 128, 127, 123, 149, 83, 1, 92, 219, 207, 197, 173, 206, 190, 150, 183, 255, 255, 255, 221, 214, 199, 170, 170, 180, 189, 242, 255, 255, 255, 254, 255, 255, 255, 255, 131, 0, 0, 0, 1, 0, 58, 255, 254, 255, 255, 254, 255, 255, 172, 80, 103, 108, 175, 220, 199, 101, 141, 255, 255, 158, 74, 100, 96, 102, 107, 87, 109, 60, 0, 4, 104, 128, 109, 115, 114, 125, 108, 97, 209, 255, 255, 88, 0, 58, 255, 255, 157, 0, 0, 0, 0, 0, 1, 0, 0, 5, 1, 1, 0, 0, 1, 0, 1, 0, 10, 221, 255, 253, 48, 2, 1, 0, 3, 12, 83, 255, 255, 254, 255, 255, 255, 254, 249, 193, 35, 0, 1, 0, 1, 1, 0, 79, 162, 233, 255, 255, 255, 255, 129, 0, 1, 8, 14, 11, 6, 8, 85, 255, 255, 70, 0, 0, 1, 1, 0, 0, 1, 1, 7, 10, 2, 1, 0, 0, 0, 0, 1, 0, 115, 255, 255, 86, 0, 80, 254, 187, 0, 0, 0, 2, 1, 1, 1, 0, 2, 6, 1, 0, 0, 0, 1, 0, 0, 0, 23, 231, 255, 255, 60, 1, 0, 1, 98, 200, 251, 254, 255, 255, 255, 241, 154, 81, 9, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 6, 70, 183, 254, 255, 254, 189, 27, 16, 8, 0, 0, 5, 169, 255, 255, 86, 0, 1, 2, 3, 5, 4, 1, 1, 3, 5, 4, 4, 0, 1, 3, 1, 3, 0, 0, 169, 255, 90, 0, 88, 255, 179, 0, 0, 0, 0, 0, 1, 2, 4, 2, 1, 5, 5, 5, 1, 0, 2, 0, 1, 57, 255, 255, 255, 86, 0, 1, 137, 255, 255, 254, 255, 254, 209, 79, 11, 1, 0, 0, 1, 2, 0, 1, 0, 0, 1, 1, 2, 1, 1, 0, 0, 42, 168, 255, 255, 252, 152, 27, 1, 0, 14, 193, 255, 255, 88, 0, 2, 0, 2, 1, 1, 3, 6, 2, 0, 6, 4, 0, 1, 0, 0, 2, 4, 0, 150, 255, 89, 0, 64, 255, 255, 73, 0, 0, 0, 0, 2, 2, 8, 8, 0, 2, 4, 3, 1, 0, 1, 1, 0, 47, 250, 255, 255, 114, 0, 141, 255, 255, 255, 255, 247, 107, 0, 0, 1, 1, 2, 0, 1, 1, 0, 0, 1, 0, 0, 1, 2, 2, 0, 2, 1, 0, 0, 77, 244, 255, 255, 200, 22, 4, 12, 141, 255, 255, 87, 0, 2, 3, 1, 1, 0, 1, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 33, 221, 255, 79, 0, 72, 255, 255, 201, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 34, 242, 255, 255, 90, 50, 255, 255, 253, 255, 220, 37, 0, 1, 9, 3, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 2, 2, 1, 1, 1, 0, 4, 4, 0, 27, 208, 255, 255, 196, 24, 8, 62, 255, 254, 86, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 255, 255, 92, 0, 99, 255, 255, 227, 16, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 64, 255, 255, 255, 87, 145, 255, 255, 255, 214, 23, 0, 2, 2, 5, 5, 0, 2, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 5, 0, 8, 192, 255, 185, 5, 2, 80, 255, 255, 91, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 172, 255, 255, 93, 0, 105, 255, 255, 226, 16, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 52, 253, 255, 243, 131, 217, 255, 255, 235, 33, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 2, 0, 0, 214, 207, 0, 0, 73, 254, 255, 89, 0, 3, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 173, 255, 255, 97, 0, 93, 255, 255, 221, 13, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 49, 252, 255, 253, 254, 255, 255, 255, 97, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 33, 239, 193, 81, 141, 255, 254, 93, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 168, 255, 255, 93, 0, 68, 255, 245, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 34, 242, 255, 255, 255, 254, 255, 139, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 113, 254, 255, 255, 254, 255, 94, 1, 3, 0, 0, 1, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 191, 255, 94, 0, 69, 255, 185, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 16, 228, 254, 255, 255, 255, 212, 15, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 5, 204, 255, 255, 255, 255, 88, 0, 2, 1, 1, 3, 2, 1, 5, 2, 2, 1, 1, 1, 1, 0, 0, 2, 1, 0, 153, 255, 95, 0, 92, 254, 179, 0, 1, 0, 0, 0, 0, 0, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 254, 255, 255, 255, 255, 111, 0, 7, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3, 0, 101, 255, 255, 255, 255, 94, 1, 0, 3, 2, 3, 4, 5, 7, 3, 5, 7, 5, 2, 0, 1, 0, 0, 5, 1, 163, 255, 93, 0, 102, 255, 241, 50, 0, 0, 0, 0, 0, 1, 0, 5, 4, 2, 0, 0, 0, 0, 0, 0, 0, 81, 255, 254, 254, 255, 233, 25, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 3, 1, 10, 216, 254, 255, 254, 86, 0, 0, 1, 0, 0, 0, 1, 2, 6, 6, 4, 0, 0, 1, 0, 0, 0, 1, 21, 220, 255, 87, 0, 72, 255, 254, 235, 163, 162, 167, 164, 164, 182, 89, 0, 0, 15, 192, 233, 211, 212, 212, 210, 236, 223, 242, 255, 255, 255, 164, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 2, 0, 0, 125, 255, 255, 255, 169, 87, 118, 97, 98, 106, 124, 72, 0, 5, 0, 39, 152, 148, 142, 143, 141, 146, 132, 216, 255, 255, 94, 0, 81, 255, 255, 255, 254, 255, 255, 255, 255, 255, 246, 63, 0, 115, 255, 255, 255, 255, 255, 255, 255, 254, 255, 254, 255, 255, 67, 0, 1, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 6, 0, 56, 255, 254, 255, 255, 255, 255, 255, 255, 254, 255, 255, 75, 0, 0, 165, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 98, 0, 87, 254, 255, 254, 255, 254, 255, 255, 255, 255, 255, 109, 0, 165, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 48, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 25, 232, 255, 237, 255, 244, 238, 255, 249, 248, 255, 254, 150, 0, 2, 212, 255, 255, 255, 255, 254, 254, 250, 252, 255, 255, 100, 0, 37, 224, 255, 173, 71, 81, 83, 70, 66, 70, 69, 20, 0, 81, 177, 148, 121, 117, 119, 116, 116, 119, 116, 118, 128, 82, 1, 1, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 1, 3, 2, 0, 25, 36, 15, 41, 23, 18, 46, 31, 26, 36, 30, 10, 0, 0, 34, 55, 37, 40, 41, 41, 40, 8, 91, 234, 222, 57, 0, 0, 11, 12, 3, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 3, 2, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4, 1, 0, 0, 0, 1, 1, 0, 0, 0, 2, 0, 3, 4, 3, 0, 0, 1, 4, 2, 2, 2, 1, 3, 2, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 3, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 3, 6, 1, 2, 1, 0, 0, 2, 5, 4, 0, 0, 2, 2, 0, 2, 10, 7, 0, 0, 2, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 4, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 5, 8, 1, 5, 7, 2, 1, 0, 0, 0, 1, 1, 0, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 1, 0, 0, 0, 0, 0, 5, 1, 1, 0, 6, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 4, 1, 0, 0, 0, 0, 0, 7, 8, 0, 0, 1, 0, 0, 0, 0, 0, 3, 56, 60, 8, 0, 1, 161, 252, 137, 40, 65, 62, 45, 46, 68, 68, 16, 0, 18, 97, 111, 100, 100, 95, 100, 111, 113, 108, 129, 137, 42, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 1, 1, 0, 11, 183, 217, 202, 207, 213, 221, 195, 189, 202, 211, 234, 99, 0, 0, 104, 182, 142, 141, 153, 141, 145, 126, 189, 255, 253, 45, 0, 18, 225, 255, 255, 255, 255, 255, 255, 255, 255, 255, 83, 0, 73, 255, 255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 177, 0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 3, 0, 62, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 103, 0, 1, 164, 255, 255, 255, 255, 255, 254, 255, 254, 255, 251, 46, 0, 8, 210, 255, 255, 254, 252, 253, 255, 253, 255, 180, 2, 0, 0, 181, 255, 255, 254, 255, 255, 254, 254, 255, 255, 255, 233, 23, 0, 4, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 1, 1, 1, 97, 255, 255, 255, 255, 247, 244, 241, 241, 244, 255, 156, 0, 0, 0, 34, 192, 208, 191, 190, 186, 186, 166, 218, 254, 249, 43, 0, 10, 225, 255, 119, 8, 26, 29, 28, 26, 28, 10, 0, 10, 1, 12, 57, 55, 52, 49, 36, 40, 35, 199, 255, 254, 255, 84, 0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 1, 0, 159, 255, 255, 255, 135, 1, 23, 22, 20, 22, 16, 3, 0, 7, 8, 0, 1, 0, 0, 1, 0, 0, 1, 74, 255, 255, 40, 0, 20, 248, 131, 1, 1, 0, 0, 0, 0, 1, 0, 3, 2, 4, 2, 0, 0, 0, 0, 0, 0, 0, 169, 254, 255, 255, 165, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 4, 1, 31, 240, 255, 255, 255, 117, 0, 1, 0, 0, 0, 0, 1, 3, 0, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 136, 255, 46, 0, 21, 246, 124, 0, 1, 0, 0, 0, 0, 2, 1, 1, 2, 1, 4, 0, 0, 0, 1, 2, 0, 1, 184, 255, 255, 254, 255, 55, 0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 1, 0, 148, 254, 255, 255, 255, 118, 0, 0, 0, 1, 2, 1, 0, 2, 1, 0, 3, 2, 1, 0, 0, 0, 2, 6, 1, 124, 255, 45, 0, 16, 250, 156, 0, 1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 1, 0, 0, 0, 0, 1, 0, 194, 255, 254, 255, 255, 171, 0, 0, 2, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 6, 0, 40, 245, 255, 255, 255, 255, 121, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 6, 0, 137, 255, 42, 0, 22, 240, 255, 93, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 196, 255, 255, 255, 255, 255, 80, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 137, 255, 255, 255, 255, 254, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 255, 255, 49, 0, 13, 219, 255, 148, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 255, 255, 255, 176, 202, 247, 31, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 2, 0, 64, 253, 255, 254, 255, 255, 255, 122, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 255, 250, 46, 0, 13, 222, 255, 113, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 0, 1, 0, 190, 255, 254, 99, 0, 134, 255, 173, 0, 0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 4, 0, 24, 248, 192, 40, 29, 128, 255, 255, 113, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 255, 255, 46, 0, 22, 227, 255, 138, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 188, 254, 197, 0, 54, 255, 255, 254, 153, 0, 1, 2, 2, 0, 0, 0, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 2, 3, 0, 1, 2, 1, 4, 7, 0, 23, 217, 255, 200, 24, 0, 94, 254, 255, 126, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 142, 255, 245, 38, 0, 17, 233, 255, 103, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 191, 255, 212, 8, 24, 181, 255, 255, 255, 186, 20, 0, 0, 3, 4, 2, 1, 2, 2, 0, 0, 0, 0, 1, 0, 2, 2, 0, 3, 2, 1, 3, 1, 0, 48, 230, 255, 255, 254, 84, 11, 84, 255, 255, 132, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 98, 254, 254, 39, 0, 18, 248, 143, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 4, 209, 255, 255, 108, 5, 4, 123, 255, 255, 255, 235, 75, 0, 0, 3, 5, 0, 4, 4, 0, 0, 1, 1, 1, 2, 0, 4, 1, 2, 4, 2, 0, 0, 109, 255, 255, 255, 254, 111, 4, 11, 165, 255, 255, 116, 1, 1, 2, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3, 1, 0, 139, 255, 48, 0, 18, 245, 126, 0, 1, 0, 1, 2, 0, 0, 2, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 183, 255, 255, 89, 1, 1, 6, 94, 239, 255, 254, 255, 186, 52, 0, 0, 1, 0, 6, 7, 0, 0, 0, 0, 2, 7, 6, 2, 0, 0, 0, 80, 211, 255, 255, 255, 179, 33, 8, 0, 5, 162, 254, 255, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 128, 255, 47, 0, 25, 255, 173, 0, 0, 0, 0, 0, 1, 0, 0, 4, 4, 2, 1, 0, 0, 0, 0, 0, 1, 0, 200, 255, 245, 48, 6, 0, 2, 11, 15, 140, 252, 255, 255, 255, 185, 74, 0, 0, 0, 4, 2, 0, 0, 2, 5, 2, 0, 1, 7, 90, 201, 255, 255, 255, 255, 113, 9, 12, 0, 0, 5, 134, 255, 255, 123, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 143, 255, 44, 0, 31, 241, 255, 140, 7, 29, 33, 29, 14, 28, 32, 10, 0, 0, 51, 65, 51, 54, 54, 55, 36, 55, 227, 255, 235, 69, 142, 200, 122, 55, 1, 1, 189, 255, 255, 255, 254, 255, 227, 194, 108, 3, 0, 0, 0, 1, 0, 12, 102, 165, 228, 255, 255, 255, 255, 255, 123, 0, 9, 92, 135, 130, 52, 149, 255, 255, 137, 0, 19, 23, 24, 11, 0, 21, 9, 0, 5, 0, 0, 0, 0, 0, 0, 0, 1, 80, 255, 255, 64, 0, 10, 213, 255, 254, 251, 249, 251, 247, 231, 249, 255, 107, 0, 32, 243, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 243, 240, 254, 255, 255, 255, 255, 254, 255, 254, 255, 65, 0, 0, 1, 0, 0, 121, 255, 255, 255, 255, 255, 255, 255, 255, 220, 213, 241, 251, 255, 255, 253, 252, 255, 255, 250, 233, 237, 239, 241, 228, 216, 254, 185, 0, 0, 132, 189, 164, 167, 156, 163, 161, 155, 228, 254, 255, 61, 0, 17, 221, 255, 255, 255, 255, 255, 254, 255, 255, 228, 37, 0, 0, 171, 255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 66, 0, 1, 0, 0, 1, 124, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 167, 0, 1, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 199, 1, 0, 21, 229, 255, 172, 79, 95, 102, 99, 95, 106, 46, 0, 2, 0, 36, 133, 138, 128, 127, 130, 132, 136, 140, 142, 141, 145, 145, 140, 142, 154, 159, 147, 157, 157, 143, 143, 146, 220, 255, 255, 153, 0, 0, 0, 0, 1, 0, 16, 200, 255, 255, 211, 148, 182, 180, 192, 203, 193, 188, 194, 196, 192, 187, 196, 189, 194, 196, 177, 187, 194, 201, 206, 220, 192, 29, 0, 0, 45, 175, 173, 158, 165, 164, 163, 137, 209, 255, 209, 12, 0, 26, 237, 255, 108, 1, 0, 0, 0, 0, 0, 0, 7, 5, 5, 3, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 152, 255, 254, 55, 1, 3, 0, 0, 0, 5, 0, 99, 255, 255, 122, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 3, 7, 5, 2, 1, 0, 0, 0, 0, 0, 0, 122, 255, 251, 43, 0, 30, 238, 255, 113, 0, 2, 0, 1, 0, 2, 2, 3, 3, 1, 2, 2, 2, 2, 2, 3, 5, 1, 1, 4, 3, 3, 3, 3, 4, 1, 1, 3, 1, 1, 1, 0, 1, 185, 255, 255, 87, 0, 1, 0, 1, 1, 0, 0, 141, 255, 255, 114, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 3, 2, 1, 0, 1, 0, 0, 0, 0, 0, 0, 142, 255, 255, 57, 0, 19, 228, 255, 120, 1, 0, 1, 0, 1, 0, 2, 0, 0, 0, 1, 2, 1, 1, 1, 2, 2, 1, 4, 2, 0, 0, 2, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 163, 255, 255, 74, 1, 1, 0, 0, 2, 0, 0, 141, 254, 255, 114, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 2, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 147, 255, 255, 54, 0, 30, 238, 255, 121, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 142, 255, 255, 66, 0, 1, 1, 0, 0, 1, 0, 128, 255, 255, 105, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 152, 255, 247, 40, 0, 25, 232, 254, 119, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 174, 255, 255, 71, 0, 1, 0, 0, 2, 0, 0, 143, 255, 255, 106, 1, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 153, 255, 250, 45, 0, 28, 235, 254, 142, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 190, 255, 255, 76, 0, 1, 0, 0, 0, 0, 0, 148, 255, 255, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 255, 248, 45, 0, 20, 244, 230, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 117, 255, 255, 75, 1, 0, 0, 0, 0, 0, 1, 145, 255, 255, 94, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 118, 255, 250, 38, 0, 40, 254, 117, 1, 5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 70, 146, 27, 0, 1, 0, 0, 0, 2, 0, 65, 197, 84, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 143, 255, 39, 0, 25, 253, 126, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 3, 2, 0, 0, 4, 4, 0, 0, 1, 0, 2, 4, 0, 0, 0, 4, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 121, 255, 43, 0, 27, 255, 137, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 1, 8, 25, 6, 1, 0, 0, 0, 0, 0, 4, 8, 11, 3, 2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 114, 255, 52, 0, 39, 254, 255, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 23, 201, 237, 27, 0, 0, 0, 1, 0, 1, 0, 39, 240, 141, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 26, 206, 255, 45, 0, 37, 244, 255, 131, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 98, 255, 236, 27, 0, 2, 1, 0, 0, 3, 0, 34, 255, 255, 52, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 138, 255, 245, 39, 0, 37, 245, 254, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 95, 255, 244, 32, 1, 0, 0, 0, 2, 1, 0, 55, 255, 255, 46, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 117, 255, 248, 39, 0, 33, 242, 255, 117, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 114, 255, 230, 21, 0, 1, 0, 0, 0, 4, 0, 51, 255, 255, 53, 0, 2, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 118, 255, 251, 43, 0, 29, 239, 255, 115, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 114, 255, 237, 35, 0, 1, 0, 0, 0, 5, 0, 45, 255, 254, 51, 0, 2, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 125, 254, 254, 48, 0, 34, 242, 255, 116, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 1, 3, 2, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 109, 255, 242, 36, 0, 1, 0, 0, 1, 2, 0, 51, 254, 255, 56, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 1, 0, 1, 124, 255, 246, 38, 0, 34, 245, 255, 111, 0, 1, 0, 0, 0, 0, 1, 0, 2, 2, 0, 0, 1, 4, 3, 7, 3, 2, 2, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 108, 255, 218, 7, 0, 0, 0, 1, 0, 3, 0, 44, 254, 255, 50, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 0, 0, 1, 3, 3, 1, 1, 3, 1, 1, 0, 1, 1, 4, 2, 2, 0, 0, 0, 0, 0, 0, 0, 128, 255, 243, 36, 0, 40, 246, 255, 157, 9, 23, 32, 30, 17, 29, 13, 0, 1, 0, 24, 61, 25, 0, 1, 0, 0, 1, 0, 0, 34, 32, 0, 1, 0, 1, 12, 6, 0, 2, 19, 22, 0, 128, 255, 240, 36, 0, 2, 1, 1, 0, 1, 1, 64, 255, 255, 64, 1, 10, 7, 10, 10, 16, 6, 0, 0, 0, 10, 35, 13, 1, 0, 0, 0, 0, 0, 0, 6, 8, 0, 0, 0, 0, 3, 8, 6, 7, 8, 2, 0, 150, 255, 248, 43, 0, 27, 229, 255, 255, 247, 242, 246, 243, 233, 254, 216, 46, 0, 43, 217, 255, 242, 61, 0, 23, 11, 18, 0, 94, 255, 255, 102, 2, 6, 121, 253, 228, 215, 220, 241, 250, 176, 197, 255, 255, 204, 6, 1, 0, 0, 2, 1, 8, 214, 255, 238, 243, 222, 230, 223, 225, 226, 255, 150, 0, 0, 49, 217, 255, 189, 22, 18, 37, 36, 41, 22, 75, 228, 250, 83, 0, 1, 44, 203, 245, 227, 226, 226, 224, 223, 249, 255, 251, 46, 0, 23, 231, 255, 254, 255, 255, 254, 255, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 252, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 254, 255, 255, 255, 255, 241, 33, 0, 1, 0, 1, 0, 14, 224, 255, 255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 254, 255, 253, 254, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 244, 39, 0, 10, 122, 153, 147, 144, 138, 142, 134, 131, 131, 138, 145, 136, 124, 122, 125, 139, 148, 151, 158, 139, 129, 143, 40, 74, 144, 135, 128, 127, 106, 109, 116, 106, 102, 99, 110, 117, 114, 97, 129, 96, 0, 0, 0, 0, 0, 0, 0, 102, 138, 112, 123, 116, 117, 107, 114, 131, 132, 140, 135, 135, 130, 112, 137, 86, 86, 118, 105, 101, 100, 108, 94, 93, 101, 103, 119, 120, 105, 100, 103, 102, 101, 99, 97, 95, 89, 83, 63, 7, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        // ARRAY OF PIXELS GOES HERE, COMMA + SPACE SEPERATED
        };
        
        void setup() {
        pinMode (0, OUTPUT);
        pinMode (1, OUTPUT);
        pinMode (2, OUTPUT);
        pinMode (3, OUTPUT);
        pinMode (4, OUTPUT);
        pinMode (5, OUTPUT);
        pinMode (6, OUTPUT);
        pinMode (7, OUTPUT); // PORT D
        
        pinMode (8, OUTPUT); // WR PIN (inverted)
        
        
        for (byte k = 0; k < strlen_P(img); k++) {
        
        digitalWrite(8, LOW); // inverted so this puts WR HIGH (to read)
        delay(10);
        
        myChar = pgm_read_byte_near(img + k);
        PORTD = myChar;
        
        digitalWrite(8, HIGH); // inverted so this puts WR LOW (to write)
        delay(10);
        
        }
        }
        void loop() {
        }
        
        

        Success finishing the board and recording Palladio distorted by the board :

         

         

         

        Here is the setup :

        I’m currently using two boards, but soon I’ll have the components to put everything on the board to the left. 

        The techniques I’m using is the one developed in the previous post :

        TO RECORD IN BURSTS: I can now save a short video : 695.45 kHz (This is the slowest I can go and still almost see an image) on the function gen AND’ed with the top 4 frequency divisions of the vertical sync. This makes for a burst of image recording every X number of frames.  I then amplify the output to make it visible on screen (and speed the clock up to 695.48 kHz). 
        TO STOP THE IMAGE JUMPING: If the reset is connected to the inverse of the freq subdivision of the vertical sync signal AND’ed with the address 20, it will play the image in a loop and always place it in the same part of the screen ! 
        EXPERIMENT : 1 bit recording 

        Recording a single bit of an image appears to produce almost the same image quality ! This means that in theory I could record 8 videos at the same time and output video to 8 screens simoultaneously !

         

        EXPERIMENT : Loading an image with Arduino
        Loading the tiny image once was a drop of water in the ocean so I recorded the image again and again until I ran out of space. This is taking a long time with a few 10ms delays in my code. 

         

        Here is the Arduino sending bits (captured after DAC) :

         

        but so far nothing that comes out looking like an image on the other side…

        I also can’t get my VGA to HDMI converter working…

        Here’s an attempt at a grid of raster variations :

        Here’s the final version of the board :

         

        Memory Experiments

         

        https://github.com/merlinmarrs/Video-Memory

        This project started with an ambitious board combining ADC, DAC and SRAM. 

         

        There were too many issues so I broke the board down into two parts: a memory board and a ADC/DAC board. This allowed me to isolate things and proceed in a step by step way.

        I discovered that the memory needed a WR signal inbetween clock address refreshing, and for the second board that my op amp choice was inappropriate and needed to work around it (by using the DAC only as an R2R ladder and skipping the op-amp for the ADC input). 

         

        During assembly I found that several 0 ohm jumpers were causing shorts. I also tore out the 8 pin header but applying too much force accidentally. Otherwise the switch system was very effective to test my understanding of the memory chip operation. It can essentially record a series of button manipulations for about 1.5 minutes.  

        Here is my first successful “recording” of a sine wave through the ADC and into SRAM and play back from SRAM through the DAC :

        BOARD ERRATA:

        -need last bit of address counter (cuts memory in half !)

        -more jumpers should be broken out for testing (like alternative op amp for input, clk for various chips, etc.)

        -leds burn out when activated and output plugged into 5V at the same time…

        -GND and 5V switched on the two boards 🙁

        -Wrong op amp spec’d. The ADC1175 suggests the LMH6702MF and a choke between power supplies. 

        -caps on all ICs

        -the LEDs I set up on the microchip are plugged into ADC…

        -Add enable header on microchip board

        -power LED on the ADC board

        -add a MSB and LSB next to LEDs on memory board **

        I calculated how much I could store in the 4K of the SRAM I am currently using, and it would be a single line of a 600×800 screen at 40Mhz pixel frequency…

        So what can I do with this ? I could record 4,000 pixels of a static image (i.e. lower the resolution of the image to 100×400 for instance) and then play it back. This would require me to control the sampling of the ADC with the CLK pin of the ADC1175 for every X pixels, somehow synchronized with the VGA in signal (use timing signals of an incoming VGA signal set), or to generate a custom VGA output signal with different timing using the VGAX library. 

        Some important references : 

        nootropicdesign.com/video-experimenter/build

        gieskes.nl/visual-equipment/?file=gvs1

        I am looking into what I could draw on screen with only 4K memory at a 20MHz clock.

        EEPROM and SD cards are also interesting before reaching the holy grail of magnetic recording media of course…

        I have the AT28C256F-15TU EEPROM chip which has 256K x 8 bits (enough to at least store an entire 600×800 static screen) and the ADV7125STZ50 8 bit triple HS video DAC. 

        https://www.mouser.fr/datasheet/2/268/doc0006-1108095.pdf

        https://www.mouser.fr/datasheet/2/609/ADV7125-1503638.pdf

        I’d like to have a standalone arduino VGAX based board (https://github.com/smaffer/vgax) which can output 120×60 pixels and reads from internal SRAM and can record and playback. With 256K I could record a static image on screen at a higher resolution or a low resolution short video.

        I have already made an SD card image saving board: 

        Just realized what I’m trying to do is called a frame grabber and it’s a massive PCB :

        https://en.wikipedia.org/wiki/Frame_grabber#:~:text=A%20frame%20grabber%20is%20an,or%20a%20digital%20video%20stream.&text=Historically%2C%20frame%20grabber%20expansion%20cards,to%20interface%20cameras%20to%20PCs.

         

        *********************************************************************************

        I have designed a similar memory board but using a 256K EEPROM chip in place of the  SRAM and with VGA connectors to begin testing images. (There do appear to be larger SRAMs, going up to 16MB. There are obviously large flash memories in SD format going into the GBs which may also be interesting to play with).

        -Added out VGA to work with VGAX library (not sure if need a second microchip to handle just the memory stuff?)

        -Added a 16MHz resonator

        -With two 8 bit counters to have the full 13 bits to control this time.

        -EEPROM will keep a recording after power down.

        -can control buffers with microchip

        Just learned that EEPROM is probably the slowest…

        Just a thought but I could save control signals for the 0c74 instead of saving actual pixel information…

        *************

        First test (WORKING):

        int switchState = 0;

        pinMode(6, OUTPUT); // CLK LED (*)
        pinMode(7, OUTPUT); // CLK

        pinMode(10, OUTPUT); // WR/RD SIGNAL TO SRAM
        pinMode(A3, INPUT); // WR/RD SWITCH (WR HIGH, READ LOW)

        pinMode(8, OUTPUT); // WR LED
        }

        // the loop function runs over and over again forever
        void loop() {

        switchState = digitalRead(A3);

        // check if the pushbutton is pressed.
        // if it is, the buttonState is HIGH:
        if (switchState == LOW) {
        // WR selected on switch so turn WR LED on and send a LOW to SRAM

        digitalWrite(8, HIGH);
        //
        digitalWrite(10, HIGH);
        digitalWrite(6, HIGH); // 
        digitalWrite(7, HIGH); // 
        delay(10);
        digitalWrite(10, LOW);
        digitalWrite(6, HIGH); // 
        digitalWrite(7, HIGH); // 
        delay(10); // 
        digitalWrite(10, LOW);
        digitalWrite(6, LOW); 
        digitalWrite(7, LOW); 
        delay(10); // 
        digitalWrite(10, HIGH);
        digitalWrite(6, LOW); // 
        digitalWrite(7, LOW); // 
        delay(10); // 

        }
        else {
        // READ selected on switch so turn WR LED OFF on and send a HIGH to SRAM
        digitalWrite(10, HIGH); // STAYS HIGH THE WHOLE TIME
        digitalWrite(8, LOW);
        //
        digitalWrite(6, HIGH); // 
        digitalWrite(7, HIGH); //
        delay(20); // 
        digitalWrite(6, LOW); // 
        digitalWrite(7, LOW); // 
        delay(20); //

        }

        }

        This is what it produces:

        Second test (WORKING): 

        int switchState = 0;

        // the setup function runs once when you press reset or power the board
        void setup() {
        // initialize digital pin 13 as an output.
        pinMode(6, OUTPUT); // CLK LED (*)
        pinMode(7, OUTPUT); // CLK

        pinMode(10, OUTPUT); // WR/RD SIGNAL TO SRAM
        pinMode(A3, INPUT); // WR/RD SWITCH (WR HIGH, READ LOW)

        pinMode(8, OUTPUT); // WR LED
        }

        // the loop function runs over and over again forever
        void loop() {

        switchState = digitalRead(A3);

        // check if the pushbutton is pressed.
        // if it is, the buttonState is HIGH:
        if (switchState == LOW) {
        // WR selected on switch so turn WR LED on and send a LOW to SRAM

        digitalWrite(8, HIGH);
        //
        digitalWrite(10, HIGH);
        digitalWrite(6, HIGH); // turn the LED on (HIGH is the voltage level)
        digitalWrite(7, HIGH); // turn the LED on (HIGH is the voltage level)
        delayMicroseconds(1);
        digitalWrite(10, LOW);
        digitalWrite(6, HIGH); // turn the LED on (HIGH is the voltage level)
        digitalWrite(7, HIGH); // turn the LED on (HIGH is the voltage level)
        delayMicroseconds(1); // wait for a second
        digitalWrite(10, LOW);
        digitalWrite(6, LOW); // turn the LED on (HIGH is the voltage level)
        digitalWrite(7, LOW); // turn the LED on (HIGH is the voltage level)
        delayMicroseconds(1); // wait for a second
        digitalWrite(10, HIGH);
        digitalWrite(6, LOW); // turn the LED on (HIGH is the voltage level)
        digitalWrite(7, LOW); // turn the LED on (HIGH is the voltage level)
        delayMicroseconds(1); // wait for a second
        // wait for a second
        }
        else {
        // READ selected on switch so turn WR LED OFF on and send a HIGH to SRAM
        digitalWrite(10, HIGH); // STAYS HIGH THE WHOLE TIME
        digitalWrite(8, LOW);
        //
        digitalWrite(6, HIGH); // turn the LED on (HIGH is the voltage level)
        digitalWrite(7, HIGH); // turn the LED on (HIGH is the voltage level)
        delayMicroseconds(1); // wait for a second
        digitalWrite(6, LOW); // turn the LED on (HIGH is the voltage level)
        digitalWrite(7, LOW); // turn the LED on (HIGH is the voltage level)
        delayMicroseconds(1); // wait for a second

        }

        }

        And here are some shots of what three different speeds (10ms, 10microseconds, 1 microseconds) as delay in the Arduino code :

         

        ***********************************************************************

        Third test (NOT WORKING):

        I recorded a sine wave at 100KHz (with WR activated on the microchip board) with the ADC being clocked at 10KHz. I made sure I had the ADC selected with the right buffers and then sent this 8 bit sequence to the memory where I recorded it with a new Arduino code:

        void setup()
        {
        DDRD = B11111111; // set PORTD (digital 7~0) to outputs
        DDRB = B11111111; // set PORTB (digital 7~0) to outputs
        }

        void loop()
        {
        if((PINC & 0b00001000)==0)
        {
        PORTD = B11111111; // set PORTD pins (digital 7~0) high
        PORTB = B00000000; // set PORTD pins (digital 7~0) low
        PORTD = B00000000; // set PORTD pins (digital 7~0) low
        PORTB = B11111111; // set PORTD pins (digital 7~0) high

        }
        else
        {
        PORTB = B00000100; // WR PULLED HIGH SO TURNED OFF
        PORTD = B11111111; // set PORTD pins (digital 7~0) high
        PORTD = B00000000; // set PORTD pins (digital 7~0) low
        }
        }

        /*
        CLOCK:
        _____ _____
        | ` | | |
        ___| |___| |____

        WR:
        _________ ____________
        ` | |
        |___|

        */

        I then switched to the DAC with the buffers and switched into READ mode on the microchip board.  Now the 8 bit sequence was being translated back into a sine wave. I took this sine wave and amplified it with an audio amp and sent it to the three color channels combined with an arduino running the VGAX pattern code without the colors plugged in and only H and V SYNC.

        I got this on the screen :

        Here is the tentacular and unoptimized setup :

        To make this actually easy to use, I need to:

        -have the microchip control the buffers and the ADC clocking based on the switch state.

        -have the memory and adc on a single board.

        -have a VGAX out setup integrated into the memory board.

        -have an op amp on the ADC board to amplify the output signal !!

        -have a dial change the speed of the playback / sampling of recording.

        *******************************************

        I added an external crystal and set the AVR dude Fuses to : Ext. Crystal Osc.8.0 – ___MHz 16K CK/14 CK + 65 ms. 

        I added F_CPU 16000000UL ABOVE the #include. 

        Forgot that if you don’t add 0b before a binary number nothing happens with the pins !

        Here is the code that still isn’t working :

        /*
        * eeprom board v.2.c
        *
        * Created: 06/02/2022 09:19:50
        * Author : Jonah
        */

        #include <avr/io.h>

        int main(void)
        {

        DDRC = 0b00000000; // PC3 is the switch for WR (0) or RD(1)

        DDRD = 0b11111111; //PD5 is CLK and PD4 is !OE

        DDRB = 0b11111111; //PB0 is the LED and PB2 is !WE

        while (1)
        {

        /*
        BYTE WRITE: A low pulse on the WE or CE input with CE or WE low (respectively) and OE high initiates a write
        cycle. The address is latched on the falling edge of CE or WE, whichever occurs last. The data is latched by the first
        rising edge of CE or WE. Once a byte write is started, it will automatically time itself to completion. Once a
        programming operation is initiated and for the duration of tWC, a read operation will effectively be a polling operation.
        */

        if((PINC & 0b00001000)==0) // WRITE MODE: (CE -> TIED LO); !WE -> TOGGLE ; !OE -> HI ; CLK -> TOGGLE
        {
        PORTD = 0b00010000; // !OE -> HI ; CLK -> LO
        PORTB = 0b00000000; // !WE -> LO
        PORTB = 0b00000001; // !WE -> LO (to make the pulse width at least 100ns)
        PORTB = 0b00000101; // !WE -> HI
        PORTD = 0b00110000; // !OE -> HI ; CLK -> HI (next address must come a max of 50 ns following !WE going HI?)

        }

        /*
        READ: The AT28C256 is accessed like a Static RAM. When CE and OE are low and WE is high, the data stored at
        the memory location determined by the address pins is asserted on the outputs.
        The outputs are put in the highimpedance state when either CE or OE is high. This dual-line control offers designers flexibility in preventing bus
        contention in their system.

        */

        else // READ MODE : (CE -> TIED LO); !WE -> HI ; !OE -> (TOGGLE ? OR JUST LO?) ; CLK -> TOGGLE
        {
        PORTB = 0b00000101; // !WE -> HI
        PORTD = 0b00100000; // !OE -> LO ; CLK -> HI
        PORTD = 0b00000000; // !OE -> LO ; CLK -> LO
        }
        }
        }

        **********************************************************************************

        One question I have is: does the EEPROM require OE to go on and off when reading unlike the SRAM I worked with in the previous board ?

        Another question is, how long does the EEPROM need to write a byte ? It talks about “access times of 150ns”…?

        Is it OK I have CE tied low the whole time ?

        As a next step I will try doing all of this but very slowly to see if that works. 

        *EDIT : tried slowing everything down and I’m still not able to load anything into the memory :(. I just get 5V and little pops of ground after writing ground into every cell of the memory. This is after changing !OE toggle and always ON also. 

        I did get the VGAX code running though so at least that works. 

        ERRATA:

        -the labeling of WR and RD are confusing, when the switch is to the RD side it is in WR mode etc. 

        -the VGA parts are COMPLETELY wrong. 

        -I need to order 16MHz of this format (I took one from a dead Arduino board). 

        -the text is too small and came off during cleaning.

         

        TROUBLESHOOTING AT28C256 EEPROM:

        -Need to wait a second before doing and writing or reading,

        -Need to either wait the maximum time for the EEPROM to write (Write Cycle Time Max AT28C256 = 10ms) or poll 1/0 7 to wait until it matches what you sent it before starting another write cycle.

        -The device may be locked (even though the devices should ship unlocked) :(. The unlock sequence is :

        LOAD DATA AA
        TO
        ADDRESS 5555

        LOAD DATA 55
        TO
        ADDRESS 2AAA

        LOAD DATA 80
        TO
        ADDRESS 5555

        LOAD DATA AA
        TO
        ADDRESS 5555

         

        LOAD DATA 55
        TO
        ADDRESS 2AAA

        LOAD DATA 20
        TO
        ADDRESS 5555

        LOAD DATA XX
        TO
        ANY ADDRESS(4)

        LOAD LAST BYTE
        TO
        LAST ADDRESS

         

        ************************

        Here are some thoughts I have on the SRAM device :

        Do I want addressed to loop or not ? (Add inverter if want to loop)

          >> Make it optional with a jumper, this expands the possibilities of experimenting.

        Max sample rate I can get from SRAM ? 
        >>Looks to be around 20MHz if the read time is 50ns ?
        What happens if I slow a recorded signal down with a knob, what visual effects produced ?
        >> I need a bunch of recorded stuff and a super fast clock or else I just output bands everyonce and a while.
        Bigger SRAM same brand? need more counters?

          >> Check out this super fast 1Mx8bit SRAM

        61-64WV10248EDBLL-276598.pdf (mouser.fr)

        Two memories ? Feedback from one memory possible ?
        Jumpers to either have vgax doing syncs or input vga.
        Record at slow speed play back at high speed ? Keeps theme of time and computation…
        How have vgax send clock and wr signal for memory circuit while doing vgax timing ? Or have separate clocks (high speed resonator) and sync somehow ? ( Or generate my own sync sign also in logic ?)
        How actually use and function with two vgas ?
        It would be cool to be able to record an image, or parts of a series of images, on an input vga signal and then play them back at different speeds and doubled with the original. This seems really challenging to do accurately but could be cool to stretch an image or sample every line from an animation.

        Also would be cool to be able to output blocky animations based on memory. Could vgax output memory values? Then could compose.

        Could the vgax do a kind of low fi screen capture ? This is a ripoff of gieske though…
        Could this thing be a composition machine, where you slowly switch 8 pin dip switches and take a “photo” of each finished byte ? Then play back at higher speeds?
        I started this board wanting to learn about memory. What have I learned ? Could have button to reset address counters, knob for clock speed, but what other Params?
        Could take memory and output it as pattern by vgax ?
        Do all first with Arduino analog read  ?? Why is this better than Arduino ? What advantage, what has it allowed me to learn ? Faster ? Physicalized memory space ?
        **********
        Retried this code modified slightly from this tutorial : https://scanlines.xyz/t/tutorials-for-generating-video-sync-signals-with-arduino/104

        #define LINE_CYCLES 508
        #define HSYNC_CYLCLES 60
        #define VSYNC_LINES 2
        #define FRAME_LINES 525
        #define PIXEL_FREQ 1

        volatile int linecount;

        void setup(){
        pinMode(7, OUTPUT); // VSync
        pinMode(9, OUTPUT); // HSync
        pinMode(6, OUTPUT); // Pixel frequency

        //inverted fast pwm mode on timer 1
        TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(WGM11);
        TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);

        ICR1 = LINE_CYCLES; // Overflow at Cycles per line
        OCR1A = HSYNC_CYLCLES; // Compare high after HSync cycles

        //timer 0 in CTC mode
        TCCR0A = _BV(COM0A0) | _BV(WGM01) ; // Toggle pin on each cycle, no CPU clock prescaling/
        TCCR0B = _BV(CS00); // no clock prescaling
        OCR0A = PIXEL_FREQ; // go super fast

        TIMSK1 = _BV(TOIE1); // Enable timer overflow interrupt
        }

        ISR(TIMER1_OVF_vect)
        {

        if (linecount == 0) PORTD = PORTD ^ 0x80; //Toggle the most significant bit of port D.;
        else if (linecount == 2) PORTD = PORTD ^ 0x80; //Toggle the most significant bit of port D.;
        else if (linecount == 3) PORTD = PORTD ^ 0x80; //Toggle the most significant bit of port D.;
        else if (linecount == FRAME_LINES) linecount = 0;
        linecount++;
        }

        void loop(){}

        *******************

        Works well. Also tried generating my own V and H sync with logic and a 4040 counter from this tutorial : https://hackaday.io/project/9782-nes-zapper-video-synth-theremin/log/32271-vga-sync-generation

        It didn’t work for me. The reset of the 4040 didn’t want to be connected to anything but GND, otherwise the whole thing was inoperational. 

        I also messed around again with floppy disk writing. I got the floppy disk motor turning, plugged the index into step, activated the drive select, and connected direction to either 5V or GND. I soldered a connector for the 5 wires froming from the recording magnets. I tried blasting them with a sine wave added to a 10KHz square wave but saw no sign of the signals when playing back the floppy. 

        I have a new plan: Triple SRAM device for copying and pasting a loop of very blocky “video” :

         

        *****************************************************************************************

        Here’s what I learned :

        version 1 :

        I need to make a smd breakout board for chips or order DIP versions so that I can do tests first. This would save a lot of time designing and redesigning boards. The idea for a minimal integrated board was good though. Break more pins out.

        version 2 :

        BOARD A : I need to make a more stable way to solder pin headers to the card so they don’t rip off easily. I should use buffers to protect the LEDs. There are also way too many jumpers between the the counter and SRAM. 

        BOARD B : The op amps don’t work. The DAC doesn’t work as I wired it. I forgot to connect the CLK of the DAC.  

        It’s a mess to connect the two boards, make the right connections. The two boards need to be integrated. 

        version 3 :

        The EEPROM doesn’t work, is it locked ? I also used the wrong VGA pins. 

        version 4 :

        It’s a beautiful board but the DAC was VERY challenging to solder by hand. I think the level of complexity and ambition of the board was a little bit too high for this production method. I think I should head back to a board that is easy to assemble, easy to test and understand. 

        version 5: 

        The idea : using buffers I can have LEDs display what is in the memory while also outputing analog output. There are fewer jumpers than in earlier versions. I am planning to use the inverter to control the SRAM WR which appears to be possible based on the datasheet. Simplified DAC with R2 ladder, I can control A12 with a switch and have an A11 LED now. I got rid of the VGA functionality – this makes things simpler. I chose switches over shunts to make connections.

        I want to make 3 of these boards, ABC, so that they can each record in two 4K memories, and then do cool logic transformations and save the results too. Connecting the ADC to the SRAM inputs requires 8 jumpers, and I’ve done away with 8 pin dip switches. To manually set data you connect the jumpers to the spare + and – headers. 

        I would like the decisions to be guided by how I could practically use this device to “record” 8 bits of data / an analog signal for a given period of time (based on the clock speed) and then play it back (while modifying the clock speed manually, and overwriting portions of the memory, possibly from another pair of memory boards) to display on a screen (with another board handling the VGA signals and optionally sharing its clock with this board to keep things in sync). I want to move to something that produces output ASAP, and away from endlessly redesigning these boards looking for something perfect. (If this works I want to make 3, then a specialized VGA board too).

        I’ve added reverse polarity protection, a STEP button, which is hardware debounced, to manually advance one address at a time. I kept the op-amp modular so that it could be used to amplify analog in or out. I switched out the edge accessible thumb pots for normal pots, so this thing is no longer really stackable.

        Not sure exactly how they stack (but many things accessible on the edges) though and not sure if I should have a battery included or not so that it remembers what it recorded…

        Version 5 UPDATE :

        Thoughts on possible  version 6 additions :

        Backup battery to record option ?

        8pin dip switch with header somewhere on board?
        Question I have to resolve with current version :
        What can I record (in analog and in digital) and for how long ?
        What does it look like on the screen with and without a generated pixel clock to do the sync?
        Going at pixel frequency speed I can draw different repeating textures on the screen when I record very slowly and then speed it up :

        Patterns are repeating, it’s like a texture. Sending a non-repeating signal, like the arbitrary function signal which leaves a non-repeating pattern on screen, seems like a cool thing to try. Another option is to have a single block of memory but to jump around in how you address it somehow ?
        How fast and how slow can it go ?
        With a 10K resistor and a 0.1uF for the CD4046 I have a max sampling speed of 1Khz that is barely enough to represent a 100 Hz sine wave.
        Apparently this max is called the Nyquist frequency :

        Nyquist Frequency=Fmax = SampleRate/2

        The SRAM module IS61C64AL can run up to 20MHz according to the datasheet (https://www.issi.com/WW/pdf/61C64AL.pdf)

        When I feed it the 3MHz pixel frequency from the modified VGAX it can record a 1.5MHz sine wave and play it back ! The problem when it is going slow is that it just outputs bands every now and then. It could act as a memory of a control signals which would change parameters in a circuit which generates more interesting output. (It could even control an analog computer like this https://the-analog-thing.org/ ).
        The other option would be to have a super fast clock that is connected to the V and H sync, one that I can divide and use to control recording of a large SRAM memory.
        Would second mem board be cool ?
        Not unless I get a bigger SRAM I think and daisy chain a series of 8 bit counters :
        https://www.mouser.fr/datasheet/2/198/62-65WV10248EALL-BLL-462620.pdf
        VERSION 5 UPDATE :

        Here are the issues :
        -I added a reset for the counters to go back to address 0. It takes a while to kick in though – I think because it resets the counter but not the 8 bit storage register ?
        -I added an LED to show when ADD21 is activated.
        -the 74F541 does not like 5V. I replaced it with an 74HCT254 and pulled the DIR pin HIGH.
        -the ADC1175 operates at 4.5-5.5V ! (I will test it’s max speed – supposedly 20MHz by powering it with 5V)
        -One pin connection missing on 8 pin dip switch
        -The phase shifter sucks and the amp doesn’t currently work.
        -CS should be held down and not toggled with WE.
        -RCO for cascading counters was not working at higher frequencies – I switched to sending the last Qout of each counter to the CLK in. 
        -I don’t have a way of taking 5V analog signals and stepping them down to 3.3V.
        -The memory should default to floating (instead of ground?) so that I can re-record over previous stuff. (Currently when I re-record, if a pin is not set it defaults to being black and just erasing everything).
        -need a 3.3V to 5V booster if you want to interface with another 5V memory. It would be nice to have an in-line 8 plug so I don’t need to plug in each wire one by one…
        -I can’t currently take VGA in at 400×800 from a computer (amplified) and saved it into the memory (it’s pixel clock is at 40MHz, I set the function generator to 4MHz). But I can record an image imperfectly from the VGAX color out at around 1.3038210 MHz.
        -I need to amplify at the input and the output (and ideally be able to offset them both and up and down – especially to hit the middly of the CMOS threshold between low and hi if I want to record in image in b&w with a single bit). The next version of the board would have two amps.
        -When I play back a recording, each time the memory restarts at zero, the frame of the video moves. (To solve this I need the freq divider, AND chip and inverter).
        -Pin 11 of the ADC should be connected to 3.3V while pin 13 should be connected to 5V.
        Possible improvements:
        -a 5V power in with 3.3V LDO so I can power things with both ?
        -if I want to upload something to memory, and then change to outputing, I have to disconnect all the wires that were connected to it previously. I should have a 74HCT254 Bus transciever that can change directions to disconnect a set of 8 wires from the memory. ALTERNATE OPTION : Just keep everything plugged in and turn off ADC?
        -Maybe an LED that tells me I’m recording (easy not to notice that it’s not in recording mode and then connect it to outputs that are pulling it up and down). 
        -Would be cool to be able to select which chunk of memory I’m replaying over with two knobs that control the start point and end point. (This could be two comparators connected to two pots ?)
        Developements:
        I can load values into the memory from Arduino’s PRGMEM like so:
        +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        /* TO LOAD DATA INTO THE MEMORY */

        #include <avr/pgmspace.h>

        byte myByte;

        #define IMG_PIRATE_BWIDTH 30
        #define IMG_PIRATE_HEIGHT 60
        //data size=900 bytes
        const byte img_pirate_data[IMG_PIRATE_HEIGHT*IMG_PIRATE_BWIDTH] PROGMEM={
        0, 7,255,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 63,252, 6, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,192, 0, 0, 0, 0, 0,
        2,255,112, 96,129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        15,222, 2, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0,
        255,240, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
        252, 65,144, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0,
        248, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,144, 0, 0, 0, 0, 0, 0,
        65,132, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0,
        0, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
        36,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0,
        65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0,
        8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0,
        16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,192, 0, 0, 0, 0, 0, 0, 0, 0,
        32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128,130,168,160, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34,138, 42, 32, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        16, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34,136, 32,130, 0, 0, 32, 0, 0, 0, 1, 69, 64, 80, 0, 20, 5, 4, 0,
        32, 0, 0, 0, 0, 0, 0, 8,170,160, 0, 0,170,170, 40, 42,170,136, 0, 0, 0, 0, 64, 17, 4, 68, 64, 16, 68, 0,
        0, 0, 0, 0, 0, 0, 0,162, 10, 2,170,170,170,170,168, 32, 0, 34, 0, 0, 0, 0, 65, 65, 4, 16, 84, 16, 68, 0,
        64, 0, 0, 0, 0, 0, 10,130,171,239,255,238,170,170,160,162,170,162, 0, 0, 0, 0, 68, 1, 4, 16, 65, 16, 64, 0,
        36, 0, 0, 0, 0, 0, 8, 42,187,250,170,170,187,238,250, 0, 0, 34, 0, 0, 0, 0, 69, 80, 80, 68, 20, 5, 4, 0,
        0, 0, 0, 0, 0, 0, 34,170,255,234,170,170,171,187,178, 60, 0,170,160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        152, 0, 0, 0, 0, 0, 0,171,254,170, 0, 2,186,255,248, 61, 3, 2, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        1,196, 0, 0, 0, 0, 10,187,254,168,112, 4,171,191,248, 54, 0, 40,136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 8,198, 96, 0, 0, 42,191,250,163,240, 14,174,255,254,136,170,138, 34,128, 1,128, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0,139,191,250,168, 53, 26,171,255,252,138, 34,186,162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 42,251,250,170,170,170,174,191,254, 34,187,174,232,128, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 2,170,174,255,186,170,170,187,255,255, 34,234,251,170,128, 0,128, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 1, 0, 0,138,187,187,171,170,238,187,191,255,138,175,255,238, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 10,239,255,186,186,170,238,239,255,224,235,255,250, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 46,175,255,255,238,186,190,255,255,248,171,255,250,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0,170,191,255,255,250,171,187,239,255,254, 42,255,238, 32, 0,144, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 42,238,255,255,251,170,174,187,255,254,174,190,234,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 1,128, 0, 0,174,187,255,255,254,235,171,255,255,254, 10,171,170,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 32, 0, 0, 0,138,174,255,255,255,186,163,255,255,255,162,186,186,128, 0, 4, 36, 0, 0, 0, 0, 0, 0, 0,
        0, 4, 0, 0, 0, 0, 43,187,239,255,254,170,234,170,174,234, 34, 42,168, 0, 0, 8, 2, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 10,174,186,255,251,171,168,128, 10,128, 2, 34,226, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0,
        0, 0, 64, 0, 0, 0, 2,171,239,187,254,234,170, 40, 0, 0, 0, 40, 32, 0, 0, 2,170,168, 0, 0, 0, 0, 0, 0,
        0, 0, 40, 0, 8, 0, 0, 42,190,239,190,186,128, 0, 0, 0, 0, 0,160, 0, 0, 64,170,170,160, 0, 0, 0, 0, 0,
        0, 2,160, 0, 60, 0, 0, 42,171,190,250,168, 0, 0, 10,170,168, 0, 0, 0, 0,128, 16, 10,168, 0, 0, 0, 0, 0,
        0, 42,128, 2,240, 0, 0,130,234,186,170,160, 0,170,170,170,174,128, 0, 0, 0, 6, 0,170,170,128, 0, 0, 0, 0,
        0,170,128, 47, 0, 0, 0, 10, 42,170,170,128, 42,170,175,255,250, 32, 0, 0, 1, 0, 42,170,170,168, 0, 0, 0, 0,
        10,170, 64, 40, 48, 0, 0, 0, 40,162,170, 0,171,255,255,255,234,136, 0, 0, 2, 0, 2,170,170,170, 0, 0, 0, 0,
        170,170, 0,160,176, 0, 0, 0, 0,130,168, 2,191,186,175,250,170, 32, 0, 0, 1, 56, 0,170,170,170, 0, 0, 0, 0,
        170,170,130,130, 64, 0, 0, 0, 2, 8,168, 10,239,255,234,170,170,136, 0, 0, 16, 60,128,170,170,170, 0, 0, 0, 0,
        170,170,128,138, 0, 60, 0, 0, 0, 8, 32, 2,255,255,255,234,186,128, 0, 0, 0, 7,232, 42,170,170,128, 0, 0, 0,
        170,170,160,168, 0,252, 0, 0, 0, 0, 0, 10,175,255,255,251,170,128, 0, 0, 0, 0, 26, 42,170,170,128, 0, 0, 0,
        170,170,168, 8, 0,240, 32, 0, 0, 0, 0, 8,190,187,235,238,168, 32, 0, 0, 0, 0, 4,170,170,170,160, 0, 0, 0,
        170,170,160,170, 0,240,192, 0, 0, 0, 0, 0,170,254,238,170,186, 0, 0, 0, 0, 0, 72, 42,170,170,168, 0, 0, 0,
        170,168, 0,170,128, 2, 64, 0, 0, 0, 0, 0, 2,171,170, 58,160, 0, 0, 0, 0, 0, 2, 10,170,170,170,128, 0, 0,
        170,170, 42,170,168, 3, 0, 0, 0, 0, 0, 0, 2, 10, 42, 8, 0, 0, 0, 0, 0, 0, 1, 42,170,170,170,168,128, 0,
        170,170,170,170,169, 8, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 18, 42,170,170,170,170,168, 0,
        170,170,170,170,168, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54,128, 0, 42,170,170,170,170,170, 0,
        170,170,170,168, 36, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,188, 96, 15,170,170,170,170,170,170, 0,
        170,170,170,170, 0,130, 14, 96, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 95, 0, 63,234,170,170,170,170,170,128,
        170,170,170,170,170,170,143,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0,249,240, 63,234,170,170,170,170,170,128,
        170,170,170,170,170,170,144, 63,240, 0, 0, 0, 0, 65, 0, 0, 0, 15,192, 0, 63,144, 63,170,170,170,170,170,170,160,
        170,170,170,170,170,170,175,239,240, 0, 1, 0, 0, 2, 24, 0, 0, 15,252, 0, 7,228, 15,218,170,170,170,170,170,168,
        170,170,170,170,170,170,168,255,240, 48, 64, 0, 13,129, 1, 0, 0, 3,255, 0, 0,114, 15,250,170,170,170,170,170,170,
        170,170,170,170,170,170,170, 62,192,188, 0, 0, 63,126,192, 36, 0, 2,127,208, 0,185,191,250,170,170,170,170,170,170,
        };

        void setup() {

        Serial.begin(9600);

        pinMode(7, OUTPUT); // I/O 8
        pinMode(6, OUTPUT);
        pinMode(5, OUTPUT);
        pinMode(4, OUTPUT);
        pinMode(3, OUTPUT);
        pinMode(2, OUTPUT);
        pinMode(1, OUTPUT);
        pinMode(0, OUTPUT); // I/O 0

        pinMode(8, OUTPUT); // CLK

        for (byte k = 0; k < 200; k++) {
        myByte = pgm_read_byte_near(img_pirate_data + k);
        Serial.print(myByte);
        digitalWrite(8, LOW); //CLK LOW
        delay(1);
        PORTD = myByte;
        digitalWrite(8, HIGH); //CLK HIGH
        delay(1);
        }
        for (byte k = 0; k < 200; k++) {
        myByte = pgm_read_byte_near(img_pirate_data + 200+ k);
        Serial.print(myByte);
        digitalWrite(8, LOW); //CLK LOW
        delay(1);
        PORTD = myByte;
        digitalWrite(8, HIGH); //CLK HIGH
        delay(1);
        }
        for (byte k = 0; k < 200; k++) {
        myByte = pgm_read_byte_near(img_pirate_data + 200+ k);
        Serial.print(myByte);
        digitalWrite(8, LOW); //CLK LOW
        delay(1);
        PORTD = myByte;
        digitalWrite(8, HIGH); //CLK HIGH
        delay(1);
        }
        for (byte k = 0; k < 200; k++) {
        myByte = pgm_read_byte_near(img_pirate_data + 200+ k);
        Serial.print(myByte);
        digitalWrite(8, LOW); //CLK LOW
        delay(1);
        PORTD = myByte;
        digitalWrite(8, HIGH); //CLK HIGH
        delay(1);
        }

        Serial.print(“FINISHED”);
        }

        void loop() {

        }

         

        ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        I have a different Arduino to trigger the memory on each pixel and generate the sync signals :
        /* TO TRIGGER MEMORY CLOCK EACH PIXEL AND GEN VGA SYNCS */

        #define LINE_CYCLES 508
        #define HSYNC_CYLCLES 60
        #define VSYNC_LINES 2
        #define FRAME_LINES 525
        #define PIXEL_FREQ 1

        volatile int linecount;

        void setup(){
        pinMode(7, OUTPUT); // VSync
        pinMode(9, OUTPUT); // HSync
        pinMode(6, OUTPUT); // Pixel frequency

        //inverted fast pwm mode on timer 1
        TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(WGM11);
        TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);

        ICR1 = LINE_CYCLES; // Overflow at Cycles per line
        OCR1A = HSYNC_CYLCLES; // Compare high after HSync cycles

        //timer 0 in CTC mode
        TCCR0A = _BV(COM0A0) | _BV(WGM01) ; // Toggle pin on each cycle, no CPU clock prescaling/
        TCCR0B = _BV(CS00); // no clock prescaling
        OCR0A=PIXEL_FREQ;

        TIMSK1 = _BV(TOIE1); // Enable timer overflow interrupt
        }

        ISR(TIMER1_OVF_vect)
        {

        if (linecount == 0) PORTD = PORTD ^ 0x80; //Toggle the most significant bit of port D.;
        else if (linecount == 2) PORTD = PORTD ^ 0x80; //Toggle the most significant bit of port D.;
        else if (linecount == 3) PORTD = PORTD ^ 0x80; //Toggle the most significant bit of port D.;
        else if (linecount == FRAME_LINES) linecount = 0;
        linecount++;
        }

        void loop(){

        }

        ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        UPDATE:
        I finally have a screen I can use to subtract the memory contents with :

        #include <VGAX.h>

        VGAX vga;

        void setup()
        {
        vga.begin();
        vga.clear(11);
        }
        void loop(){for (int y=0; y!=VGAX_HEIGHT; y++) {
        for (int x=0; x!=VGAX_BWIDTH; x++) {
        vga.putpixel(x, y, 3);
        }}}

         

        +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        I now can use this with an AND chip and my own function gen frequency and output from the SRAM !

        And here are some of the images I’m getting (using an AND also for the clock coming from the signal gen) :

        And here I am trying to record VGA…
        …from VGAX (120×60):

         

        …and from a computer VGA at 800×600 passing along the sync signals:

        Here was what was onscreen :

        Test card - Wikipedia

        I can reproduce this reliably with :
        -op amp taking memory and amplifying it
        -a pixel clock at 1.007MHz (pixel clock for 640×480 of 25.175 MHz divided by 25)
        But it kinda turns on and off and also jumps around on the screen for some reason.
        I also found out I could use the vertical sync along with a counter (taking as input a 4MHz fucntion generated square wave) and a bunch of AND gates to essentially only record only every X frame and store it in memory. This meant I can save way more frames. Getting the right spacing for the frames will be key to be able to store a short video. 
        UPDATE 1:
        I can now save a short video : 695.45 kHz (This is the slowest I can go and still almost see an image) on the function gen AND’ed with the top 4 frequency divisions of the vertical sync. This makes for a burst of image recording every X number of frames.  I then amplify the output to make it visible on screen (and speed the clock up to 695.48 kHz). One problem is that it looks like everything is in fast forward and it’s barely possible to interpret the images. The other issue is once the video starts again from memory its frame moves with regards to the screen. 
        UPDATE 2:
        If the reset is connected to the inverse of the freq subdivision of the vertical sync signal AND’ed with the address 20, it will play the image in a loop and always place it in the same part of the screen ! 
        +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        TESTING MAX ADC SPEED:
        I’m not sure what to make of this as the signal was super noisy but, I can “follow” a sine that is at 50Hz sampling it at 5MHz fine. I’m not sure what conclusions to draw from this…

        VGA controller board :

        -Attiny(?) 
        -several clock speeds to chose from
        -knob to select diff screen resolutions
        -PLL to sync to input VGA? Use an XOR to see delay with signal and then get closer to it ?
        -easy setup to pass input VGA syncs to an output VGA
        -filters to knock out certain frequencies to tune into what I want. 

         

        ANALOG BOARD:

        Based on the Analog Thing functionality (summing, multiplying, comparing, integrating, differentiating, etc.) along with counters to divide the screen by powers of two !

         

        UPDATE:

        Working now on a next version of the 16MB board. The idea is to fix the issues in the 16MB board, add more functionality, and also to move towards a finished board that is more plug-and-play with regards to recording VGA signals and less of a prepared instrument for testing. It’s a test to see if I could make something that behaves like a product.

        One question is if it requires an analog function generator or not, and to what degree the machine is mono-functional or not. (Though I think if it could record video reliably and display to a screen that would already be not bad.)

        Fixes :

        • A21 LED added
        • Top and bottom bias of ADC now controllable with pots
        • op amps on input and output both with variable gain and bias
        • fixed 5V and 3.3V power (ADC and op amps need 5V) with onboard LDO.
        • got rid of phase shifter and 8 pin dip switch
        • CS no longer tied to WR on SRAM.
        • various enables broken out so they can be turned on an off with external control 64KBSRAM
        • counters daisy chained with last address going to clock input of the following counter (no longer using RCO)
        • Replaced 74*541 with 74*245 (so it would have big footprint to let wires pass under but work with 3.3V)
        •  Added VGA in and out connectors and made pass thru mode for input.
        • Added f divider, AND, and NOT gates.

        This board should allow me to test the idea of having a control board chroreograph a sequence of memory operations, and allow me to begin testing the analog board with a stable (not breadborded) video recorder. 

         

        Sunflower Array

         

        The github for this project :

        https://github.com/merlinmarrs/tournesol

        I’ve made the PCB for with the 74AC240 based on this design from http://www.smfr.org/robots/robot_images/dualsolarhead_schematic.png :

        Hoping that it works better than the 540 version with the solar midpoint.

        IT DOES! It is far more responsive and efficient and works with the solar panel! It has a funny quirk where it will first turn the wrong way then correct itself in one direction though…Next i’ll make it on a super small extra compact board and try the other circuit option:

        It’s much more compact that the previous version. The photodiodes could be removed to make it even smaller. It took me around 25 minutes to assemble at a comfotable speed. By far the least efficient element of the assembly is the braided wire which is causing mini shorts :

        There is already some interesting interplay with the two machines side by side…

        3D model of fancy motor version:

        Here’s a version with a tiny gear motor. The motor appears to lock up at low currents however so it doesn’t work super well. It’s also very difficult to attach the motor axle to the bottom platform. 

        With the metal gearmotor it works much better:

        And this version with the panels lower down:

        Here is a tall version:

        Here’s a compact design with a “flipper” arm:

        The idea of pulling the solar panels from the board, exposing the electronics and creating the tilt naturally.

        The little metal cap is actually a nice part of the look of the machine:

        I have made the board thinner so that it fits perfectly over the gear motor:

        Here’s a shot of it looking pretty:

        Llama edition (it’s a heat sink). This photo makes me think I could have fun with shadows like in the 1970 Il Conformista film:

        I see a monocled mustachioed Monopoly man here somehow:

        This idea distributes the things to look at around the machine (one side has solar panels and the gear motor, the other has electronics and the underside gold lettering on the solar panels). When the machine is facing the sun we get to see the electronics. I guess the electronics is also sheltered from the heat of the sun a bit?:

        Here are some feet ideas for the resin printer using small set screws and (in the right model) Pogo pins:

        Here are two versions of the tripod design. One has the electronics as a backpack and hides it from the sun, the robot has two faces. The other has the “face” of the robot face the sun, in one photograph the whole robot is visible.

        Version 1:

        Version 2:

        ****

        Side to side comparisons:

         Another idea for pogo pin feet using the crowned tips:

        ***

        I made the barebones circuit in extra slim also:

        debugging the previous version, it appears that the solar panels are indeed producing the expected voltage and current (2 x 1.67V, 18.4 mA), the voltage trigger and mosfet are working, the capacitor is charging, and the voltage to the motors is spiking. However the motors aren’t so much turning as twitching. The motors appear to be identical to others that are working however. Changing the capacitor from .22F to .15F changed nothing. 

        EDIT:

        I had another stab at this:

        I noticed that the charging and triggering part of the circuit appeared to be working but that the motor was not firing. I assumed then that there was a soldering problem on the 74AC240 pins somewhere. I tested a copy of the same boards from the same batch to see if there were any problems with the etching itself, they all seemed good with no shorts and clean traces. I tested a blank 74AC240 as well and found that the only pins that were internally connected were 9 (Y1) and 10 (GND) – though I’m not sure why this is the case. I took a voltmeter to measure if there were any shorts between the pins on the IC and found that everything was mostly normal. Looking under the microscope it occured to me that the problem might be that the pins of the IC aren’t fully touching the pads below as they appeared to have been soldered quickly. In order to assume myself that everything was indeed well connected I reheated each of the pin connections and retested that I didn’t introduce any shorts. I then switched out the larger motor for a more efficient smaller DC motor. Testing with artificial light I now have normally functionality!

        The second malfunctioning solar head has a different issue: It charges up fine from having no power and spins the motor no problem. But after a few times it stops triggering at hovers around 3.6V…I resoldered the connections and had the same issue. The voltage trigger appears to be triggering despite the voltage staying high. In the end I switched the bigger metal gearmotor for a small vibe motor and it worked just fine. I think I can conclude from this that the circuit I used is not suitable for the metal gear motors but works fine with smaller ones.

        I am now looking into making this circuit even smaller using TSSOP-20 packages of the 74AC240 and maybe 0603 smd components too. I plan to make this double sided and to have the boards made at PCB way after testing a prototype circuit in the lab.

         

        The circuit works fine after I had to resolder some of the IC pins to the pads (they seem to hover just above sometimes despite the soldering) and fixing a short between the two sides of the MOSFET by lifting up the positive side of the SMD cap up.

         

        Testing with a single 3.4V panel and…it turns! I’m going to try to add the light detection now.

        And this is how it could be integrated into the 2DOF design: 

        This is a second option which would involve probably halving the above circuit and making it double sided. 

         

        Another idea is to see how far this minimalism and miniturization can go, for instance with a single solar panel capable of charging up the cap to 3.3V ish. It could be 1DOF or 2DOF. Testing with the power supply with 4,46V @ 5,9mA it works no problem with the small gear motor. We’ll see if that’s the case with the actual solar cell soon.

        Monocrystalline Solar Cell 24.5mW (4,46V Current – Short Circuit (Isc) 5,9mA)

        https://www.digikey.fr/product-detail/en/anysolar-ltd/KXOB25-01X8F-TR/KXOB25-01X8F-DKR-ND/10127250

        KXOB25-01X8F-TR ANYSOLAR Ltd | KXOB25-01X8F-DKR-ND DigiKey Electronics

        Monocrystalline Solar Cell 26.3mW 5.53V:

        https://www.digikey.fr/product-detail/en/anysolar-ltd/KXOB25-02X8F-TR/KXOB25-02X8FDKR-ND/9990483

        KXOB25-02X8F-TR ANYSOLAR Ltd | KXOB25-02X8FDKR-ND DigiKey Electronics

        Here is what it could look like 1DOF:

        …and 2DOF:

        If the solar panels are a bit offset from the boards then the light sensors could be directly on the board and not need to be offset themselves from the board. 

        ****

        I actually have one of these lying around to test the one panel idea (3,4V
        @ 4,4mA): 

        https://www.digikey.fr/product-detail/fr/ixys/KXOB22-01X8/KXOB22-01X8-ND/2754274

        Here it is working! (remember that the “GND” side of the photo bridge actually connects to the ENABLE of the 74HC240)

        some other panel configurations:

        I could have the light sensors on either end. 

        A little higher up…

        Satellite inspired:

        Here’s a quick 3D print:

        Not even sure if adding another DOF will have a big effect, over the course of a day how much will it actually tilt up or down?…

        Dual Solar Tracking Part III

        Another idea is to start with the actual solution (two small servos) and make it look better. It’s sturdy and already balanced. It just needs a hinge and a tilting arm:

        With a makeshift hinge and tilt arm:

        This would only require an Arduino mini pro and some super caps charged by a solar panel through a diode.

        Some other more simple options:

        For a miniature 2DOF.

        A mini 1 DOF.

          The 2 metal gear motor design I was working with early on.

        *****

        Here is a super simple two servo motor controller that recharges supercaps through a diode:

        This works with the code below as a test:

        #include <Servo.h>

        Servo myservo1; // create servo object to control a servo

        Servo myservo2; // create servo object to control a servo

        int pos = 0; // variable to store the servo position
        int threshold = 100;

        void setup()
        {
        myservo1.attach(5); // attaches the servo on pin 9 to the servo object
        myservo2.attach(6); // attaches the servo on pin 9 to the servo object
        }

        void loop()
        {

        if (analogRead(A2)-analogRead(A3)>threshold)
        {
        myservo1.write(90);
        delay(15);
        }

        if (analogRead(A3)-analogRead(A2)>threshold)
        {
        myservo1.write(-90);
        delay(15);
        }

        if (analogRead(A4)-analogRead(A5)>threshold)
        {
        myservo2.write(90);
        }

        if (analogRead(A5)-analogRead(A4)>threshold)
        {
        myservo2.write(-90);
        }
        }

        This is the monster in all its tentacularity:

        ****

        Testing the barebones circuit with the solarbotics white DC gearmotors and the SMD solar engine. It works with only two mini solar cells and the SMD cap as long as the sun is medium strong!

         

        This is exciting as a result (but I just needed the change the motor wire polarity):

        Here’s what the circuit looks like:

        This suddenly stopped working and I did the following things:

        Checked to see if the cap was charging, if the voltage monitor was triggering (and I manually triggered the MOSFET to see if the motor fired), the voltage at the midpoint between the two photodiodes, and if there were any shorts.

        In the end I replaced the zener with a normal SMD diode which I found, replaced the photodiodes with green LEDs because one lead was broken on the photodiode, and then discovered the motor was broken (or at least the blue wire was disconnected on the inside)! It gave me a chance to look inside the tiny gear boxes of the motors though: 

        Here is the, once again, working prototype:

         

        I would like to be able to tune the midpoint between the LEDs. I would also like smd light sensors. 

        Phototransistor Output Voltage Circuit connects the phototransistor and 2 k-ohm resistor in series

        Some options for solar panel and motor assemblies:

        Here’s with SMD LEDs soldered on:

        Not sure why but the thing is only turning clockwise…

        I tried the following things:

        1. A 10K trimpot (with each photodiode into each side of the resistor and the variable pin going to the logic chip). The trim pot at either extreme does indeed make the circuit turn in the opposite direction. However, in the middle it stops either side from firing.
        2. Replacing the photodiodes with Silicon PIN Photodiode BP34s, green LEDs 5mm, Green SMD LEDS, and LDRs.
        3. changing the ground of the photodiode pair to the ground of the capacitor instead of the ground connected to the floating connected to the MOSfet.

        Just found this image here (http://solarbotics.net/library/circuits/bot_popper_240.html) which seems to imply I could use phototransistors also:

        Looking at other Smart Head circuits, it looks like the + side of the photodiode pair is connected directly to the Vcc but that the negative side is connected to the “local” ground, not the negative of the capacitor. However, there are also counter examples…

        I tried just setting up the dual photodiode circuit on a breadboard. Two photodiodes , two LDRs etc. They each move from a midway value up or down depending on which device is in the dark. However, they don’t always have 1/2V as a midpoint, which appears to be the important threshold point for the oscillator. **EDIT** I think what they meant was half of VCC not 0.5V. I could try to find the exact threshold voltage that is the balance point with the power supply?

        2.25V for my desktop breadboard version of the circuit while supplying 4V for the solar panel. 2.85V was the threshold with solar panels at 5V. It also has a dead zone where neither motor will fire if it is perfectly in the middle. 

        For the soldered version the threshold is around 1.3-1.4V when being fed 4V as solar panel. 

        At 3.5V solar panel supply, I have a lower threshold bound of 1.25V and a higher bound of 1.4V. In the deadzone neither direction turns. 

        At 3V funny things happen. It will only turn one direction unless I suddenly make the input 0V. This might explain why my circuit is behaving strangely! I am using a 2.92V voltage supervisor so I don’t think my current circuit is ever going to work. 

        I should replace it with a higher voltage threshold like this one perhaps: https://www.mouser.fr/ProductDetail/968-ISL88001IH46ZT7A 

        In the meantime I can use a voltage divider like I did for the 1381. 

        Here’s the scope output attached to one lead of the motor. The fat, fuzzy part is when the motor is not turning and the threshold is reached. Either side it sends between 1.5-2V to the motor turning one way or the other.

        Setting up my own little trim pot between the LDRs I was able to tune them to the correct voltage threshold and everything works well on a breadboard at least.

        Here is the 74AC240 datasheet: 

        Checked out the 74AC240 datasheet, for 3.5V the threshold should be between 0.9V-2.1V.

        ****

        Version 2 of the board has trimpots for the voltage trigger and the light sensors (which are now SMD and on either side of the board):

        Here are the parts laid out with the new board:

        Post-soldering. The size of the board versus two small solar cells:

        I had to put electric tape under the trim pots because contacts were exposed underneath…  The full assembly:

        ****

        Found this online here (http://www.smfr.org/robots/robot_images/dualsolarhead_schematic.png): 

        It uses the solar panels as the light sensors! It also lays things out in an easy to understand way (the midpoint is the boundary between what the inverter sees as HIGH or LOW. Once the solar engine has enough power this decision of HIGH vs. LOW then powers the motor through six remaining gates – presumably to increase the current as each pin seems to be limited to 75mA according to the datasheet above?).

        ***

        Testing V.2:

        I can get 3.5+ volts (4V outside in full sunlight even) with the current setup of two solar cells + little cap!

        BUT…When I short the voltage supervisor 5K trim pot the motor fires, but even in its least resistance position the supervisor is not triggering…It is also only doing mini movements, not full shots. I removed the trim pot and now it behaves normally. I don’t understand why the trim pot doesn’t work when it was flawless with the benchtop power supply…

        With a 1M trimpot the variable voltage trigger does work just as expected, however! I moved it up to 3.5V ish.

        The 5K trim pot for fine tuning the light sensors also does not appear to work. Also doesn’t work with a 1M and two green LEDs…

        I tried using the two solar panels as sensors as suggested in the found circuit and it appears to work, there remains the same problem of how to tune the voltage level however if my trim pot solution doesn’t work. 

        ***

        I think I need to order 74AC240s (I think the 74LS540s I am using might be slightly different in terms of their HIGH/LOW thresholds) or voltage triggers for 3.5V+. This is a nicely tuned circuit that seems hard to mess with.

        Just a reminder of how much the complexity has been reduced in this project!:

        ***

        I have realized I need to understand the circuit I am working with better in order to tinker with it. 

        First thing, the 74LS540 that I am using has a minimum voltage of 4.5V! It also can only supply 45mA max as an ouput if I understand correctly. However, I also have a 74F244D octal buffer which works as low as 2V. However it is Bipolar and not CMOS technology like the 74AC240 AND more importantly, it has non-inverting outputs. Clearly getting the right logic chip and family is an important element and not a detail for this circuit…

        So, the LDR “photo bridge”. After testing 10 we had lying around I arrived at these average values:

         

        Here’s some R Thevenin calculations (Thanks to Learning the Art of Electronics!) to figure out what size pot I can afford to pull up or push down the midpoint from the LDR divider:

         

        And here’s the current logic circuit in a more understandable format:

        ****

        Did some tests, looks like I need to bring the midpoint DOWN in my circuit:

        Yellow is the cap voltage (3.42V) and green in the midpoint (2V) with equal light on both phototransistors. 

        This scope image shows first both photodiodes in light, then the left (connected to GND) in darkness, and finally the right (connected to VCC) in darkness:

        Without even needing to take any measurements it’s evident that the midpoint is too high. This causes the motors to always turn the same direction when the light intensity is equal between the two phototransistors. I flipped the photo transistors and observed the same effect so I think it’s the circuit not the difference between the phototransistors.  

        This scope image shows me adjusting the phototransistor trim pot. (Fully attenuating on the left, halfway in the middle and then fully open on the right). 

        And here’s testing with the middle threshold covering and uncovering each phototransistor:

        This shows that the 10K trimpot effectively drops the entire midpoint! I can effectively dail in the midpoint to exactly half of VCC now (3.42 Vcap, 1.71V threshold)!:

        Now the machine is not turning in only one direction! But it also might need some resistors for when the two photodiodes are both seeing bring light. I also need to replace the LS540 with an AC240 because I am currently operating outside the maximum minimum voltage of the IC. It would also be nice to use the solar panel as a sensor and thereby reduce the part count (I could still tune the midpoint in the same way once I determine which way the imbalance is – it would also solve the shorting problem in high light). 

        I varied the pot and everything is working as expected. I also added heat shrink to the phototransistors. Here is a video:

        I tried some 2DOF designs but I think this needs to stay MINIMAL and the force of the project, if it comes, will come from the array more than individual complexity. Plus they already seem imbalanced and more likely to malfunction…

        Some tests with the solar panel voltage divider:

        Here’s what it looks like when I change the solar panel midpoint with the 10K trimpot:

        Here is the solar panel midpoint trim pot all the way to one side:

        Here is the solar panel midpoint trim pot all the way to the other side (not sure why it goes all the way to the ground here only):

        Here is me putting one panel in the shade, then the other. The difference is not massive.

        The problem is that now I fall into a large deadzone where no movement happens in the middle. I’m trying to find the right combination of voltage trigger trim level and solar midpoint level but it’s not easy without sunshine…

        ****

        Here’s the cost per unit of this design:

        74AC240: 0,462 €

        0.2F cap: 1,21 €

        solar cell: 3.56 € (1.78 € x 2)

        micro gear motor: 4.25€

        voltage trigger: 0,356 €

        diode + 0.47uF cap: 0,1€

        PCB: ?

        Total: 9.938€ + PCB cost

        ****

        I have now a 1M trimpot on the solar midpoint and have added a 1M between the midpoint and VCC to try to balance things out.

        I think 3×3 is better than 4×4 ? 5×5 would be out of control.

        ****

        Working towards simplifying the dual servo sun seeker. Here is a post for controlling servos without a microcontroller:

        https://www.pololu.com/blog/18/simple-hardware-approach-to-controlling-a-servo

        Could use LDR pair to replace pots inside the servos, based on ideas from Hackaday post shared with me by Paolo Salvagione:

        Light Tracking Robot Relies on LDRs

         

        ***

        Went back to the super simple business card sun shaker and added some feet. I also tested the new 0.2F caps and they work with outdoor light but might be too tough to charge with indoor light (unless it’s actually bright then they work fine). After Paolo Salvagione’s comment about using a smaller motor to rotate the device, it occured to me that I could use the vibe motors in two directions to create movement that follows the sun:

         

        ****

        Ordered this motor: 

        Here is a new design with (human) adjustable tilt. It has more of a “pro” vibe. The motor adds 5 euros to the total cost of the machine bringing it up to 15 euros:

        ****

        Looks like the trick with the light sensing is just to put the photodiodes behind the board so that the shadow cast by the sun creates a clear preference for one side or the other. They can be bent around a little bit to be perfectly tuned but it also adds a bit of idiosyncracy to the ‘bot. So it doesn’t appear that I need a trim pot for this option. I’m not sure how to create the same contrast with the solar panels as putting either in the shade is obviously not a good idea. Pointing them in different directions creates some difference but not a great deal.

        ****

        I’ve redone the circuit for the 74AC240 and make it more compact:

        Here is my next design with the smaller DC gear motor. I’m imagining a soldering a pin to the PBC in order to attach the pivoting arm to it. Otherwise the issue is how to make this attachment without creating a whole frame and bumping into components etc. It has a small M2 bolt to fix the print to the motor and to adjust the angle:

        I’ve made a cleaner video of the first prototype:

        Dual Solar Tracking Part II

        Continuing the project of making a small solar tracking device.

        ****

        Idea to simplify the circuit drastically and make the 3D model a bit more robust and less fragile so it is far easier to assemble and won’t topple over (while hopefully maintaining some elegance…) 

        -Battery-powered (and possibly solar recharging through very simple circuit):

         

        plus Attiny with two steppers only powered directly from output pins (max 40mA and danger of kick back which might be solved with diodes) and no light sensing – just the almanac algorithm for sun following. *

        *(Remember you can test the stepper resistances to know which are pairs!)

        Tried powering a stepper directly with an Atmega using the Stepper library in Arduino with a bunch of different step and speed values – if it turns at all it’s extremely weak…So this doesn’t appear to be an option.

        Tried powering an atmega328 directly to cap and solar panel through diode, in medium sun there is no action. With bright sunlight it manages to power up despite blinking a blue LED every secon. I could try with the sleep timer I recently acquired (https://www.adafruit.com/product/3573). Or maybe a simple sleep code will be enough. This seems like a viable option which would simplify the circuit considerably.

        Another cheap and simple option is the ULN2003, but this requires 5V to function unlike the low power DRV8833 I have. 

        ***

        Tried driving the stepper gearmotor chain drive and it moves very smoothly and almost imperceptably. 

        I dug up some DC DVD raft drives, they are nice mechanisms:

        The black rack was attached to the raft. 

        I tried powering these with Wilf’s SPSH circuit and the bearbones solar head and they were too big a motor to be powered unfortunately. I could still try to increase the 1381 trigger level with a resistor though…

        I’ve redesigned the microchip version of the board to make the wiring to the stepper much easier this time. I’ve also added two caps to smooth out the motor supply. 

        And here is a more robust version of the previous two stepper, this one has two parallel bars, a more robust hinge, and the lower bearing is encased completely for more stability hopefully. I am also getting rid of the lead screw plastic piece and trying to replace that part with a thin metal rod. In general it’s simplified and a bit thicker all around :

        I am using a dremel to clear out the holes before inserting things and this is reducing shattering of the more fragile white resin. 

        The bottom bearing:

        Part of the solar panel hinge:

        Only one of the two stepper drivers worked on this board but I haven’t found the issue yet…I wonder if back reverse polarity I destroyed it by accident or over heated it during soldering. I’m going to replace it with a fresh one and see if that changes anything.

         

        Here’s the assembled machine:

        Here is a look at some of the technical details:

         

        A different panel orientation option: 

        Comparing the two versions (the newer is far more compact and stable but less playful and off-beat/asymmetrical):

        Check out the angle range, it’s better than I expected!

        ERRATA:

        1. The idea of putting a pin accross to act as the lead screw is not working with only one side screwed in. AND the lead screw platform is impossible to insert into position (I had to break it in half and glue it back together once in position). I should definitely not try to go under the lead screw, with the nut that holds the pin, it actually makes contact with other nuts holding the whole linear rail assembly. I think I should go back to integrating the existing plastic lead screw – aesthetically it adds more flavour and asymmetry.
        2. The part that attaches to the panel should be one connected part because this gives more surface to attach to and more rigidity. (X)
        3. There is a volume which enters into the bearing socket that I needed to remove with the dremel. (X)
        4. The three bottom holes for screws are not evenly spread out around the circle. (X)

        POSSIBLE CHANGES:

        1. Tensioner for bottom chain or larger bottom gear?
        2. The top hinge is too symmetrical for my liking but otherwise it’s super stable.
        3. Transparent resin is definitely nicer.
        4. The bottom nut needs a tiny bit more room, but it just works.
        5. The bolts are hitting the bottom stepper gearmotor.

        Here is a new version which solves the errata, but it looks hideous:

        I am considering returning to a one bar setup. I am also considering how I can make one version of this device that will work for the different linear actuating steppers and the different rotational motors (the micro steppers and DC gear motors have the same face plate thankfully). The top and bottom of device are also not connected (so varying heights of linear drives can be accomodated). Should one single design be able to work with belt drive or chain? I think I should now focus on producing a bunch of these.

        Here is how the little plastic parts that act as lead screws work:

         

        There are all kinds of shapes but in the end they all appear to have one main screw location and a bunch of other nibs and slots to help with alignement. I can include a sliding slot for the main screw in the 3D print but I may use a dremel to adapt it to different nib locations. 

        I’ve revisited the version 1 and have made the following changes:

        1. I’ve kept the more robust base from version 2.
        2. I’ve revised the lead screw plastic part holder and made the print compatible with the highest number of these peices and allowed for longer stroke.
        3. I’ve kept the magical setup that allowed for a great variation in the panel angle.
        4. I’ve kept the circular base which is more stable and made a larger base gear so it looks more stable. (The pitch of chain I am using is 0.1227″ https://www.mcmaster.com/miniature-roller-chain-sprockets/pitch~0-1227/)
        5. I adjusted the motor so the bolts are no loner hitting the bottom stepper gearmotor.

         

        ****

        Wire management:

        I like the ribon wires from the CD drives and the mini locking sockets and plan to use them:

        The top option seems sensible.

        Here the bottom motor ribbon is going to be super stretched when the panel is at full extension in angle…

        I like the asymmetry of the second option, it implies that the lead screw and hinge action is taking place on one side and the electrical connections on the other side.

        (I’m just realizing that some of the ribbon cables that come with the stepper linear drive are not very long + the brown/orange doesn’t go super well with the white. Should I replace those cables with white ones so that everything is the right length and homogenous?)

         

        There is also the possibility of folding the ribbon cable…

        I’m trying to find this product (it seems to be called “flexible flat ribbon cable”) but not having luck on Farnell or Mouser (*correction: https://www.mouser.fr/ProductDetail/Wurth-Elektronik/687606050002?qs=qR1qlUC5%2FYSp8nXHAtNchQ%3D%3D) …Here it is on Ebay and Amazon:

        The other option is to harvest wire from the old CD drives I have, the wire lenghts seem to vary from 8-11cm.

        Some solar panel options:

         

        ****

        Next version sucesses:

        orienting parts so that the supports don’t touch faces that will be seen is a good idea, it saved the bottom platform from pockmarks.

        Errata:

        1. The main issue is the lead nuts. They are massive and therefore reduce the stroke considerably. Most have a setup where they push against the leadscrew and twist the sliding cam which is now only fixed to one rod.  I should try to design the nut myself, if the solar panel is in the middle it is hidden in any case. The other option is to find another one in a million lead nut that works like the one in version one where it doesn’t push against the cam / use two bars. (X)
        2. Somehow the ratio of arm lengths is producing a totally different range of angles, mostly in the negative direction! (X)
        3. the layer on top and below of the bearing is too thin and comes out wavy. (X* I did the top visible part but not the bottom).
        4. missing a subtraction around the collar of the bottom stepper motor shaft. (X)

        Possible changes:

        1. all black hardware would be nice. Or perhaps all white?

        ******

        Modeling the lead screw:

        The threads themselves:

        I want it to be possible to adjust this thing so I am planning to use a stack of washers to accomplish this. I am also adding a notch in the purple volume to prevent the green volume from rotating. It should also mean that the green volume can be made to accomodate different threads and the purple volume will still work regardless (i.e. it’s modular).

        The backside leaves space for a nut to fix the green piece in place at the right height.

        I’ve also adjusted the angles to match the version 2 as exactly as possible this time:

        Here is the finished version 3:

        Some quick ideas about how this project could be displayed in an expo:

        1. I can make a bunch of solar critters that react to the sun in different ways (sweeping in circles, following the sun but staying in place, making sound by tapping the ground or playing a song, etc.). This is the kind of ludic, “diversity of nature” bio-inspired option. The robots are indifferent to one another but occupy the same space. I’m not sure how to chose the materials but maintain difference/cohension of the group but presumably I could pick a kind of 3D printing and use the same motors.

         

        2. Make one such critter (like the sun follower) and create an array of them in a given space and watch the group effect of their behaviour. This is kind of obvious but at least it’s abstract and sober, hopefully fitting in my portfolio with my other more abstract/representational work. It would also be easier to mass produce one design than to make a bunch of one-off designs I think.

        And here is the BOM so far:

        And here is the newest build:

        ERRATA:

        1. The lead screw does not work. It is not perfectly aligned with the threaded rod and the system for holding it tightly in place is not working. I think I need to put the lead screw on the bottom side of the threaded rod and have a tensioning mechanism on the front (accessible) side.
        2. The angles are still wrong. If I make the top arm (that is glued to the solar panel) shorter I think it will improve.

        I’ve put the lead screw on the back of the threaded rod and inserted alignment rods and increased the borders to hold the top piece in place:

        ****

        The solar panel angles are fixed! With two of the threaded nut sides making a sandwich it does actually work as a lead screw nut (even though it really doesn’t look nice) !

        ***

        Thoughts from artist Paolo Salvagione:

        1. Make a power budget (what does everything i.e. steppers, microchips, drivers use when not on, when on)
        2. Run motors 24/7 for weeks before to test that nothing falls apart.
        3. Replace bolt hinges with flanges or miniature bearings from McMaster Carr. (The smallest available at a reasonable price is 5mm OD no flange).
        4. Try pulling up everything to the top beneath the board and alternatively pushing everything else to the ground to create a sunflower.
        5. Think about the center of balance of the machine and how you can place it directly over the pivot point (or move it there with weights).
        6. What kind of choreography would I like to see here.
        7. Alibaba has super inexpensive solar panels that look great.
        8. Could the array relate to different light sources which exist in a kind of solar system around the plinth.
        9. How can visitors experience this piece at different distances (“layers” of experience)?
        10. Medium-runs of a finished design are easier than making a ton of one offs or mass producing.
        11. It’s possible to order flexible PCBs from PCB Way.

        ****

        Some thoughts on the layers possible:

        I have found a few 10mm OD bearings and will redesign the arm portion with these:

        I’m not totally satisfied with the bearings in the design (McMaster has perfect 5mm bearings that are reasonably priced but they don’t ship out of the U.S.).

        ****

        Calculating the balance point with help of this site: https://www.school-for-champions.com/science/gravity_center.htm#.X2B-cFUzaUk

        Calculating CG of weights 

        CG = (aM + bN + cP)/(M + N + P)

        Seems like I should try to keep the solar panel light in general (under 12 grams?), but that I have the option of adding some weight above the stepper in the rear. In the other axis I can shift the solar panel a bit to the side to compensate for the weight of the bearings in the arms. 

         

        ****

        New version with arm bearings:

        It was hard to attach the solar panel to that arm model so I switched it for one with mini bolts:

           

        I don’t like the look of the three wide and narrow bearings so I am trying to hide one under the panel and switch up the second by putting the black bolt through it. One of these only would look great but three is too much.

         

        A new design with McMaster 5mm bearings (I can in fact order them it would appear). In one I keep a single large bearing and in the other all the bearing are small:

        Making a third prototype to test the following things:

        1. Durable resin for everything but especially for the lead screw.
        2. Adding flanges to hold the current bearings in place.
        3. Slight tweak to the currently improvised lead screw design.
        4. Fix angles again with shorter arm.
        5. Hiding the other bearings a bit under the panel.
        6. Making a third so that I can begin testing the “swarm” effect of the robots actually moving.

        ****

        I began two stress tests based on Paolo’s advice:

        Some things to note:

        1. These are so much fun, people visiting see what you’re doing as Paolo suggested!
        2. It fights anxiety – you know if it will work or not.
        3. Working for 1 minute is not the same as working for two weeks 😉

        Unfortunately there is a serious issue: The linear drive is drawing 600mA at 5V to move up and down…I should try to mess with the steps and speeds to see if I can get this down. The rotating motor is drawing less than 150mA. 

        Looking on Alibaba, the most popular size of smaller panel is 156mmx156mm.  But also found some 47×47:

        and some 55mmx14mm

        37x21.8mm 6V 2mA outdoor used Thin Film Amorphous Silicon Solar Cells for Outdoor Products

        37×21.8×1.1mm 

        Here are the best links I’ve found so far:

        https://www.alibaba.com/product-detail/55x14mm-3-0V-7uA-dim-light_60585863880.html?spm=a2700.galleryofferlist.0.0.43be4630X6CXwJ

        https://www.alibaba.com/product-detail/Dim-Light-Amorphous-Silicon-Thin-Film_208238751.html?spm=a2700.details.deiletai6.5.561929devzX1BP

        https://www.alibaba.com/product-detail/3V-10uA-Dim-Light-Amorphous-Thin_62187655652.html?spm=a2700.details.deiletai6.1.561929devzX1BP

        https://www.alibaba.com/product-detail/37×21-8mm-6V-2mA-outdoor-used_60506651563.html?spm=a2700.galleryofferlist.0.0.2430367cO11E7R&s=p

         

        I sent my first email: 

        Hello,

        I’m a research engineer at the Universite of Paris Sud in France and an M.I.T. graduate.

        I’m working on a low-power solar follower product and reqiure small cells like yours to create a series of panels (with each an array of 5×2) to combine with an energy harvesting IC.

        I am interested in your product and would like to request a sample if possible to test with my setup.

        Thank you ,
        Jonah Marrs

        *********

        Easy stepper motor driver can drive both steppers at around 150mA at 5V with no load. 

        The DRV8833 with correct step and speed settings can drive the linear with the plastic lead screw found in the CD drive at around 360mA but has a super high idle current of 600mA. It gets super hot as a result. The DRV8833 cannot drive my lead screw setup however… Basically there is too much friction in my lead screw design currently. So, the conclusion is that it’s possible to drive this thing if I put the driver to sleep and I use the existing lead screw.

        I am leaning towards using the found CD drive mechanisms (because they are perfectly made to minimize friction etc. already and look super cool) and add a second bar parallel to the first to improve stability and compensate for the rotating that the plastic lead screw wants to do.

        I’m also adding another bolt to the bottom angle bearing, and looking for some 3mm bushings to add to the sliding portion. Here seem to be the two primary options:

        The side by side bars on the left are less flexible with different found lead screws as the bolt location can’t move much to either side. It also makes the front part flatter and longer, whereas the bar behind makes the design feel more three dimensional and compact. 

        I prefer the bar behind. 

        Bending joints options:

        I like the asymmetry of not having the top and bottom joints unaligned in elevation and in plan views, it feels more dynamic and optimized. I like the bottom bearing being centered with the cam. 

        Hopefully this version will allow me to do some stress testing with the tilt mechanism.

         

        Here are the parts laid out:

        The assembly turned out well:

         

        ERRATA:

        1. The lead screw is a tiny bit too high above the threaded rod, I had to carve out some material with the dremel…
        2. The elbow joint connected to the cam can’t work with a captive nut the way I originally concieved but it can work with the cap of the screw in the place of the nut.
        3. The set screw for the sprocket needs to be almost flush with the sprocket surface to not hit the bearing squeezing screw. The bearing squeezing screw between the parallel bars needs to come from the top not the bottom or else it interferes with the chain. 
        4. I think I need to shift the two bolts that hold in place the bottom motor 90 degrees. And I may need to add a little circular band to keep this motor straight.  A tiny drop of hot glue solves this…
        5. The solar panel surface is not flat but tilts inwards.

        I got the test set up working again with two Easy Stepper Motor Drivers (with Enables and 5V+ connected also) this time. Now the bottom motor is driving 100mA and the linear is pulling 200mA. Both are cool to the touch. However, there is a little bit of power lacking for the linear lifting motor and it is skipping some steps. Switching to 1/4 instead of 1/8 microstepping, and making sure all the bearings are set up smoothly, appears to mostly fix this though.

        Some experiments taking nicer photos:

        A with and without solar panel gif:

        *****

        I’m doing a next design based on the observation that the motor is struggling to pull up the solar panel and that the weight could be repositioned closer to the center of gravity and rotation axis. It’s more sunflower like, and the lead screw is now exposed as in version one. Nicer balance in terms of the bending joints which now aren’t all on the same side. One of the three bearings is hidden so the emphasis is more on the two side ones now. Far more sturdy rotation mechanism for the solar panel so no more warping and bending hopefully. The arm will have to travel far less distance to change the angle of the solar panel also.

        This experiment is interesting to me because it’s an attempt to move towards a more robust design while trying to keep it visually interesting and maintaining the attention to form.

         

        I adapted it for a mirrored version of the linear drive and new lead screws (it wasn’t too hard to do). Now I have right and left handed machines:

        This is interesting as it shows how easy or not it is to adapt to the repurposed CD drive mechanisms with the current design and how much variation can be created organically. 

        ***

         

        It looks like I should go with white Rigid resin and that I should use bushings for the sliding portions so no part of the print has friction.

        These appear to be the key characteristics of the lead screw/nut setup.

        ****

        Adding some grease to the rails:

        *****

        Here’s the tall version:

        It has great tilt range.

        It feels much taller and more slender. It also looks far more like a sunflower. It’s also lost its animate / cuteness quality that comes from being compact and now feels more like a machine. It does make more sense in terms of weight distribution, however.

        It proves that the idea of adjusting for found CD drives works in principle. 

        V.4 ERRATA:

        1. The Durable resin is too flexible to make rigid longer parts. The whole assembly is flopping all over the place.
        2. I need to modify the central solar panel hinge – it’s not possible with the hardware I have to make it function properly. Because the tilt arm pulls from the side, it has a tendancy to tweak the central hinge. I could use the large bottom bearing up top?

        ****

        The new version features a pole that pushes up to tilt the solar panel, and lets the panel rotate down when it descends. This means fewer bearings and maybe less strain on the linear actuator stepper motor:

        I’ve redesigned the control board. It now has a nicer spacing between the motor driver out pins and doesn’t have any other wires nearby to get in the way of these pins. It also has the motor driver sleep pins connected to the microchip, and can drive two steppers instead of one stepper and one DC motor as with the previous version. It also has more noise attenuating caps:

        I am also considering using an Arduino Nano with DRV8834s and a mini lithium ion just to get this prototype running!

        This works but it’s very heavy with the battery packs. I may need to glue the bearings in place or work with a more robust design for this phase.

         

        I’m using the StepperDriver Arduino library and this is what my loop looks like:

        if (analogRead(A0)-analogRead(A1)>threshold)
        {
        stepperTop.move(-MOTOR_STEPS*MICROSTEPS);
        }

        if (analogRead(A1)-analogRead(A0)>threshold)
        {
        stepperTop.move(MOTOR_STEPS*MICROSTEPS);
        }

        if (analogRead(A2)-analogRead(A3)>threshold)
        {
        stepperBottom.move(-MOTOR_STEPS*MICROSTEPS);
        }

        if (analogRead(A3)-analogRead(A2)>threshold)
        {
        stepperBottom.move(MOTOR_STEPS*MICROSTEPS);
        }

         

        And here’s the new print:

        ****

        Just discovered why I may have been having issues with the DRV8833s…Their max soldering temperature is 260C, I have been using 300C!

        ****

        I tried to get a full prototype running with the Arduino breakoutboard version but it’s surprisingly heavy and both attempts were unsuccessful. The second:

        Without the weight of the circuit board everything is fine however:

         

        I think I may have to admit that this thing is too janky to work beyond as a quick video. 

         

        *****

        Meanwhile I’m working on a DC prototype of the more complex 2DOF design:

         

        The idea here is to have 2 1DOF solar engine circuits + a dark activated pummer circuit. The machine would then spend its days searching for the sun and charging up a bigger capacitor to then blink at night like a lighthouse. I’m not sure if I should make the blinking synchronise with other nearby blinkers using this circuit:

        https://www.instructables.com/Synchronizing-Fireflies/

        One DC motor (controlling the tilt) is so slow it’s barely noticable at all while the other one swings the machine round dramatically. The design has less magic than the servo version but is far easier to control and has no issues with the weight of the solar panels. 

         

        Robot Arm

        Here is the final product:

         

         

        1 Common robot arm configurations | Download Scientific Diagram

        After the relative success of the foam cutting machine, I would like to build a medium-sized robot arm which can actually be a useful tool in the lab and not just a toy.

        The main questions appear to be: how to increase the torque of the stepper motors? Should we make a classic robot arm or a Selective Compliance Assembly Robot Arm SCARA?  Should the parts be 3D printed or CNC milled (or a combo)? and, Do I already have the parts necessary or do I need to order them in which case how much should I order?

        **********

        Stepper torque options:

        1. Use our super beefy stepper motors. Unfortunately, none of the other hardware (like belt sprockets) fit the diameters of the shafts.

        NEMA 34 Stepper Motor (6A , 12 Nm , 151mm) | DIYElectronics

        2. 3D print a planetary gear or buy one:

        (but these make the front of the stepper bulky and a bit ungainly)

        3. Get a large timing belt pully with a short belt drive: (unfortunately we don’t have anything like this lying around the lab).

        4.  3D print a large gear to turn directly with the stepper:

        A different concept that involves a motors not all at the base:

        **********

        SCARA vs. Conventional robot arm.

         

        Building SCARA Robot Arm pick and place using vision system - YouTube

        YK400XE

        #SCARAs fold up easily and are faster for certain tasks than conventional robot arms.

         

        pyBot: OPEN SOURCE and 3D printed Robotic Arm controlled with Python

        Custom SCARA printer finally taking shape. : 3Dprinting

        Scara arm solution

        A Desktop SCARA Robot using Stepper Motors

        298 Best CNC images in 2020 | Cnc, Diy cnc, Cnc machine

        But there are some neat conventional robot arm designs out there too:

        *******

        CNC milling vs. 3D printing:

        CNC milling some parts (like the large gears) seems possible:

        Nylon and ZMorph CNC PRO Milling Toolhead

        But I also need either to buy a large bearing or to make my own bearing…Unless I make a geared slew bearing:

        Single Row External Gear Slewing Bearing China Manufacturer

        Here’s a cool guide on designing gears: https://lcamtuf.coredump.cx/gcnc/ch6/

        Some DIY slew bearings:

        Project | Mammoth ARM | Hackaday.io

        A 2.5D Slew Ring | Hackaday.io

        ***********

        Robot background information: http://www.societyofrobots.com/robot_arm_tutorial.shtml

        Cool tutorial includes info about DOF, calculating torque for each joint (fewest joints possible with shortest lever arms the better): http://www.societyofrobots.com/robot_arm_calculator.shtml

        Some simple arm linkage ideas:

        Homemade Scara Robot Arm DIY Robotic Frame Projects Laser 3D Printer Chassis Draw Arduino Control d - YouTube

        3D Printer Technology and Innovation | Desktop cnc, Robotic arm diy, Diy robot

        **********

        And these Five-bar linkage machines (aka Parallel SCARA robots), they appear to be known for being super fast:

        https://en.wikipedia.org/wiki/Five-bar_linkage

        Homemade Scara Robot Arm DIY Robotic Laser 3D Printer Chassis Draw Arduino Control Frame Projects 2 - FutureTribe.Me

        SCARyllA: Dual (parallel) arm SCARA laser engraver build

        ÉTS : Laboratoire de commande et de robotique

        Related image

        Lightning-Fast SCARA Robot Prototype by Proto G - Hackster.io

        **********

        For the end effector I think either air pressure or electromagnet:

        Vacuum End Effectors VEE

        Robotics Industry Insights - Latest Trends in Intellig...

        ************

        Some early models:

        With only two rails (I’m not sure if this is sufficiently stable or not as many designs have 3 or even 4 rail):

        Similar but with a larger motor at the bottom and less ugly mounting plate. 

        A completely different SCARA design, I like the silhouette but it would involve tons of cut planar material that would need to slide into together and we might not have everything on hand to make this either: 

        Quick attempts at 3D printing planetary gears:

        https://www.thingiverse.com/thing:2114390

        https://www.thingiverse.com/thing:3231908

        (interesting belt option here: https://www.thingiverse.com/thing:2911407)

        Using the 0.8 nozzle was not ideal and none of the pieces fit properly. I would love to try this as a cnc cut project but I think it would be highly technical and difficult and at the end would end up in a tiny box. (I’m going for medium scale machines at the moment not tiny intricate things).

        We have 12mm HDPE in white and blue in the lab, I’m going to first 3D print then mill it. 

        I’ve come to a compromise with the design. It will give me the opportunity to design with a Nema 23 a complex peice (geared slew bearing), to work for the first time with a 5 linkage parallel robot design, to CNC mill HDPE for a more robust machine, but also builds on the syringe pump linear actuator and the foam cutter designs. Most importantly, I have all the components I need to build the full prototype already in the lab. 

        I added hardware to this model to make sure everything makes sense:

        *********

        Developement of the geared slew bearing:

        Outlined this drawing in Rhino and then applied it to a circle:

        To make everything fit (increments of 2mm) I changed the size slightly of the gear:

         

        Here is the first prototype:

        Here is a section showing the bearings cut through and how two layers of 6mm HDPE should come together to make the bearing. After assembling we’ll see if .1mm was too much spacing between the bearing and the walls, and also if there need to be two rows of 4mm bearings.

         

        *******

        Here are the 5 bar parallel robot 3D printed parts (view from underneath):

        A combination of nuts, washers, 16mm bearings and socket shoulder bolts:

        There is an offset in order to make everything work on the stepper motor shafts:

         

        *****

        With the final additions here is the final machine:

        Here’s the 3D print file with all the parts: 

        Here is the kit of parts (minus the parallel arm rods):

        The geared slew bearing assembly is tricky because the bearings want to fall within the inner moat.

        Adding the top bit:

        Finishing it off:

        Here is the drive mechanism with an idler I added:

        Assembling the parallel robot arm components:

        Hammering in the bearings caused splitting in the print…

        I sawed the metal bar and deburred before assembly. 

        The top threaded rod motor assembly (I broke one side hammering the nut in a little too vigorously)  

        Despite checking and rechecking I somehow forgot to put holes in the middle of the rod bearings (!)

        Here’s the linear actuator assembly:

         

        With the parallel arms attached, just waiting for the base to finish printing:

        With the base:

         

        Errata with the bottom part of the robot:

        -impossible to tighten hex nut to fasten gear to bottom motor axel. (Added slot for this purpose)

        -not nut subtractions to help hold bolts which fix geared slew bearing to base plate. (Added captive nut subtractions to bottom of base plate)

        -I subtrated a cylinder from the base for the motor gear but this makes no sense as it rotates around and does not stay fixed. (I filled this in)

        -With current arrangement the belt between the drive shaft and geared slew bearing was loose. (Either I add a spot for an adjustable bearing or I change the location of the motor to pull it further away…I chose the latter).

        ****

        CNC milling:

        The top motor and rail mount:

        I replaced one part of the 3D printed robot parts at a time:

        The middle component:

        Everything fits together nicely!

        And here is the final result:

        The CNC mill using HDPE is extremely fun to use. The only part that is not easy to make is the bearing assembly. This I will print in durable white resin on the Formlabs.

            

        Medium-sized CNC Foam Cutter (with Léon Reboul)

        Here is the finished product:

         

        And here is a quick video cutting foam:

         

        Another video showing the stage testing:

         

         

        Here are the files on Thingiverse: https://www.thingiverse.com/thing:4428184

        We’ll be harvesting parts from an old Ultimaker 2.

        The stuff we plan to use:

        Based on what we found, we plan on making two threaded rod actuators and two belt-driven actuators.

        Here is the prototype of the linear threaded rod actuator:

        Here is the prototype of the linear belt-driven actuator:

        Here is the prototype of the entire foam cutter assembly:

         Here are all the parts 3D printed:

        Here are the 3D printed parts along with the hardware:

        Here is the belt drive assembly:

         

        Here are the two axes completed:

        Here is the first of the two linear actuator drives:

        Slight differences between the two based on different hardware but essentially the same:

        Here is the full assembly minus electronics:

        The electrical connections to the TinyG:

        All plugged in and ready to go:

         

        Using the webpage CNC controller Chilippeper :

        Running local JSON script to allow the website to access the COM ports, if I understand correctly:

         

        Chilipeppr Configuration (https://github.com/synthetos/TinyG/wiki/TinyG-Configuration-for-Firmware-Version-0.97):

        To change the Motor – Axis assignment in Chilipeppr:

        $1ma=0 Maps motor 1 to the X axis
        $2ma=1 Maps motor 2 to the Y axis
        $3ma=0 Maps motor 3 to the X axis
        $4ma=1 Maps motor 4 to the Y axis

        To assign the same travel per revolution for axis 4 as for the other 3 axes:
        $4TR 1.25
        To swap polarity of a motor:

        $1PO

        • 0 = Normal motor polarity
        • 1 = Invert motor polarity

        To set X axis minimum and maximum end stop limit switches (the switches connect GND to XMIN or XMAX on the Tiny G).

        $XSN 3 and $XSX 3

        To change the distance for every movement of 1mm:

        $1tr VALUE

        To change max feedrate in X:

        $xfr

        *************

        Here is the video of the machine making its first moves:

        **************

        Here is a version two fixing some errors in the 3D modeling:

        Linear actuator redesign (forgot to subtract the motor and threaded rod previously):

        Here’s the finished belt-drive assembly:

        I reprinted this part with 40% density instead of 20% (which broke last time when I tightened), corrected the diameter of the rods (which destroyed the rigity of the actuator), reoriented the embedded nut, and used shorter bolts:

        Also 40% instead of 20%:

        This was a bit tricky but using two bolts with washers I replaced the hot glue to fix the belt in place. 

        I corrected the orientation of the holes and the two actuators line up perfectly:

        Here’s the final assembly version 2:

         

        *************

        I’ve added some limit switches and have a preliminary setup for the hot wire cutting fixtures:

        **************

        Here is the first test cut:

         

        Here’s what it looked like on the software side:

         

        I used CAMBAM to take in a DXF and output the Gcode. 

        I had to additionally make everything related to the Z axis zero, and set the start point.

        DIY Centrifuge

        Based on Hackteria’s DIY Centrifuge (https://hackteria.org/wiki/Hacked_Hard-disc_Centrifuge)

        Controlling a Three-Phase DC Brushless motor (BLDC) of a Hard Drive primer: https://www.monolithicpower.com/pub/media/document/Brushless_DC_Motor_Fundamentals.pdf

        Similar to a stepper motor (except a BLDC is meant to run smoothly not in steps) but with three stators (if it’s an inrunner, an outrunner has the permanent magnets around the shaft): 

        What are Brushless DC Motors | Renesas Electronics

         

        At the top are the signals we need to generate with signal phase 120 degrees offset (steppers are typically two phase with 90 degrees apart):

        BLDC Motor Control With Arduino, Salvaged HD Motor, and Hall ...

        I’m using the SN754410 Quadruple Half-H Driver IC based on this tutorial: https://elabz.com/bldc-motor-with-arduino-circuit-and-software/

        Here’s the circuit breadboarded (motor connection not shown) with a pot to change the speed:

        Here is the scope capture of the sigals generated:

        This is the 3D file I downloaded: https://www.thingiverse.com/thing:2539744

        Here’s the 3D print with two 1.5ml Eppendorf tubes screwed on to the BLDC hard disk motor. 

        I had difficulty distinguishing between which of the three wires were connected to coils and which on wasn’t and got inconsistent resistance measurements. My current thinking is that the motor was damaged and is not working.

        Testing with another kind of BLDC and still not working…so is most likely my circuit?

        EWOD (electro wetting on dialectric)

        This project is based on Gaudi Lab’s OpenDrop (https://www.gaudi.ch/OpenDrop/)

        Here is the github for Fablab Digiscope’s iteration of the project: https://github.com/merlinmarrs/OpenDrop

        We don’t have high voltage MOSFETs so we are using relays instead. In parallel we will be sourcing the components to make the high voltage power supply using a boost converter that takes 12V as input. In the meantime, we will be using a transformer that sources 100V directly to the board to test functionality. 

        Here is the board cut:

        To supply the minimum 100V necessary, I found this high voltage boosting circuit here: https://www.multisim.com/content/dUPNxLKfTuK3sYfGNjNxNg/9v-to-100v-boost-converter/

        With what we have in the lab, it’s peaking at just under 80V. 

        Testing the relay:

        Testing the relay with a motor and 9V battery:

        Here is the first full prototype:

         

        What we need now is some hydrophobic dialectric like Teflon…

        Fever monitor

        This device is intended to be installed within the 3D printed forehead mount of a visor. 

        It’s being developed at this github: https://github.com/merlinmarrs/Fever-Meter

        The top board is a boost regulator taking the 2V from a pair of miniature backup batteries, and the bottom side is an attiny85 with I2C temperature sensor and indication led. 

        This project necessitated the use of rivets. I used a dremel to make a hole in the board then inserted the rivet and gave it a tap with our riveting tool.

        The results are relatively clean:

        IR thermometer with POV display

        This contactless temperature sensor has a POV display for the temperature readout. 

        Here is the github: https://github.com/merlinmarrs/POV-fever-meter

        Here is what it looks like in a dark room with long exposure:

        The plan was to have the device spell out the message in reverse after displaying it normally to allow a certain frequency of waving the wand back and forth to display perfectly. Alas…this does not work well currently.

        I am still waiting for the IR temperature sensor to arrive in the mail.

        Dispositif Désinfection

        Developed by Fablab Digiscope.

        A board to sense the temperature and illumination, and act as an interface, inside a disinfection device:

        https://github.com/merlinmarrs/anti-viral-dispositif/

        A main board and secondary board to control a disinfection chamber based on this tutorial: http://www.needlab.org/face-masks-disinfection-device

        Here is the BOM:

        Standard components:

        7 segment display x2 595 Shift Register x2 LED x 2 Photocell Piezo Atmega 328p micro-swith (CHECK MAX VOLT + AMP RATING)

        exotic components:

        lampe UV-C à Amazon.fr:

        https://www.amazon.fr/Germicide-Ultraviolet-st%C3%A9rilisateur-Submersible-radiations/dp/B07Y82SM36/ref=sr_1_4?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&dchild=1&keywords=uv+aquarium+sterilizer&qid=1587383305&s=hi&sr=1-4-catcorr

        1. MLX90614 (pas en rayon a Mouser, Farnell, Sparkfun, Polulu ou Adafruit) à Digikey (ou c’est “hors prix”) ou Amazon.fr:

        https://www.digikey.com/product-detail/en/melexis-technologies-nv/MLX90614ESF-DCA-000-TU/MLX90614ESF-DCA-000-TU-ND/2025323

        https://www.amazon.fr/MLX90614ESF-MLX90614ESF-BAA-000-TU-ND-thermom%C3%A8tre-infrarouge-compatible/dp/B07JBKM2K3/ref=sr_1_1?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&dchild=1&keywords=MLX90614&qid=1587470750&s=hi&sr=1-1-catcorr

         

         

         

        Still waiting for the temperature sensor to arrive in the mail. 

        Syringe pump mechanism + electronics

         

        This is the stepper motor driver design we are hoping to use to control a syringe. Here is the PDF describing the assembly: etapes fab venti v2

        And here is the github repo:

        https://github.com/merlinmarrs/FabVenti

         

        This is the syringe pumping prototype I developed. It uses 8020, a NEMA stepper with built in threaded rod, NEMA motor brackets, and linear bearings. The rest is 3D printed and uses M3 and M4 screws. 

        Solar Electronics Primer

        The goal of this primer is to introduce people to mini DIY solar energy applications.

        New solar product I just found out about: 

        Solar Digital Calipers

                          

        Fab 15 Part VII

        Here are some videos of the current prototypes:

         

         

         

         

        I want to focus this project on the design of the 2 axis rotation mechanism. The electronics and code side has already been explored and I can’t offer anything new here as a designer.

        (I’ll abstract the sensing and radio side of thing for a now and design the PCB to be sent to manufacture later. This will be a good exercise in applying the new PCB design best practices that I have since learned and for overcoming the limitations of PCB making in the lab. I could imagine this board having a compass and accelerometer to find out its own direction, and possibly an wifi connection to send its data to the internet.

        To simplify things further, I could think of this design as an indoor weather station. Instead of the tpical arduino inside a plastic box though, this project would emphasize the dynamic quality of solar energy, turning solar tracking into an exciting technical spectacle. The design would make reference to off-grid technology and thereby embody the mythical appeal of the power-autonomous and the off-grid (tiny house movement, prepper phenomenon, etc.).

        *************

        I’m inspired by weather buoy design:

         

        And also the possibility of some kind of Wes Jones inspired multi solar panel actuated louver system:

        Louver Dampers / Industrial Dampers • Kelair Products Inc.

        It could look like the MIT solar panel project but it would have a lower center of gravity and be motor controlled.

        Hyperstealth's "Declipse™" Shadow Reduction Technology Solves MIT ...

        Some quarantine sketches:

        *************

        Problems with the previous prototypes:

        V.1

        Top heavy and always falling over.

        Middle connection to ball bearing not sufficiently robust

        Nothing keeping motor on track, it lifts up off the gear track.

        Center mast not easy to fix in place.

        V.2

        Will topple over in certain positions.

        Tilt cannot rotate 360 and is holding a lot of weight.

        No way to tell exactly what position reached.

        ****************

        V.3 Hinge tilt + 3D printed bearing with motor mount integrated

        The hinge will be based on this idea from https://www.generationrobots.com/en/402702-pan-tilt-kit-for-pixy-camera.html:

        Some detail shots of the assembly (from https://docs.pixycam.com/wiki/doku.php?id=wiki:v2:assembling_pantilt_mechanism):

        Here is the one sided threaded bracket:

        https://www.sparkfun.com/products/10228

        Angle Bracket - 4-40

        Here is a 3D printed version: https://www.thingiverse.com/thing:978300. I would be replacing the servo with the DC gear motor and changing the bolts:

        And this is how the servo arm fastening works:

        In combination with this is the idea to design a completely custom rotation system. This would combine Paolo’s 3D printed bearing mount (https://www.instructables.com/id/3D-Printed-Bearing-with-Driven-Miter-Gear/) with DIY stepper motor coils:

        Image result for paolo 3d printed bearing

        3D Printed Bearing With Driven Miter Gear

        And these are two 3D printed stepper motors based on wrapping wire around nails and using rare earth magnets. I could also incorporate feedback by printing stripes and counting them with a line follower.

        The only problem appears to be the voltage required to power these motors which seems to be between 5V and 20V. Perhaps I could take some industrially made coils (which would be presumably more efficient) and reuse them. Like from the inside of a real stepper motor:

        Image result for 3d printed stepper

        The magnetic field of a coil appears to be based on the coil density (number of turns per length of wire), the material in the middle of the coil (like iron), and the current passed through. I would need a dense coil if I didn’t want to drain the caps.

        I could also work with small stepper motor linear actuators like this:, but then I would need to convert linear to rotary motion:

        Image result for mini stepper motor

        As for the code:

        https://www.hindawi.com/journals/js/2019/3681031/

        According to the above article, loading the Astrologer’s Almanac onto a sensorless microchip appears to be even more efficient than giving a microchip light sensors in the classic approach (https://create.arduino.cc/projecthub/ioarvanit/dual-axis-solar-tracker-panel-with-auto-and-manual-mode-41cfd9).

        Code has already been optimized for arduino: https://pdfs.semanticscholar.org/ffd3/bb8b5e85c796da0ae3eac3be11117291289f.pdf

        *****

        Here is the next version of this design:

        The thinking is to look around and find existing parts of medium to high quality in the lab, and design something which works around those preexisting parts. (I have the gears, the chain, the 3mm rod, and the ball bearings. The bearings we have don’t have 3mm inner diameter so right now they just turn around a hole in the 3D print / CNC milled axle holders. 

        Here’s a right angle design with only one bearing and less hardware (there may be a weight distribution argument for one of these assemblies):

        Remaking the gear (less improperly this time) with Sweep1 and scaled outline which I bent into a circle:

        Here’s the model ready for print:

        lazy explosion:

        The 3D printed parts:

        The begining of the bearing assembly:

        The alignment was impossible to get right here:

         

         I ended up hacking together something with Paolo’s bearing and some old prints. After some sanding and forcing of parts together, here is the prototype:

         

        Errata: 

        -ball bearing spacing not great enough.The bearing did not fit together, I’m guessing because there wasn’t a big enough gap between the ball bearings and the two sides of the bearing.

        -the upper bearing needs more depth in order to comfortably rest in place, but it just bearly works.

        -the motor holders required many minutes of sanding and filing to fit the motors, but now it fits perfectly.

        -the gear should probably be a miter, also there is nothing holding the sprocket down in place.

        ******

        Took apart a bunch of small stuff to have small screws and rods:

        To fix the board to the bar I used a part I found in a floppy disk drive:

         

        Showing the M1.6 screws holding the top 3D print in place and some M3 black bolts to keep the inner bearing off the ground.

        I wired up the two motors as well to see how that would work:

        *******

        Here is an attempt at making something similar from stepper motors (of course they need to be powered to hold their position so they can’t really be weight bearing). The nice thing about steppers is the mini linear threaded drives which could be added to the pan-tilt to give the machine the ability to move around shadows? 

         

        3D modeling the pan tilt with two steppers:

        …and one stepper / one DC gearmotor:

        Here is the design worked a bit more and ready to print:

        Here is how the print and assembly turned out:

         

        I like how the translucent print blends with the different metal and plastic shades. For the electronics I’m planning four LRDs and a super simple program (with batt for super reactive demo?). One could either imagine a whole array of the same sun tracking devices, or a series of different families of devices mixed together in an array, which would all react in the same way to moving a light bulb into the space above the robots….

        Errata:

        -Everything is still a bit too snug with 0.1mm offsets. I should have made the spacing for the rail even bigger because it needs to actually slide…

        -I designed using a bearing I only have one of and was already using in another machine…

        -The holes for the read DC motor mounting didn’t come through.

        -The top part which holds the rod and attaches to the linear rail broke because it was super weak in the middle.

        -The bottom brace is not long enough to force the panel into more acute angles.

        -The tension in the rubber belt is not sufficient to turn the base…

        -The threaded rod nut plastic piece is not pushed sufficiently into the threaded rod to grip effectively. It needs to be closer to the rod so the built-in spring is in tension.

        ****************

        I have some new parts: M2 bolts and nuts as well as steppers with gear reductions.

        Taking apart more DVD roms I had the idea of remaking this device entirely from e-waste parts. I have six kits so far:

        The idea would be to make this design:

        There are about four sets of the same components available from the 8 DVD drives I took apart. I am redesigning the 3D model with these parts in mind.

        There is also the possibility of using the motor drivers from the boards themselves except that they require a 12V VCC…

        Here is a post describing how they could be hacked: https://electronics.stackexchange.com/questions/341519/i-need-help-controlling-the-sled-motor-of-a-cd-rom-based-off-of-a-datasheet

        Image of the BD7907FS Circuit Diagram

        Here are some details about this next design:

        -it incorporates another found motor of the DVD rom device instead of the geared DC motor ordered from Polulu. 

        -it has tracks instead of holes for sensitive spots (like the DC motor mount and the threaded rod nut) so that things can be adjusted and then bolted when they are just perfect.

        -I replaced the central bearing for the correct diameter.

        -I made the upper 3D printed peice more structurally sound so hopefully it won’t break this time. 

        -I made larger offsets for inserts (except for the rod holders which I want to be snug) 0.2mm all around instead of 0.1mm this time. 

        -I made a recessed track for the gear belt holder on the DC motor. 

        -I fiddled such that the angle range for the panel is greater now. 

         

        In parallel I’m working on the simplest version of a test board possible:

        -super cap will need to be manually charged

        -4 photodiodes for solar orientation will be manually wired to avoid challenging trace designs.

        -only the most essential stuff for the motor drivers and microchip. 

        The idea is that I can make a video showing the device tracking a light source in real time super “responsively”. This could then easily be modified to be the actual sun seeking circuit. 

        I will have four little directional shaders which will cast shadows if the four photodiodes are not all pointed directly at the same light source. 

        https://www.sciencedirect.com/science/article/abs/pii/S2213138818300080

        Open hardware/software test bench for solar tracker with virtual instrumentation - ScienceDirect

        I made a quick PCB then decided what I really need is an air wired arduino with motor drivers to test this.

        Errata from second stepper prototype:

        -The 3D printed piece that holds the leadscrew nut is too thick for my M2 bolts and so it cannot be bolted on.

        -I forgot to subtract the bottom toothed part of the black gear belt wheel from the base.

        -most of the offsets are too loose. 

        -with the wider heavier DC motor the base is no longer stable enough.

        -I forgot to subtract the flange of the DC motor (like a collar around the axle) from the bracket that it attached to.

        Next I will get a quick mock up with some light sensors mounted on the four corners of the solar panel enabling an arduino to follow a light source. This won’t be embedded on a board yet, it will just be a test.

         

        ***********

        I’m using a DRV8835 motor shield and an easy stepper motor driver for this mock up with the LDRs wired like this (but with 1Ks instead of 10Ks):

        Working with Light Dependent Resistor (LDR) - Arduino Project Hub

        Here is the code I’m using to control the LDR sensing sun seeker;

        #include <DRV8835MotorShield.h>

        #define LED_PIN 13

        DRV8835MotorShield motors;

        //Declare pin functions on Redboard
        #define stp 2
        #define dir 3
        #define MS1 4
        #define MS2 5

        #define BPHASE 10
        #define BEN 9

        #define EN 6

        int sensorPin1 = A0;
        int sensorPin2 = A1;
        int sensorPin3 = A2;
        int sensorPin4 = A3;
        int sensorValue1 = 0;
        int sensorValue2 = 0;
        int sensorValue3 = 0;
        int sensorValue4 = 0;

        //Declare variables for functions
        char user_input;
        int x;
        int y;
        int state;

        void setup() {

        Serial.begin(9600);
        pinMode(sensorPin1, INPUT);
        pinMode(sensorPin2, INPUT);
        pinMode(sensorPin3, INPUT);
        pinMode(sensorPin4, INPUT);

        pinMode(stp, OUTPUT);
        pinMode(dir, OUTPUT);
        pinMode(MS1, OUTPUT);
        pinMode(MS2, OUTPUT);
        pinMode(EN, OUTPUT);
        resetEDPins(); //Set step, direction, microstep and enable pins to default states

        pinMode(LED_PIN, OUTPUT);

        pinMode(BPHASE, OUTPUT);
        pinMode(BEN, OUTPUT);
        }

        //Main loop
        void loop() {

        sensorValue1 = (analogRead(sensorPin1) + analogRead(sensorPin1) + analogRead(sensorPin1))/3;
        sensorValue2 = (analogRead(sensorPin2) + analogRead(sensorPin2) + analogRead(sensorPin2))/3;

        sensorValue3 = (analogRead(sensorPin3) + analogRead(sensorPin3) + analogRead(sensorPin3))/3;
        sensorValue4 = (analogRead(sensorPin4) + analogRead(sensorPin4) + analogRead(sensorPin4))/3;

        if(sensorValue1 > 500 && sensorValue1 > sensorValue2)
        {
        StepForwardDefault();
        }

        if(sensorValue2 > 500 && sensorValue2 > sensorValue1)
        {
        ReverseStepDefault();
        }

        if(sensorValue3 > 500 && sensorValue3 > sensorValue4)
        {
        forward(10);
        nomove();

        }

        if(sensorValue4 > 500 && sensorValue4 > sensorValue3)
        {
        backward(10);
        nomove();

        }

        //StepForwardDefault();
        //ReverseStepDefault();

        //forward(50);
        //backward(50);
        //nomove();

        }

        void forward(int t)
        {

        digitalWrite(BEN, HIGH); //forward
        digitalWrite(BPHASE, LOW);
        delay(t);

        }

        void backward(int t)
        {

        digitalWrite(BEN, HIGH); //backward
        digitalWrite(BPHASE, HIGH);
        delay(t);

        }

        void nomove()
        {

        digitalWrite(BEN, LOW); //no movement
        digitalWrite(BPHASE, LOW);

        }

        //Reset Easy Driver pins to default states
        void resetEDPins()
        {
        digitalWrite(stp, LOW);
        digitalWrite(dir, LOW);
        digitalWrite(MS1, LOW);
        digitalWrite(MS2, LOW);
        digitalWrite(EN, HIGH);
        }

        //Default microstep mode function
        void StepForwardDefault()
        {

        digitalWrite(dir, LOW); //Pull direction pin low to move “forward”
        for(x= 0; x<100; x++) //Loop the forward stepping enough times for motion to be visible
        {
        digitalWrite(stp,HIGH); //Trigger one step forward
        delay(1);
        digitalWrite(stp,LOW); //Pull step pin low so it can be triggered again
        delay(1);
        }

        }

        //Reverse default microstep mode function
        void ReverseStepDefault()
        {

        digitalWrite(dir, HIGH); //Pull direction pin high to move in “reverse”
        for(x= 0; x<100; x++) //Loop the stepping enough times for motion to be visible
        {
        digitalWrite(stp,HIGH); //Trigger one step
        delay(1);
        digitalWrite(stp,LOW); //Pull step pin low so it can be triggered again
        delay(1);
        }

        }

        // 1/8th microstep foward mode function
        void SmallStepMode()
        {

        digitalWrite(dir, LOW); //Pull direction pin low to move “forward”
        digitalWrite(MS1, HIGH); //Pull MS1, and MS2 high to set logic to 1/8th microstep resolution
        digitalWrite(MS2, HIGH);
        for(x= 0; x<1000; x++) //Loop the forward stepping enough times for motion to be visible
        {
        digitalWrite(stp,HIGH); //Trigger one step forward
        delay(1);
        digitalWrite(stp,LOW); //Pull step pin low so it can be triggered again
        delay(1);
        }

        }

        //Forward/reverse stepping function
        void ForwardBackwardStep()
        {

        for(x= 1; x<5; x++) //Loop the forward stepping enough times for motion to be visible
        {
        //Read direction pin state and change it
        state=digitalRead(dir);
        if(state == HIGH)
        {
        digitalWrite(dir, LOW);
        }
        else if(state ==LOW)
        {
        digitalWrite(dir,HIGH);
        }

        for(y=0; y<1000; y++)
        {
        digitalWrite(stp,HIGH); //Trigger one step
        delay(1);
        digitalWrite(stp,LOW); //Pull step pin low so it can be triggered again
        delay(1);
        }
        }

        }

         

        ***********************

        To make an on-board version of the breadboard prototype I made, I think I can reuse a previous version of this circuit which has two motor drivers and even a couple of spots for LDRs (I would just ignore the radio and sensor side of things for the moment at least):

        (I may need to add a CS pull-up resistor and a 0.1uF cap to power as these were two erratta from this version of the board.) 

        This turned out to be a bad idea, there are approximately 3,590 jumpers between the two boards, so no practical at all.

        I am thinking of making miniature smd light sensor pairs (we have smd photodiodes in the lab) that would attach to the outside of the board on 3D printed T-shaped things:

        When light is straight ahead, difference between LDRs will be minimal. Moment that one is significantly darker than the other, the motor which turns the dark side towards the light needs to come on until they are equalized again. So more of a nested if loop?

        if(sensor1-sensor2 > 200){// difference in sensors

        if(sensor1>100&&sensor2<50) //turn motor CW

        else()//turn motor CCW

        ****************

        Because the board already has the radio built in, I wonder if I should reincorperate it into the project. I also recieved some female SMA board antenna connectors which I could incorperate into the new version.

        10Pcs SMA Female Solder Edge PCB Mount Straight RF Connector Plug ...

        ************

        New code:

        #include <Stepper.h>

        // change this to the number of steps on your motor
        #define STEPS 200

        // create an instance of the stepper class, specifying
        // the number of steps of the motor and the pins it’s
        // attached to
        Stepper stepper(STEPS, A2, A3, A5, A4);

        #define RED 3 // RED LED

        int sensorPin1 = A0;
        int sensorPin2 = A1;
        int sensorPin3 = A6;
        int sensorPin4 = A7;
        int sensorValue1 = 0;
        int sensorValue2 = 0;
        int sensorValue3 = 0;
        int sensorValue4 = 0;

        void setup()
        {

        pinMode(sensorPin1, INPUT);
        pinMode(sensorPin2, INPUT);
        pinMode(sensorPin3, INPUT);
        pinMode(sensorPin4, INPUT);

        pinMode(6, OUTPUT); //

        pinMode(7, OUTPUT); //

        // set the speed of the motor to 30 RPMs
        stepper.setSpeed(60);
        }

        void loop()
        {

        digitalWrite(6, LOW); //
        digitalWrite(7, LOW); //

        sensorValue1 = (analogRead(sensorPin1) + analogRead(sensorPin1) + analogRead(sensorPin1))/3;
        sensorValue2 = (analogRead(sensorPin2) + analogRead(sensorPin2) + analogRead(sensorPin2))/3;

        sensorValue3 = (analogRead(sensorPin3) + analogRead(sensorPin3) + analogRead(sensorPin3))/3;
        sensorValue4 = (analogRead(sensorPin4) + analogRead(sensorPin4) + analogRead(sensorPin4))/3;

        if(sensorValue1 > 500 && sensorValue1 > sensorValue2)
        {
        clockwise();
        delay(500);
        }

        if(sensorValue2 > 500 && sensorValue2 > sensorValue1)
        {

        counter_clockwise();
        delay(500);
        }

        if(sensorValue3 > 500 && sensorValue3 > sensorValue4)
        {
        stepper.step(STEPS);

        }

        if(sensorValue4 > 500 && sensorValue4 > sensorValue3)
        {
        stepper.step(-STEPS);
        }

        digitalWrite(6, LOW); //
        digitalWrite(7, LOW); //

        }

        int clockwise()
        {

        digitalWrite(6, HIGH); //
        digitalWrite(7, LOW); //

        }

        int counter_clockwise()
        {

        digitalWrite(6, LOW); //
        digitalWrite(7, HIGH); //

        }

        Circuit Protection + PCB Design Rules of Thumb

        I just destroyed a circuit I spent over a week preparing and I decided I would learn how to better protect my circuits.

        Reverse Polarity protection:

        A diode can be used to protect a circuit but it will drop 0.7V and will waste power. A shotky diode will only drop .3-.4V but it has the same issue. A P-Channel MOSFET set up where the gate is connected to ground will only turn on when the polarity is correct. If Vin is not greater than the maximum allowed voltage you can put accross the MOSFET from gate to source (Vgs max.), or the Vin is not less than the threshold required for the P-FET to turn on (Vgs th), then it doesn’t need any other components.

        Image result for how to protect circuits from reverse voltage polarity

        For overvoltage protection (https://circuitdigest.com/electronic-circuits/overvoltage-protection-circuit):

        When the voltage is below the Zener Diode’s 5.1V reverse threshold, Q2’s base is HIGH  (through the 2.2K resistor) so the PNP is off. This means that Q1 is ON (because it’s PNP base is connected to ground via 6.8K) and the circuit is powered.

        When the voltage is above the Zener Diode’s 5.1V reverse threshold, Q2 is connected to ground and is turned on. This connects Q1 with VCC and therefore turns it off, disconnecting the circuit from the power. 

        Overvoltage Protection Circuit Diagram

        For short circuit protection:

        Q2 is a PNP, Q1 is an NPN. Here is my understanding based on the site https://circuitdigest.com/electronic-circuits/short-circuit-protection-circuit-diagram

        First a small amount of current reaches the base of Q1 (via R5 and the D2), causing it to turn on Q2. Now current flows through Q2 into R4 and D1 while keeping Q1 on. Current is no longer flowing through D2.

        When a short occurs, current passes directly towards ground from Q2 and therefore no longer turns Q1 on. This turns off Q2 and now current passes via R5 and D2, skipping D1 and still skipping R2 towards Q1, directly to ground. Instead of having a short, we now have current passing through R5 and D2. 

        Short Circuit Protection Block Diagram

        PCB Design (from https://www.youtube.com/watch?time_continue=5&v=NJKZZArjdg8&feature=emb_logo):

        -rounded corner for logic levels, straight corners “round edges”. (Though not everyone seems to agree on this). 45 degree angles are good, 90 are bad.

        -minimize trace lengths, components electronically close should be physically close too. Longer traces have higher resistance, capacitance and inductance. 

        -Think in terms of a signal chain, with input on the left moving towards output on the right. Divide things into building blocks. Build each block separately and then throw it on the main board. 

        -VCC should be connected via a star-like formation to sub systems, with branches of equal length, NOT in a cascaded or serial way where the last sub system is furthest away from VCC. This will make the later sub systems vulnerable to noise produced by the upstream ones.

        -Mechanical parts (pots, switches, barrel connectors) fail before electrical parts.

        -watch out for your ground returns (the path that a component’s ground path with take to make it back to the power regulator). If it is zig zaggy and not short this is not ideal.

        -don’t forget to add test points! 

        This website helps to calculate the correct width of traces for a given current:

        http://circuitcalculator.com/wordpress/2006/01/31/pcb-trace-width-calculator/

        This resource looks good for more in depth: http://alternatezone.com/electronics/files/PCBDesignTutorialRevA.pdf

        Here is a table showing minimum trace widths for different thicknesses of copper:

        •••••••••••••••

        -work in mils, most components are designed with 100 mil spacing (0.1″). 

        -For your Eagle grid, had a 25 thou alt grid with a 50 thou main grid. Or a 25mil/10mil for finer work. Use a SNAP grid.

        -bigger traces are better generally, they have lower DC resistance and lower inductance.

        -25 thou for signal traces, 50 thou for power, 10-15 for traces going in between ICs is a good place to start. 

        -oval shaped pads for ICs, circular for leaded resistors and caps. 

        -minimize the number of drill bits that will be required by the board house to make your circuit.

        -check that when you actually add the components, screws, etc., it all fits and doesn’t cause potential shorts. 

        -do not mix analog and digital sub systems in the same circuit, nor high current or frequency and low current and frequency. 

        -leave a rectangle at the top of the solder mask so you can write something in pen on the board!

        -for a two sided board, the bottom can be a ground plane. You should not have the ground plane extend all the way to the edge of the board. The more copper in your ground path the lower the impedance. 

        -Use multiple vias to connect the same signal to ground to lower impedance. 

        -One bypass cap per IC: 10nF or 1nF for higher frequencies, and 1uF or 10uF for low frequencies

        -boards are typically made with 1oz. copper thickness and are 1.6mm thick fiberglass FR4.

        -don’t forget to add a signature/logo to your board!

        Visualizing your circuit before building:

        https://www.falstad.com/circuit/

        Fab 15 Part VI

        Here is my first attempt at making a video of this all working:

        I would like the next version of the video to show:-starting charging from zero.-the burst of packets sent at the begining and how it finds a balance afterwards. 
         -the sky in the background so it’s easy to understand how the clouds effect the charging rate.Here is me assembling the final boards:

         

        I tried to power the motors using the motor driver on the flip side, I got weird results until I unplugged the ICSP header. After that everything worked as expected except when the power supply was turned off for some reason the motor drivers stopped working.

        I have a problem of not having enough pins broken out.

        For the VEML7700, I downloaded the following Adafruit libraries:

        https://github.com/adafruit/Adafruit_VEML7700

        https://github.com/adafruit/Adafruit_BusIO

        For some reason I also needed to include the SPI.h file otherwise it would not compile.

        Adding the VEML7700 code somehow makes the original code no longer work… I should now try with a vanilla arduino just to make sure the test code works.

        The VOUT voltage sensing appears to work. This is a nice option because it means that just by taking the integral of the VOUT we can see if the solar panel position is getting more light (i.e. no need for multiple light sensors if we’re willing to spend some energy on motors in the spirit of trail and error sensing).

        I have noticed that LDO is not stable at 2.2V but dips to below 2V. I’m not sure how related this is to the power supply being connected to the LTC3105.

        I am working on a new minimal iteration of the sun seeking functionality.

        Here is another 3D model I’m working on. The idea is that it cannot destroy itself by accident (it can turn all the way around in both axes and not break anything). It is also independent of whatever surface it is placed on. The bearing at the center allows everything to rotate freely.

        Here are some sketches of me trying to figure out how to model the rotary thing:

        The resin prints were pretty disastrous… The bearing didn’t fit and neither did the motors or the central axis pin! After some filing and hot gluing things fit a bit better.

        P1000330

        Here is the full assembly. It’s extremely inelegant. Here are some issues:

        -stiff wires.

        -the weight of things…Everything is straining the central axis pin, and the main mast supporting the solar panel board is not being held straight up.

        -The tilt arm is hitting the rotary gear, it needs to be smaller. 

        -everything is incredibly fragile and finicky.

         

        Replacing the mast with just a simple arm is less inelegant:

        Of course now the machine can destroy itself by just turning into the board hits something…It also needs a better solution for the solar panel being attached to the board somehow that can be easily removed for programming. 

        The main axis which fits into the bearing is still problematic. 

        This beautiful object was made by Mr. Paulo Salvagione at Pier 9. This would make a better base for the rotating part of this project I think. It could be made so that it fits the motor very snugly with no play.

        I’ve decided to make my life easier and to go towards a more obvious solution for the next iteration. It will at least give me a chance to see how the balance of the object behaves over the course of a day. I’ve printed this version before in PLA but never in resin.

        I’m taking the offset of .1mm all around to make the motors fit snugly. 

        Here is the Arduino code I have developed so far:

        /*

        */

        const int pgood = 3;
        const int cap_voltage = A2;
        const int delay_time = 100;
        const int motor_time = 500;

        float cap_reading_0;
        float cap_reading_1;
        float cap_reading_2;
        float cap_reading_3;
        float cap_reading_4;

        float cap_reading_average;

        int integral_0;
        int integral_1;
        int integral_2;
        int integral_3;
        int integral_4;

        int integral_total;

        int current_reading;
        int last_reading;

        // the setup function runs once when you press reset or power the board
        void setup() {

        // motor driver pins
        pinMode(A4, OUTPUT);
        pinMode(A5, OUTPUT);

        pinMode(A0, OUTPUT);
        pinMode(7, OUTPUT);

        pinMode(cap_voltage, INPUT); //
        pinMode(pgood, INPUT); //
        }

        // the loop function runs over and over again forever
        void loop()
        {
        if (pgood==HIGH)
        {
        evaluate_east_west();
        evaluate_north_south();
        }
        }
        int evaluate_east_west()
        {
        last_reading = integral();
        clockwise();
        delay(motor_time);
        current_reading = integral();

        if(last_reading>current_reading) // move back if less sun in new position
        {
        counter_clockwise();
        delay(motor_time);
        }

        last_reading = integral();
        counter_clockwise();
        delay(motor_time);
        current_reading = integral();

        if(last_reading>current_reading) // move back if less sun in new position
        {
        clockwise();
        delay(motor_time);
        }
        }

        int evaluate_north_south()
        {
        last_reading = integral();
        tilt_up();
        delay(motor_time);
        current_reading = integral();

        if(last_reading>current_reading) // move back if less sun in new position
        {
        tilt_down();
        delay(motor_time);
        }

        last_reading = integral();
        tilt_down();
        delay(motor_time);
        current_reading = integral();

        if(last_reading>current_reading) // move back if less sun in new position
        {
        tilt_up();
        delay(motor_time);
        }
        }

        int cap_voltage_read()
        {
        cap_reading_0 = analogRead(cap_voltage);
        delay(delay_time);
        cap_reading_1 = analogRead(cap_voltage);
        delay(delay_time);
        cap_reading_2 = analogRead(cap_voltage);
        delay(delay_time);
        cap_reading_3 = analogRead(cap_voltage);
        delay(delay_time);
        cap_reading_4 = analogRead(cap_voltage);
        delay(delay_time);

        return cap_reading_average = (cap_reading_0 + cap_reading_1 + cap_reading_2 + cap_reading_3 + cap_reading_4) / 5;
        }

        int integral()
        {

        integral_0 = (int)cap_voltage_read(); // convert to int
        delay(delay_time);
        integral_1 = (int)cap_voltage_read(); // convert to int
        delay(delay_time);
        integral_2 = (int)cap_voltage_read(); // convert to int
        delay(delay_time);
        integral_3 = (int)cap_voltage_read(); // convert to int
        delay(delay_time);
        integral_4 = (int)cap_voltage_read(); // convert to int
        delay(delay_time);

        return integral_total = integral_0 + integral_1 + integral_2 + integral_3 + integral_4;
        }

        int clockwise()
        {
        digitalWrite(A5, HIGH); //
        digitalWrite(A4, LOW); //

        digitalWrite(A0, LOW); //
        digitalWrite(7, LOW); //
        }

        int counter_clockwise()
        {
        digitalWrite(A5, LOW); //
        digitalWrite(A4, HIGH); //

        digitalWrite(A0, LOW); //
        digitalWrite(7, LOW); //
        }

        int tilt_up()
        {
        digitalWrite(A5, LOW); //
        digitalWrite(A4, LOW); //

        digitalWrite(A0, HIGH); //
        digitalWrite(7, LOW); //
        }

        int tilt_down()
        {
        digitalWrite(A5, LOW); //
        digitalWrite(A4, LOW); //

        digitalWrite(A0, LOW); //
        digitalWrite(7, HIGH); //
        }

         

        The current issue I’m having is with PGOOD which is for some reason hovering around 1V instead of 2.2V. When I remove the PGOOD test things appear to work.

        Here is the resin print, it’s not bad! I think I will turn the top arm outwards so that no matter what happens the machine won’t destroy itself (at least during the testing phase).

        And here is with the board attached:

        And here is a video of the thing rotating:

        P1000390

        PGOOD is not working so I’m just making the code test the voltage of the capacitor when it wakes up. I’ve also added a motor_stop() function because I forgot to stop the motors turning during the time consuming voltage checking functions. This appears to work.

        void loop()
        {
        if(((int)cap_voltage_read()) > 410)

        // this is 1/3 – 20K/10K R divider of 2.7V out of a 2.2V LDO scale, mapped into 1023
        {
        evaluate_east_west();
        evaluate_north_south();
        }
        }

        I was noticing that there was considerable leakage from the capacitor without serious sun. I decided this was probably from having the motor driver sleep pin connected to PGOOD. I have disconnected PGOOD from the micro-controller, and now used that pin to turn on and off the motor sleep. If this doesn’t solve the problem the other candidate is the leakage from the resistor divider which could be replaced by a mosfet as per the JeeLabs zero current measuring post. This would require another pin from the microchip and a bit more space on my already crammed board…

        (The next version of this board may need to be manufactured at a board house, not being able to easily make two sided boards is becoming very frustrating. I also want to use the fancy RF connectors and antennae I ordered.)

        Hunting for the cause of the leaking current. The voltage seems to stabilize at 1.29V, not sure what that tells us though..

        I’m using our super fancy Keithley DMM7510 7 1/2 Digit Multimeter. Check it out:

        I added sleep mode to the code and now once the voltage hits 2.6V it stays essentially stable. (The motor driver uses 1.6-2.5uA @ 5V, the atmega 328p 1uA @ 3V, and the 3V/30K voltage divider draws 100uA.)

        Already mentioned blog posts from Jeelabs on measuring battery voltage:

        https://jeelabs.org/2013/05/15/what-if-we-want-to-know-the-battery-state/

        https://jeelabs.org/2013/05/16/measuring-the-battery-without-draining-it/

        https://jeelabs.org/2013/05/17/zero-powe-battery-measurement/

        I’m wondering if I want to make things easier and have four mini solar panels and two comparators like this (http://www.ti.com/lit/ds/sbos589/sbos589.pdf) so that only two pins are required to determine which direction to move in. I could also possibly use the BEAM solarbotics “suspended bicore” solution (http://www.beam-online.com/Robots/Circuits/circuits.html#Phototropic%20Bicore).

        There is a flaw in my system, if suddenly the sun comes out at the perfect time, the robot can think that turning away from the sun led to finding more sun whereas in fact it was just the sun that changed. I could solve this by having the robot do multiple experiments before committing to actually changing its angle. 

        I should also include in the code a moment where the machine decides if the difference between the past and current reading is sufficiently great to necessitate moving. 

        I have another problem. There is some give in the gearmotors, therefore they do not turn the same amount when they switch directions. This is bad but it’s critical when combined with the code not working very well. This is more an issue for the tilt motor, so if I could replace this with an earlier design rotating a metal rod and using a mast to take the weight of the board perhaps that would solve my problem. 

        I’m hoping I wouldn’t need these: https://www.pololu.com/product/4761…

        Just realized my code is not doing what I want it to do. The integral() function is absolute, it is adding bigger and bigger numbers instead of looking at the difference between relative values…Fixed this. 

        I find the machine spends a lot of time moving back and forth between the same positions. It is rarely deciding that a new orientation is better than an old. I should get the radio set up so I can see the values that are being sent.

        I also need to replace the tilt mechanism with one that holds the weight of the board like the mast/rotation system I tried earlier. I should also do east/west rotation before moving on to doing both axes at once.

        Did a quick test, analogRead() has 10 bit resolution, giving a value between 0-1023. The resolution is barely good enough for taking a reading every 10 seconds and seeing a dropping voltage value in the cap. I think I should be taking measurements every few minutes to be able to detect a difference in charging. 

        I think I need to turn the radio off for long delays, otherwise it doesn’t work. I’m trying radio.sleep(). 

        I also realized it would make far more sense to sense the solar panel voltage rather than the capacitor voltage to sense the solar aspect. However this would add another input pin…

        *I think I might need to update the below code to include the floor() function which stops arduino from rounding? https://www.nongnu.org/avr-libc/user-manual/group__avr__math.html#ga0f0bf9ac2651b80846a9d9d89bd4cb85

        Here is the far from optimal code so far:

        // Include the RFM69 and SPI libraries:
        #include <SPI.h>
        #include <avr/wdt.h>
        #include <RFM69.h>
        #include <avr/sleep.h>
        #include “LowPower.h”

        // Addresses for this node. CHANGE THESE FOR EACH NODE!

        #define NETWORKID 0 // Must be the same for all nodes
        #define MYNODEID 2 // My node ID
        #define TONODEID 1 // Destination node ID

        // RFM69 frequency, uncomment the frequency of your module:

        //#define FREQUENCY RF69_433MHZ
        #define FREQUENCY RF69_915MHZ

        // AES encryption (or not):

        #define ENCRYPTKEY “TOPSECRETPASSWRD” // Use the same 16-byte key on all nodes

        // Use ACKnowledge when sending messages (or not):

        #define USEACK true // Request ACKs or not

        // Packet sent/received indicator LED (optional):

        #define LED A3 // LED positive pin

        #define PGOOD 3 // PGOOD

        #define MOSFET 9//

        // Create a library object for our RFM69HCW module:

        const int cap_voltage = A2;
        const int delay_time = 100; //30 x delay_time (?) is how long it takes to take an integral of cap voltage
        const int long_delay_time = 7000; //30 x delay_time (?) is how long it takes to take an integral of cap voltage

        double T;

        const int motor_sleep = 3;
        const int motor_time = 200;

        int current_reading;
        int last_reading;

        RFM69 radio;

        void setup()
        {
        // Open a serial port so we can send keystrokes to the module:

        pinMode(LED,OUTPUT);
        digitalWrite(LED,LOW);
        pinMode(MOSFET,OUTPUT); //mosfet
        // pinMode(PGOOD, INPUT); // PGOOD

        // motor driver pins
        pinMode(A4, OUTPUT);
        pinMode(A5, OUTPUT);

        pinMode(A0, OUTPUT);
        pinMode(7, OUTPUT);

        pinMode(motor_sleep, OUTPUT);
        pinMode(cap_voltage, INPUT); //

        }

        void loop()
        {

        wdt_enable(WDTO_8S);

        digitalWrite(MOSFET, LOW); // turn off MOSFET

        if(((int)cap_voltage_read()) > 410)
        {
        digitalWrite(motor_sleep, LOW); // sleep motor driver
        Radio_Send(); //includes init and mosfet turn on
        digitalWrite(MOSFET, LOW); // turn off radio

        T = integral();

        Radio_Send();
        digitalWrite(MOSFET, LOW); // turn off MOSFET
        evaluate_east_west();
        }
        else
        {
        digitalWrite(motor_sleep, LOW); // sleep motor driver
        digitalWrite(MOSFET, LOW); // turn off MOSFET
        sleep(); // sleep microchip
        }
        }

        int evaluate_east_west()
        {
        last_reading = integral();
        wdt_reset(); // do this before 8 seconds has elapsed so we don’t reset
        motors_off(); //also sleeps driver
        wait_long();
        wdt_reset(); // do this before 8 seconds has elapsed so we don’t reset
        clockwise();
        delay(motor_time);
        motors_off();
        current_reading = integral();
        wdt_reset(); // do this before 8 seconds has elapsed so we don’t reset

        if(last_reading>=current_reading) // move back if less sun in new position
        {
        counter_clockwise();
        delay(motor_time);
        motors_off();
        }
        wdt_reset(); // do this before 8 seconds has elapsed so we don’t reset

        wait_long();

        last_reading = integral();
        wdt_reset(); // do this before 8 seconds has elapsed so we don’t reset
        counter_clockwise();
        delay(motor_time);
        motors_off();
        wdt_reset(); // do this before 8 seconds has elapsed so we don’t reset
        delay(long_delay_time);
        wdt_reset(); // do this before 8 seconds has elapsed so we don’t reset
        current_reading = integral();
        wdt_reset(); // do this before 8 seconds has elapsed so we don’t reset

        if(last_reading>=current_reading) // move back if less sun in new position
        {
        clockwise();
        delay(motor_time);
        motors_off();
        }
        digitalWrite(motor_sleep, LOW);
        }

        int Radio_Send()
        {
        char Tstr[10];

        digitalWrite(MOSFET, HIGH); // turn on MOSFET
        delay(100);
        radio.initialize(FREQUENCY, MYNODEID, NETWORKID);
        radio.setHighPower(); // Always use this for RFM69HCW

        // Turn on encryption if desired:

        radio.encrypt(ENCRYPTKEY);

        char buffer[50];

        dtostrf(T, 5,5, Tstr);

        static int sendlength = strlen(buffer);

        sprintf(buffer, ” T:%s”, Tstr);

        radio.sendWithRetry(TONODEID, buffer, sendlength);

        Blink(LED,100);

        }

        int integral()
        {
        int integral_0;
        int integral_1;

        //int integral_total;

        int integral_total_1;

        integral_0 = (int)cap_voltage_read(); // convert to int
        wdt_reset(); // do this before 8 seconds has elapsed so we don’t reset
        wait_long();
        wait_long();
        wdt_reset(); // do this before 8 seconds has elapsed so we don’t reset
        integral_1 = (int)cap_voltage_read(); // convert to int
        wdt_reset(); // do this before 8 seconds has elapsed so we don’t reset

        integral_total_1 = integral_1 – integral_0;

        return integral_total_1;
        }

        int cap_voltage_read()
        {
        float cap_reading_0;
        float cap_reading_1;
        float cap_reading_2;
        float cap_reading_3;
        float cap_reading_4;

        float cap_reading_average;

        cap_reading_0 = analogRead(cap_voltage);
        delay(delay_time);
        cap_reading_1 = analogRead(cap_voltage);
        delay(delay_time);
        cap_reading_2 = analogRead(cap_voltage);
        delay(delay_time);
        cap_reading_3 = analogRead(cap_voltage);
        delay(delay_time);
        cap_reading_4 = analogRead(cap_voltage);
        delay(delay_time);

        return cap_reading_average = (cap_reading_0 + cap_reading_1 + cap_reading_2 + cap_reading_3 + cap_reading_4) / 5;
        }

        void Blink(byte PIN, int DELAY_MS)
        // Blink an LED for a given number of ms
        {
        digitalWrite(PIN,HIGH);
        delay(DELAY_MS);
        digitalWrite(PIN,LOW);
        }

        void sleep(void)
        {
        digitalWrite(MOSFET, LOW); // turn on MOSFET
        set_sleep_mode(SLEEP_MODE_PWR_DOWN); //select PWR DOWN, the most power savings
        sleep_enable(); //set SE bit
        sei(); // enable global interrupts
        sleep_cpu(); //actually sleep
        sleep_disable(); //code reaches this point after interrupt
        }

        int motors_off()
        {
        digitalWrite(motor_sleep, LOW); //put driver to sleep
        digitalWrite(A5, LOW); //
        digitalWrite(A4, LOW); //

        digitalWrite(A0, LOW); //
        digitalWrite(7, LOW); //
        }

        int clockwise()
        {
        digitalWrite(motor_sleep, HIGH); //wake up driver
        digitalWrite(A5, HIGH); //
        digitalWrite(A4, LOW); //

        digitalWrite(A0, LOW); //
        digitalWrite(7, LOW); //
        }

        int counter_clockwise()
        {
        digitalWrite(motor_sleep, HIGH); //wake up driver
        digitalWrite(A5, LOW); //
        digitalWrite(A4, HIGH); //

        digitalWrite(A0, LOW); //
        digitalWrite(7, LOW); //
        }

        int tilt_up()
        {
        digitalWrite(motor_sleep, HIGH); //wake up driver
        digitalWrite(A5, LOW); //
        digitalWrite(A4, LOW); //

        digitalWrite(A0, HIGH); //
        digitalWrite(7, LOW); //
        }

        int tilt_down()
        {
        digitalWrite(motor_sleep, HIGH); //wake up driver
        digitalWrite(A5, LOW); //
        digitalWrite(A4, LOW); //

        digitalWrite(A0, LOW); //
        digitalWrite(7, HIGH); //
        }

        int wait_long()
        {

        for(int i=0;i<10;i++)
        {
        wdt_reset(); // do this before 8 seconds has elapsed so we don’t reset
        digitalWrite(motor_sleep, LOW); //put driver to sleep
        digitalWrite(MOSFET, LOW); // turn off radio MOSFET
        LowPower.idle(SLEEP_4S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF,
        SPI_OFF, USART0_OFF, TWI_OFF);
        }

        }

        DIY Mini Drone

        For a change, this is a video of me working on the boards for this project:

         

         

        Radios are working, I’m struggling with the best way to poll the joysticks. I think I need to use Pin Change Interrupts (Interrupts for pins other than those dedicated for interrupting). The other option is just to have polling:

        if (UP == HIGH){ Send(UP);}

        if (DOWN == HIGH){ Send(DOWN);}

        etc.

        *Later realized that I messed this up, what I want is to check if the pin is LOW (i.e. if it is connected to ground and not internally pulled up) and then have an else at the end which turns to HIGH.

        *I forgot to put pull-up resistors on the actual board so I used the avr’s internal ones. In arduino you do this like so:

        pinMode(A0, INPUT_PULLUP);

        I reprinted the drone frame with .1mm extra space around the circle and now the motors fit snuggly. 

        Dissapointed that the joysticks only register an up or down when also pushed down…

        Here is the joystick sending code:

        ********************************************************************************

        // RFM69HCW Example Sketch
        // Send serial input characters from one RFM69 node to another
        // Based on RFM69 library sample code by Felix Rusu
        // http://LowPowerLab.com/contact
        // Modified for RFM69HCW by Mike Grusin, 4/16

        // This sketch will show you the basics of using an
        // RFM69HCW radio module. SparkFun’s part numbers are:
        // 915MHz: https://www.sparkfun.com/products/12775
        // 434MHz: https://www.sparkfun.com/products/12823

        // See the hook-up guide for wiring instructions:
        // https://learn.sparkfun.com/tutorials/rfm69hcw-hookup-guide

        // Uses the RFM69 library by Felix Rusu, LowPowerLab.com
        // Original library: https://www.github.com/lowpowerlab/rfm69
        // SparkFun repository: https://github.com/sparkfun/RFM69HCW_Breakout

        // Include the RFM69 and SPI libraries:

        #include <RFM69.h>
        #include <SPI.h>

        // Addresses for this node. CHANGE THESE FOR EACH NODE!

        #define NETWORKID 0 // Must be the same for all nodes (0 to 255)
        #define MYNODEID 1 // My node ID (0 to 255)
        #define TONODEID 2 // Destination node ID (0 to 254, 255 = broadcast)

        // RFM69 frequency, uncomment the frequency of your module:

        //#define FREQUENCY RF69_433MHZ
        #define FREQUENCY RF69_915MHZ

        // AES encryption (or not):

        #define ENCRYPT true // Set to “true” to use encryption
        #define ENCRYPTKEY “TOPSECRETPASSWRD” // Use the same 16-byte key on all nodes

        // Use ACKnowledge when sending messages (or not):

        #define USEACK true // Request ACKs or not

        // joystick and leds

        #define LED1 A0 // LED positive pin
        #define UP1 A2//
        #define LEFT1 A3//
        #define RIGHT1 A4//
        #define DOWN1 A5//

        #define LED2 A1 // LED positive pin
        #define UP2 8//
        #define LEFT2 9//
        #define RIGHT2 7//
        #define DOWN2 6//

        // Create a library object for our RFM69HCW module:
        RFM69 radio;

        void setup()
        {
        // leds and joystick init

        pinMode(LED1,OUTPUT);
        pinMode(LED2,OUTPUT);

        pinMode(UP1,INPUT_PULLUP);
        pinMode(LEFT1,INPUT_PULLUP);
        pinMode(RIGHT1,INPUT_PULLUP);
        pinMode(DOWN1,INPUT_PULLUP);

        pinMode(UP2,INPUT_PULLUP);
        pinMode(LEFT2,INPUT_PULLUP);
        pinMode(RIGHT2,INPUT_PULLUP);
        pinMode(DOWN2,INPUT_PULLUP);

        // Initialize the RFM69HCW:

        radio.initialize(FREQUENCY, MYNODEID, NETWORKID);
        radio.setHighPower(); // Always use this for RFM69HCW

        // Turn on encryption if desired:

        if (ENCRYPT)
        radio.encrypt(ENCRYPTKEY);
        }

        void loop()
        {
        //setting up variables for button states

        int sensorVal0 = digitalRead(UP1);
        int sensorVal1 = digitalRead(LEFT1);
        int sensorVal2 = digitalRead(RIGHT1);
        int sensorVal3 = digitalRead(DOWN1);
        int sensorVal4 = digitalRead(UP2);
        int sensorVal5 = digitalRead(LEFT2);
        int sensorVal6 = digitalRead(RIGHT2);
        int sensorVal7 = digitalRead(DOWN2);

        // SENDING

        // In this section, we’ll gather serial characters and
        // send them to the other node if we (1) get a carriage return,
        // or (2) the buffer is full (61 characters).

        // If there is any serial input, add it to the buffer:

        char buffer[5] = “z”;

        //BUTTON READING AND SENDING

        if (sensorVal0 == LOW) {
        buffer[0] = ‘a’;
        digitalWrite(LED2, HIGH);
        }

        else if (sensorVal1 == LOW) {
        buffer[0] = ‘b’;
        digitalWrite(LED2, HIGH);
        }

        else if (sensorVal2 == LOW) {
        buffer[0] = ‘c’;
        digitalWrite(LED2, HIGH);
        }

        else if (sensorVal3 == LOW) {
        buffer[0] = ‘d’;
        digitalWrite(LED2, HIGH);
        }

        else if (sensorVal4 == LOW) {
        buffer[0] = ‘e’;
        digitalWrite(LED1, HIGH);
        }

        else if (sensorVal5 == LOW) {
        buffer[0] = ‘f’;
        digitalWrite(LED1, HIGH);
        }

        else if (sensorVal6 == LOW) {
        buffer[0] = ‘g’;
        digitalWrite(LED1, HIGH);
        }

        else if (sensorVal7 == LOW) {
        buffer[0] = ‘h’;
        digitalWrite(LED1, HIGH);
        }

        else {
        buffer[0] = ‘z’;
        digitalWrite(LED2, LOW);
        digitalWrite(LED1, LOW);
        }

        // END OF BUTTON STUFF

        if(buffer[0]!=’z’)
        {

        static int sendlength = 3;

        radio.sendWithRetry(TONODEID, buffer, sendlength);
        }

        }

        ********************************************************************************

         

        And here is the receiver code:

         

        *********************************************************************************

        // RFM69HCW Example Sketch
        // Send serial input characters from one RFM69 node to another
        // Based on RFM69 library sample code by Felix Rusu
        // http://LowPowerLab.com/contact
        // Modified for RFM69HCW by Mike Grusin, 4/16

        // This sketch will show you the basics of using an
        // RFM69HCW radio module. SparkFun’s part numbers are:
        // 915MHz: https://www.sparkfun.com/products/12775
        // 434MHz: https://www.sparkfun.com/products/12823

        // See the hook-up guide for wiring instructions:
        // https://learn.sparkfun.com/tutorials/rfm69hcw-hookup-guide

        // Uses the RFM69 library by Felix Rusu, LowPowerLab.com
        // Original library: https://www.github.com/lowpowerlab/rfm69
        // SparkFun repository: https://github.com/sparkfun/RFM69HCW_Breakout

        // Include the RFM69 and SPI libraries:

        #include <SPI.h>
        #include <RFM69.h>

        // Addresses for this node. CHANGE THESE FOR EACH NODE!

        #define NETWORKID 0 // Must be the same for all nodes
        #define MYNODEID 2 // My node ID
        #define TONODEID 1 // Destination node ID

        // RFM69 frequency, uncomment the frequency of your module:

        //#define FREQUENCY RF69_433MHZ
        #define FREQUENCY RF69_915MHZ

        // AES encryption (or not):

        #define ENCRYPT true // Set to “true” to use encryption
        #define ENCRYPTKEY “TOPSECRETPASSWRD” // Use the same 16-byte key on all nodes

        // Use ACKnowledge when sending messages (or not):

        #define USEACK true // Request ACKs or not

        // Packet sent/received indicator LED (optional):

        #define LED 1 // LED positive pin

        // Create a library object for our RFM69HCW module:

        RFM69 radio;

        void setup()
        {
        // Open a serial port so we can send keystrokes to the module:

        pinMode(LED,OUTPUT);
        digitalWrite(LED,LOW);

        radio.initialize(FREQUENCY, MYNODEID, NETWORKID);
        radio.setHighPower(); // Always use this for RFM69HCW

        if (ENCRYPT)
        radio.encrypt(ENCRYPTKEY);

        }

        void loop()
        {

        // RECEIVING

        // In this section, we’ll check with the RFM69HCW to see
        // if it has received any packets:

        if (radio.receiveDone()) // Got one!
        {
        // Print out the information:

        // The actual message is contained in the DATA array,
        // and is DATALEN bytes in size:

        // char message = radio.DATA[0];

        // RSSI is the “Receive Signal Strength Indicator”,
        // smaller numbers mean higher power.

        // Send an ACK if requested.
        // (You don’t need this code if you’re not using ACKs.)

        if (radio.ACKRequested())
        {
        radio.sendACK();
        }
        Blink(LED,10);
        }

        }

        void Blink(byte PIN, int DELAY_MS)
        // Blink an LED for a given number of ms
        {
        digitalWrite(PIN,HIGH);
        delay(DELAY_MS);
        digitalWrite(PIN,LOW);
        }

        *********************************************************************************

         

        Now just waiting for some new pager motors to arrive…

        I’ve got some old drones which I have taken apart for batteries, motors and propellers. The motors are slightly larger to I have redesigned the 3D print. The motors are 8.5mm in diameter and I found that subtracting with an 8.8mm cylinder make for the snuggest fit.

        Binary Visualizing

        Using the tool bin vis, which simply assigns a shade of grey for each byte in a file, it’s possible to represent the structure of pieces of a file. Check it out, these are found 3D files from various software programs:

         

        Found a cool surprise when I uploaded a .BMP file, the bytes map almost perfectly onto the image;

        I want to try other uncompressed formats like RAW. Loading PNGs and JPEGs are uninteresting so far, they are just noise.

        I have tried some experiments processing this data. These stem from the observation that changing the width of the viewing window, which causes the binary data to wrap around the screen, changes the representation of the data dramatically. 

        In this experiment I tried to adjust the window of the binary visualizing screen to get the data to make columns, to the extend that this was possible. Some of this info must repeat at a point in the middle of a byte and therefore not map onto a grid of pixels perfectly (?).

        Here is an attempt to isolate just the modules which repeat in the above image and to display only them;

        I am imagining a program which goes through the file and figures out the proper width for each chunk of data so that this process can be automated. 

        Some experiments I would like to try:

        -what does the same form look like (say, a sphere) in different software formats?

        -is it possible to identify which part of the binary file links up with which part of the 3D file? Could you isolate the repeating element and figure out exactly what it’s composed of?

        -what are the similarities and differences between different 3D objects saved by the same software? (I.e. what is the software grain?)

        -what do the Rhino.exe, Adobe Illustrator.exe and Revit.exe files look like?

        -show a before and after compression image.

        -somehow look at a program’s operation by examining it’s buffer memory live somehow?

        and here’s the view of just the pixels in the same file just changing the window width:

        Essentially each eight bits (eg. 01101001) are transformed into a pixel with a value of grey between black (0) and white (255):

        I just visited the Glasgow Botanical Garden and watched the film Aquarela. Both experiences reminded me about the diversity of patterns and permutation of simple rules at play in the cosmos.

        Image result for aquarela film"Image result for aquarela film"

        Aquerela (2019) Screenshot

        Image result for botanical garden glasgow"

        Glasgow botanical gardens (image from http://martinbrookes.blogspot.com/2018/06/the-botanical-gardens-glasgow.html)

         

        Looking at a Rhino file in a text editor you get that same “grain” feel as with bin vis:

        this is from https://developer.rhino3d.com/api/RhinoCommon/html/N_Rhino_FileIO.htm

        but there is also: https://developer.rhino3d.com/guides/#opennurbs

        It would seem a lot easier to start with a file format that is ASCII encoded though…

        This is a RAW file opened in binview, it has several images contained in it, one for each color channel I guess:

         

        Here is a Bitmap file saved with interleave:

        An IGS file format, ASCII readable, visualized. Pretty boring unfortunately.

        I found a head inside an example Rhino file :D!

        Some tests with a single image saved in different formats:

         

        Sphere test:

        Cube test:

        Zooming in to some patterns:

        Some patterns I found in 3D files (would be cool to compare to image file observations!):

        With red to help identify the patterns:

        For comparison here are some samples from image files:

        Comparing an all white vs. all black file:

        Next experiment is to copy and paste pieces of one file into others. Possibly from a compressed file into a raw file! (Wait – why?)

        Insight into what we’re seeing in these files: http://www.rumint.org/gregconti/publications/taxonomy-bh.pdf

        when things line up into a structure, data is fixed length. When things don’t line up we get variable length data chunks. Repeating values seem to help align data regions. Nois is encrypted in some way typically which has scrambled the structure. 

        *****

        The problem with this visualization is that it does not represent TIME, like my other ones. It would be cool to stretch and compress these images based on the speed with which the files were written somehow. 

        **************

        Update: This software called HeapMemory (https://www.nirsoft.net/utils/heap_memory_view.html) allows you to see memory created by active programs. You can output the data and then visualize it with binvis. If you did this regularly while you worked on a 3D model, you could have a representation of the file you’re working on through the eyes of the computer program. 

        Here is a gif of a rhino operation and the change in the 3dm file:

        and here’s a timelapse of a rhino file with transformations taking place:

        UPSI Lecture Prep

         

        To “present my projects or any other insights on digital fabrication, education and architecture.” The context; “The class is the second part of an intensive course on 3d modelling and CNC milling to make an 1:1 architectural structure based on simple modules.”

        Here are some questions I could respond to: What are the seams, the gaps, and the moments of translation, between various subsystems in our contemporary digital design workflow?
        How has software engineering culture given form to our software?
        How does the computer internally represent our work – how does it “see” our design files and “understand” our design work?
        Does software have a “grain”, just like wood and other materials? What is the ontology of Revit and what is the world according to Rhino 3D, for instance?
        Do technological artifacts, and the objects that designers make, have innate force in themselves, as Bruno Latour and Jane Bennett seem to suggest?

        I am planning a revised lecture from my UdK workshop. I would also like to present some new research but my compression research is not leading to a tangible visualization yet, I need to ask for help from programmers to accelerate this process perhaps. The other solution appears to be to go towards simpler technology from further back in history. This has the added bonus of me not working on visualizing something that a million other people are already working on as with image compression visualization. Taking a look at my map of gaps diagram…

        I would like to make a GIF of the Revit startup elements. I also want to incorporate Schlossman lecture material like the number of software programs that used to exist that have all been eaten by Autodesk:

        OMG here is the mother lode for all things memory visualization, this is incredibly impressive: https://reverseengineering.stackexchange.com/questions/6003/visualizing-elf-binaries

        downloaded bin vis from this link, it has an .exe in the debug file: http://www.rumint.org/gregconti/publications/binviz_0.zip

        I could use the techniques from the following video to compare Rhino to Revit live, or look at types of files that architects use. At the very least I could make a nice slide showing the “grain” of these different programs! I could also make the argument that because computers are so much faster than us, they can see more data at a time than we can and therefore it looks to them like watching an image (I could get the speed of their reading to equal the number of pixels we can see at a given time). 

        Here is a lecture which outlines the method of mapping binary to pixel shades and how you can begin to identify species of data: https://www.youtube.com/watch?v=h68VS7lsNfE

        keywords:

        memory map, visual reverse engineering

        https://reverseengineering.stackexchange.com/questions/6003/visualizing-elf-binaries:

        word document:

        Windows PE visualization

        Documents Format Files

        enter image description here

        enter image description here

        pdf:

        http://actinid.org/vix/

        http://binvis.io/

        …I could look at things like scanners, or floppy disks?

         

        In the illustration above, you can see how the disk is divided into tracks (brown) and sectors (yellow).

        Floppy Disks are divided into tracks. 

        Image result for kryoflux"

        Nice images on how floppy disks can be damaged and the effect it has on the data https://goughlui.com/2013/04/21/project-kryoflux-part-3-recovery-in-practise/

        magnetic field

        speed variation

        From https://link.springer.com/referenceworkentry/10.1007%2F0-387-23483-7_95 :

        Data remanance:

        • Write heads used on exchangeable media (e.g., floppy disks, magstripe cards) differ slightly in position and width due to manufacturing tolerances. As a result, one writer might not overwrite the entire area on a medium that had previously been written to by a different device. Normal read heads will only give access to the most recently written data, but special high-resolution read techniques (e.g., magnetic-force microscopy) can give access to older data that remains visible near the track edges.

        • Even with a perfectly positioned write head, the hysteresis properties of ferromagnetic media can result in a weak form of previous data to remain recognizable in overwritten areas. This allows the partial recovery of..

        Data is not written linearly ( 😛 ) but interleaved https://en.wikipedia.org/wiki/Interleaving_(disk_storage):

        Information is commonly stored on disk storage in very small pieces referred to as sectors or blocks. These are arranged in concentric rings referred to as tracks across the surface of each disk. While it may seem easiest to order these blocks in direct serial order in each track, such as 1 2 3 4 5 6 7 8 9, for early computing devices this ordering was not practical.

        […]

        To correct for the processing delays, the ideal interleave for this system would be 1:4, ordering the sectors like this: 1 8 6 4 2 9 7 5 3. It reads sector 1, processes for three sectors whereby 8 6 and 4 pass by, and just as the computer becomes ready again, sector two is arriving just as it is needed.

        From the Scanning world:

        scanners can interpolate pixels between scanned pixels and assume their value. This is done in software but also on chip.

        Here are the proposed changes to the UdK presentation and my stock clickspace project presentation:

        1. I think I will first present my work, then talk a little about the materiality and social dimensions of technology afterwards. 

        Since doing this workshop Hannah Perner-Wilson connected me with this article by Tim Ingold: https://journal.culanth.org/index.php/ca/article/view/ca30.4.03/200

        I forgot to mention the materiality of how electricity is produced (by either wresting with the energy within matter itself, or burning compressed dinosaurs). Very interesting point about how electricity in modern industrial society becomes a controlling apparatus, closing itself off from people who become consumers sheltered from the actual infrastructure of electricity. When we use electricity we’re participating in a woven super structure which can help us move away from the concept of the object.

        I should investigate more the Anthropocene, thunder and the electric chair along with the references in this article.

        Second article The Textility of Making by Tim ingold he writes: “I want to argue that what Klee said of art is true of skilled practice in general, namely that it is a question not of imposing preconceived forms on inert matter but of intervening in the fields of force and currents of material wherein forms are generated. Practitioners, I contend, are wanderers, wayfarers, whose skill lies in their ability to find the grain of the world’s becoming and to follow its course while bending it to their evolving purpose”.

        This seems to fit with the harnessing of electricity and, based on an understanding of it’s movement through various media and in various situations, co-creating something that works with this “grain”. It seems important to note that modern software has a “grain” too.

        Since this workshop I have also became more interested in Legacy code (and if it could be possible to look at the varying ages of code in a big program somehow), code that needs to be ported or refactored from old code paradigmes into new ones. https://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052. So far what’s interesting here is the language used to describe “code rot”. Here are some samples: “the phrase strikes disgust in the hearts of programmers. It conjured images of slogging through a murky swamp of tangled undergrowth with leaches beneath and stinging flies above. It conjures odors of murk, slime, stagnancy and offal…tangled, opaque, convoluted system…” referred to collectively as “rot”. 

        Wikipedia talks about the Big Ball of Mud: A big ball of mud is a software system that lacks a perceivable architecture. Although undesirable from a software engineering point of view, such systems are common in practice due to business pressures, developer turnover and code entropy.  

        They link to Brian Foote’s article:

        A Big Ball of Mud is a haphazardly structured, sprawling, sloppy, duct-tape-and-baling-wire, spaghetti-code jungle. These systems show unmistakable signs of unregulated growth, and repeated, expedient repair. Information is shared promiscuously among distant elements of the system, often to the point where nearly all the important information becomes global or duplicated.

        The overall structure of the system may never have been well defined.

        If it was, it may have eroded beyond recognition. Programmers with a shred of architectural sensibility shun these quagmires. Only those who are unconcerned about architecture, and, perhaps, are comfortable with the inertia of the day-to-day chore of patching the holes in these failing dikes, are content to work on such systems.

        — Brian Foote and Joseph Yoder, Big Ball of Mud. Fourth Conference on Patterns Languages of Programs (PLoP ’97/EuroPLoP ’97) Monticello, Illinois, September 1997

        I can improve this presentation by having more knowledge about the slides presented (such as the comparison between the two different factories, slicon disk defects, the frequency in different parts of the world). 

         

        Image result for japan electricity grid"

         

        This is a fascinating new practice called obfuscation where you make a product hard to reverse engineer by hiding its behavior:

        Image result for hardware obfuscation circuit"

        Software obfuscation:

        Image result for software obfuscation"

        The silicon lottery involves binning. When you overclock your chips some will max out at 4.3GHZ while others will at 5GHz. They will also require different amounts of power to achieve this. 

        The reasons for the difference in the two plants: The San Diego plant emphasized zero defects while Sony of Tokyo, working with a robust quality viewpoint, strove to hit the target value for color density. I can also talk about variations in what is referred to as noise, 1. conditions of use noise (too cold, too hot, wrong voltage, too humid too dry), 2. production variation like of the two factories, 3. wear and deterioration. All these effect idiosyncrasy of products. 

        Silicon impurities derive from the crystal growth process if microscopic growth is not uniform it can result in striations. Next, impurities in the silicon (carbon, boron, oxygen, nitrogen). Next, it must be annealed and then lithographically transformed. More info: https://www2.irb.hr/korisnici/icapan/paper.pdf

        I also want to add that data rots, and that machines become obsolete so the digital is incredibly material.

        Contactless Current Meter (IN PROGRESS)

        In line with a series of low-power and energy harvesting devices I would like to make a simple Current Meter which can display the amount of energy being consumed by a given machine in our lab. I imagine the unit as a standalone thing which would use the power it harvests to power itself if possible. 

        From https://www.sparkfun.com/products/11005:

        Non-Invasive Current Sensor - 30A

        The main issue is that this method requires a special power cord which can be split in two like so:

        Image result for diy non-invasive current meter"

        Using this device to monitor energy  is documented here: https://learn.openenergymonitor.org/electricity-monitoring/ct-sensors/introduction

        Ordering this product from the U.S. is not easy at the moment and requires filling in some paper work. It’s also difficult to find european power wires that can be separated into two strands.

        hand-powered flashlight (IN PROGRESS)

        As part of a series of low-power and power harvesting devices (such as https://steemit.com/technology/@proteus-h/my-diy-passive-mini-radio-microwave-detector), I would like to make a hand-crankable or shakeable torch. I like these projects because they are not too complex, and they demonstrate very tangibly the energy that can be harvested in the environment.

        from https://www.kjmagnetics.com/blog.asp?p=shake-flashlight:

         

        A Servo makes a nice gear reduction (from https://www.youtube.com/watch?v=9dIkxQ_1H1k):

        …and a mini version inside a tic-tac container:

        Image result for shake powered flashlight

        And a relay coil with a magnet makes a super tiny generator (from https://hackaday.com/2014/01/13/a-simple-led-flashlight-composed-of-a-relay-and-a-magnet/):

        I’m curious to try this circuit with the solar engine voltage trigger circuit…Or to make it into another business card version.

        Design Primer – Presentation for Digital Fabrication class at UP-Sud December 2019

         

        This presentation serves to introduce non-designers to the culture of design. It proposes that design is like a trip, one that should take the designer outside of the “diameter of the obvious” (everything that can be imagined on day 1 and constitutes low-hanging fruit or cliche), to an unexpected place. Every design journey is unique, and this is how it should be – formulaic design is to be avoided at all costs.

        The starting place of a design project is some kind of research. This could be in the form of a trip to the museum, an old book of science experiments, a thought experiment, or a found object. Research provides the material against which you push to move in some direction away from your starting place. Because design is new to academia, we usually end up borrowing ideas and data from neighboring disciplines. Research is a time when you should avoid looking at work from other designers, this is research that has already been digested in some sort. We’re looking for primary research at this stage.  (Here I could add something about the perspective you have gained from your new position on your original position – this is art!)

        Design involves regular check-ins with your mentors. It’s best to have lots of objects and drawings on your desk for these moments. Many great inventions were discovered by accident (corn flakes, safety glass, the slinky, silly putty, post-it notes, etc.), and having things on your desk allows for misunderstandings and serendipitous associations.

        Constraints are a key part of design. Face with a blank canvas, a rule of some kind can provide something tangible to work with. Minimilist works which test out the parameters of a single constraint and curate the results in a series are an example of one design approach. Examples are Sophie Calle’s Chromatic Regime, Sol Lewitt’s variations on open cubes, George Perec’s La Disparition, and Eadweard Muybridge’s Motion Studies. (I should know more about the examples I am bringing up to speak to them with more depth).

        Constraints can be self-imposed or can be adopted from a situation. Once the work is prepared for a final presentation, the constraints can be removed like scaffolding from a building or they can be presented to the audience.

        Getting stuck in a design process is natural. Brian Eno’s Oblique Strategies, a set of lateral thinking prompts, is a way to inject a foreign thought into your mind at one of these moments which may help you unblock your progress and see things from a different perspective.

        Things I would like to add to this presentation:

        Representation: How to describe your project? The power of collage and Scale models/scale model photography. One simple, clear idea. Do my images help describe this idea? Start at a large scale then increase when ready. Design your presentation.

        Ambiguity being a tool to wield by the designer. The spectacle of the final presentation.

        On research, sometimes what is most interesting is isolating a single metric, peeling away lots of data to highlight one metric. (Mauricio taught me this).

        On not being precious with your creations: not allowed to destroy what you make, or edit what you make afterwards. Make tons of stuff but don’t identify with what you are making.

        More on constraints: Like scaffolding, they can be removed before showing the project. Or, they can become part of the project and be presented at the end. Constraints can either be self-imposed or adopted.

        I want to also introduce the concept of sketching, then moving to big scaled all the way to fine scale in the process of the design.

        Talk about specificity being your friend (versus making something that does everything). Think about the thing in different situations, different seasons, many peoples, few people, etc.

        The side of art/design being about you and how well you know yourself to a certain degree. The fact that this activity is more than a job but a kind of lifestyle. Self-knowledge, maturity…

        Synthesizer

         

        Karl and I are planning to work on other sides of the ocean on this project together. Karl will continue working on the overall collection and interaction of the modules, I’ll work on prototyping one module at a time. We hope to find a collection of modules that are cool and to see which modules we can make ourselves easily and which we need to buy chips for (such as delay).

        The plan is to converge on a prototype with Karl in simulation and me in prototyping. I can mail prototypes for Karl to try in Vancouver and once we have a basic prototype we can move on to test different interfaces, and begin to think about the kit and the cost of the machine. We’re aiming currently for an intermediate level device.

        We want to document things well and possibly even produce a collection of music from this device made by Karl’s artist friends. Our collaboration accross the ocean could be a theme in a video we make of the project, for instance.

        *******

        I am colaborating with Karl Fousek, https://karlfousek.com/, a fantastic Vancouver based sound artist. He proposed the following synth prototype:

        Breaking this design up into smaller bits, I have the following designs. They are inspired also by the tinybits modular synth design found here: http://d2q6sbo7w75ef4.cloudfront.net/SYNTH-booklet.pdf

         

        *****************

        NOISE GENERATION

        I tried this but it didn’t work with my 3.3V zener…

         

        No dice either! Two more simplish options:

        Simple White Noise Generator - EEWeb

        Basic discrete white noise generator schematic

        I could just generate noise with a microchip: https://forum.arduino.cc/index.php?topic=617302.0

        I put this through a high pass filter and here are the results:

        This is what the signal looks like before being put through the filter:

        ****

        We have noise! I built the circuit from this site: http://freenrg.info/Physics/Scalar_Vector_Pot_And_Rick_Andersen/Rick_Andersen_Noisegen.htm

        I used a 1M trim pot in place of the 470K to tune the noise and used 0.1uF caps in place of 0.047uF, PN2222As in place of 3904, and a 3.9K instead of a 4.7K:

        Here’s what is sounds like:

         

        ************************

         

        ~~~~~~~~~~~~~~~~~

         

        Sample and hold:

        https://www.ti.com/product/LF398-N

        Which is the circuit below (minus the comparator, I’m using Arduino to generate a pulse every 1ms of 1ms duration):

        I’m using two MCP6241 op amps (I tried 741s but they don’t go rail to rail which I think was the issue) powered with 5V. I kind of arbitrarily picked 0.1uF as the C. I’m using an IRF 740. The signal generator is outputing a sine wave with 2V amplitute and a 1V offset.

        Here are more screep caps :

        With a triangle wave:

         

        With noise:

        Again with a sine:

         

        XOR:

        Using an 74LS86 with an arduino generated square wave every millisecond and a sine wave from the function generator, fun and easy!

        PART I: OSCILLATOR

        Checking to make sure everything works on breadboard first!

        Synth on the oscilloscope:

         

        I have taken the Atari Punk Console design and made it quickly in Eagle:

        This design had two main flaws:

        1. The ground plane was too close to the signal traces, making soldering through hole components very challenging. The moment the soldering iron touches the ground plane it takes all the heat away from the iron and makes it hard to melt the solder.

        2. The 3.5mm audio output jack connection was not done properly.

        Also: I have also changed to two potentiometers of a different kind.

        Here is rev.2:

          

        And here is the final product:  

         

        Errata:

        I still forgot to make a dignified place for a 9V power connection…Oh well.

         

        I would like to make a business card version of this circuit, seeing as we have SMD 555 and 556 in the lab!

        Here is a first attempt:

         

        This synth does not currently work. I think it is the replacement of the 1M pots with 50K pots. I am looking at other 556 circuits which don’t require 1M pots (which are hard to find it would seem). 

        This is from the Spark Punk kit: https://core-electronics.com.au/sparkpunk-sound-kit.html

        I started with this circuit then added the other modules in the spark punk kit (except the power and amplication parts).

        For the frequency dividing I used a JK flip flop 74107 instead of a D-type CD4013 and set it up like so:

        Image result for jk flip flop frequency divider"

        I also eliminated the switches in the mixer portion (all the outputs are mixed together all the time) and used an LM386 op amp instead of the LM358.

        Here is what the circuit looks like set up:

        And here is what it sounds like:

        Here is the PCB version before populating:

        This is from Forrest Mim’s Engineer’s Mini Notebook Stepped-Tone Generator, except I don’t have any 500K pots either…

        Here is a diagram that helped me understand how the 555 works:

        555 Timer IC How It Works

        I am now making a PCB version of the sparkpunk design. Here is the schematic (mostly copied from https://cdn.sparkfun.com/datasheets/Kits/sparkpunk-v13.pdf but with changes for the parts I have and simplified for my situation):

        Meanwhile Karl has made a Max 8 patch for me to check out which varies oscillation, filter pass band and resonance (I’m thinking that this means using an inductor to make a filter):

        Some important other modules:

        noise + sample and hold (check the Art of Electronics)

        an envelope generator (allows control of Attack, Sustain, Decay and Release.

        simple-synthesis-adsr

        Here is one simple design for just attack and decay from https://synthnerd.files.wordpress.com/:

        Solar-Powered Autonomous Wildlife Photography

        This project uses the LTC3105 Solar Harvesting IC from Linear Technologies, an Atmega328P along with a Serial TTL camera and an SD mini card for saving the images.

        The plan is to create a waterproof casing for this device and then install it in a nearby forest and set it to take photographs when it sees movement.

        Here is the circuit layout and schematic:

        THe LTC3105 can supply between 6mA and 12mA which isn’t enough to power the 3.3V SD card which draws 100mA during R/W at 25MHz mode and 200mA during R/W in 50MHz mode. My attempt to solve this issue is to have a voltage divider step down from 5V (which is powering the camera). The voltage divider uses a 3.3K and a 2.2K resistors and should draw .9mA. The camera has 3.3V TTL logic and draws 75mA at 5V. Both the SD card, the voltage divider and the camera are shut on/off by a MOSFET.

        The camera will go on the backside of the circuit board along with a 33x37mm solarbotics solar panel possibly acting as a mini roof. The camera pin spacing is 2mm, not the typical.

        I am using two Adafruit tutorials and the relevant preexisting Arduino libraries:

        https://learn.adafruit.com/ttl-serial-camera/

        https://learn.adafruit.com/adafruit-micro-sd-breakout-board-card-tutorial/introduction

        The board took an entire day to engrave and there were still many shorts caused by tiny copper hairs, our lasercutter needs to be serviced:

        The first step went smoothly, loading a blink code works fine.

        Here things started to get difficult.

        1. I didn’t place the ICP header perfectly square in the middle of the pins and didn’t use an SMD header so the reset pin pulled off. (I only realized this after I replaced the Atmega328 on the board and wasted a perfectly good chip…)
        2. I forgot to add a 10K pull-up for the CS, meaning I guess that the SD card could have been disrupting things.
        3. The soldering of the SD card could have gone smoother, I need to check that the contacts are correct. I am placing the board on a soldering plate and using solder paste that melts at around 130 C.
        4. I really need a USART connection to be able to debug this and didn’t break out pins RX and TX unfortunately. My improvised solution might be adding noise to the serial communication.
        5. It appears that when the SD card is plugged in it is not a good idea to program the Atmega328. I get errors when AVR studio writes or reads data.
        6. Of course don’t forget to set the CLKDIV fuse off so that everything works smoothly with Arduino.

        After testing with the SD Arduino libraries I had varied success, sometimes it would pass the SD card initialization but then report the wrong size of SD card. I reformated my SD card and that didn’t change anything. I tried a Read Write example code and that didn’t pass the initialization phase.


        I found a serious error in my board, I connected both VDD and VSS to 3.3V but in fact only VDD should go to 3.3V. VSS should be connected to ground.

        I can get the Ardiuno SD Read/Write code to at least create a file on the SD card now but it also produced a bunch of USART noise:

        I’m not sure if the issues are related to my improvised USART connection or something more fundamental in the circuit.


        I put the SD card and the Atmega328 on the same power supply, and also checked the connections between the SD card and the microchip (I added a bit of solder to the MOSI pin which wasn’t passing the continuity test with certainty) and now things seem to be working!

        As a quick test it seems that if the SD card and the micro share the same ground everything still works even if the SD card is being supplied with a different VCC.

        I added the camera and everything seems to work:

        And here is the image quality:

        And here’s what the current setup looks like:


        Getting to the LTC3105 solar power management part of the circuit and have made some errors:

        I want LDO (voltage for microchip) to be at 3.3V so it is at the same logic level as the SD card and camera which it will be communicating with. This means I need a resistor divider to select this voltage level. To get 3.3V I am using 1M and 430K (it adds 1V to the ratio).

        To set the Vout at 5V I am using 1M+82K and 270K on the other side.

        The solar harvester appears to be working! I’m using a single 1F 5.5V super capacitor which is basically two capacitors put together into one package:

        The 1F 5.5V super capacitor I am using isn’t performing great, the moment the sun hits a cloud the voltage falls fast. It might be because of the internal resistance of the cap which is 500mOhms. Contrast this with the wasm 10F 3V supercapacitors I used for the RF solar-powered sensor which had only 34mOhms of ESR. However, the fall in stored power might also be related to the fact I’m asking the LTC3105 to bring Vout to 5V, previously I had only tested to 3.3V. I am also using a solar panel in the 3.5V range which is outside the optimal for this chip.


        I am now stuck after having soldered the MOSFET and tried to load the code that would test the SD card after having turned on the MOSFET. This involved also a voltage divider connected to Vout which I am not too sure about. I removed the MOSFET and voltage divider and I am still finding a voltage reaching the SD card mysteriously. I think there may be a short between the 3.3V SD card power and a logic pin for instance.

        I think I should have used a voltage regulator instead of a resistor divider, Vout fluctuates quite a bit and this may be throwing off the communication with the SD card.

        As a sanity check I powered the SD card with a power supply and the microchip with the super cap/LTC3105 and everything works.

        I’m using a 3.3V voltage regulator called the LM3480IM3X-3.3/NOPB which can supply 100mA which is just enough for R/W if the SD card is in 25MHz mode.

        The SD card works when being powered from the solar cap if it has a voltage regulator. The VR works up until around 4.3V of capacitor charge.

        The camera also works, including in motion capture mode. (All these tests are connected to a power supply simulating the solar panel, I’ve yet to have a sunny day for testing. When I disconnect the power the cap drains super fast but I think this is because I’m using a 1F cap which is leaking heavily. I have also not yet tested the MOSFET to turn on and off the camera and SD card.)

        After start up the motion control detects motion even if there isn’t any but then appears to settle down later.

        I got several photos of my hand which was waving quickly through the camera’s field which I thought was impressive:

        But I also got some strange lighting:

        …and at least a quarter of the jpegs were corrupt.


        Finished the redesign of the board:

        Side one:

        Side two:

        It has the following improvements over Rev.1:

        -A USART friendly plug-in location for debugging.

        -A nicer breakout of the pins (RX, TX, MOSFET GND and VCC) which need to go to the camera on the flip side (they can be bridged with bent pins)

        -A 10K on CS line

        -A resistor divider to set LDO to other than 2.2V.

        -Corrected SD card Vcc and GND connections.

        -A 3.3V voltage regulator for the SD card instead of the resistor divider.

        -Generally increasing the size of the board and making certain areas a little less squeezed.

        -Not a reset button, as there was not enough space, but a RST pin which can be easily touched to a nearby ground pin.

        -A set of traces and a header for easy installation of the camera on the back of the board.

        The lasercutter is cutting beautifully:

        But when I flip the board over to flip the backside I am cutting through and creating holes:

        This one barely survived:

        I had similar issues with this board unfortunately. 

        I jumped ship and made a modular version, thinking that this would be fool proof:

         

        Tiny aside: Wow, a camera which gets energy from the same light it is recording! https://www.discovermagazine.com/technology/this-camera-is-powered-by-light

        Drawing Machine

        We wanted to make an interactive Draw Bot which would make nice drawings near the entrance of the Fab Lab and invite guests to upload their own drawings too. I am working with our PhD candidate Nawel Khenak on this project.

        We downloaded most of the 3D parts from this kit on Thingiverse: https://www.thingiverse.com/thing:798076

        But we eventually made our own custom motor mounts by attaching two of these adafruit products together and screwing them into the wall: https://www.adafruit.com/product/1297

        We used basic timing pulley wheels (https://www.mcmaster.com/timing-pulley-wheel ) and Nema 17 stepper motors. We spooled a good few meters of the wire to ensure that there wouldn’t be any slipping or running out of line.

        After some issues using first a gearbelt (inelegant and not sufficiently flexible), and then a very fine thread (not sufficiently strong), we landed on using conductive thread (https://www.sparkfun.com/products/11791). It is strong and has a nice color also.

        We are powering two Easy Stepper Motor Drivers with 12V and using an Arduino Mega.

        We’re using Makelangelo for the software: https://github.com/MarginallyClever/Makelangelo-firmware

        Image result for makelangelo drawbot interface

        In the Arduino code we selected the RUMBA machine and looked at the pinouts for the motor drivers and the servo motor to make the correct connections on the Mega:

        #define MAX_MOTORS (5)

        #define MOTOR_0_DIR_PIN (55) #define MOTOR_0_STEP_PIN (54) #define MOTOR_0_ENABLE_PIN (38) #define MOTOR_0_LIMIT_SWITCH_PIN (3) /* X min */

        #define MOTOR_1_DIR_PIN (61) #define MOTOR_1_STEP_PIN (60) #define MOTOR_1_ENABLE_PIN (56) #define MOTOR_1_LIMIT_SWITCH_PIN (14) /* Y min */

        #define MOTOR_2_DIR_PIN (48) #define MOTOR_2_STEP_PIN (46) #define MOTOR_2_ENABLE_PIN (62) #define MOTOR_2_LIMIT_SWITCH_PIN (18) /* Z Min */

        #define MOTOR_3_DIR_PIN (28) #define MOTOR_3_STEP_PIN (26) #define MOTOR_3_ENABLE_PIN (24) #define MOTOR_3_LIMIT_SWITCH_PIN (2) /* X Max */

        #define MOTOR_4_DIR_PIN (34) #define MOTOR_4_STEP_PIN (36) #define MOTOR_4_ENABLE_PIN (30) #define MOTOR_4_LIMIT_SWITCH_PIN (15) /* Y Max */

        #define MAX_BOARD_SERVOS (4) #define SERVO0_PIN (11) /* Servo 1 */ #define SERVO1_PIN (6) #define SERVO2_PIN (5) #define SERVO3_PIN (4)

        #define LIMIT_SWITCH_PIN_LEFT (MOTOR_0_LIMIT_SWITCH_PIN) #define LIMIT_SWITCH_PIN_RIGHT (MOTOR_1_LIMIT_SWITCH_PIN)

        We commented HAS_LCD from the config file to avoid a problem where the Drawbot gets stuck waiting for the user to click OK on the LCD screen (the “M226” command in the drawing code):

        //—————————————————————————— // LCD panels supported //——————————————————————————

        #define HAS_LCD // if you have an LCD panel #define HAS_SD // if you have SD card support on your LCD panel (must be on panel?)

        // only uncomment one of these options //#define LCD_IS_128X64 // reprapdiscount Full Graphic Smart LCD Controller #define LCD_IS_SMART // reprapdiscount Smart LCD Controller (including XXL model)

        Everytime we load the software we need to re-input the parameters of our machine but Nawel is looking into regenerating the Java code with our parameters baked in based on the info at this site: https://github.com/MarginallyClever/Makelangelo-software/wiki/Getting-Started

        We are working to rationalize the wire management, right now it’s unsightly:

        Currently the wires from the motors go through the ceiling and come through a ceiling tile to our a nifty box that Nawel made.

        We are printing Nice clips for wire management: https://www.thingiverse.com/thing:2798689

        And also a nice pen-holder that Nawel made:

        Fablab RGB LED Sign

         

        Image result for new museum new york sanaa

        This project has the goal of making the Fab Lab Digiscope location visible from the exterior of the building and meanwhile creating an a display that users can code themselves.

         

        After testing with painting tape, it was clear that giant letters made from material would not be sufficient to make the Fablab visible from outside. This led to the idea of illuminating the letters with LEDs.

        Each letter will require about 5 meters of LED strip. We chose RGB leds in a strip from Adafruit that were reseanably priced:

        https://www.adafruit.com/product/285?length=1

        The LEDs are not individually addressable but the strips are RGB and can of course be faded in and out. Because each letter will be a seperate strip, we can have three different colors and brightnesses happening at once, one for each letter. We could potentially design in interface that lets users create animations, or we could invite them to modify the Arduino code directly as well.

        Unlike Neopixels the LEDs in a strip will all change color at the same time and there is no microcontroller embedded in the LED strip. An external microcontroller will be needed to vary the PWM signal being sent to the MOSFETs which will control the power for each color in the strips (one for Red, one for Green and the last for Blue). The LEDs are powered by 12V. There are 30 LEDs/meter, 150 LEDs per letter and 450 LEDs in total which will be controlled by one microchip using 9 output pins.

        The choice to work with non-addressable RGB LED strips was motivated by the desire to keep the project within a reasonable budget and level of complexity but to also allow users of the fablab to engage with the intricacies of programming three RGB LED colors at varying intensity changing over time. Good taste dictates not having too many garish colors flashing at the same time as well.

        In terms of font, I’m thinking something which reflects the history of computation. Proxy lowercase or uppercase:

        Inspiration from Troika London:

        Image result for the weather yesterday

        Image result for new museum new york sanaa

        Raster Image Compression/Interlacing/Encoding Index Representation (IN PROGRESS)

        How can non experts quickly get an idea of  the way in which our computers see our raster images?

        I have previously focused my energy on how vectors are internally represented because I had the impression that pixel-based rasters were always stored in a predictable top to bottom, left to right arrays in computer memory. However, after a bit of Wikipedia research I discovered that pixels in formats JPEG and PNG are resequenced when they are compressed, downsampled/encoded, or prepared to be sent via a slow internet connection. The last process is called (I think?) Interlacing for JPEGs and Progressive Scanning for PNGs. 

        This gif from wikipedia describes the Adam7 algorithm sequencing pixels in a 16×16 bitmap image. (https://en.wikipedia.org/wiki/Interlacing_(bitmaps)) – clearly I want to find something which is dependent on user loaded images and does not transform all images equally.

        Apparently when JPEGs are Progressively Scanned the resulting sequence of pixels is not the same irrespective of the image. In spectral selection or successive approximation low-frequency components might be sequenced first before higher-frequency components  (citing http://www.libpng.org/pub/png/book/chapter08.html). I.e. the resulting sequence of pixels is not determined by spatial location but by value (color?) of the pixel. 

        There also appears to be some zig-zag action that takes place in JPEG encoding (https://en.wikipedia.org/wiki/JPEG) :

        So, it might be interesting to look at these images in “exploded index” view (displace the pixels in one dimension to show their place in the index). My plan right now is to use Grasshopper to load an image and be able to manipulate the pixels in space based on their place in the input image index. There appear to be a few different Grasshopper plugins for working with images like Shapediver (https://www.food4rhino.com/app/shapediver), Hedgehog(https://www.food4rhino.com/app/hedgehog) and something developed by Professor Andrew Witt at the GSD which I can’t quite track down at the moment. 

        To get the ball rolling though I found a browser-based hex editor called Hexed (https://hexed.it/?hl=en) and began looking at the raw JPEG and PNG data. For PNG the image info is between IDAT and IEND, and there may be several chunks (possibly to make the file more robust in case there is an error in a particular chunk). For JPEG the pixel data is between SOS (0xFF 0xDA) and EOI (0xFF 0xD9). I highlighted one chuck of pixel data in a JPEG image below using Hexed.

        I was expecting to find only one image chunk as this image was not saved from Photoshop as “Progressive” but instead I found three chunks. When I save the image as Progressive though I get 9 chunks. Despite saving the image 16×16 with all black pixels I still get a bunch of different hex values for the pixels and I’m not sure why (I thought possibly a CMYK vs RGB situation but a CMYK image at 100% doesn’t make finding a block of pixels with the same value easier for some reason…). So far I am liking PNG files because they are a whole lot smaller.  

         

        I just did some tests deleting parts of JPEG and PNG files. I tried removing single bits, and removing chunks. Both resulted in corrupt files which could not be opened.

        From https://en.wikipedia.org/wiki/Portable_Network_Graphics:

        • IHDR must be the first chunk; it contains (in this order) the image’s width (4 bytes), height (4 bytes), bit depth (1 byte), color type (1 byte), compression method (1 byte), filter method (1 byte), and interlace method (1 byte) (13 data bytes total).[10]
        • PLTE contains the palette; list of colors.
        • IDAT contains the image, which may be split among multiple IDAT chunks. Such splitting increases filesize slightly, but makes it possible to generate a PNG in a streaming manner. The IDAT chunk contains the actual image data, which is the output stream of the compression algorithm.[11]
        • IEND marks the image end.

        If I change the bit value after IHDR I can mess with the width of the image and this leaves a file which is still readable. When I delete multiples of 8 bits from the file it still appears to load fine also and deletes parts of the image and then adds a single row of random colors. 

        It looks like the images I have made in Photoshop are 16×16 pixels (256 pixels in total) and have a bit depth of 24. There appear to be three Hex pairs of data per pixel  (for R,G, and B channels[?]) because there are three times as many hex pairs to describe the image pixels as there are pixels in the image (but also confirmed here: http://www.libpng.org/pub/png/book/chapter08.html#png.ch08.div.4). A Hex pair contains 8 bits worth of data, so 24 bits in total per pixel because that is the bit depth at RGB. Changing the Hex values results in different colors. 

        Wikipedia says that only the 3 lower bits of each 8 bits of data is used to describe the color…

        OK so after reading the PNG specs I am realizing that things are FAR more complicated than I originally assumed. The IDAT pixel data is compressed by the Zlib deflate algorithm and a kind of custom checksum called a CRC is placed at the end. All of this makes it not possible to edit the raw compressed IDAT without first unzipping it and rezipping it with Zlib before reinserting it into the image.

        It looks like there is a Python LibZ tool which is described here: https://stackabuse.com/python-zlib-library-tutorial/

        There are also people trying to make their own PNGs and documenting their progress on stack exchange:

        https://stackoverflow.com/questions/22672112/png-idat-specification?rq=1

        Ah, but I also just realized that if I output a file from Photoshop and select uncompressed it will output a hex stream that more or less directly relates to the pixels in the file. 

        By making a new image with a single white dot in a field of black I can then move this white dot around by copy pasting FE FE FE (R,G,B) in the sea of 00 00 00. Apparently this doesn’t cause any issues for the CRC. I can also change the color of any individual pixel by finding the triplet of hex pairs that describes that pixel. This appears to work nicely with 16×16 pixel images of black (white not so much?). I can even find the pixels if the image is white:

        02 appears to mark every new line, I could 48 bytes between each 02 (divided by 3 = 16, the number of pixels in each row of the image). 

        I have no idea why, but I can’t edit the bottom 1/4 of the image, these two pixels represent the limits of what I can edit.

        I failed trying to add an additional row of pixels by changing this value in the IHDR (using Tweak PNG to generate the right CRC once I changed the height of the image in this program) then adding 48 pairs of hex numbers into the IDAT and also changing the CRC at the end of the IDAT using Tweak PNG again. 

        I did find these nice visuals to describe the PNG files:

        https://www.w3.org/TR/2003/REC-PNG-20031110/

        ***********

        After reading a blog post about glitch art and consulting the Image Glitch App page, I decided to give JPEG, TIFF or BMP a try. 

        I found a nice program which allows for inspection of various kinds of image formats called Exiftool:

        So this program provdes a ton of metadata about a file, but it’s not really optimized for editing pixel info and reloading. Still, this is useful going forward.

        Making a B-line directly for image glitching software I found this handsome program Snorpey:

        https://snorpey.github.io/jpg-glitch/

         

         

        It makes very pretty glitches, I wonder if I can find out how it is working under the hood and comandeer it’s behavior to my ends…

        I am looking through the source code for the program Glitch Canvas (which seems to be the inspiration for this app) here: https://github.com/snorpey/glitch-canvas

        Under Sources, there is a glitch Jscript code which does the glitching I think. Before the image is edited it appears to be first translated into a byteArray, other things are determined beforehand like the start of the image data at SOS. At the heart of the code is a loop which appears to iterate over the bytes in the array. The three glitch parameters, seed, amount and iteration, 

        Here is the code:

        “””

        import jpgHeaderLength from ‘./jpgHeaderLength’;

        export default function ( byteArray, seed, amount, iterationCount ) {
        const headerLength = jpgHeaderLength( byteArray );
        const maxIndex = byteArray.length – headerLength – 4;

        const amountPercent = amount / 100;
        const seedPercent = seed / 100;

        for ( var iterationIndex = 0; iterationIndex < iterationCount; iterationIndex++ ) {
        const minPixelIndex = ( maxIndex / iterationCount * iterationIndex ) | 0;
        const maxPixelIndex = ( maxIndex / iterationCount * ( iterationIndex + 1 ) ) | 0;

        const delta = maxPixelIndex – minPixelIndex;
        let pixelIndex = ( minPixelIndex + delta * seedPercent ) | 0;

        if ( pixelIndex > maxIndex ) {
        pixelIndex = maxIndex;
        }

        const indexInByteArray = ~~( headerLength + pixelIndex );

        byteArray[indexInByteArray] = ~~( amountPercent * 256 );
        }

        return byteArray;
        }

        “””

        So to conclude:

        1. iteration: changes how many times the main loop runs. This will impact how many bytes are rewritten in the image data portion of the image (smaller the iteration, the more loops and the more bytes transformed).
        2. seed: changes how many bytes are skipped over each time the loop runs as it moves from the top to the bottom of the pixel data. (larger the parameter, fewer bytes overwritten because more bytes skipped over).
        3. amount: the value of the new byte which is overwritten each time through the loop. (larger the value, larger the byte value the byte is replaced with).

        Clearly I need to learn how the images are prepared as a string of bytes now:

        1. Images are converted into Base64 (6 bits per digit)
        2. Then Base64 is converted to an array of bytes (8 bits per digit)
        3. The bytes are overwritten selectively based on the 3 parameters of the glitching. 
        4. The byte array is recoverted into Base 64.
        5. The Base 65 is recoverted into an image.

        Quick note: I like this stepping effect that is created, this could be useful for showing sequence.

        JPEGs seem to be just as super compressed and complex as PNGs. (Though they do appear to load even when defective which is way cooler than PNGs). I’m curious about BMP, it seems simpler…

        Nice ASCII patterns from this BMP.

        I started moving towards Processing for a moment there, it can easily load and manipulate image data. But I have the feeling it resamples image data similar to Grasshopper. There are several example of Processing scripts which sort pixels by color in an image. They are cool but they don’t appear to relate to the original sequence of pixels. (https://steemit.com/art/@granturismo89/how-to-make-amazing-glitch-art-with-processing-and-asdf-pixel-sort or http://datamoshing.com/tag/processing/)

        Now I am looking at a website which outlines a series of artistic strategies for datamoshing: 

        http://jameshconnolly.com/glitchtalk.html

        https://www.glitchet.com/resources

        I wonder if I can use my wilderness camera somehow…or hack a digital camera?

        RAW files offer the most hackability, they can be saved as a byte array with no header. Different formats start their scanlines in different places on the canvas and sequence RGB differently. Apparently RAW non-interleaved files don’t save pixels sequentially but instead have different layers per color?

        compressed files are harder to work with, but they seem the most interesting to visualize because they are going to be messing with the original sequence of bytes and removing data and possibly re sequencing bytes in the process?

        She manages to interrupt a Adam7 algorithm to inspect after steps 1-2 (pre-compression) somehow…. 

        JPEG have a Run Length Encoding (RLE) which groups together the same frequencies. 

        visualizing files by opening them as audio files can quickly show you the overall organization: http://blog.animalswithinanimals.com/2008/09/databending-and-glitch-art-primer-part.html

        This author suggests don’t add or subtract bytes to files.

        Maybe what I want to be doing is to have a simple processing script that draws pixels from an image file (onto a canvas the size of which I can control) based on a string of bytes (representing the same image) that it reads to get the pixel sequence info.

        These are some of the encoding algorithms it would be cool to visualize. I need to find some way to determine the sequence in which these algorithms are processing data:

        https://en.wikipedia.org/wiki/Run-length_encoding: runs of data (sequences in which the same data value occurs in many consecutive data elements) are stored as a single data value and count, rather than as the original run. (eg. WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWBWWWWWWWWWWWWWW becomes: 12W1B12W3B24W1B14W)

        https://en.wikipedia.org/wiki/Entropy_encoding: replaces each fixed-length input symbol with a corresponding codeword; the most common symbols use the shortest codes.

        https://en.wikipedia.org/wiki/Huffman_coding: Makes a frequency tree of content.

        https://en.wikipedia.org/wiki/Discrete_Fourier_transform: a  frequency domain  representation of the original input sequence.

        https://en.wikipedia.org/wiki/DEFLATE

        I should check out the program that processes the TTL serial camera images:

        https://learn.adafruit.com/ttl-serial-camera?view=all

        Page 20 here has a diagram of the hardware compression:

        https://www.st.com/content/ccc/resource/technical/document/application_note/group0/a5/9d/22/46/61/6d/4a/ab/DM00356635/files/DM00356635.pdf/jcr:content/translations/en.DM00356635.pdf

        **********

        New approach: find python code which compresses images and mess with the code to output what I want.

        https://github.com/JoshuaEbenezer/huffman_encoding

        This code outputs the list of codes and the integer they replace (the length of the code corresponds to the frequency that integer is found in the image).

        Here is an output sample:

        116 11111111
        119 111111101
        129 111111100111
        132 111111100110
        255 111111100101
        149 111111100100111
        148 111111100100110
        142 111111100100101
        171 1111111001001001111
        173 1111111001001001110
        172 1111111001001001101
        194 1111111001001001100111
        197 11111110010010011001101
        190 111111100100100110011001
        209 111111100100100110011000111
        213 1111111001001001100110001101
        224 111111100100100110011000110011
        227 1111111001001001100110001100101
        233 111111100100100110011000110010011
        236 1111111001001001100110001100100101
        240 111111100100100110011000110010010011
        241 111111100100100110011000110010010010

        This diagram explains it all: https://en.wikipedia.org/wiki/Huffman_coding#/media/File:Huffman_coding_visualisation.svg

        If I rebuilt the image in grasshopper, and then used the sequence generated by this algorithm, I could alter the grasshopper version of the image based on the algorithm output. I think the effect would not be very interesting though? The other option is to compress the lines of pixels in the image to the same degree they have been compressed by the algorithm. 

        Here is a nice article breaking down Huffman encoding: https://medium.com/iecse-hashtag/huffman-coding-compression-basics-in-python-6653cdb4c476

        I could write a processing code which loads an BMP, goes through each pixel in the image comparing it to the Huffman table (which has pixel value and code to replace it with) and then create a new image which either makes the original pixels longer (adding the same pixel value to the right, thus displacing adjacent pixels and pushing them around the frame) or makes runs of the same pixel shorter based on the length of the encoded Huffman table value. This would result in an image which is compressed based on the frequency of the various pixels.

        Processing seems to be good at this, as with the pixel sorting example by Kim Asendorf:Image result for kim asendorf mountain tour"

        And here is a primer for dealing with images in Processing: https://www.processing.org/tutorials/pixels/

        The other approach of sampling a JPEG compression chip could head towards this kind of product: http://pdf.datasheetcatalog.com/datasheets/120/130181_DS.pdf

        It has a test mode where the user has access to all internal memory.

        From https://cscie12.dce.harvard.edu/lecture_notes/2007-08/20080312/slide19.html:

        The GIF to the right is twice the size of the GIF to the left because GIF LZW compresses horizontal pixels of the same color:

        horizontal linesvertical lines

        Here is a nice article on LZW compression: https://marknelson.us/posts/2011/11/08/lzw-revisited.html . Here is a dictionary or look-up table from Alice and Wonderland that was created by the “greedy” algorithm (which looks for the longest string to replace?),

        34830 : 'even\n'
        34831 : '\nwith t'
        34832 : 'the dr'
        34833 : 'ream '
        34834 : ' of Wo'
        34835 : 'onderl'
        34836 : 'land'
        34837 : 'd of l'
        34838 : 'long ag'
        34839 : 'go:'
        
        LZW compression apparently outputs the library along with the compression files. So this could be opened in order to see which sequence of pixels is the most oft repeated.
        Retro article describing compression techniques: https://www2.cs.duke.edu/courses/spring03/cps296.5/papers/welch_1984_technique_for.pdf
        
        This site has really helpful animations for dictionary creation: http://www.data-compression.com/index.shtml. Check this one out explaining Huffman encoding: 
        
        could try to visualize series of repeated pixel patterns and the "live" processing of these by different algorithms, some greedy others with different priorities?
        
        Check out this stuff on frame buffers:
        This is a CRT screen showing it's memory from wikipedia https://en.wikipedia.org/wiki/Framebuffer:
        
        and this is the color palette for an image:
        Adaptative 8bits palette sample image.pngAdaptative 8bits palette.png
        
        still from Wikipedia, here is that same image given the wrong color index:
        
        
        gzthermal is a psuedo heatmap that shows the words that get  compressed and those that don't
         https://encode.su/threads/1889-gzthermal-pseudo-thermal-view-of-Gzip-Deflate-compression-efficiency:
        Image result for deflate image compression visualization"
        
        here is a visualization of LZ77 by Canadian programmer   Julia Evanshttps://jvns.ca/blog/2013/10/24/day-16-gzip-plus-poetry-equals-awesome/
        

        Solar Business Cards

        Inspired by the various existing designs of PCB business cards, we were inspired to make a similar thing for Fablab Digiscope.So far there have been popular business card projects which have had LED patterns display, have miniature screens, Magic 8-ball prediction capabilities, act as MIDI controllers, others bring up a text file on a computer when pluged in to USB or offer file storage.

        Starting with the constraints:

        -We have single and double sided small PCB boards which are business card shaped at 76mm x 50.7mm. However there is also the possibility of working in card stock and laminating vinyl-cut double sided copper tape.

        -Because it takes at least 15 minutes to make a single raster pass on a board this size (and around 8 passes to completely ablate the copper), chosing a smaller space on the board to actually engrave is the only way to make this project possible with our time constraints. This may also be another opportunity to revisit earlier tests using the lasercutter in vector mode but this time to offset the vector (by how much?) at least once (probably thrice) to make sure their are no copper bridges this time. This would allow more compositional designs.

        -We want to have our name and address written on them “Digiscope Fablab, 630 Rue Noetzlin, 91190 Gif-sur-Yvette” or perhaps just our website “https://fablabdigiscope.gitlab.io/” or just perhaps the logo!

        -It has to look cool (composition is an element of the PCB business card “genre”) and/or do something cool!

        -Business cards have to be flat – but they can of course pop-out and become 3D at some point. This means that anything soldered to the card also has to be flat. The big question is about batteries, they add thickness, weight but also inelegance, in my opinion. Perhaps the power should come from USB?

        -it must have a sufficient number of components to look “technological”.

        -obviously to be cost effective the price per card definitely can’t exceed a few dollars.

        What it could do:

        -a usb device which stores important information for fablab users, such as the fablab charter, information about our website, address, etc. in a text file. The problem I have with making usb connectors out of the edge of boards is I find they don’t work very well.

        -a mini pair of joysticks and leds to make some kind of light/memory game.

        -a very simple 1381 miller solar engine circuit which causes the card to vibrate every few seconds when in the sun. I like this idea because it associates the lab with clean energy and I find the solar kit I made to be the one which interests people the most immediately.

        Technically: this would require SMD versions of the 1381/MCP112 (easy), the 2907 (super expensive), the 2222 (super expensive) and, more importantly, an Aluminum Electrolytic Capacitor SMD version of an 2200uF cap (another cap option is to have four 560uF SMD Aluminum Electrolytic caps in parallel). This raises the question if it’s possible to substitute the transistors with MOSFETs or if using through hole components…

        This website: https://www.fetchmodus.org/projects/beam/photopopper/ has an interesting circuit which involves switching the transistors for low-gate-turn-on-voltage MOSFETs (DMN2215UDM-7) which are much easier to find in SMD form. The designer also uses gigantic tantalum capacitors (1500uF!) which are flat. These unfortunately cost 7+ euros each on Mouser. However this circuit appears to be represent an SMD version of the classic solar engine. The designer also uses an alternate of the MCP112 voltage trigger in the ISL88001 which require only 160nA to test the voltage.

        In terms of cost:

        $2 – single solar panel (from Digikey) $1 – ISL88001 $0.4 – DMN2215UDM-7 $2 – vibrating motor (but we have plenty lying around the lab so they are free-ish!) $3 – compact 3000uF capacitor (only .90 for a larger capacitor – how important is it to make it tiny?) $1 – for the circuit board

        under $10USD but that’s a little on the expensive side…

        Trying to optimize for cost.

        $2 – single solar panel (from Digikey) $1 – ISL88001 $0.4 – DMN2215UDM-7 free – vibrating motor (we have plenty lying around the lab so they are free-ish!) $0.6 – 1000uF capacitor (smaller cap maybe will make less annoying vibrations also) $1 – for the circuit board

        $5USD total is not horrible?

        Here are some tests with the composition:

        Here is my first shot at the circuit:

        Here is an attempt at the USB version:

        I gleaned the circuit from various available online:

        From the internet I got some answers about the components in the circuit:

        The USB signal operates at around 3.3V D- needs pulling up (via 1K5) to signal the presence of a USB device The current limiting resistors (68ohm) protect the microchip from a large current sink by the zeners and also serve the function of a termination resistor for the USB (which is supposed to be around 20ohms). V-USB is the firmware of choice for turning avrs into USB devices it seems.

        I’m interested in finding an aesthetic language with the lasercutter PCB engraving process we have at the lab. My instinct is that by pulling a thin layer of yellow paint into an engraved path we can make some text that will really stand out.

        With the single-sided board there is a nice effect when looking from the reverse and seeing the yellowish-looking traces.

        I tried to do an acetone transfer on both sides of the PCB but that yeilded poor results.

        One thing I discovered is that you can lasercut out text and then pass a soldering iron and solder over it to silver-ize it. This means you can have two “colors”, copper and silver. If the text were thicker the effect would be more striking.

        I’m concerned about how easy this will plug into a usb jack. In fact in the single sided mode it’s far too thin to insert so it looks like a double-sided board is required or a usb male connector.

        Some tests cutting out of metal fabric/tape:

        The only way one of the lasercutter produced boards looks good is when it is crammed with components, then the components visually take precedence over the charred background. I am retrying the business card but in a much smaller area to eliminate the visual effect of the majority of the object being charred fiberglass (it also means we can make more of them faster). I’m doing this on double sided board so it is the right thickness for a usb plug. (The double sided board looks less bad with the copper removed, more like mdf than charred matrix).


        The parts have arrived, here is an adapted version of Nick Ames’ circuit (https://www.fetchmodus.org/projects/beam/photopopper/):

        Here are the specs for the two special ICS:

        And here is the PCB:

        It turns out with some testing that with these 2V solar panels the minimum is a couple of panels in series along with a 1000-1500uF normal or SMD tantalum capacitor. I’m rearranging the board to accomodate two solar panels and only one capacitor.

        If I could do the order again I would have taken the 4.46V KXOB25-01X8F instead of the 2.07V KXOB25-05X3F-TR and then it might have been possible to pull of the card with only a single panel. It might have been smart to take a lower voltage version of the voltage trigger (2V instead of 2.9V maybe).

        For the interest of time it seems silly to engrave a large board only to use a portion for the circuit. It might be possible to cut a smaller board and integrate it into a cardstock business card form. This could be achieved by delaminating a part of the business card and installing the circuit board there.

        Here is the board assembled and working (after a long debug which concluded that there was a short under a capacitor in the board. Better checking of the board before soldering being the solution.)

        Here are some options for how the circuit could be integrated into a business card:

        And here is a mock up of what the card could look like:

        Talking with our doctorat student Nawel she had some ideas for how to improve this prototype:

        -why not lasercut the card itself, and make the card represent a hybrid of everything that is possible to make in the fablab? Printing the card on card is not easy to do in the lab. -we could 3D print a kind of stand that cool keep the card standing up so that the panels face the sun.

        I don’t mind that the card won’t “work” unless it’s sunny, but it would be nice to have a lamp which would allow us to show how it works when there is sun when it is overcast.

        The associations with the card that are good for us are the renewable energy element and the designing with a connection to nature. It’s also nice that the card vibrates – the card is suggesting it is associated with dynamism and that the fablab is by extension a dynamic place.

        The USB version of the card for me is kind of dead as it leads to a series of driver issues with certain kinds of windows which is not optimal.

        Here is a result I am happy with:

        Here is how it all fits together and folds to stand upright:

        Making the first run of multiples to check how easy the process is.

        The effect of having six of these boards in your hard when the sun out is quite something. It’s as if they are a swarm of bees.

        I changed the laser lens and got about ten times more power. I switched from 1200DPI to 600DPI and increased power to 8/100/1 from 16/100/1. Here is the 8-bit effect of the DPI change:

        I originally thought it would be neat to combine acetone ink transfer with lasercutting but the results were dissapointing:

        Here’s the final prototype:

        *********

        This project clearly needs to move in the direction of paper PCB or acrylic substrates:

        Debugging

         

        I like to draw out my circuits and then color over the lines after I’ve soldered them or re-checked them.

        I really enjoyed this overview of electronics debugging made by MIT TA D. Elliott Williams: http://web.mit.edu/6.101/www/reference/TheArtofDebuggingCircuits.pdf

        Here is a summary of the main thinking as I see it:

        -Circuits cannot be wrong, if there is a problem it is “either be because you do not understand what you built or you did not build what you understand.”

        -You must first understand how a circuit works before you can debug it. If you are using components, understand the datasheets.

        -To debug a circuit, first establish what it is doing, this narrows the list of possible errors down considerably.

        -Think, what could cause this type of behaviour? Make a list of possible causes and cross them off one by one.

        -Get a fresh pair of eyes on your problem.

        -Systems are composed of sub-systems. Isolate sub-systems and test them each in isolation.

        -Build your circuit neatly and logically, this makes it easy to check connections and reduces noise from entering long wires.

        -Use the continuity mode on your multimeter to test connections.

        -Make sure you are not misusing your oscilloscope (check the x1/x10, if in DC/AC coupling, that the ground clips are attached to ground, )

        -Rebuild your circuit but with all different components on a different breadboard.

        RF communication with the RFM12B module

         

        (For the working code scroll all the way to the bottom!)

        I free-formed a simple RFM12B atmega168 temperature sensor circuit using a 3.7 lithium ion battery I would one day like to solar recharge. Behold!:

        Notes: DO for Data Out and DI for Data In, from the perspective of the RFM12B. I’m using a 7cm long antenna because these modules are 868MHz (you can tell the frequency based on the existence or not of a certain capacitor).

        The MISO line is dead, the RFM12B is not responding to anything I’m sending it…

        The RFM works with 16 bit data and I am currently trying to squeeze two 8 bit data transmissions, this is why I think things are not currently working. This project might be the limit of where it is easy to make one’s own code and when one must rely on code produced by the manufacturer of the chip in question.

        An additional problem with the particilar project is the poor documentation by the manufacturer of the RFM12B module. Even the numbering of the registers is not consistent and there are errors in example code.

        Here is the (not yet working) TX side code:

        
        // *
        // * Atmega168 RFM12B TX
        // *
        // * Created:
        // * Author : FablabDigiscope
        // */
        
        #define F_CPU 8000000
        
        #include <avr/io.h>
        #include <util/delay.h>
        
        #define NIRQ PB0 // input for nIRQ which goes low after a transmit is received?
        
        
        void SPI_Init();
        void SPI_Transmit(char dataout);
        void SPI_Stop();
        void rfInit();
        
        
        int main(void)
        {
        		SPI_Init();
        		SPDR = (0x00); // byte to send
        		SPDR = (0x00); // byte to send
        		rfInit();
        		DDRD = 0b10000000; //LED for RX/TX rec/sent
        
        	while (1)
        	{
        		char dataout = 0b10011001; // 0x99 //
        		
        
        		
        		SPI_Transmit(0xB8); //this must precede data, it is the transmit register write command.		
        		SPI_Transmit(0xAA); // PREAMBLE
        		
        		SPI_Transmit(0xB8); //this must precede data, it is the transmit register write command.
        		SPI_Transmit(0xAA);
        		
        		SPI_Transmit(0xB8); //this must precede data, it is the transmit register write command.		
        		SPI_Transmit(0xAA);
        		
        		SPI_Transmit(0xB8); //this must precede data, it is the transmit register write command.		
        		SPI_Transmit(0x2D); // SYNC
        		
        		SPI_Transmit(0xB8); //this must precede data, it is the transmit register write command.
        		SPI_Transmit(0xD4); // Network ID
        
        
        		SPI_Transmit(0xB8); //this must precede data, it is the transmit register write command.
        		SPI_Transmit(dataout);
        		
        		SPI_Transmit(0x00); //
        		SPI_Transmit(0xAA); // DUMMY BYTES, optional?
        		
        		SPI_Transmit(0x00); //
        		SPI_Transmit(0xAA);
        		
        		SPI_Transmit(0x00); //
        		SPI_Transmit(0xAA);
        		
        		SPI_Stop();
        
        		_delay_ms(1);
        		
        		PORTD = 0b10000000; // turn on LED
        
        	}
        }
        
        
        
        void SPI_Init()
        {
        	DDRB = ((1<<DDB2)|(1<<DDB5)|(1<<DDB3)); //SPI pins on port B: SS, SCK, MOSI outputs
        	//set MISO as input
        	PORTB |= (1<<DDB2); //start with SS high (slave not selected). DO THIS BEFORE BEGINING ISP
        	PORTB |= (1<<DDB4); //MISO pull-up activated
        	SPCR = ((1<<SPE)|(1<<MSTR)|(1<<SPR1));  // SPI enable, Master enable, f/64. DO THIS AFTER DDR!
        }
        
        void SPI_Transmit(char dataout)
        {
        	//SPI_Transmit
        	PORTB &= ~(1<<DDB2); // pull slave select low
        	while(PINB & (1<<NIRQ)); // wait until ready signal (low)
        	SPDR = (dataout); // byte to send
        	while(!(SPSR & (1<<SPIF))); // wait for SPIF transmit flag to be set. After this, SPDR will contain the received byte!
        }
        
        
        void SPI_Stop()
        {
        	PORTB |= (1<<DDB2); //slave select high
        }
        
        void rfInit()
        {
        	SPI_Transmit(0x80);
        	SPI_Transmit(0xE7); //EL (turn on internal data reg.),EF (FIFO rx mode enabled, data and dclk used for data and data clock output),868band,12.0pF
        	
        	SPI_Transmit(0x82);
        	SPI_Transmit(0x39); //er (for rec. mode),!ebb,ET(for rec. mode),ES,EX,!eb,!ew,DC for receiver mode//
        	
        	SPI_Transmit(0xA6);
        	SPI_Transmit(0x40); //frequency select
        	
        	SPI_Transmit(0xC6);
        	SPI_Transmit(0x47); //4.8kbps
        	
        	SPI_Transmit(0x94);
        	SPI_Transmit(0xA0); //VDI,FAST,134kHz,0dBm,-103dBm
        	
        	SPI_Transmit(0xC2);
        	SPI_Transmit(0xAC); //AL,!ml,DIG,DQD4
        	
        	SPI_Transmit(0xCA);
        	SPI_Transmit(0x81); //FIFO8,SYNC,!ff,DR ***** (this must be set to 0xCA83 to rx)
        	
        	SPI_Transmit(0xCE);
        	SPI_Transmit(0xD4); //SYNC=2DD4 ,AG
        	
        	SPI_Transmit(0xC4);
        	SPI_Transmit(0x83); //@PWR,NO RSTRIC,!st,!fi,OE,EN
        	
        	SPI_Transmit(0x98);
        	SPI_Transmit(0x50); //!mp,90kHz,MAX OUT
        	
        	SPI_Transmit(0xCC);
        	SPI_Transmit(0x17); //OB1 , ACOB0, LPX,Iddy,CDDIT,CBW0
        	
        	SPI_Transmit(0xE0);
        	SPI_Transmit(0x00); //NOT USED
        	
        	SPI_Transmit(0xC8);
        	SPI_Transmit(0x00); //NOT USED
        	
        	SPI_Transmit(0xC0);
        	SPI_Transmit(0x40); //1.66MHz,2.2V
        }
        
        
        

        Here is the (not yet working) RX side of the code:

        
        // *
        // * Atmega168 RFM12B RX
        // *
        // * Created:
        // * Author : FablabDigiscope
        // */
        
        
        		#define F_CPU 8000000
        
        		#include <avr/io.h>
        		#include <util/delay.h>
        
        		#define NIRQ PB0 // input for nIRQ which goes low after a transmit is received?
        		
        		void FIFO_Reset();
        		void SPI_Transmit(char dataout);
        		void SPI_Init();
        		void rfInit();
        		void SPI_Stop();
        		char SPI_Receive();
        
        		int main(void)
        		{
        		DDRD = 0b10000000; //LED for RX/TX rec/sent
        		
        		SPI_Init();
        		rfInit();
        
        
        		
        	
        		
        		while (1)
        		{
        		char storage; 
        		SPI_Transmit(0x00);
        		SPI_Transmit(0x00);
        		
        		FIFO_Reset();
        		storage = SPI_Receive(); //should get only last byte of message?	
        			
        		if(storage == 0b10011001) // 0x99
        		{
        		PORTD = 0b10000000; //turn on LED
        		FIFO_Reset();
        		}
        		SPI_Stop();
        		_delay_us(1);
        		}
        		}
        
        
        
        
        void SPI_Init()
        {
        	DDRB = ((1<<DDB2)|(1<<DDB5)|(1<<DDB3)); //SPI pins on port B: SS, SCK, MOSI outputs
        	//set MISO as input
        	PORTB |= (1<<DDB2); //start with SS high (slave not selected). DO THIS BEFORE BEGINING ISP
        	PORTB |= (1<<DDB4); //MISO pull-up activated
        	SPCR = ((1<<SPE)|(1<<MSTR)|(1<<SPR1));  // SPI enable, Master enable, f/64. DO THIS AFTER DDR!
        }
        
        char SPI_Receive() //must be in receive mode for this to work//
        {
        	char received;
        	//SPI_Receive
        	PORTB &= ~(1<<DDB2); // pull slave select low
        	while(PINB & (1<<NIRQ)); // wait until ready signal (low)
        	SPDR = (0xB0); // byte to send
        	while(!(SPSR & (1<<SPIF))); // wait for SPIF transmit flag to be set. After this, SPDR will contain the received byte!
        	
        	while(PINB & (1<<NIRQ)); // wait until ready signal (low)
        	SPDR = (0x00); // byte to send
        	while(!(SPSR & (1<<SPIF))); // wait for SPIF transmit flag to be set. After this, SPDR will contain the received byte!
        	
        	received = SPDR;
        	return received;
        }
        
        void SPI_Transmit(char dataout)
        {
        	//SPI_Transmit
        	PORTB &= ~(1<<DDB2); // pull slave select low
        	while(PINB & (1<<NIRQ)); // wait until ready signal (low)
        	SPDR = (dataout); // byte to send
        	while(!(SPSR & (1<<SPIF))); // wait for SPIF transmit flag to be set. After this, SPDR will contain the received byte!
        }
        
        void FIFO_Reset()
        {
        	SPI_Transmit(0xCA);
        	SPI_Transmit(0x81);
        
        	SPI_Transmit(0xCA);
        	SPI_Transmit(0x83);
        }
        
        
        void rfInit()
        {
        	SPI_Transmit(0x80); 
        	SPI_Transmit(0xE7); //EL (turn on internal data reg.),EF (FIFO rx mode enabled, data and dclk used for data and data clock output),868band,12.0pF
        	
        	SPI_Transmit(0x82); 
        	SPI_Transmit(0x99); //er (for rec. mode),!ebb,ET(for rec. mode),ES,EX,!eb,!ew,DC for receiver mode//
        	
        	SPI_Transmit(0xA6); 
        	SPI_Transmit(0x40); //frequency select
        	
        	SPI_Transmit(0xC6); 
        	SPI_Transmit(0x47); //4.8kbps
        	
        	SPI_Transmit(0x94); 
        	SPI_Transmit(0xA0); //VDI,FAST,134kHz,0dBm,-103dBm
        		
        	SPI_Transmit(0xC2); 
        	SPI_Transmit(0xAC); //AL,!ml,DIG,DQD4	
        	
        	SPI_Transmit(0xCA); 
        	SPI_Transmit(0x81); //FIFO8,SYNC,!ff,DR ***** (this must be set to 0xCA83 to rx)	
        	
        	SPI_Transmit(0xCE); 
        	SPI_Transmit(0xD4); //SYNC=2DD4 ,AG
        		
        	SPI_Transmit(0xC4); 
        	SPI_Transmit(0x83); //@PWR,NO RSTRIC,!st,!fi,OE,EN	
        	
        	SPI_Transmit(0x98); 
        	SPI_Transmit(0x50); //!mp,90kHz,MAX OUT
        		
        	SPI_Transmit(0xCC); 
        	SPI_Transmit(0x17); //OB1 , ACOB0, LPX,Iddy,CDDIT,CBW0	
        	
        	SPI_Transmit(0xE0); 
        	SPI_Transmit(0x00); //NOT USED	
        	
        	SPI_Transmit(0xC8); 
        	SPI_Transmit(0x00); //NOT USED	
        		
        	SPI_Transmit(0xC0); 
        	SPI_Transmit(0x40); //1.66MHz,2.2V
        }
        
        void SPI_Stop()
        {
        	PORTB |= (1<<DDB2); //slave select high
        }
        
        
        
        

        This code works (most of the time!), it’s from http://dlehard.narod.ru/quick_start.pdf, dlehard did a wonderful job making this clear and legible, thank you for your work!

        I think the main reason it is working is the fact that it does not rely on the AVR USI but has its own custom 16 bit Write(Cmd) function. This function is straight forward, it takes a 16 bit input and bit bangs the SDO (Microchip input from RFM12B output) one bit at a time while also reading the SDI (Microchip output to RFM12B input) and writing it to the recv variable. It manually does the basic SPI stuff, pulling Chip Select (CS) low and pulling the clock low before putting SDI in the appropriate state and ticking the clock up. The part I had difficulty understanding was the ANDing with 0x8000, now I understand this is a bitmask which looks only at the highest bit in the 16 bit value (0x8000 = 0b1000000000000000) while the loop bitshifts the Cmd to the left each time, ANDing the next bit with 1. ANDing produces a 1 only if A and B are both 1 and otherwise produces a 0, this is why it is useful as a bitmask.

        I don’t know how the timing of this SPI setup is controlled but I guess it’s not critical.

        The TX side turns on an LED everytime it transmits, the RX side turns on an LED if it receives the byte 0x30.

        I was too lazy to get the USART to work with the Atmega168 (I have a previous post on getting it going with an Attiny2313) so that part of the code is commented out.

        Here is the RX side:

        /*
         * Atmega168 RFM12B rev.2 RX.c
         *
         * Created: 4/30/2019 2:56:31 PM
         * Author : FablabDigiscope
         */ 
        
        
        #include <avr/io.h>
        
        /* RFM12B INTERFACE */
        #define SCK 5 // SPI clock
        #define SDO 4 // SPI Data output (RFM12B side)
        #define SDI 3 // SPI Data input (RFM12B side)
        #define CS 2 // SPI SS (chip select)
        #define NIRQ 2 // (PORTD)
        
        /* IO CONTROL */
        #define HI(x) PORTB |= (1<<(x))
        #define LO(x) PORTB &= ~(1<<(x))
        
        
        /* LED */
        #define LED 6
        #define LED_OFF() PORTD &= ~(1<<LED)
        #define LED_ON() PORTD |= (1<<LED)
        
        /* USART */
        //#define BAUDRATE 25 // 19200 at 8MHz
        
        void portInit() {
        	HI(CS);
        	HI(SDI);
        	LO(SCK);
        	DDRB = (1<<CS) | (1<<SDI) | (1<<SCK);
        	DDRD = (1<<LED);
        }
        
        unsigned int writeCmd(unsigned int cmd) {
        	unsigned char i;
        	unsigned int recv;
        	recv = 0;
        	LO(SCK);
        	LO(CS);
        	
        	for(i=0; i<16; i++) {
        		if(cmd&0x8000) HI(SDI); else LO(SDI);
        		HI(SCK);
        		recv<<=1;
        		if( PINB&(1<<SDO) ) {
        			recv|=0x0001;
        		}
        		LO(SCK);
        		cmd<<=1;
        	}
        	HI(CS);
        	return recv;
        }
        
        
        /* 
        void rsInit(unsigned char baud) {
        	UBRRL = baud;
        	UCSRC = (1<<UCSZ0) | (1<<UCSZ1); // 8N1
        	UCSRB = (1<<RXEN) | (1<<TXEN); // enable tx and rx
        }
        void rsSend(unsigned char data) {
        	while( !(UCSRA & (1<<UDRE)));
        	UDR = data;
        }
        unsigned char rsRecv() {
        	while( !(UCSRA & (1<<RXC)));
        	return UDR;
        }
         */
        
        void rfInit() {
        	writeCmd(0x80E7); //EL,EF,868band,12.0pF
        	writeCmd(0x8299); //er,!ebb,ET,ES,EX,!eb,!ew,DC (bug was here)
        	writeCmd(0xA640); //freq select
        	writeCmd(0xC647); //4.8kbps
        	writeCmd(0x94A0); //VDI,FAST,134kHz,0dBm,-103dBm
        	writeCmd(0xC2AC); //AL,!ml,DIG,DQD4
        	writeCmd(0xCA81); //FIFO8,SYNC,!ff,DR (FIFO level = 8)
        	writeCmd(0xCED4); //SYNC=2DD4;
        	writeCmd(0xC483); //@PWR,NO RSTRIC,!st,!fi,OE,EN
        	writeCmd(0x9850); //!mp,90kHz,MAX OUT
        	writeCmd(0xCC17); //!OB1,!OB0, LPX,!ddy,DDIT,BW0
        	writeCmd(0xE000); //NOT USE
        	writeCmd(0xC800); //NOT USE
        	writeCmd(0xC040); //1.66MHz,2.2V
        }
        
        /*
        void rfSend(unsigned char data){
        	while(PIND&(1<<NIRQ));
        	writeCmd(0xB800 + data);
        }
        */
        
        unsigned char rfRecv() {
        	unsigned int data;
        	while(1) {
        		data = writeCmd(0x0000);
        		if ( (data&0x8000) ) {
        			data = writeCmd(0xB000);
        			return (data&0x00FF);
        		}
        	}
        }
        void FIFOReset() {
        	writeCmd(0xCA81);
        	writeCmd(0xCA83);
        }
        
        int main(void) {
        
        	unsigned char data, i;
        	LED_OFF();
        	portInit();
        	rfInit();
        //	rsInit(BAUDRATE); 
        	FIFOReset();
        	while(1) {
        		//waitForData();
        		
        		for (i=0; i<16; i++) {
        			data = rfRecv();
        			if (data == 0x30){
        			LED_ON(); //delete this
        			}
        		}
        		FIFOReset();
        		LED_OFF();
        	}
        	return 0;
        }
        
        
        
        

        Here is the TX side:

        /*
         * Atmega168 RFM12B rev.2.c
         *
         * Created: 4/25/2019 5:27:15 PM
         * Author : FablabDigiscope
         */ 
        
        #include <avr/io.h>
        
        /* RFM12B INTERFACE */
        #define SCK 5 // SPI clock
        #define SDO 4 // SPI Data output (RFM12B side)
        #define SDI 3 // SPI Data input (RFM12B side)
        #define CS 2 // SPI SS (chip select)
        #define NIRQ 2 // (PORTD)
        
        /* IO CONTROL */
        #define HI(x) PORTB |= (1<<(x))
        #define LO(x) PORTB &= ~(1<<(x))
        
        
        /* LED */
        #define LED 6
        #define LED_OFF() PORTD &= ~(1<<LED)
        #define LED_ON() PORTD |= (1<<LED)
        
        void portInit() {
        	HI(CS);
        	HI(SDI);
        	LO(SCK);
        	DDRB = (1<<CS) | (1<<SDI) | (1<<SCK);
        	DDRD = (1<<LED);
        }
        
        unsigned int writeCmd(unsigned int cmd) {
        	unsigned char i;
        	unsigned int recv;
        	recv = 0;
        	LO(SCK);
        	LO(CS);
        	
        	for(i=0; i<16; i++) {
        		if(cmd&0x8000) HI(SDI); else LO(SDI);
        		HI(SCK);
        		recv<<=1;
        		if( PINB&(1<<SDO) ) {
        			recv|=0x0001;
        		}
        		LO(SCK);
        		cmd<<=1;
        	}
        	HI(CS);
        	return recv;
        }
        
        void rfInit() {
        	writeCmd(0x80E7); //EL,EF,868band,12.0pF
        	writeCmd(0x8239); //!er,!ebb,ET,ES,EX,!eb,!ew,DC
        	writeCmd(0xA640); //frequency select
        	writeCmd(0xC647); //4.8kbps
        	writeCmd(0x94A0); //VDI,FAST,134kHz,0dBm,-103dBm
        	writeCmd(0xC2AC); //AL,!ml,DIG,DQD4
        	writeCmd(0xCA81); //FIFO8,SYNC,!ff,DR
        	writeCmd(0xCED4); //SYNC=2DD4,AG
        	writeCmd(0xC483); //@PWR,NO RSTRIC,!st,!fi,OE,EN
        	writeCmd(0x9850); //!mp,90kHz,MAX OUT
        	writeCmd(0xCC17); //OB1,ACOB0, LPX,Iddy,CDDIT,CBW0
        	writeCmd(0xE000); //NOT USED
        	writeCmd(0xC800); //NOT USED
        	writeCmd(0xC040); //1.66MHz,2.2V
        }
        
        
        void rfSend(unsigned char data){
        	while(PIND&(1<<NIRQ));
        	writeCmd(0xB800 + data);
        }
        
        
        int main() {
        	volatile unsigned int i,j;
        	asm("cli");
        	for(i=0;i<1000;i++)for(j=0;j<123;j++);
        	portInit();
        	rfInit();
        	while(1){
        		LED_ON();
        		writeCmd(0x0000);
        		rfSend(0xAA); // PREAMBLE
        		rfSend(0xAA);
        		rfSend(0xAA);
        		rfSend(0x2D); // SYNC
        		rfSend(0xD4);
        		for(i=0; i<16; i++) {
        			rfSend(0x30+i);
        		}
        		rfSend(0xAA); // DUMMY BYTES
        		rfSend(0xAA);
        		rfSend(0xAA);
        		LED_OFF();
        		for(i=0; i<10000; i++) // some not very
        		for(j=0; j<123; j++); // sophisticated delay
        	}
        }
        
        
        

        And here’s a photo of the two modules communicating when each is battery powered from two 1.5 watch battery cells. They continue to be able to communicate after a shortish but not insignificant walk to the coffee machine (50 meters or so).

        Here is the code for one RFM12B taking an analog measurement and sending it to the other which is printing the result via USART to a serial port.

        TX side:

        
        /*
         * Atmega168 RFM12B TX rev.2.c
         *
         * Created: 4/25/2019 5:27:15 PM
         * Author : FablabDigiscope
         */ 
        
        #include <avr/io.h>
        
        /* RFM12B INTERFACE */
        #define SCK 5 // SPI clock
        #define SDO 4 // SPI Data output (RFM12B side)
        #define SDI 3 // SPI Data input (RFM12B side)
        #define CS 2 // SPI SS (chip select)
        #define NIRQ 2 // (PORTD)
        
        /* IO CONTROL */
        #define HI(x) PORTB |= (1<<(x))
        #define LO(x) PORTB &= ~(1<<(x))
        
        
        /* LED */
        #define LED 6
        #define LED_OFF() PORTD &= ~(1<<LED)
        #define LED_ON() PORTD |= (1<<LED)
        
        void analogInit() {
        	ADMUX |= (1 << REFS0); // AREF ref voltage connected to power
        	// PC0 input select
        	ADCSRA |= (1 << ADPS2) | (1 << ADPS0); // set clock to 32 divisions for 8MHz
        	ADCSRA |= (1 << ADEN); /* enable ADC */
        	
        	
        }
        
        unsigned int analogRead() {
        	uint16_t adcValue; //16 bit variable because the ADC on the Attiny84 is 10 bits.
        	ADCSRA |= (1 << ADSC); /* start conversion */
        	adcValue = ADC; /* store high byte into adcValue */
        	return adcValue;
        }
        
        
        void portInit() {
        	HI(CS);
        	HI(SDI);
        	LO(SCK);
        	DDRB = (1<<CS) | (1<<SDI) | (1<<SCK);
        	DDRD = (1<<LED);
        }
        
        unsigned int writeCmd(unsigned int cmd) {
        	unsigned char i;
        	unsigned int recv;
        	recv = 0;
        	LO(SCK);
        	LO(CS);
        	
        	for(i=0; i<16; i++) {
        		if(cmd&0x8000) HI(SDI); else LO(SDI);
        		HI(SCK);
        		recv<<=1;
        		if( PINB&(1<<SDO) ) {
        			recv|=0x0001;
        		}
        		LO(SCK);
        		cmd<<=1;
        	}
        	HI(CS);
        	return recv;
        }
        
        void rfInit() {
        	writeCmd(0x80E7); //EL,EF,868band,12.0pF
        	writeCmd(0x8239); //!er,!ebb,ET,ES,EX,!eb,!ew,DC
        	writeCmd(0xA640); //frequency select
        	writeCmd(0xC647); //4.8kbps
        	writeCmd(0x94A0); //VDI,FAST,134kHz,0dBm,-103dBm
        	writeCmd(0xC2AC); //AL,!ml,DIG,DQD4
        	writeCmd(0xCA81); //FIFO8,SYNC,!ff,DR
        	writeCmd(0xCED4); //SYNC=2DD4,AG
        	writeCmd(0xC483); //@PWR,NO RSTRIC,!st,!fi,OE,EN
        	writeCmd(0x9850); //!mp,90kHz,MAX OUT
        	writeCmd(0xCC17); //OB1,ACOB0, LPX,Iddy,CDDIT,CBW0
        	writeCmd(0xE000); //NOT USED
        	writeCmd(0xC800); //NOT USED
        	writeCmd(0xC040); //1.66MHz,2.2V
        }
        
        
        void rfSend(unsigned char data){
        	while(PIND&(1<<NIRQ));
        	writeCmd(0xB800 + data);
        }
        
        
        int main() {
        	volatile unsigned int i,j;
        	asm("cli");
        	for(i=0;i<1000;i++)for(j=0;j<123;j++);
        	portInit();
        	analogInit();
        	rfInit();
        	while(1){
        		LED_ON();
        		writeCmd(0x0000);
        		rfSend(0xAA); // PREAMBLE
        		rfSend(0xAA);
        		rfSend(0xAA);
        		rfSend(0x2D); // SYNC
        		rfSend(0xD4);
        		for(i=0; i<16; i++) {
        			rfSend(analogRead());
        		}
        		rfSend(0xAA); // DUMMY BYTES
        		rfSend(0xAA);
        		rfSend(0xAA);
        		LED_OFF();
        		for(i=0; i<10000; i++) // some not very
        		for(j=0; j<123; j++); // sophisticated delay
        	}
        }
        
        

        RX side:

        
        /*
         * Atmega168 RFM12B rev.2 RX.c
         *
         * Created: 4/30/2019 2:56:31 PM
         * Author : FablabDigiscope
         */ 
        
        
        #include <avr/io.h>
        
        /* RFM12B INTERFACE */
        #define SCK 5 // SPI clock
        #define SDO 4 // SPI Data output (RFM12B side)
        #define SDI 3 // SPI Data input (RFM12B side)
        #define CS 2 // SPI SS (chip select)
        #define NIRQ 2 // (PORTD)
        
        /* IO CONTROL */
        #define HI(x) PORTB |= (1<<(x))
        #define LO(x) PORTB &= ~(1<<(x))
        
        
        /* LED */
        #define LED 6
        #define LED_OFF() PORTD &= ~(1<<LED)
        #define LED_ON() PORTD |= (1<<LED)
        
        /* USART */
        #define BAUDRATE 25 // 19200 at 8MHz
        
        void portInit() {
        	HI(CS);
        	HI(SDI);
        	LO(SCK);
        	DDRB = (1<<CS) | (1<<SDI) | (1<<SCK);
        	DDRD = (1<<LED);
        }
        
        
        
        unsigned int writeCmd(unsigned int cmd) {
        	unsigned char i;
        	unsigned int recv;
        	recv = 0;
        	LO(SCK);
        	LO(CS);
        	
        	for(i=0; i<16; i++) {
        		if(cmd&0x8000) HI(SDI); else LO(SDI);
        		HI(SCK);
        		recv<<=1;
        		if( PINB&(1<<SDO) ) {
        			recv|=0x0001;
        		}
        		LO(SCK);
        		cmd<<=1;
        	}
        	HI(CS);
        	return recv;
        }
        
        void rsInit(unsigned char baud) {
        	UBRR0L = baud;
        	UCSR0C = (1<<UCSZ00) | (1<<UCSZ01); // 8N1
        	UCSR0B = (1<<RXEN0) | (1<<TXEN0); // enable tx and rx
        }
        
        void rsSend(unsigned char data) {
        	while( !(UCSR0A & (1<<UDRE0)));
        	UDR0 = data;
        }
        
        
        unsigned char rsRecv() {
        	while( !(UCSR0A & (1<<RXC0)));
        	return UDR0;
        }
        
        /*    FOR ATTINY 2313
        void rsInit(unsigned char baud) {
        	UBRRL = baud;
        	UCSRC = (1<<UCSZ0) | (1<<UCSZ1); // 8N1
        	UCSRB = (1<<RXEN) | (1<<TXEN); // enable tx and rx
        }
        void rsSend(unsigned char data) {
        	while( !(UCSRA & (1<<UDRE)));
        	UDR = data;
        }
        unsigned char rsRecv() {
        	while( !(UCSRA & (1<<RXC)));
        	return UDR;
        }
         */
        
        void rfInit() {
        	writeCmd(0x80E7); //EL,EF,868band,12.0pF
        	writeCmd(0x8299); //er,!ebb,ET,ES,EX,!eb (low batt detector disabled),!ew,DC (bug was here)
        	writeCmd(0xA640); //freq select
        	writeCmd(0xC647); //4.8kbps
        	writeCmd(0x94A0); //VDI,FAST,134kHz,0dBm,-103dBm
        	writeCmd(0xC2AC); //AL,!ml,DIG,DQD4
        	writeCmd(0xCA81); //FIFO8,SYNC,!ff,DR (FIFO level = 8)
        	writeCmd(0xCED4); //SYNC=2DD4;
        	writeCmd(0xC483); //@PWR,NO RSTRIC,!st,!fi,OE,EN
        	writeCmd(0x9850); //!mp,90kHz,MAX OUT
        	writeCmd(0xCC17); //!OB1,!OB0, LPX,!ddy,DDIT,BW0
        	writeCmd(0xE000); //NOT USE
        	writeCmd(0xC800); //NOT USE
        	writeCmd(0xC040); //1.66MHz,2.2V
        }
        
        
        void rfSend(unsigned char data){
        	while(PIND&(1<<NIRQ));
        	writeCmd(0xB800 + data);
        }
        
        
        unsigned char rfRecv() {
        	unsigned int data;
        	while(1) {
        		data = writeCmd(0x0000); // I think I would add here responses to Status 
        		if ( (data&0x8000) ) {
        			data = writeCmd(0xB000);
        			return (data&0x00FF);
        		}
        	}
        }
        void FIFOReset() {
        	writeCmd(0xCA81);
        	writeCmd(0xCA83);
        }
        
        int main(void) {
        
        	unsigned char data, i;
        	LED_OFF();
        	portInit();
        	rfInit();
        	rsInit(BAUDRATE); 
        	//analogInit();
        	FIFOReset();
        	while(1) {
        		//waitForData();
        	for (i=0; i<16; i++) {
        		data = rfRecv();
        		rsSend(data);
        		}
        		FIFOReset();
        		LED_OFF();
        	}
        	return 0;
        }
        

        I2C Demo Board Design

         

        A mockup of the first prototype showing the size and locations of key components and plugs.

        This project is inspired by the 8-bit computer of Ben Eater (https://eater.net/8bit/) along with the eccentric PCBs of electronics designers like Teennage Engineering and Bastl instruments. I would like to make a board that is didactic and pedagogical, that can reveal the inner workings of the microcontroller so that students can better understand how they work. I also want the board to be fun, and not dry like typical engineering demo boards. I plan to draw inspiration from the DIP universe of 1990’s through hole components.

        Here are a couple of examples of typical demo boards that I found well conceived:

        In the end there were so many issues with my initial I2C protoboards that I had added LEDs for each different error type with little notes describing the error in question. This experience also convinced me that it would help to have a dedicated I2C board for first-timers.

        Here is the idea in Eagle form.

        Mockup of possible component locations.

        An information display that goes above the board and is automatically retractable.

        Different ways of changing parameters, with jumpers or by attaching crocodile jumpers in different locations, for instance.

        A fold out information card and testing different locations for the wires.

        The original idea with data tables printed on the board itself.

        How should the board stand-up?

        An idea about how to incorperate printed information to help the student with reference data.

        Figuring out how to minimize the number of extra and unneccessary cables. Here there is only one header per board. Also testing the 7 segment LED display.

        Questioning the value of displaying information in bits and or in hex.

        A prototype of the kind of information card that could accompany the PCB.

        Information about the pertinent registers is shown.

        Information about the pertinent bits is shown, but they are laid out in a more readable way.

        This last example is a nice balance, it selects the pertinent registers to show but doesn’t give every single last piece of information. Now I have to find a way of relating to the actual components (should they be on the other side or in SMD on the top face?). Also, should the user be able to enter data into the TWDR in hardware? This requires a 8 pin DIP switch and then some way of communicating that the TWDR is either being loaded by the DIP switch or an actual reflection of the contents of the register. Adding a DIP increases the playfulness factor of the board but if it also adds confusion then I’d rather avoid it.

        Fab15 part III

        Finally have something that works and is easy enough to assemble and work with.Here is the radio side:

        Here are the two sides of the sandwhich:

        And here is the assembled sandwhich:

        I also tried, in vain, to solder a few SMD components by hand. First the DRV8838 motor driver. It’s devilishly tiny and despite proudly soldering tiny wires nothing worked.

        With our (expired) solder paste and reflow oven I didn’t get much further either. I’ll wait for our refrigerator to arrive before trying the reflow method again.

        I also failed to hand-solder the LUX sensor which was not quite as small but had fragile pads.

        I found a note on solarbotics which explained how their 6+V solar panel could be reduced to half the voltage with an exacto knife and a soldering iron here: https://solarbotics.com/product/scc3733/

        Most importantly the circuit appears to function. I only have a few revisions to make to the board:

        -one of the caps is very close to the traces and needs heat shrink tubing.

        -the edge of the radio board next to the MOSFET is too close to the edge and was burned off.

        -There was a short between the 1M and the 150K resistors connected to the FB pin of the LTC3105.

        -Things are a little tight for the inductor if this SMD component is to be soldered by hand (things are also tight for the other 1206 components next to the IC). Not sure what the plan is for beginners in the workshop here…

        -The female headers I’m using aren’t particularly pretty when snipped up and are also too long, making the sandwhich not very compact.

        -I need the DRV8838 and the Lux sensor in SOIC or I need solder paste and a fridge.

        -Using double sided FR4 is not fun, it’s difficult to cut the second side of copper effectively when you can’t set the focus easily. There is plenty of space on the LTC3105 side of the board for a motor driver.

        -Could I find a way to organise the jumper pins between the boards into “banks” so there are just a few rows of pins to solder (instead of individual pins which are prone to brake on this type of board)?

        -the through-board antenna works but it is kind of awkward.

        -I now have to switch to the RFM69

        UPDATE

        I’m going to do one more thing before switching to the RFM69, I will make a new board with a motor driver to test the ability to adjust the panel angle.

        Here is the plan:

        Here is what is already built:

        This will involve recutting the LTC3105 circuit but will not require redoing the radio board. This new board will get around the difficulty of doing a double sided board and uses up the unused space on the LTC board. Here is the new LTC3105 circuit which includes the DRV8833 (a motor driver I happen to have which works at 2.7V):

        Here is the board (not yet inverted) for cutting:

        and here is the circuit diagram in three parts.

        The microchip:

        The solar harvester:

        And the motor driver:

        Here is a photo of the final thing cut out:

        Here is a photo of me assembling and testing bits at a time:

        And here is the RX side of the board freshly cut:

        Here’s the board from eagle:

        And the schematic:

        Here’s the code for the Atmega328 based board to test the USART functionality before soldering the RF module

        
        /*
        ATMEGA 328P USART.c
        
        Created: 6/21/2019 10:39:33 AM
        Author : FablabDigiscope
         
        USB - FTDI 3.3V:
        Rx to Tx
        Tx to Rx
        Gnd to Gnd
        
        (NO LED ON chip's RX, but the one on TX doesn't seem to interfere)
        
        To view your Serial output in AVR Studio, 
        1. Click Tools > Data Visualizer
        2. Under Configuration>Modules, click External Connection.
        3. Double click Serial Port
        4. Configure your port. e.g. Set the Baud Rate to 115200
        5. Select your COM port
        6. Click Connect.
        
        appears to send ASCII from AVR Studio terminal
        
        LED on PC0 blinks when you send a 'j' in serial
        
         */ 
        #define F_CPU 8000000
        
        #include <avr/io.h>
        #include <util/delay.h>
        #include <stdlib.h>
        
        
        #define BAUD 9600                                   // define baud
        #define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)            // set baud rate value for UBRR
        
        // function to initialize UART
        void uart_init (void)
        {
        	UBRR0H = (BAUDRATE>>8);                      // shift the register right by 8 bits
        	UBRR0L = BAUDRATE;                           // set baud rate
        	UCSR0B|= (1<<TXEN0)|(1<<RXEN0);                // enable receiver and transmitter
        	UCSR0C|= (1<<UCSZ01)|(1<<UCSZ00);			   // 8bit data format + asynchronous mode
        }
        
        // function to send data
        void uart_transmit (unsigned char data)
        {
        	while (!(UCSR0A & (1<<UDRE0)));                // wait while register is free
        	UDR0 = data;                                   // load data in the register
        }
        
        unsigned char uart_recieve (void)
        {
        	while(!(UCSR0A) & (1<<RXC0));           // wait while data is being received
        	return UDR0;                             // return 8-bit data
        }
        
        int main(void)
        {
        	
        DDRC = 0b00000001;
        uart_init();
        
        unsigned char a;
            /* Replace with your application code */
            while (1) 
            {
        		//transmit
        // 	uart_transmit('alpha');
        // 	PORTA = 0b00000001;
        // 	_delay_ms(1000);
        // 	PORTA = 0b00000000;
        // 	_delay_ms(1000);
        
        		//recieve
        	a = uart_recieve(); 
        	_delay_ms(100);
        	
        
        	if (a == 'j') //corresponds to a 'j' for instance, but not a 'c'
        		{
        			PORTC = 0b00000001;
        			_delay_ms(1000);
        			PORTC = 0b00000000;
        			_delay_ms(1000);
        			PORTC = 0b00000001;
        			_delay_ms(1000);
        			PORTC = 0b00000000;
        			_delay_ms(1000);	//some kind of local echo thing makes it repeat?? Or else the register is not being cleared
        		}
            }
        }
        
        

        Testing parts at time, first the microchip with ISP and LED, then adding the radio, then retesting and finishing the rest of the circuit.

        Things to change after this version: add a .1uF capacitor between power for the Atmega328 if using same power as the RF.

        The connector for the 3.3V USART plug has gaps and a strange pitch, not a standard header.

        Generating code with arduino and uploading it with AVR Studio: Compile the code with Arduino, then make a search for the filename with .hex at the end. Copy this to a convenient location. In AVR Studio you will want to flash a .hex from file location, instead of generating it from a C code. Make sure to let Arduino know that you are using a Pro Mini 3.3V at 8MhZ and make sure you program the CLKDIV fuse to not divide the internal clock by 8. I am using a Arduino pin map image to help with the translation from my pins to the arduino software:

        https://www.google.com/url?sa=i&source=images&cd=&ved=2ahUKEwjDtpCx8vriAhVNyxoKHSQLBpEQjRx6BAgBEAU&url=https%3A%2F%2Flearn.sparkfun.com%2Ftutorials%2Fusing-the-arduino-pro-mini-33v%2Fall&psig=AOvVaw2h4IYqUccgSOhJAcis_Tzq&ust=1561217456527846

        Here is a helpful website for wiring and the code: https://learn.sparkfun.com/tutorials/rfm69hcw-hookup-guide/all

        I’m using LOW POWER LABS’ code for the RF69 here. Make sure to install their two libraries (SPI Flash and RFM69). Here is the code they provide, I have made a tiny modification for the LED pin.

        // RFM69HCW Example Sketch
        // Send serial input characters from one RFM69 node to another
        // Based on RFM69 library sample code by Felix Rusu
        // http://LowPowerLab.com/contact
        // Modified for RFM69HCW by Mike Grusin, 4/16
        
        // This sketch will show you the basics of using an
        // RFM69HCW radio module. SparkFun's part numbers are:
        // 915MHz: https://www.sparkfun.com/products/12775
        // 434MHz: https://www.sparkfun.com/products/12823
        
        // See the hook-up guide for wiring instructions:
        // https://learn.sparkfun.com/tutorials/rfm69hcw-hookup-guide
        
        // Uses the RFM69 library by Felix Rusu, LowPowerLab.com
        // Original library: https://www.github.com/lowpowerlab/rfm69
        // SparkFun repository: https://github.com/sparkfun/RFM69HCW_Breakout
        
        // Include the RFM69 and SPI libraries:
        
        #include <RFM69.h>
        #include <SPI.h>
        
        // Addresses for this node. CHANGE THESE FOR EACH NODE!
        
        #define NETWORKID     0   // Must be the same for all nodes
        #define MYNODEID      1   // My node ID
        #define TONODEID      2   // Destination node ID
        
        // RFM69 frequency, uncomment the frequency of your module:
        
        //#define FREQUENCY   RF69_433MHZ
        #define FREQUENCY     RF69_915MHZ
        
        // AES encryption (or not):
        
        #define ENCRYPT       true // Set to "true" to use encryption
        #define ENCRYPTKEY    "TOPSECRETPASSWRD" // Use the same 16-byte key on all nodes
        
        // Use ACKnowledge when sending messages (or not):
        
        #define USEACK        true // Request ACKs or not
        
        // Packet sent/received indicator LED (optional):
        
        #define LED           A0 // LED positive pin
        
        
        // Create a library object for our RFM69HCW module:
        
        RFM69 radio;
        
        void setup()
        {
          // Open a serial port so we can send keystrokes to the module:
        
          Serial.begin(9600);
          Serial.print("Node ");
          Serial.print(MYNODEID,DEC);
          Serial.println(" ready");  
        
          // Set up the indicator LED (optional):
        
          pinMode(LED,OUTPUT);
          digitalWrite(LED,LOW);
        
        
          // Initialize the RFM69HCW:
        
          radio.initialize(FREQUENCY, MYNODEID, NETWORKID);
          radio.setHighPower(); // Always use this for RFM69HCW
        
          // Turn on encryption if desired:
        
          if (ENCRYPT)
            radio.encrypt(ENCRYPTKEY);
        }
        
        void loop()
        {
          // Set up a "buffer" for characters that we'll send:
        
          static char sendbuffer[62];
          static int sendlength = 0;
        
          // SENDING
        
          // In this section, we'll gather serial characters and
          // send them to the other node if we (1) get a carriage return,
          // or (2) the buffer is full (61 characters).
        
          // If there is any serial input, add it to the buffer:
        
          if (Serial.available() > 0)
          {
            char input = Serial.read();
        
            if (input != '\r') // not a carriage return
            {
              sendbuffer[sendlength] = input;
              sendlength++;
            }
        
            // If the input is a carriage return, or the buffer is full:
        
            if ((input == '\r') || (sendlength == 61)) // CR or buffer full
            {
              // Send the packet!
        
        
              Serial.print("sending to node ");
              Serial.print(TONODEID, DEC);
              Serial.print(", message [");
              for (byte i = 0; i < sendlength; i++)
                Serial.print(sendbuffer[i]);
              Serial.println("]");
        
              // There are two ways to send packets. If you want
              // acknowledgements, use sendWithRetry():
        
              if (USEACK)
              {
                if (radio.sendWithRetry(TONODEID, sendbuffer, sendlength))
                  Serial.println("ACK received!");
                else
                  Serial.println("no ACK received");
              }
        
              // If you don't need acknowledgements, just use send():
        
              else // don't use ACK
              {
                radio.send(TONODEID, sendbuffer, sendlength);
              }
        
              sendlength = 0; // reset the packet
              Blink(LED,10);
            }
          }
        
          // RECEIVING
        
          // In this section, we'll check with the RFM69HCW to see
          // if it has received any packets:
        
          if (radio.receiveDone()) // Got one!
          {
            // Print out the information:
        
            Serial.print("received from node ");
            Serial.print(radio.SENDERID, DEC);
            Serial.print(", message [");
        
            // The actual message is contained in the DATA array,
            // and is DATALEN bytes in size:
        
            for (byte i = 0; i < radio.DATALEN; i++)
              Serial.print((char)radio.DATA[i]);
        
            // RSSI is the "Receive Signal Strength Indicator",
            // smaller numbers mean higher power.
        
            Serial.print("], RSSI ");
            Serial.println(radio.RSSI);
        
            // Send an ACK if requested.
            // (You don't need this code if you're not using ACKs.)
        
            if (radio.ACKRequested())
            {
              radio.sendACK();
              Serial.println("ACK sent");
            }
            Blink(LED,10);
          }
        }
        
        void Blink(byte PIN, int DELAY_MS)
        // Blink an LED for a given number of ms
        {
          digitalWrite(PIN,HIGH);
          delay(DELAY_MS);
          digitalWrite(PIN,LOW);
        }
        
        
        

        And here is the one I modified for the RX (it doesn’t have serial so it just blinks when it receives something. However the other board can tell us by serial that it received an ACK)

        // RFM69HCW Example Sketch
        // Send serial input characters from one RFM69 node to another
        // Based on RFM69 library sample code by Felix Rusu
        // http://LowPowerLab.com/contact
        // Modified for RFM69HCW by Mike Grusin, 4/16
        
        // This sketch will show you the basics of using an
        // RFM69HCW radio module. SparkFun's part numbers are:
        // 915MHz: https://www.sparkfun.com/products/12775
        // 434MHz: https://www.sparkfun.com/products/12823
        
        // See the hook-up guide for wiring instructions:
        // https://learn.sparkfun.com/tutorials/rfm69hcw-hookup-guide
        
        // Uses the RFM69 library by Felix Rusu, LowPowerLab.com
        // Original library: https://www.github.com/lowpowerlab/rfm69
        // SparkFun repository: https://github.com/sparkfun/RFM69HCW_Breakout
        
        // Include the RFM69 and SPI libraries:
        
        #include <RFM69.h>
        #include <SPI.h>
        
        // Addresses for this node. CHANGE THESE FOR EACH NODE!
        
        #define NETWORKID     0   // Must be the same for all nodes
        #define MYNODEID      2   // My node ID
        #define TONODEID      1   // Destination node ID
        
        // RFM69 frequency, uncomment the frequency of your module:
        
        //#define FREQUENCY   RF69_433MHZ
        #define FREQUENCY     RF69_915MHZ
        
        // AES encryption (or not):
        
        #define ENCRYPT       true // Set to "true" to use encryption
        #define ENCRYPTKEY    "TOPSECRETPASSWRD" // Use the same 16-byte key on all nodes
        
        // Use ACKnowledge when sending messages (or not):
        
        #define USEACK        true // Request ACKs or not
        
        // Packet sent/received indicator LED (optional):
        
        #define LED           14 // LED positive pin
        
        
        // Create a library object for our RFM69HCW module:
        
        RFM69 radio;
        
        void setup()
        {
          // Open a serial port so we can send keystrokes to the module:
        
          // Set up the indicator LED (optional):
        
          pinMode(LED,OUTPUT);
          pinMode(7,OUTPUT);
          digitalWrite(LED,LOW);
        
        
          // Initialize the RFM69HCW:
        
          radio.initialize(FREQUENCY, MYNODEID, NETWORKID);
          radio.setHighPower(); // Always use this for RFM69HCW
        
          // Turn on encryption if desired:
        
          if (ENCRYPT)
            radio.encrypt(ENCRYPTKEY);
        }
        
        void loop()
        {
        
        digitalWrite(7, HIGH); // turn on MOSFET
        
          // RECEIVING
        
          // In this section, we'll check with the RFM69HCW to see
          // if it has received any packets:
        
          if (radio.receiveDone()) // Got one!
          {
        
            if (radio.ACKRequested())
            {
              radio.sendACK();
            }
            Blink(LED,1000);
          }
        }
        
        void Blink(byte PIN, int DELAY_MS)
        // Blink an LED for a given number of ms
        {
          digitalWrite(PIN,HIGH);
          delay(DELAY_MS);
          digitalWrite(PIN,LOW);
        }
        
        

        AVR studio troubles

        I am having a very strange problem with AVR Studio. It is having difficulty finding the programmer and, more alarming, it is finding that brand new Atmega 328 chips have all their fuses set to high, most problematically the RSTDSBL fuse which essentially bricks the chips unless you have a high voltage programmer (we don’t).

        I replaced my 328p on the board and have the same problem (I did this by holding on to the chip with a pair of tweezers and then using a heat gun in a circle around all the pins until the board fell and I was holding the chip. Then I removed the remaining solder with a solder braid.) After this you have to look under a microscope to make sure no solder schrapnel has been sent across the board to cause a short somewhere.

        I’m installed AVR Studio on another computer and am checking to see if the same situation exists. I have replaced my programmer as well as tried with a new jumper cable, nothing is making a difference.

        Did a search online and it looks like I need a pull up (10K) on CS so that the RF69 doesn’t introduce gibberish while programming…Yep, that looks like it was it! Updated all boards with a pull-up on CS.

        Cool, now everything works, even with the LTC3105 powering the transmitter…but only when connected to the same power supply! Urg.

        Nope, now that’s fixed. I was just not giving the reciever enough juice in terms of mA.

        Errata: (0.1uF on power and new header for USART also)

        This is the Arduino test code I used to make sure the transmitter could function with the LTC3105 based power with an 8 second watchdog timer, it sends the voltage of the cap (I had to remove the MOSFET meant to make this task less wasteful though) and then goes back to charging.

        
        // RFM69HCW Example Sketch
        // Send serial input characters from one RFM69 node to another
        // Based on RFM69 library sample code by Felix Rusu
        // http://LowPowerLab.com/contact
        // Modified for RFM69HCW by Mike Grusin, 4/16
        
        // This sketch will show you the basics of using an
        // RFM69HCW radio module. SparkFun's part numbers are:
        // 915MHz: https://www.sparkfun.com/products/12775
        // 434MHz: https://www.sparkfun.com/products/12823
        
        // See the hook-up guide for wiring instructions:
        // https://learn.sparkfun.com/tutorials/rfm69hcw-hookup-guide
        
        // Uses the RFM69 library by Felix Rusu, LowPowerLab.com
        // Original library: https://www.github.com/lowpowerlab/rfm69
        // SparkFun repository: https://github.com/sparkfun/RFM69HCW_Breakout
        
        // Include the RFM69 and SPI libraries:
        
        #include <avr/wdt.h>
        #include <RFM69.h>
        #include <SPI.h>
        #include <avr/sleep.h>
        
        // Addresses for this node. CHANGE THESE FOR EACH NODE!
        
        #define NETWORKID     0   // Must be the same for all nodes
        #define MYNODEID      2   // My node ID
        #define TONODEID      1   // Destination node ID
        
        // RFM69 frequency, uncomment the frequency of your module:
        
        //#define FREQUENCY   RF69_433MHZ
        #define FREQUENCY     RF69_915MHZ
        
        // AES encryption (or not):
        
        #define ENCRYPTKEY    "TOPSECRETPASSWRD" // Use the same 16-byte key on all nodes
        
        // Use ACKnowledge when sending messages (or not):
        
        #define USEACK        true // Request ACKs or not
        
        // Packet sent/received indicator LED (optional):
        
        #define LED          14 // LED positive pin
        
        #define PGOOD         4 // PGOOD
        
        #define VTEST         8 // VTEST MOSFET
        
        
        // Create a library object for our RFM69HCW module:
        
        
        int level = 0;
        
        RFM69 radio;
        
        void setup()
        {
          // Open a serial port so we can send keystrokes to the module:
        
           pinMode(LED,OUTPUT);
          digitalWrite(LED,LOW);
            pinMode(7,OUTPUT); //mosfet
            pinMode(PGOOD, INPUT); // PGOOD
          pinMode(A1, INPUT);
           pinMode(LED,OUTPUT);
          pinMode(VTEST,OUTPUT);
          
        wdt_enable(WDTO_8S);
        
          
        
        
        }
        
        void loop()
        {
          digitalWrite(VTEST, HIGH); //turn on MOSFET to read voltage
        digitalWrite(7, LOW); // turn off MOSFET
        level = digitalRead(PGOOD);
        if(level == HIGH)
        
        {
          
          digitalWrite(VTEST, HIGH); //turn on MOSFET to read voltage
          digitalWrite(7, HIGH); // turn on MOSFET
          radio.initialize(FREQUENCY, MYNODEID, NETWORKID);
          radio.setHighPower(); // Always use this for RFM69HCW
        
          // Turn on encryption if desired:
        
            radio.encrypt(ENCRYPTKEY);
        
          
        
          char Pstr[10];
          char Hstr[10];
          char buffer[50];
          double P = (analogRead(A1)*0.006451); // analog read out of 1023 (based on .975V as highest value), multiply this ratio by 3.33333 to get the actual.
          double H = analogRead(A1);
          
          dtostrf(H, 3,3, Hstr);
          dtostrf(P, 3,3, Pstr);
          
          static int sendlength = strlen(buffer);
        
        
        sprintf(buffer, "BAT: P:%sV H:%s", Pstr, Hstr);
        
           
              radio.sendWithRetry(TONODEID, buffer, sendlength);
              Blink(LED,1000);
             level = LOW;
             sleep();
        }
          else
          {
           
          digitalWrite(7, LOW); // turn off MOSFET
          sleep();
          }
        
        }
        void Blink(byte PIN, int DELAY_MS)
        // Blink an LED for a given number of ms
        {
          digitalWrite(PIN,HIGH);
          delay(DELAY_MS);
          digitalWrite(PIN,LOW);
        }
        
        void sleep(void)
        {
          set_sleep_mode(SLEEP_MODE_PWR_DOWN); //select PWR DOWN, the most power savings
          sleep_enable(); //set  SE bit
          sei(); // enable global interrupts
          sleep_cpu(); //actually sleep
          sleep_disable(); //code reaches this point after interrupt
        }
        
        
        

        The formula for the voltage sensing using a 20K and 10K voltage divider is the following:

        ((x/1023)*2.2V)*3

        I.E. take the analogRead value (which is out of 1023) and multiply it by the maximum 2.2V that could be sensed to get the voltage after the 20K. This is one third of Vcap, so multiply by three to get Vcap. (We need a voltage divider because the microchip is using LDO at 2.2V while the Vout of the RF is getting Vout at 3.3V)

        Fab15 part II

        Here is the REV.2 board.It’s far more compact, around 38mm x 38mm, and is stackable.

        The ICSP acts as a connector linking the top and bottom boards, with one extra pin for the Vout from the LTC3105 to the RF transciever. The idea is to use long pins which will pass through the board but allow a ISCP header to plug in top so it remains programmable.

        I have added a ground plane and made sure to make leads thick and short for decoupling caps connected to Vin and Vout.

        In this version the solar panels don’t live on a board but instead float above the sandwich.

        The board is still basic, it doesn’t allow for PGOOD, SHUTDOWN from LTC3105 nor does it have the diode and cap in series for MPPC. I have also not switched over the the RFM69 as I cannot find anyone who has made it work with the Attiny2313. It does have an I2C LUX sensor included on the solar harvesting board though, it speaks to the microchip on the other board via the ICSP header.

        I did a little touching up of the inverted eagle image, I find the ground planing leaves some ugly left over bits. I set distances to .6mm for the ground plane and used .4mm thick traces elsewhere apart from power traces which I made at 1mm. I made the cut out holes .68mm which is the width I measure of a header pin. Making this work nicely will be trail and error I suspect.

        I have a hole cut through the top board to allow the antenna to pass through, not sure if this is sacrilege or not but I thought it was nifty.

        I have ordered some Attiny2313Vs in SOIC package, LTC3105s, Vishay’s VEML6030 I2C LUX sensor (datasheet: http://www.farnell.com/datasheets/2118174.pdf?_ga=2.101548161.1466299159.1558364926-1966659865.1549463489&_gac=1.82301412.1555072928.Cj0KCQjw7sDlBRC9ARIsAD-pDFol1bBBXfgfW_WkUDGae3GBfZ2j4YvsX5g-ECjGaAFDm9bDDEVXEwgaAkgpEALw_wcB) -I edited the TSL2561 from Sparkfun in Eagle- and some 10F supercapacitors. I have also found a Coilcraft inductor (LPS4018-103MRB) which matches the specs for the LT demo board.

        Here’s the board:

        And here’s the schematic:

        I am also thinking about the indoor/outdoor-ness of this project. If this is placed outdoor it should be potted in resin with maybe a way to strap it to a post or magnetically place it somewhere (is a mini suction cup a real solution?). If there are dual axis motors moving this thing around I could imagine the circuit being inside a kind of clear plexiglass dome, the indoor version wouldn’t need this of course.

        So, if everything miraculously works with the circuit I can move to add a dual H-bridge to move this panel around. I could possibly put this on the flip side of one of these double sided boards and use our rivets if necessary…Though I would need to first source some nice motors from one of our approved suppliers. I need a small DC motor with a gear reduction that runs at 3V for 5 euros. Solarbotics seems to be the best source for these, I especially like this https://solarbotics.com/product/gm23/ .

        Here is the first attempt at lasercutting on double sided FR4:

        Evidently the holes for the passage of pins did not work out. For next time remember that you have to first ablate the metal with the Fiber laser before you can use the C02 laser. Once you get through the fiberglass you need to take out the copper at the reverse side, if you’re using a double sided board, so this requires switching back to the Fiber laser?

        In any case, the holes for pins passing through need to be much smaller and the copper traces need to be much bigger. This board will at least serve to test with the newly arrived LTC3105 to see if that circuit can get going.

        The parts have arrived, here is the test set up I have got:

        The voltage of the small 24 x 33mm solar panel from Solarbotics is around 4.7V, so under the 5V max of the unit. I can confirm that Vin and GND are shorted together by the LTC3105 and so if you see a short there it’s not a problem. I can also confirm that power supplies will give funny readings when supplying the LTC, this appears to be because the device is pulling and pushing current around and making it difficult to tell exactly how much voltage is going where. When I test my solar panels disconnected I get several volts and when I test again while they are plugged in to the LTC I get only a few tens of millivolts.

        The 10F supercap is slowly charging, but the light is too strong and is melting the solar panel! I’m concerned that it takes so long to get the LDO going with the solar panels. Using the power supply I can get LDO up and running at very low voltages.

        The kxob22 solar cells I am using are super sensitive to soldering temperature, max 220 C for them, this is not enough to melt leaded solder in my experience. I just destroyed 10 of them by accident…

        http://ixapps.ixys.com/DataSheet/20110302-KXOB22-12X1-DATA-SHEET.pdf

        In principle the idea was to connect four or five in series (plus to minus) to get around 2.5V in total, hitting the efficiency sweet spot of the LTC3105 of 90%+.

        Things are looking good charging with a 10V 4700uF cap. It is slowly charging (at 2.8V now) with an Attiny2313V plugged in and waiting for LDO to come on. The Attiny2313 doesn’t seem to be frustrating the charging process, disconnecting it has little effect on the charging. It looks like even when the Vout has reached its target voltage it is not turning on the LDO. Could this be because I accidentally put the Vin too high when testing with the power supply? The datasheet explains that the Vout is not charged until the LDO regulation is met…What’s going on here?

        The next question, if this test works, is if a 4700uF cap charged to 3V can power the RFM12B. In previous tests I found that anything less than 2.7V was insufficient for a signal to be received. It looks like it works about 13 of the time with a 4700uF and the Attiny2313V TX circuit I made. With the 10F 3V supercap charged up to 2.9V the RFM12B works nicely…but how to use the LTC3105 to charge such a big cap when it is having difficulty with a .005F….Maybe trying a 0.5F (two 1F 2.5V in series). This works well, no problems transmitting around four times on a full charge. Maybe the magic number is .1F?

        WOW! Major change on functionality when I activate MPPC. Almost instantly I hit LDO and start charging the cap. Completely different operation now. Suddenly everything seems possible with this device and this project :). Attaching the RF12B and turning it on once the cap has fully charged successfully sends a trasmission and is fully recharged ten seconds later…Ahh the sweet taste of a working IC.

        Moving on to the MOSFET switching, it appears that the microchop voltage of 2V is too low to turn on the transistor very much. I switched to an SMD MOSFET which has a minimum Vgs of 1V and max of 2V. It seems to work now once I add a 10K pull up resistor on the Gate. I’m using the 512-RFD16N05LSM9A.

        Replaced the Attiny2313V with an Attiny44A and it’s hogging so much power Vout is decreasing under the same conditions…turns out it’s important to have the microchip exploiting sleep etc.

        OK, I’ve got things working such that the moment a HIGH signal is seen by PD6 on the Attiny (from LTC3105’s PGOOD) it beings transmitting. Right now it’s not optimized to use minimal power while waiting but I can work on this next if everything goes smoothly with this test.

        Current problem is that once the microchip has slept and let the cap charge up, it sends a transmission and then the cap drains quite rapidly until I hit reset. I think the MOSFET is somehow staying on? I could measure the current draw of the different modules and see whats happening. Another idea is to put the RFM12 initialization AFTER I check that the PGOOD is HIGH.

        I have decided to make my life easier and to change to INT1 from PCINT0. INT0 and INT1 can be set up to trigger on RISING or FALLING signals wherease PCINTx only trigger on pin changes, which is causing my code to be more complicated than it needs to be. Here is how the code should work:

        The problem with this though is that the interrupt will only work when PGOOD turns on and the state of the pin CHANGES. Once PGOOD is on and stays on it can’t wake up the ATTINY. So before going to sleep the ATTINY needs to make sure PGOOD is OFF (so that it can be awoken by PGOOD going ON). So I need to add a while loop that says which PGOOD is high send transmissions.

        I like that this project is making me more connected to the sun. I can’t work without it and I have become much more dependent on good weather to continue working. It makes one think of all the ecology applications this kind of device has too.

        Grrrr…wasted an hour plus today learning that in POWER_DOWN mode only LEVEL interrupts for INT0 and INT1 will function (not rising/falling edge or voltage changes). From IDLE mode these work, however.

        Here’s the code which works when testing in isolation. Once I connect INT0 to power it wakes up and sends stuff. When reconnected to GND it goes back to sleep to wait for another interrupt. Cool. To get this working with PWR_DOWN I need to add an inverter (as only LOW signal can be detected from deep sleep by INT0 and we get a HIGH from PGOOD).

        I have only tested this with a power supply, need to now test with sun (weather didn’t permit this today).

        *THIS DOESN’T WORK WITH THE LTC3105. The next code does though further down.

        /*
         * Attiny2313 sleep + RFM12B + ltc3105.c
         *
         * Created: 5/27/2019 3:47:37 PM
         * Author : FablabDigiscope
         */ 
        
        
        #define F_CPU 8000000 
        
        #include <avr/interrupt.h>
        #include <avr/sleep.h>
        #include <util/delay.h>
        
        /* RFM12B INTERFACE */
        #define SCK 7 // SPI clock
        #define SDO 6 // SPI Data output (RFM12B side)
        #define SDI 5 // SPI Data input (RFM12B side)
        #define CS 4 // SPI SS (chip select)
        #define NIRQ 2 // (PORTD)
        
        /* IO CONTROL */
        #define HI(x) PORTB |= (1<<(x))
        #define LO(x) PORTB &= ~(1<<(x))
        
        
        /* MOSFET */
        #define MOSFET 4
        #define MOSFET_OFF() PORTD &= ~(1<<MOSFET)
        #define MOSFET_ON() PORTD |= (1<<MOSFET)
        
        
        void portInit() 
        {
        	HI(CS);
        	HI(SDI);
        	LO(SCK);
        	DDRB = (1<<CS) | (1<<SDI) | (1<<SCK);
        	DDRD |= (1<<MOSFET);
        }
        
        unsigned int writeCmd(unsigned int cmd) 
        {
        	unsigned char i;
        	unsigned int recv;
        	recv = 0;
        	LO(SCK);
        	LO(CS);
        	
        	for(i=0; i<16; i++) {
        		if(cmd&0x8000) HI(SDI); else LO(SDI);
        		HI(SCK);
        		recv<<=1;
        		if( PINB&(1<<SDO) ) {
        			recv|=0x0001;
        		}
        		LO(SCK);
        		cmd<<=1;
        	}
        	HI(CS);
        	return recv;
        }
        
        
        void rfInit() 
        {
        	writeCmd(0x80E7); //EL,EF,868band,12.0pF
        	writeCmd(0x8239); //!er,!ebb,ET,ES,EX,!eb,!ew,DC
        	writeCmd(0xA640); //frequency select
        	writeCmd(0xC647); //4.8kbps
        	writeCmd(0x94A0); //VDI,FAST,134kHz,0dBm,-103dBm
        	writeCmd(0xC2AC); //AL,!ml,DIG,DQD4
        	writeCmd(0xCA81); //FIFO8,SYNC,!ff,DR
        	writeCmd(0xCED4); //SYNC=2DD4 ,AG
        	writeCmd(0xC483); //@PWR,NO RSTRIC,!st,!fi,OE,EN
        	writeCmd(0x9850); //!mp,90kHz,MAX OUT
        	writeCmd(0xCC17); //OB1,ACOB0, LPX,Iddy,CDDIT,CBW0
        	writeCmd(0xE000); //NOT USED
        	writeCmd(0xC800); //NOT USED
        	writeCmd(0xC040); //1.66MHz,2.2V
        }
        
        
        void rfSend(unsigned char data)
        {
        	while(PIND&(1<<NIRQ));
        	writeCmd(0xB800 + data);
        }
        
        
        void sleep()
        {
        	set_sleep_mode(SLEEP_MODE_IDLE); //select PWR DOWN, the most power savings
        	sleep_enable(); //set  SE bit
        	sei(); // enable global interrupts
        	sleep_cpu(); //actually sleep
        	sleep_disable(); //code reaches this point after interrupt
        }
        
        
        ISR(INT1_vect) //PB0 level change
        {
        	//do nothing
        }
        
        
        
        int main(void)
        {
        
        		
        			portInit();
        			GIMSK |= (1 << INT1); // enable the  external interrupt on pin PD3
        			MCUCR |= (1<<ISC11) | (1<<ISC10); //set to rising edge triggered
        
        while(1)
        	
        	if(PIND & (1<<PD3))
        	{
        	cli(); 
        	
        	HI(CS);
        	HI(SDI);
        	LO(SCK);
        			
        				MOSFET_ON();
        				volatile unsigned int i,j;
        				for(i=0;i<1000;i++)for(j=0;j<123;j++);
        				rfInit();
        				portInit();
        				writeCmd(0x0000);
        				rfSend(0xAA); // PREAMBLE
        				rfSend(0xAA);
        				rfSend(0xAA);
        				rfSend(0x2D); // SYNC
        				rfSend(0xD4);
        				for(i=0; i<1; i++) 
        				{
        					rfSend(0x30+i);
        				}
        				rfSend(0xAA); // DUMMY BYTES
        				rfSend(0xAA);
        				rfSend(0xAA);		
        				for(i=0; i<10000; i++) // some not very
        				for(j=0; j<123; j++); // sophisticated delay
        	}
        
        else
        {
        		MOSFET_OFF();
        		sleep();
        }
        }
        
        

        So what works well with a power supply does not appear to work well in practice with the LTC3105. Things work the first time then either slowly drain the battery mysteriously or don’t drain the battery at all and sleep on a full capacitor!

        Another idea is to have the chip wake up from PGOOD and then just drain the cap until the brown out detector resets the machine when LDO collapses. This is extremely wasteful but is one way to be sure we go from awake state into sleep state based on power levels.

        I think one issue is that PGOOD goes up and then down so fast that checking its state in code after it causes an interrupt is not useful.

        OK, here it is, the WORKING code! Something happens in the TX code that just continues to drain the cap no matter what kind of interrupt scheme I use so my solution is brute force, reset the entire thing every 8 seconds, poll PGOOD, then transmit until PGOOD goes low or the 8 second reset comes along to wipe the slate clean.

        
        /*
         * Attiny2313 sleep + RFM12B + ltc3105 REV.2.c
         *
         * Created: 5/31/2019 11:21:27 AM
         * Author : FablabDigiscope
         */ 
        
        
        #define F_CPU 8000000 
        
        #include <avr/interrupt.h>
        #include <avr/sleep.h>
        #include <util/delay.h>
        
        /* RFM12B INTERFACE */
        #define SCK 7 // SPI clock
        #define SDO 6 // SPI Data output (RFM12B side)
        #define SDI 5 // SPI Data input (RFM12B side)
        #define CS 4 // SPI SS (chip select)
        #define NIRQ 2 // (PORTD)
        
        /* IO CONTROL */
        #define HI(x) PORTB |= (1<<(x))
        #define LO(x) PORTB &= ~(1<<(x))
        
        
        
        
        /* MOSFET */
        #define MOSFET 4
        #define MOSFET_OFF() PORTD &= ~(1<<MOSFET)
        #define MOSFET_ON() PORTD |= (1<<MOSFET)
        
        volatile unsigned short int flag; // variables that are changed in ISR should be global AND volatile
        
        void portInit() 
        {
        	HI(CS);
        	HI(SDI);
        	LO(SCK);
        	DDRB = (1<<CS) | (1<<SDI) | (1<<SCK);
        	DDRD |= (1<<MOSFET);
        }
        
        unsigned int writeCmd(unsigned int cmd) 
        {
        	unsigned char i;
        	unsigned int recv;
        	recv = 0;
        	LO(SCK);
        	LO(CS);
        	
        	for(i=0; i<16; i++) {
        		if(cmd&0x8000) HI(SDI); else LO(SDI);
        		HI(SCK);
        		recv<<=1;
        		if( PINB&(1<<SDO) ) {
        			recv|=0x0001;
        		}
        		LO(SCK);
        		cmd<<=1;
        	}
        	HI(CS);
        	return recv;
        }
        
        
        void rfInit() 
        {
        	writeCmd(0x80E7); //EL,EF,868band,12.0pF
        	writeCmd(0x8239); //!er,!ebb,ET,ES,EX,!eb,!ew,DC
        	writeCmd(0xA640); //frequency select
        	writeCmd(0xC647); //4.8kbps
        	writeCmd(0x94A0); //VDI,FAST,134kHz,0dBm,-103dBm
        	writeCmd(0xC2AC); //AL,!ml,DIG,DQD4
        	writeCmd(0xCA81); //FIFO8,SYNC,!ff,DR
        	writeCmd(0xCED4); //SYNC=2DD4 ,AG
        	writeCmd(0xC483); //@PWR,NO RSTRIC,!st,!fi,OE,EN
        	writeCmd(0x9850); //!mp,90kHz,MAX OUT
        	writeCmd(0xCC17); //OB1,ACOB0, LPX,Iddy,CDDIT,CBW0
        	writeCmd(0xE000); //NOT USED
        	writeCmd(0xC800); //NOT USED
        	writeCmd(0xC040); //1.66MHz,2.2V
        }
        
        void sleep()
        {
        	set_sleep_mode(SLEEP_MODE_PWR_DOWN); //select PWR DOWN, the most power savings
        	sleep_enable(); //set  SE bit
        	sei(); // enable global interrupts
        	sleep_cpu(); //actually sleep
        	sleep_disable(); //code reaches this point after interrupt
        }
        
        
        
        void rfSend(unsigned char data)
        {
        	while(PIND&(1<<NIRQ));
        	writeCmd(0xB800 + data); //TX is ready
        }
        
        
        
        void initWTD(void)
        {
        	WDTCSR |= (1<<WDE); //enable Watchdog Timer RESET MODE
        	WDTCSR |= (1<<WDP3) | (1<<WDP0); //set to 8 seconds
        	sei();//enable global interrupts
        }
        
        
        void shutdown()
        {
        		HI(CS);
        		HI(SDI);
        		HI(SCK);
        		MOSFET_OFF();	
        }
        
        // ISR(WDT_OVERFLOW_vect) //PD3 level change
        // {
        // 	if(PIND& (1<<PD3))
        // 	{
        // 	flag = flag + 1;
        // 	}
        // 	else
        // 	{
        // 	flag = 0;	
        // 	}
        // 
        // }
        
        
        
        int main(void)
        {
        	
        	 initWTD();
        				
        		while(1)
        		{
        			DDRD |= (1<<MOSFET); // get rid of this if code not working!
        			shutdown();
        			
        					if(PIND &(1<<PD3))
        					{
        					portInit();
        					MOSFET_ON();
        					volatile unsigned int i,j;
        					for(i=0;i<1000;i++)for(j=0;j<123;j++);
        					rfInit();
        					portInit();
        					writeCmd(0x0000);
        					rfSend(0xAA); // PREAMBLE
        					rfSend(0xAA);
        					rfSend(0xAA);
        					rfSend(0x2D); // SYNC
        					rfSend(0xD4);
        					for(i=0; i<16; i++)
        					{
        						rfSend(0x30+i);
        					}
        					rfSend(0xAA); // DUMMY BYTES
        					rfSend(0xAA);
        					rfSend(0xAA);
        					for(i=0; i<10000; i++) // some not very
        					for(j=0; j<123; j++); // sophisticated delay
        					}											
        					
        					else
        					{
        						shutdown();
        						sleep();
        					}
        }
        }
        
        

        Here is the code flow diagram:

        Some random things I learned about interrupts:

        -keep ISRs short and sweet, preferably just to set a flag or get out of sleep. Don’t use delay or call functions within an ISR. -level sensing is the only interrupt that still works for INT0 and INT1 in deep sleep (power-down mode) -the watchdog timer can save you from getting stuck in a piece of code. -the watchdog timer can sleep you for a maximum of 8 seconds.

        Just for fun I tested if the LTC3105 could power an h-bridge and turn a motor. While it can power a motor directly, the transistors I was using were consuming massive amounts when off (more than 100mV). I moved to using a mosfet to turn on/off the entire h-bridge but ran into trouble here. I think my best option is to make a practice h-bridge pcb circuit which uses the low VGS MOSFETs we have in the lab. These will include noise reduction and pull down resistors.

        UPDATE

        Curiously the radio works even better when the RF module is plugged-in to LDO and the microchip is plugged in to Vout…In either case the microchip draws around 2 to 3.5mA and the RF module draws between 13 and 23mA. Taking off the LED from the MOSFET pin improves performance and adding a code that would check PGOOD is still good after a minute would makes things even better I would assume.

        UPDATE

        Here is the Rev.4 board based on a mirrored sandwhich principle one slice of which is double-sided. The boards include DRV8837 motor drivers which work with low voltages (http://www.ti.com/lit/ds/symlink/drv8837.pdf). It operates on a motor power supply voltage from 0 to 11 V, and a device power supply voltage of 1.8 V to 7 V and a sleep pin. The only difficulty is the package size which will require the use of the reflow oven. The idea with the drivers is to run a pair of motors to adjust the angle of the panels. This will be a first test to see if this is feasable but I don’t doubt that 1/2F cap has enough juice to run a small DC gearmotor for less than a second.

        Here is the schematic which shows the three seperate circuits:

        And here is how I imagine everything coming together:

        Here are the Rev.5 boards, they are on double sided FR1. Cutting all the way through is not nice but the quality of the cut side is very nice. I lost a trace while cleaning but otherwise these turned out well.

        And here is the backside of the microchip board. I didn’t do a sufficient number of passes and had to go around with an exacto knife to finish it off.

        REFLOWING

        The VEML6030 datasheet explains the maximum temperatures for reflow:

        I am using the XG-Z40 solder paste: https://www.mouser.com/datasheet/2/265/tds-4860P-952714.pdf

        • Continued on the following page…

        Fab15 part I

        pHere is the video explaining the intention of the workshop:

        https://vimeo.com/336378467

        New idea of using 0ohm jumpers to isolate sub sections of the circuit. This allowed me to test the RF side independent of the LTC3105 side which turned out to be very useful.

        Took about 5 or 6 passes on the lasercutter, 20 minutes per pass as it was 100x60mm in size.

        BIG things to change:

        1.) I am seeing a short between Vin and GND of the LTC3105…which them magically is no longer a short. The resistance I measure between Vin and GND changes between around 100K, to a few hundred ohms, to a handful of ohms. I notice the same thing on other LTC3105 boards I have soldered. When I plug in the IC to a power supply it shorts. I fear I may have destroyed the IC during soldering, though I limited the soldering iron temperature to 300C and never applied it for more than a few seconds at a time. I have built this circuit about 5 times over the years and have varied the circuit layout -it has never worked-, I wonder if the entire stock of ICs has been compromised at some point.

        Rereading the datasheet, it says that an internal N-channel MOSFET connects SW and GND in certain modes while waiting for the inductor current to reach its limit. The inductor is a short to Vin so this might explain the apparent short observed between Vin and GND. The datasheet explains that this mode is reached when Vin is greater than the voltage on the driven output (Vout or Vaux), or when the driven output is less than 1.2V (typical). Essentially it would appear that this device temporarily stores energy inside the inductor, and therefore connecting Vin to GND for short moments is part of its normal operating behavior.

        Is it possible that the voltage that the multimeter is using to test conductivity is causing the IC to enter this mode?

        Why does my power supply thing there is a short circuit? If the circuit is waiting for the inductor to charge and it is not charging is this because the inductor is broken?

        Do I need to attach a load to see this thing working?

        2.) the goshdarn ICSP header is mirrored somehow 3.) replace the switch with a button (because it’s RESET silly) 4.) get some Attiny2313V packages 5.) the RFM12B is depreciated, I need to get started on the RFM69…

        SMALL things to change:

        1.) get rid of double highway exchange of wires with 0 ohm jumpers 2.) be able to isolate ground of ltc3105 (need one more zero jumper where I cut with the exacto) 3.) I should make this thing fit on the board stock size, so I don’t need to cut it down. 4.) it should be WAY smaller and possible modular and stacking 5.) there is no RF12B power LED!! 6.) There should be one function per board, with jumpers that allow for stacking. So much easier to trouble shoot.

        Working with the LTC3105

        Rereading the datasheet some suggestions I missed the first time around:

        -The most efficient Vin possible is around 2.3V with Vout at 3V, Iload at 10mA and LDO at 2.2V. So I should chose solar cells that produce this voltage at normal operating light levels. -The MAX Vin is 5V.

        -Suggested components: Low ESR output capacitor between Vout and GND Low DC resistance power inductor of 10uH with a “sufficient saturation current rating”. (Saturation current rating = The value of D.C. current when the inductance decreases to 65% of it’s nominal value.) The coil they use on their demo board is the SUMIDA CDRH3D18NP-100N which has:

        Courant CC max.: 900 mA Résistance CC max.: 205 mOhms

        D.C.R. of max 205mOhm (typical 164Ohm) and a saturation current of 0.90A at 20C.

        4.7uF or larger between LDO and GND 10uF or larger ceramic decoupling cap between Vin and GND 10uF or larger cap between Vout and GND 1uF between AUX and GND

        -Special Traces: Vin and Vout pins and decoupling cap: wide and as short as possible. GND traces should be lowest impedance path possible. SW and Vin and inductor traces should be as short and possible.

        The TX code (for Attiny2313), thank you to http://dlehard.narod.ru/quick_start.pdf :

        
        /*
         * Attiny2313 RFM12B.c
         *
         * Created: 5/16/2019 12:54:04 PM
         * Author : FablabDigiscope
         */ 
        
        #include <avr/io.h>
        
        /* RFM12B INTERFACE */
        #define SCK 7 // SPI clock
        #define SDO 6 // SPI Data output (RFM12B side)
        #define SDI 5 // SPI Data input (RFM12B side)
        #define CS 4 // SPI SS (chip select)
        #define NIRQ 2 // (PORTD)
        
        /* IO CONTROL */
        #define HI(x) PORTB |= (1<<(x))
        #define LO(x) PORTB &= ~(1<<(x))
        
        
        /* LED */
        #define LED 4
        #define LED_OFF() PORTD &= ~(1<<LED)
        #define LED_ON() PORTD |= (1<<LED)
        
        
        
        void portInit() {
        	HI(CS);
        	HI(SDI);
        	LO(SCK);
        	DDRB = (1<<CS) | (1<<SDI) | (1<<SCK);
        	DDRD = (1<<LED);
        }
        
        unsigned int writeCmd(unsigned int cmd) {
        	unsigned char i;
        	unsigned int recv;
        	recv = 0;
        	LO(SCK);
        	LO(CS);
        	
        	for(i=0; i<16; i++) {
        		if(cmd&0x8000) HI(SDI); else LO(SDI);
        		HI(SCK);
        		recv<<=1;
        		if( PINB&(1<<SDO) ) {
        			recv|=0x0001;
        		}
        		LO(SCK);
        		cmd<<=1;
        	}
        	HI(CS);
        	return recv;
        }
        
        
        void rfInit() {
        	writeCmd(0x80E7); //EL,EF,868band,12.0pF
        	writeCmd(0x8239); //!er,!ebb,ET,ES,EX,!eb,!ew,DC
        	writeCmd(0xA640); //frequency select
        	writeCmd(0xC647); //4.8kbps
        	writeCmd(0x94A0); //VDI,FAST,134kHz,0dBm,-103dBm
        	writeCmd(0xC2AC); //AL,!ml,DIG,DQD4
        	writeCmd(0xCA81); //FIFO8,SYNC,!ff,DR
        	writeCmd(0xCED4); //SYNC=2DD4 ,AG
        	writeCmd(0xC483); //@PWR,NO RSTRIC,!st,!fi,OE,EN
        	writeCmd(0x9850); //!mp,90kHz,MAX OUT
        	writeCmd(0xCC17); //OB1,ACOB0, LPX,Iddy,CDDIT,CBW0
        	writeCmd(0xE000); //NOT USED
        	writeCmd(0xC800); //NOT USED
        	writeCmd(0xC040); //1.66MHz,2.2V
        }
        
        
        void rfSend(unsigned char data){
        	while(PIND&(1<<NIRQ));
        	writeCmd(0xB800 + data);
        }
        
        
        
        int main() {
        	volatile unsigned int i,j;
        	asm("cli");
        	for(i=0;i<1000;i++)for(j=0;j<123;j++);
        	portInit();
        	rfInit();
        	while(1){
        		LED_ON();
        		writeCmd(0x0000);
        		rfSend(0xAA); // PREAMBLE
        		rfSend(0xAA);
        		rfSend(0xAA);
        		rfSend(0x2D); // SYNC
        		rfSend(0xD4);
        		for(i=0; i<16; i++) {
        			rfSend(0x30);
        		}
        		rfSend(0xAA); // DUMMY BYTES
        		rfSend(0xAA);
        		rfSend(0xAA);
        		LED_OFF();
        		for(i=0; i<10000; i++) // some not very
        		for(j=0; j<123; j++); // sophisticated delay
        	}
        }
        
        

        Continued in Fablab15 part deux…

        Electronics 101

        A series of boards and graphic aids to learn about electronics. The main idea here is that each kit teaching some basic principle of electronics, and that it makes the theory portion of electronics more hands on and fun. The kits themselves should be beautiful, so that the students want to keep them, and therefore continue to have access to the learning they represent. The packaging should be minimal and the idea is to make everything in house. Because we won’t have massive numbers of students we can do small production runs to prepare the kits on a just in time basis using the lasercutter and printer. We can also get the students to help prepare their own kits, by finding the parts and even preparing the boards and packaging.

        A secondary goal of this project is to explore the aesthetic potential of our in house making processes.

        Here is the overall progression of the course:

        The idea is that students gradually move towards microchips but first understand some of their subsystems and the things that they cannot do (amplify sound, for instance). Further to the end of the course compound projects are introduced which combine several different skills put to a specific application.

        They go hand in hand with this basic electronics course:

         

        I have some new slides which use the analogy of the big city and the beach, and the different kinds of roads that exist between them:

         

        Engraving this board takes 1 hour, and will require several passes:

        The other option is to convert the board into a series of vectors.

        Step 1: Export the board in png image format from eagle. (Unfortunately, ground planes make things more complicated for this process and so I work without them).

        Step 2: In photoshop use the magic wand tool to select the outlines of the board.

        Step 3: Path> Make Work Path transforms the selectio into a series of vector lines. Hide your original image.

        Step 4: Export the vector lines in .SVG file format. I use illustrator to examine the vectors and check for errors.

        Step 5: Open the .SVG file and resize it (Corel Draw will have changed the size) to the correct size. Convert the line weights to “hairline”.

        This produces a different effect.

        Unfortunately it doesn’t cut after 5 passes with 16/100/1 in Vector mode!

        Here are the inserts for the kits:

        Here are the bags for the kits:

        I like the idea of using the packaging as a place where information can be stored.

        Using Acetone and a printed sheet of A4 I transfered the ink to the card, of course everything has to be printed mirrored!

        I prefer this way of laying out information than a tiny book with staples, it seems more practical.

        Here is the instruction manual for the 8-bit register kit:

        Here is the instruction manual for the logic gates kit:

        Here are the other instructions:

        And here is a C primer which I’m also working on:

        Some ideas about how to learn to code C:

        -online code bebuggers

        -modular code with unit testing

        -println() + LEDs

        -AVR Studio breakpoints

         

        Fab 15 part IV

         
        
        // RFM69HCW Example Sketch
        // Send serial input characters from one RFM69 node to another
        // Based on RFM69 library sample code by Felix Rusu
        // http://LowPowerLab.com/contact
        // Modified for RFM69HCW by Mike Grusin, 4/16
        
        // This sketch will show you the basics of using an
        // RFM69HCW radio module. SparkFun's part numbers are:
        // 915MHz: https://www.sparkfun.com/products/12775
        // 434MHz: https://www.sparkfun.com/products/12823
        
        // See the hook-up guide for wiring instructions:
        // https://learn.sparkfun.com/tutorials/rfm69hcw-hookup-guide
        
        // Uses the RFM69 library by Felix Rusu, LowPowerLab.com
        // Original library: https://www.github.com/lowpowerlab/rfm69
        // SparkFun repository: https://github.com/sparkfun/RFM69HCW_Breakout
        
        // Include the RFM69 and SPI libraries:
        #include <Wire.h>
        #include <LSM303.h>
        #include <avr/wdt.h>
        #include <RFM69.h>
        #include <SPI.h>
        #include <avr/sleep.h>
        
        LSM303 compass;
        
        // Addresses for this node. CHANGE THESE FOR EACH NODE!
        
        #define NETWORKID     0   // Must be the same for all nodes
        #define MYNODEID      2   // My node ID
        #define TONODEID      1   // Destination node ID
        
        // RFM69 frequency, uncomment the frequency of your module:
        
        //#define FREQUENCY   RF69_433MHZ
        #define FREQUENCY     RF69_915MHZ
        
        // AES encryption (or not):
        
        #define ENCRYPTKEY    "TOPSECRETPASSWRD" // Use the same 16-byte key on all nodes
        
        // Use ACKnowledge when sending messages (or not):
        
        #define USEACK        true // Request ACKs or not
        
        // Packet sent/received indicator LED (optional):
        
        #define LED          14 // LED positive pin
        
        #define PGOOD         4 // PGOOD
        
        #define VTEST         8 // VTEST MOSFET
        
        
        // Create a library object for our RFM69HCW module:
        
        
        int level = 0;
        
        RFM69 radio;
        
        void setup()
        {
          // Open a serial port so we can send keystrokes to the module:
          Wire.begin();
          compass.init();
          compass.enableDefault();
            compass.m_min = (LSM303::vector<int16_t>){-32767, -32767, -32767};
          compass.m_max = (LSM303::vector<int16_t>){+32767, +32767, +32767};
          
           pinMode(LED,OUTPUT);
          digitalWrite(LED,LOW);
            pinMode(7,OUTPUT); //mosfet
            pinMode(PGOOD, INPUT); // PGOOD
          pinMode(A1, INPUT);
           pinMode(LED,OUTPUT);
          pinMode(VTEST,OUTPUT);
          
        wdt_enable(WDTO_8S);
        
          
        
        
        }
        
        void loop()
        {
          digitalWrite(VTEST, HIGH); //turn on MOSFET to read voltage
        digitalWrite(7, LOW); // turn off MOSFET
        level = digitalRead(PGOOD);
        if(level == HIGH)
        
        {
          
          digitalWrite(VTEST, HIGH); //turn on MOSFET to read voltage
          digitalWrite(7, HIGH); // turn on MOSFET
          radio.initialize(FREQUENCY, MYNODEID, NETWORKID);
          radio.setHighPower(); // Always use this for RFM69HCW
        
          // Turn on encryption if desired:
        
            radio.encrypt(ENCRYPTKEY);
        
           compass.read();
           double H = compass.heading();
        
          char Pstr[10];
          char Hstr[10];
          char buffer[50];
          double P = (analogRead(A1)*0.006451); // analog read out of 1023 (based on .975V as highest value), multiply this ratio by 3.33333 to get the actual.
          
          dtostrf(H, 3,3, Hstr);
          dtostrf(P, 3,3, Pstr);
          
          static int sendlength = strlen(buffer);
        
        
        sprintf(buffer, "BAT: Power:%sV Heading:%s", Pstr, Hstr);
        
           
              radio.sendWithRetry(TONODEID, buffer, sendlength);
              Blink(LED,1000);
             level = LOW;
             sleep();
        }
          else
          {
           
          digitalWrite(7, LOW); // turn off MOSFET
          sleep();
          }
        
        }
        void Blink(byte PIN, int DELAY_MS)
        // Blink an LED for a given number of ms
        {
          digitalWrite(PIN,HIGH);
          delay(DELAY_MS);
          digitalWrite(PIN,LOW);
        }
        
        void sleep(void)
        {
          set_sleep_mode(SLEEP_MODE_PWR_DOWN); //select PWR DOWN, the most power savings
          sleep_enable(); //set  SE bit
          sei(); // enable global interrupts
          sleep_cpu(); //actually sleep
          sleep_disable(); //code reaches this point after interrupt
        }
        
        
        
        

        Errata:

        -I used SDA and SCL for motor driving but they should have been kept for I2C sensor reading.

        -can’t use XTAL in arduino software environment (PB6 and PB7)

        -I should have picked up a LUX sensor which already has readily available libraries AND is in stock, such as the VEML7700

        -Because soldering tiny components is difficult and not fun I am considering eliminating the compass from the project. If the user sets the device to point north on start up it can track itself knowing the rotation of the motors.

        And this code uses both the compass and the lux sensor, sharing the same lines.

        This is the Lux sensor I’m using:

        https://www.adafruit.com/product/1980

        And I’m getting the temperature (and received transmission power reading) from inside the RF69.

        I’m also reading two photodiodes and turning two motors. The photodiodes are set up like so: https://electronics.stackexchange.com/questions/73732/how-to-use-sfh235-ir-photodiode-correctly

        
        
        
        // RFM69HCW Example Sketch
        // Send serial input characters from one RFM69 node to another
        // Based on RFM69 library sample code by Felix Rusu
        // http://LowPowerLab.com/contact
        // Modified for RFM69HCW by Mike Grusin, 4/16
        
        // This sketch will show you the basics of using an
        // RFM69HCW radio module. SparkFun's part numbers are:
        // 915MHz: https://www.sparkfun.com/products/12775
        // 434MHz: https://www.sparkfun.com/products/12823
        
        // See the hook-up guide for wiring instructions:
        // https://learn.sparkfun.com/tutorials/rfm69hcw-hookup-guide
        
        // Uses the RFM69 library by Felix Rusu, LowPowerLab.com
        // Original library: https://www.github.com/lowpowerlab/rfm69
        // SparkFun repository: https://github.com/sparkfun/RFM69HCW_Breakout
        
        // Include the RFM69 and SPI libraries:
        #include "Adafruit_TCS34725.h"
        #include <Wire.h>
        #include <LSM303.h>
        #include <avr/wdt.h>
        #include <RFM69.h>
        #include <SPI.h>
        #include <avr/sleep.h>
        
        LSM303 compass;
        
        // Addresses for this node. CHANGE THESE FOR EACH NODE!
        
        #define NETWORKID     0   // Must be the same for all nodes
        #define MYNODEID      2   // My node ID
        #define TONODEID      1   // Destination node ID
        
        // RFM69 frequency, uncomment the frequency of your module:
        
        //#define FREQUENCY   RF69_433MHZ
        #define FREQUENCY     RF69_915MHZ
        
        // AES encryption (or not):
        
        #define ENCRYPTKEY    "TOPSECRETPASSWRD" // Use the same 16-byte key on all nodes
        
        // Use ACKnowledge when sending messages (or not):
        
        #define USEACK        true // Request ACKs or not
        
        // Packet sent/received indicator LED (optional):
        
        #define LED          14 // LED positive pin
        
        #define PGOOD         4 // PGOOD
        
        #define VTEST         8 // VTEST MOSFET
        
        
        // Create a library object for our RFM69HCW module:
        
        
        int level = 0;
        
        RFM69 radio;
        
        Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_700MS, TCS34725_GAIN_1X);
        
        void setup()
        {
          // Open a serial port so we can send keystrokes to the module:
          Wire.begin();
          compass.init();
          compass.enableDefault();
            compass.m_min = (LSM303::vector<int16_t>){-32767, -32767, -32767};
          compass.m_max = (LSM303::vector<int16_t>){+32767, +32767, +32767};
          
           pinMode(LED,OUTPUT);
          digitalWrite(LED,LOW);
            pinMode(7,OUTPUT); //mosfet
            pinMode(PGOOD, INPUT); // PGOOD
          pinMode(A1, INPUT);
           pinMode(LED,OUTPUT);
          pinMode(VTEST,OUTPUT);
        
          pinMode(0, OUTPUT); //motors
          pinMode(1, OUTPUT);
          pinMode(5, OUTPUT);
          pinMode(6, OUTPUT);
        
          pinMode(A2, INPUT);
          pinMode(A3, INPUT);
          
        wdt_enable(WDTO_8S);
        
           tcs.begin();
            
          
        
        
        }
        
        void loop()
        {
          uint16_t r, g, b, c, colorTemp, L;
          
          digitalWrite(VTEST, HIGH); //turn on MOSFET to read voltage
        digitalWrite(7, LOW); // turn off MOSFET
        level = digitalRead(PGOOD);
        if(level == HIGH)
        
        {
          
          digitalWrite(VTEST, HIGH); //turn on MOSFET to read voltage
          digitalWrite(7, HIGH); // turn on MOSFET
          radio.initialize(FREQUENCY, MYNODEID, NETWORKID);
          radio.setHighPower(); // Always use this for RFM69HCW
        
          // Turn on encryption if desired:
        
            radio.encrypt(ENCRYPTKEY);
        
           compass.read();
           double H = compass.heading();
        
           double T = radio.readTemperature();
        
        
         char Leftstr[10];
         char Rightstr[10];
        
         char Tstr[10];
          char Pstr[10];
          char Hstr[10];
           char Lstr[10];
          char buffer[50];
        
          
          double P = (analogRead(A1)*0.006451); // analog read out of 1023 (based on .975V as highest value), multiply this ratio by 3.33333 to get the actual.
        
        
          tcs.getRawData(&r, &g, &b, &c);
          // colorTemp = tcs.calculateColorTemperature(r, g, b);
          colorTemp = tcs.calculateColorTemperature_dn40(r, g, b, c);
          L = tcs.calculateLux(r, g, b);
        
          double left = analogRead(A3);
          double right = analogRead(A2);
        
         dtostrf(left, 3,3, Leftstr);
         dtostrf(right, 3,3, Rightstr);
           
          dtostrf(T, 3,3, Tstr);
          dtostrf(H, 3,3, Hstr);
          dtostrf(P, 3,3, Pstr);
          dtostrf(L, 3,3, Lstr);
           
          static int sendlength = strlen(buffer);
        
        
        
        
        sprintf(buffer, "B:%sV H:%s Lx:%s T:%s L:%s R:%s", Pstr, Hstr, Lstr, Tstr, Leftstr, Rightstr);
        
           
              radio.sendWithRetry(TONODEID, buffer, sendlength);
        
        
          if(left>right)
          {
            digitalWrite(0, HIGH);
            digitalWrite(1, LOW);
            digitalWrite(5, HIGH);
            digitalWrite(6, LOW);
            delay(1000);
          }
          else
          {
            digitalWrite(0, LOW);
            digitalWrite(1, HIGH);
            digitalWrite(5, LOW);
            digitalWrite(6, HIGH);
            delay(1000);
          }
              
              Blink(LED,1000);
             level = LOW;
             sleep();
        }
          else
          {
           
          digitalWrite(7, LOW); // turn off MOSFET
          sleep();
          }
        
        }
        void Blink(byte PIN, int DELAY_MS)
        // Blink an LED for a given number of ms
        {
          digitalWrite(PIN,HIGH);
          delay(DELAY_MS);
          digitalWrite(PIN,LOW);
        }
        
        void sleep(void)
        {
          set_sleep_mode(SLEEP_MODE_PWR_DOWN); //select PWR DOWN, the most power savings
          sleep_enable(); //set  SE bit
          sei(); // enable global interrupts
          sleep_cpu(); //actually sleep
          sleep_disable(); //code reaches this point after interrupt
        }
        
        
        
        

        I am making the BOM list now for the conference. Here is the list of components so far (all in 1206 and hand-solderable packages):

        Solar Cell SCC3733 3.3V FTDI Cable MOMENTARY-SWITCH-SPST-SMD-6.2MM-TALL Atmega328P TQFP RFM69HCW Wireless Transceiver – 915MHz solid core wire for antenna Single + double row detachable male headers jumpers of various types (male to female, female to female, male to male) DRV8833 Motor Driver HTSSOP LTC3105 MSOP VEML7700 Lux Sensor Short female double row headers RED LED 1206 GREEN LED 1206 N-CHANNEL POWER MOSFET 2.7V 1F Supercapacitor 647-JUWT1105MCD 10uH Inductor 994-MSS5131-103MLC Photodiode BPW34 Micro Gearmotor – 175 RPM (6-12V) Atmel ICE programmer Gear Motor 23 – 1:192 Offset Shaft GM23 Gear Motor 24 – 1:192 90 Degree Shaft GM24

        0.1uF 33pF 10uF 1uF 4.7uF 500K 2.2K 1M 240K 150K 499K 1M 20K 0K 10K 500K 2.2K 1M 240K 150K 499K 1M 20K 0K

        And here is a 3D print of how gearmotors could be used to make a two axis panel tilt set up.

        And here is the newest version of the board:

        Errata: SDA and SCL switched on LTC3105 board. Also, the lux sensor I bought VEML6030 doesn’t work. -break out PGOOD to a pin for testing purposes

        Other idea for a minimal version of this circuit with attiny84.

        Same thing with Atmega328p (getting things to work software side with Attiny + RF69 is tough…)

        I’m calling it the wasp (a 10F cap has some sting!). I like that it fits on one board, this solves the most challenging aspect of the last design – connecting a bunch of pins between two boards and dealing with the cubersome thickness that it has in the end. It has less functionality but it has the essential stuff and maybe the rest was not necessary in the end. It’s ready to be embed in resin and strapped to a tree outdoors!

        Here is the mould ready to go for resin test:

        I’m using this resin: https://www.boutique-resine-epoxy.fr/resine-biosourcee/352-resine-epoxy-sr-greenpoxy-56-durcisseur-sd-surf-clear.html

        SR greenPoxy 56 SD GreenPox 505 V2 (catalizer)

        The ratio is 1:2, thanks to Remy Ducros for his documentation: http://fabacademy.org/2019/labs/digiscope/students/remy-ducros/assignement/week18/

        My plan is to wrap the capacitor in foam so that it can expand if necessary and to keep all the pins accessible (outside the resin) then to put it outside for a while and see if it still works later!


        Had an issue where the device needed to be restarted everytime the microchip lost power (overnight for instance). Came up with a fix, here is the new code:

        
        
        // RFM69HCW Example Sketch
        // Send serial input characters from one RFM69 node to another
        // Based on RFM69 library sample code by Felix Rusu
        // http://LowPowerLab.com/contact
        // Modified for RFM69HCW by Mike Grusin, 4/16
        
        // This sketch will show you the basics of using an
        // RFM69HCW radio module. SparkFun's part numbers are:
        // 915MHz: https://www.sparkfun.com/products/12775
        // 434MHz: https://www.sparkfun.com/products/12823
        
        // See the hook-up guide for wiring instructions:
        // https://learn.sparkfun.com/tutorials/rfm69hcw-hookup-guide
        
        // Uses the RFM69 library by Felix Rusu, LowPowerLab.com
        // Original library: https://www.github.com/lowpowerlab/rfm69
        // SparkFun repository: https://github.com/sparkfun/RFM69HCW_Breakout
        
        // Include the RFM69 and SPI libraries:
        #include <SPI.h>
        #include <avr/wdt.h>
        #include <RFM69.h>
        #include <avr/sleep.h>
        
        
        
        // Addresses for this node. CHANGE THESE FOR EACH NODE!
        
        #define NETWORKID     0   // Must be the same for all nodes
        #define MYNODEID      2   // My node ID
        #define TONODEID      1   // Destination node ID
        
        // RFM69 frequency, uncomment the frequency of your module:
        
        //#define FREQUENCY   RF69_433MHZ
        #define FREQUENCY     RF69_915MHZ
        
        // AES encryption (or not):
        
        #define ENCRYPTKEY    "TOPSECRETPASSWRD" // Use the same 16-byte key on all nodes
        
        // Use ACKnowledge when sending messages (or not):
        
        #define USEACK        true // Request ACKs or not
        
        // Packet sent/received indicator LED (optional):
        
        #define LED          A3 // LED positive pin
        
        #define PGOOD         3 // PGOOD
        
        
        #define MOSFET      9//
        
        
        // Create a library object for our RFM69HCW module:
        
        
        int level;
        
        RFM69 radio;
        
        
        void setup()
        {
          // Open a serial port so we can send keystrokes to the module:
        
            
            pinMode(LED,OUTPUT);
            digitalWrite(LED,LOW);
            pinMode(MOSFET,OUTPUT); //mosfet    
            pinMode(PGOOD, INPUT); // PGOOD
        
        }
        
        void loop()
        {
        
        wdt_enable(WDTO_8S);
         
        level = digitalRead(PGOOD);
        
        if(level == HIGH)
        
        {
        
         char Tstr[10];
        
          
          digitalWrite(MOSFET, HIGH); // turn on MOSFET
          delay(100);
          radio.initialize(FREQUENCY, MYNODEID, NETWORKID);
          radio.setHighPower(); // Always use this for RFM69HCW
        
          // Turn on encryption if desired:
        
            radio.encrypt(ENCRYPTKEY);
        
           double T = radio.readTemperature();
        
        
          char buffer[50];
        
          
          dtostrf(T, 3,3, Tstr);
        
           
          static int sendlength = strlen(buffer);
        
        
        
        
        sprintf(buffer, " T:%s", Tstr);
        
           
              radio.sendWithRetry(TONODEID, buffer, sendlength);
        
        
              Blink(LED,100);
        
        }
        
        else
        {
        sleep();
        }
        
        }
        
        
        
        void Blink(byte PIN, int DELAY_MS)
        // Blink an LED for a given number of ms
        {
          digitalWrite(PIN,HIGH);
          delay(DELAY_MS);
          digitalWrite(PIN,LOW);
        }
        
        void sleep(void)
        {
          digitalWrite(MOSFET, LOW); // turn on MOSFET
          set_sleep_mode(SLEEP_MODE_PWR_DOWN); //select PWR DOWN, the most power savings
          sleep_enable(); //set  SE bit
          sei(); // enable global interrupts
          sleep_cpu(); //actually sleep
          sleep_disable(); //code reaches this point after interrupt
        }
        
        
        

        I have a new scheme to maintain the flatness of the circuit board but “pop-up” from it. I like the idiosyncratic form it gives:

        Here are some of the slides from the workshop itself:

        For the TX module A-side:

        And for the RX module:

        The 1381 Solar Engine

         

         

        This circuit (found here: http://solarbotics.net/library/circuits/se_t1_1381.html) works with a MCP112 voltage supervisor to save up solar energy until it is significantly large to make turn a motor. With a small solar panel charging a large capacitor, it is possible to make something move even on an overcast day!

        The circuit works by charging the capacitor with a solar panel until it reaches the MCP112’s tripping point (in this circuit I’m using the 2.7V tripping point flavor but they range from 1.8V to 4.38V) at which point the MCP112 activates the PN2222 transistor which powers the motor. The PN2907 turns on at this point and latches the PN2222 in the on position until the capacitor voltage level runs below the PN2907 turn on threshold (of about 0.6V). Without the PN2222 the MCP112 would deactivate at 0.3V below it’s trigger threshold (2.4V in this case).

        I’m using a 37x33mm Monocrystalline Solar Cell from Solarbotics.com (https://solarbotics.com/product/scc3733/) which produces 6.7V and 20mA. It is at maximum efficiency of 106.335mW with a load of 300 ohms.

        By changing the size of the capacitor from 2200uF to 4700uF you can change the amount of charge that will be passed on to the motor.

        On an overcast day the little motor is activated every 5 seconds or so for about half a second, not bad for a tiny solar panel!

        Here’s the trace image showing a comparison of the same circuit with different capacitor sizes and voltage trigger flavours:

        Neato!

        I am interested in using this circuit to power something other than a motor. However, simply replacing the motor with a given circuit (like a microchip) does not work.

        Replacing the motor with a 220 ohm resistor appears to allow the circuit to cause an LED to blink and to charge and discharge, however.

        The Complete Solar Power Smart Head Rev.3 by Wilf Rigter

        Here is a photo of the full assembly in action!

        I found the circuit at this website: http://solarbotics.net/library/circuits/bot_head_pshead.html

        I am building Wilf Rigter’s 74HC240 circuit but replacing the 74HC240 with two 74HC540s (and replacing the 1381 with a MCP112) because I couldn’t source a 74HC240 from my local electronics store. The two logic chips are inverting octal buffers but the 74HC540 has one enable for all eight inverters while the 74HC740 has two for two sets (A and B in the circuit above). To make the translation, first I connected the two AND input enables of the 74HC540 to behave the same way as the single enable of the 74HC240, then I remapped the pins from one 74HC240 to two 74HC540s.

        Here is the comparison of the 74HC240 with the 74HC540 and my remapped circuit:

        Once the voltage trigger is reached (2.9V in my case) the MCP112 sends a high signal to inverter 1 which turns on the Standby Flasher circuit.

        Meanwhile the High/Low oscillator circuit which was oscillating is pushed into either the clockwise or anticlockwise motor turn configuration depending on the values of the two light dependent resistors (LDRs). Either the low voltage that exists to the bottom will win out or the high voltage that exists at the top.

        This configures the motor drivers to turn on the motor in one direction or the other.

        I have “air wired” this circuit after redrawing the circuit here:

        Here is the play by play of the assembly:

        Here is how I glued the solar head to the post:

        And here is another circuit which claims to do the same thing but which I failed to make work:

         

         

        *********

        Making a version 2 following this schematic in SMD version. The goal is to replace the expensive and tech overkill 2DOF sunhead using LTC3105 and atmega developed on this page (https://www.marrs.io/fab-15-part-vii/). This comes with one caveat, I can’t use the cool stepper motor linear actuators I am collecting from CD/DVD drives… I have opened up 15 of these drives and only one came with a DC motor powered laser raft, and it was an ugly plastic mechanism. The other option is for me to design such a linear rail from scratch 3D printing the gears but then I lose the tech recyling angle and add considerable engineering challenge. The benefit is that it costs very little and is a simpler more minimal analog electronics solution. It could work for this design:

         

        Here is the SPSH 2DOF circuit:

        The different between photodiodes and phototransistors: https://circuitglobe.com/difference-between-photodiode-and-phototransistor.html#:~:text=One%20of%20the%20major%20difference,conversion%20of%20light%20into%20current.

        Photodiodes appear to be rare, at least in the lab. Could something like the following turn a phototransistor into a photodiode? By changing the value of R1 you could change the degree of current change?

        Phototransistor Output Voltage Circuit connects the phototransistor and 2 k-ohm resistor in series

        I could also just buy some photodiodes from Hackspark in Paris, though I would love to make this entirely SMD.

        The challenge I want to set myself is to make an SMD version of this circuit. I have 74*540s in SMD, and I plan to build on the business card version of the 1381 solarengine which I’ve built here (https://www.marrs.io/solar-business-cards/). Rewiring the 540 based on the 240s in the original schematics but integrating the SMD solarengine is a possible stumbling block. I am working with the understanding that the solarengine essentially enables the buffer oscillators (instead of powering a motor ), by connecting the enables to low.

         

        I’m not sure how easy the wiring is going to be on a single-sided board however…

        It turned out to be not too hard, the main issue was not having 10M and 6.2M resistors and having to make them from 1M resistors…As a result the board is larger than I would like (50mmx65mm approx.).

        Here’s the completed board:

        It’s not currently working, I added a higher power solar cell and a higher capacity cap. The cap is charging and the enable is high (oh wait it should be LOW! This must be the purpose of those extra resistors in the solar engine portion of the circuit. However it still doesn’t work when I pull the enable low artificially…) for the octal buffer but no power is arriving at the motors. So I’m thinking the issue is with the inverting oscillator parts of the circuit.

        I removed the SMD solar engine and copied the schematic closely, but still no luck. I’m guessing there is an error or short on my board. I am going to try to breadboard this circuit to see if I have more luck.

        It may be simpler to make this circuit twice (from https://www.fetchmodus.org/projects/beam/photopopper/) as I already have everything I need. The circuit is straight forward and there is no analog business:

        I just confirmed, the circuit can power the DC gearmotors I’m using well, even with the 1500uF cap. The package of MOSFETs I have comes in doubles so at least that is efficient. It doesn’t look great but my focus right now is the finalize the circuit so I can focus on the design of the sun seeker.

         

        Here is the schematic:

        After making this didn’t realize that it is for FOUR motors, i.e. there are not two motors which change direction…I tried wiring up the same motor for both and it doesn’t work of course because it’s not an H-bridge.

        Here is another option, it would involve 2X this bare bones sun seeker, replacing the 240 with the 540 and the 1381 solar engine with the SMD version I am working with. From http://grant.solarbotics.net/Circuits.htm :

        Ok this circuit is dead easy and works reliably so far. It has very few parts also! (Something I didn’t notice last test – it requires at least 4V to operate with these DC gearmotors…I also just tested and LEDs appear to be able to replace photodiodes no problem on the power supply powered breadboard version!)

        Here’s the SMD version ready to cut and prototype:

        Unfortunately I need probably a larger capacitor and or more solar panels to make this setup work. With the 4700uF and the 6V solar cell everything works great, with either smaller caps or smaller cells there is some movement but very little. In his circuit he uses a 3.4V solar panel and a 3300uF cap so it may be possible to reduce these. 

        I’ve come to the realization that the SMD version of the miller engine I am working with doesn’t seem to vibe well with the head seeking circuits. I have been behaving like they are interchangeable but they may not be.

        I went back to the breadboard version and included his miller engine. Everything works if you use a super light dc gearmotor (in white) but the gearmotors I am using aren’t happy with the voltage it would appear (so I need a higher 1381 flavor – like G instead of E). So I think this explains why the PCB I made didn’t work. (I tried unplugging the 1381 trigger pin and triggering the 2222 myself once the voltage maxed at 6V and alternating between putting each photodiode in darkness and it gives it a good spin. So I think it is indeed related to the top voltage – motor combination.)

        *EDIT*

        I have since tried adding a voltage divider to the sensor pin of the 1381 to effectively change the trigger voltage. With this technique it’s possible to make the circuit work with the small metal gearmotors if the voltage trigger is set around 4V+. This means that I could make a simple sunseeking circuit using 2x barebones and metal gear motors + prints! 

        I’m testing if this could work with modified Servo motors at the moment as well but so far no luck. 

        As for the issue with running those nice steppers with lead screws, it looks like it’s possible to buy DC gearmotors with leadscrews:

        Micro Metal DC Geared Motor with Lead Screw (6V 150RPM M4*55) - KD Robot Kit

        In parallel I am sketching some other ideas:

        This is a double stepper version of a previous prototype. And here are some sketches of mechanisms I’d like to try out:

        Attempt to convert a DC gear motor into linear actuator not starting smoothly…

        This works nicely – except! – the black LED-style photodiode packages work best and the circuit only works with super tiny DC gear motors…This despite increasing the solar panels from 3V to 6V and it having a capacitance of almost 100,000uF. I don’t know what part of the circuit controls the trigger but this could be something to fiddle with potentially? (See documentation here: http://wilf.solarbotics.net/PSH/heads101.html ). It works super well with those inexpensive 2V garden lamp solar panels!!

        Covering one photodiode at a time changes the direction. It’s quite efficient. 

        There is another BEAM related circuit to possibly check out…to drive stepper motors! From http://www.redrok.com/beamcircuits.htm

        There is also this from http://solarbotics.net/library/circuits/driver_stepper_beamstep.html :

        I would need to make this twice however, in order to have 2DOF and therefore have two solarengines which is not ideal. 

        Here is the almost finished uchip version of the circuit with 10 solar cells:

        Quick look at cost of the entire thing: 

        Micro DC metal gearmotors – $14-17 !! (cf with Micro Stepper gearmotor – $5, solarbotics mini motors from $4.50-$9.25, and FREE found motors)

        McMaster Carr sprockets – 4$/piece, roller chain $20/foot !

        Fancy Solar Cells – $2 x 10 = $20 !! (cf with Solarbotics  22×24 $4.50, 22×33 $6.50, 33×37 – $8, and CHEAP lawn latern panels)

        LTC3105 – $5.70 ! (but $3.25 if buy >25) 

        Supercap – $2-$4

        DRV8833 – $1.25

        Clearly recycling old motors and hardware is cost effective, as is finding a cheaper solar panel (taking one from a lawn latern if it’s powerful enough). Avoiding the need for the LTC saves a bit too but not as much as I originally thought.  At bulk the electronics is negligeable and if the parts are all recycled it becomes relatively inexpensive. That said, the cheapest by far is found hardware and solar panel plus a BEAM style 2DOF sun seeker that would come in under $10 easily (especially because the circuit works super well with the garden lamp solar panels at 2V). 

        ****

        I haven’t got the stepper drivers working yet on the board. The 3D print had to be made out of a different material – so the offsets were not perfect. Some parts didn’t survive the printing and assembly as they were too fine also. I also didn’t realize that I have another identical linear actuator but not an identical threaded sliding block.

        The two base options side by side:

        I am starting to think this project is a little too ambitious! Perhaps a single axis machine, or something with a simple circuit.

        ****

        I tried the SPSH 2DOF again in SMD:

        And here’s the circuit for testing:

        USART

         

        Elliot Williams’ Make: AVR Programming was helpful for this code.

        Don’t forget to plug Rx to Tx and Tx to Rx! This is the only protocol I know of where this reversal is important.

        I am using the Attiny2313 because it is, as far as I know, the only chip in the Attiny family that supports USART in hardware.

        To view your Serial output in AVR Studio,

        1. Click Tools > Data Visualizer
        2. Under Configuration > Modules, click External Connection.
        3. Double click Serial Port
        4. Configure your port. e.g. Set the Baud Rate to 9600
        5. Select your COM port
        6. Click Connect.
        7. Enter in ‘j’ to see if the LED on pin PA0 blinks

        A reminder, this will loop until a bit is set:

        while (!(BIT & (1<<BIT)));  
        
        

        Here is how to hook up the USART to your Attiny2313. Your chip should be powered by a 5V adapter. I have a status LED on PA0.

        /*
         * ATTINY 2313 USART.c
         *
         * Created: 2/7/2019 6:04:16 PM
         * Author : FablabDigiscope
        
        USB - FTDI 5V:
        Rx to Tx
        Tx to Rx
        Gnd to Gnd
        
        (NO LED ON chip's RX, but the one on TX doesn't seem to interfere)
        
        To view your Serial output in AVR Studio, 
        1. Click Tools > Data Visualizer
        2. Under Configuration>Modules, click External Connection.
        3. Double click Serial Port
        4. Configure your port. e.g. Set the Baud Rate to 9600 
        5. Select your COM port
        6. Click Connect.
        
        appears to send ASCII from AVR Studio terminal
        
         */ 
        
        #include <avr/io.h>
        #include <util/delay.h>
        #include <stdlib.h>
        
        #define BAUD 9600                                   // define baud
        #define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)            // set baud rate value for UBRR. I copied the UL and don't understand it!
        
        // function to initialize UART
        void uart_init (void)
        {
        	UBRRH = (BAUDRATE>>8);                      // shift the register right by 8 bits
        	UBRRL = BAUDRATE;                           // set baud rate
        	UCSRB|= (1<<TXEN)|(1<<RXEN);                // enable receiver and transmitter
        	UCSRC|= (1<<UCSZ0)|(1<<UCSZ1);			   // 8bit data format + asynchronous mode
        }
        
        // function to send data
        void uart_transmit (unsigned char data)
        {
        	while (!(UCSRA & (1<<UDRE)));                // wait while register is free
        	UDR = data;                                   // load data in the register
        }
        
        unsigned char uart_recieve (void)
        {
        	while(!(UCSRA) & (1<<RXC));           // wait while data is being received
        	return UDR;                             // return 8-bit data
        }
        
        int main(void)
        {
        	
        DDRA = 0b00000001;
        uart_init();
        
        unsigned char a;
        
            while (1) 
            {
        		//transmit
        // 	uart_transmit('alpha');
        // 	PORTA = 0b00000001;
        // 	_delay_ms(1000);
        // 	PORTA = 0b00000000;
        // 	_delay_ms(1000);
        
        		//recieve
        	a = uart_recieve(); 
        	_delay_ms(100);
        	
        
        	if (a == 'j') //corresponds to a 'j' for instance, but not a 'c'
        		{
        			PORTA = 0b00000001;
        			_delay_ms(1000);
        			PORTA = 0b00000000;
        			_delay_ms(1000);
        			PORTA = 0b00000001;
        			_delay_ms(1000);
        			PORTA = 0b00000000;
        			_delay_ms(1000);	//some kind of local echo thing makes it repeat?? Or else the register is not being cleared
        		}
            }
        }
        
        
        
        

        You might also want to check out an ASCII character to hex/binary table.

        Here’s the scope while sending different characters from AVR Studio’s serial monitor.

        Timers

         In this code I am using an Attiny 84 to produce different frequencies for a piezo buzzer (PB2). The Attiny ADC works a little differently than the Atmega.

        Essentially, you provide a value for the OCR0A and it will count up to that value and send a high to PB2 when it does. Then it will get back to counting again. By setting the prescale first and then changing the OCR0A you can produce different frequencies.

        Elliot Williams’ Make: AVR Programming was helpful for this code!

        
        /*
         * Attiny84 timers.c
         *
         * Created: 2/1/2019 5:14:43 PM
         * Author : FablabDigiscope
         * Variable frequency with timer
         */ 
        
        #include <avr/io.h>
        #include <util/delay.h>
        
        int main(void)
        {
        	
            TCCR0A	|= (1<<COM0A0);  // toggle pin on match
        	TCCR0A	|= (1<<WGM01);  // CTC mode (once hits OCRA)
        	TCCR0B	|= (1<<CS00) | (1<<CS01); // Prescale: CPU clock div. by 64
        	DDRB = 0b00000100; // PB2 is OC0A (HOOK UP BUZZER HERE)
        	
            while (1) 
            {
        		OCR0A = 0b00111111;	
        		_delay_ms(1000);
        		OCR0A = 0b11111111;
        		_delay_ms(1000);		
        		OCR0A = 0b00000111;
        		_delay_ms(1000);
        		OCR0A = 0b00000011;
        		_delay_ms(1000);
            }
        }
        
        

        The problem I had with this project was reading the high and low bytes of the ADC.

        Serial Peripheral Interface – SPI

         

        Serial Peripheral Interface requires more wires than I2C, four instead of two, but is easier to implement. The CS line tells the slave that it has been selected, the clock line provides the timing and the MOSI (Master Out Slave In) and MISO (Master In Slave Out) lines each carry one way messages. Unlike I2C the data is usually read on the rising edge of the clock (depending on the configuration of the SPI).

        Conceptually things are easier too. With SPI the two chips communicating share the contents of their 8 bit shift registers. The moment you send something out of the SPDR, the contents of this register are replaced with the received bit. You’ll also notice that the ISP pins that you use to flash programs on to your atmel are the same ones used for SPI communication between chips. This is because programs are flahsed onto the chip using SPI! This is handy because the six pin header can serve as both the header for programming and for plugging in slaves.

        I found Elliot Williams’ Make: AVR Programming book very helpful getting this to work.

        Here is the SPI Master Code:

        
        // *
        // * Atmega168 SPI Master.c
        // *
        // * Created: 3/6/2019 10:38:11 AM
        // * Author : FablabDigiscope
        // */
        
        #define F_CPU 8000000
        
        #include <avr/io.h>
        
        
        int main(void)
        {
        
        while (1)
        {
        	//SPI_init MASTER
        	char dataout = 0b11111100; // 0xFC ...
        	char datain = 0b00000000;
        	
        	DDRB = ((1<<DDB2)|(1<<DDB5)|(1<<DDB3)); //SPI pins on port B: SS, SCK, MOSI outputs
        	//set MISO as input
        	DDRD = 0b11111111; //green LED on port D
        	DDRC = 0b11111111; //orange LED on port C
        
        	PORTB |= (1<<DDB2); //start with SS high (slave not selected). DO THIS BEFORE BEGINING ISP
        	PORTB |= (1<<DDB4); //MISO pull-up activated
        
        
        	SPCR = ((1<<SPE)|(1<<MSTR)|(1<<SPR1));  // SPI enable, Master enable, f/64. DO THIS AFTER DDR!
        
        	//SPI_Transmit
        	PORTB &= ~(1<<DDB2); // pull slave select low
        	SPDR = dataout; // byte to send
        
        
        		while(!(SPSR & (1<<SPIF))) //wait for SPIF transmit flag to be set. After this, SPDR will contain the received byte!
        		{
        		PORTD = 0b11111111;//turn on LED
        		datain = SPDR; 
        			
        			if(datain == 0b11100001) // 0xE1 byte from slave
        			{
        				PORTC = 0b11111111;
        			}
        		}
        		PORTB |= (1<<DDB2); //slave select high
        		}
        }
        
        

        And here is the Slave side code:

        
        /*
         * Atmega168 SPI Slave.c
         *
         * Created: 19/03/06 4:14:19 PM
         * Author : marrs
         */ 
        
        #define F_CPU 8000000
        
        #include <avr/io.h>
        
        
        int main(void)
        {
           
            while (1) 
            {
        			char datain =  0b11111100; //0xFC
        			char dataout = 0b10101010; //0xAA
        			
        			
        	       //SPI_init SLAVE
        		   //MOSI, SS and SCK should be inputs, MISO should be output
        
        			DDRB |= (1<<DDB4) ;//set MISO as output
        			DDRD = 0b11111111; //green LED on port D
        			DDRC = 0b11111111; //orange LED on port C
        						
        			//PORTB |= (1<<DDB2); //SS pull-up...does this make sense?
        			PORTB |= (1<<DDB3); //MOSI pull-up...does this make sense?
        			
        			SPCR |= (1<<SPE);  // SPI enable. DO THIS AFTER DDR SETTING!
        			SPDR = dataout; // in the byte trade this will be sent to the master
        	
        			while(!(SPSR & (1<<SPIF)))
        			{
        			PORTD = 0b11111111;//turn on LED
        			}
        				if (SPDR == datain)
        					{
        					PORTC = 0b11111111;//turn on orange LED
        					}
        			
        			
        		}
        		
          }
        

         

        Sleep Mode + Nocturnal Solar Circuits

         
        I have put my Attiny84 into Powerdown Mode while it waits for a supercapacitor to charge up. Once the solar charging circuit goes low, it turns on an LED until it runs out of juice. With a florescent lamp (not ideal) 10 cm away from a 33x36mm 6.7V panel it took about an hour and a half to charge up to 2.7V.I am using Wifl Rigter’s SIMD1 V0 circuit found here: http://solarbotics.net/library/circuits/se_noct_SIMD1V0.htmlHere are some other options I found from Evil Mad Scientist (https://www.evilmadscientist.com/2008/simple-solar-circuits/):solar4…and with a joule thief:solar5

        …and with a microcontroller:

        solar6

        …and just to turn on a battery at night:

        circuit diagram 2

        At the moment the solar charging circuit works well on it’s own, you can see the effect of me covering the solar panel with my hand and the low signal it produces in the scope trace below. The problem is that even in sleep mode with the BOD disactivated the Attiny84 is still drawing too much current for the small solar panel to cover. One easy solution would be a larger solar panel. I will try disactivating more subsystems in the attiny too.

        Moving over to the Attiny2313 things are looking better, with BOD enabled and set to 1.7

        The next step for this circuit is to have the microchip check the voltage of the supercapacitor while drawing as little current as possible. Jeelabs has some cool articles on this subject here: https://jeelabs.org/2013/05/16/measuring-the-battery-without-draining-it/.

        Attiny Power Saving

        -Running the clock at 1MHz -Running at a lower voltage (AttinyVs can run on 1.8V, non-Vs can go to 2.7V) -Turn off Brown Out Detection (BOD) -Turn off ADC (in the Power Reduction Register) -Turn off Timers 0 and 1 (in the Power Reduction Register)

        With these settings I have an Attiny84 drawing sub uA in sleep mode at 2.7V. This corresponds to the datasheet which states that the typical power consumption:

        – Active Mode 1 MHz, 1.8V: 230 μA 32 kHz, 1.8V: 20 μA (including oscillator)

        – Power-down Mode < 0.1 μA at 1.8V

        I have lowered the clock speed to 128KHz which necessitates changing the SPI clock speed in AVR Studio down to less than 14 of that speed. At 8KHz SPI seems to work. I am not dividing this 128KHz clock with the CLOCKDIV8 fuse.

        Here is the charging diagram:

        Attiny2313’s absolute maximum Vcc rating is 6V. To avoid nearing this threshold I tried using smaller panels but this led to false triggering of the interrupt enable! I am now experimenting with putting two super caps in series to add the maximum voltage they can hold from 2.7V to 6.4V (closer to the maximum voltage that the panel is producing of 6.7V). I am assuming that the panel will probably never be able to power the capacitors up to 6.7V while the chip is suckling and considering that it gets harder to charge capacitors the more full they are (and these are supercaps!).

        Another improvement could be having the 2313 wake up and then check the voltage of the capacitor using zero voltage power circuit developed by Jeelabs to decide whether it was a good thing it woke up or whether it might not just go back to sleep. I have not yet improved the efficiency of the Attiny2313 when it has been activated but at this point I don’t know what the application is (activate a mini robot sweeper for a few seconds, send out a series of nocturnal temperature transmissions, or something more connected to the fact that it has just gotten dark…).

        Other options to lower power consumption include:

        -using a series of rechargable NiCad batteries (1.2V each usually) in the place of capacitors,

        -connecting the microcontroller to the enable low signal and then inverting it,

        -using a more sophisticated IC like the LTC3105 solar harvester,

        -having the microcontroller seperately powered with a smaller battery and a joule theif circuit,

        Here is a nice analysis of power saving: https://sites.google.com/site/dannychouinard/Home/atmel-avr-stuff/low-power

        
        /*
         * Attiny84 Sleep.c
         *
         * Created: 4/9/2019 3:05:44 PM
         * Author : FablabDigiscope
         */ 
        
        #define F_CPU 128000 // 128 KHz: internal 
        
        #include <avr/interrupt.h>
        #include <avr/sleep.h>
        #include <util/delay.h>
        
        
        
        void sleep()
        {
        	set_sleep_mode(SLEEP_MODE_PWR_DOWN); //select PWR DOWN, the most power savings
        	sleep_enable(); //set SE bit
        	sleep_bod_disable(); // disable BOD
        	sei(); // enable global interrupts
        	sleep_cpu(); //actually sleep
        	sleep_disable(); //code reaches this point after interrupt
        }
        
        
        int main(void)
        {
        	ADCSRA &= ~(1<<ADEN); // disable ADC
        	PRR |= (1<<PRADC); //disable ADC
        	PRR |= (1<<PRTIM0); //disable timer0 
        	PRR |= (1<<PRTIM1); //disable timer1
        
        	DDRA = 0b00000110; // two LEDS output
        	GIMSK |= (1 << INT0); // enable the INT0 external interrupt on pin PB2
        	PORTA= 0b00000000;
        	sleep();
        	
        	while(1)
        {
        	PORTA= 0b00000110;
        
        }
        
        }
        
        ISR(INT0_vect) //PB2 level change
        {
        	//nothing, just here to get us past sleep line in code
        }
        
        
        

        I also free-formed this circuit and added an 8 pin DIP switch and piezo buzzer. The idea is that once the circuit has sufficient charge it reads the value of the 8bit DIP and then plays a tune on the piezo based on the ONs and OFFs. The idea is to demonstrate microchip functionality with solar power, and to have some fun too.

        /*
         * sleep piano.c
         * ATTINY 2313
         * Created: 4/19/2019 10:39:56 AM
         * Author : FablabDigiscope
         */ 
        
        
        #define F_CPU 1000000 // 1MHz: internal 
        
        #include <avr/interrupt.h>
        #include <avr/sleep.h>
        #include <util/delay.h>
        
        
        
        void sleep()
        {
        	set_sleep_mode(SLEEP_MODE_PWR_DOWN); //select PWR DOWN, the most power savings
        	sleep_enable(); //set  SE bit
        	sei(); // enable global interrupts
        	sleep_cpu(); //actually sleep
        	sleep_disable(); //code reaches this point after interrupt
        }
        
        
        int main(void)
        {
        	//initialize Timer0 for playing piezo frequencies
        	TCCR0A	|= (1<<COM0A0);  // toggle pin on match
        	TCCR0A	|= (1<<WGM01);  // CTC mode (once hits OCRA)
        	TCCR0B	|= (1<<CS00) | (1<<CS01); // Prescale: CPU 1MHz clock div. by 64 gets us in 100-800Hz audible range
        	
        	DDRA = 0b00000010; // LED output
        	DDRB = 0b00000100; // piezo output and OC0A Timer0 output
        	GIMSK |= (1 << INT0); // enable the INT0 external interrupt on pin PD2
        	PORTA= 0b00000000; // LED off
        	sleep();
        	
        	while(1) //this executes once awoken
        {
        	PORTA= 0b00000010; //LED on
        	
        	//read DIP switches in order from 1 to 10 = PD0, PB7, PB6, PB5, PB4, PB3, PD1, PB1, PB0, PD6,
        	
        	if ((PIND & (1 << PD0)) == 0) 	//if switched on (grounded) play note
        	{
        		OCR0A = 0b00011001; // going up in intervals of 25 to get to 250 over ten notes
        		_delay_ms(10);
        	}
        
        	if ((PINB & (1 << PB7)) == 0) 	//if switched on (grounded) play note
        	{
        		OCR0A = 0b00110010; //50
        		_delay_ms(10);
        	}
        	
        	if ((PINB & (1 << PB6)) == 0) 	//if switched on (grounded) play note
        	{
        		OCR0A = 0b01001011; //75
        		_delay_ms(10);
        	}
        	
        	if ((PINB & (1 << PB5)) == 0) 	//if switched on (grounded) play note
        	{
        		OCR0A = 0b01100100; //100
        		_delay_ms(10);
        	}	
        
        	if ((PINB & (1 << PB4)) == 0) 	//if switched on (grounded) play note
        	{
        		OCR0A = 0b01111101; //125
        		_delay_ms(10);
        	}
        
        	if ((PINB & (1 << PB3)) == 0) 	//if switched on (grounded) play note
        	{
        		OCR0A = 0b10010110; //150
        		_delay_ms(10);
        	}
        
        	if ((PIND & (1 << PD1)) == 0) 	//if switched on (grounded) play note
        	{
        		OCR0A = 0b10101111; //175
        		_delay_ms(10);
        	}
        	
        	if ((PINB & (1 << PB1)) == 0) 	//if switched on (grounded) play note
        	{
        		OCR0A = 0b11001000; //200
        		_delay_ms(10);
        	}
        
        	if ((PINB & (1 << PB0)) == 0) 	//if switched on (grounded) play note
        	{
        		OCR0A = 0b11100001; //225
        		_delay_ms(10);
        	}
        
        	if ((PIND & (1 << PD6)) == 0) 	//if switched on (grounded) play note
        	{
        		OCR0A = 0b11111010; //250
        		_delay_ms(10);
        	}
        }
        
        }
        
        ISR(INT0_vect) //PB2 level change
        {
        	//nothing
        }
        
        
        

        Here is the formula to calculate the lifespan of a supercapacitor once it is charged:

        
                C(Vs - Vf)
         T =	----------		
        	      	I
        
        

        T = Time in seconds C = Capacitance in Farads Vs= is initial voltage of cap in Volts Vf= is final voltage of cap in Volts I = Current draw in Amps

        In our case, it would look something like this during sleep taking these from the Attiny2313 datasheet:

        “Typical Power Consumption – Active Mode 1 MHz, 1.8V: 230 μA 32 kHz, 1.8V: 20 μA (including oscillator)

        – Power-down Mode < 0.1 μA at 1.8V”

        So, just the Attiny in active mode:

        
                1F (2.9V-1.8V)
         T =	 ----------		
        	     0.000230 A
        
        

        T = 4782 seconds = 80 minutes = 1hr 20 minutes

        What about just an LED:

        
                1F (2.9V-1.8V)
         T =	  ----------		
        	        0.02 A
        

        T = 55 seconds! Not even a minute…

        Adding a second supercapacitor gets us to 110 seconds only! Putting a second capacitor in parallel rather than series (to increase to max voltage), we could get up to 6.4V which gives us 230 seconds of LED blinking for an ideal capacitor.

        *****************************

        I wonder if I could make a business card version of this using SMD components (like an SMD dip switch, led, attiny and solar cells)! I would need more SMD 1500uF capacitors or work with the bigger super caps and incorporate them into the design. 

        Also, this new project seems to work with a similar principle: https://hackaday.io/project/85457/instructions

        The designer also sets the AVR fuses to fast 0ms wakeup which I didn’t do in this project but probably should!

        I’m curious what kind of charging circuit is being used here.

        Interrupts

         This code will blink an LED while listening for a button press. Not amazingly impressive but difficult to do with Arduino and the delay() function (while the delay macro is executing the microchip is kind of useless and can’t be listening to the state of a button). Be sure to debounce your button in hardware if you use this code (check out the Debounce page on this site).Just a note that the INT0 pin on the Attiny84 is PB2 and I have LEDS on PA1 and PA2.Also a quick primer on bit twiddling:

        
        BYTE |= (1<<i); // set a bit
        
        BYTE = (1<<i) | (1<<j); // set two bits
        
        BYTE &= ~(1<<i); // clear a bit
        
        BYTE ^= (1<<i); //toggle a bit (change its state to the opposite)
        
        

        Here is the demo code but it does not work reliably and needs to be rewritten!:

        /*
         * Attiny84 interrupt.c
         *
         * Created: 2/1/2019 12:58:37 PM
         * Author : FablabDigiscope
         */
        
        #include <avr/interrupt.h>
        #include <util/delay.h>
        
        ISR(INT0_vect)
        {
        	if (PINB & (0b00000100)) // if this works out as non-zero it will execute...
        	{
        	PORTA |= 0b00000010; // set interrupt LED on
        	PORTA &= ~0b00000100; // clear blinking LED bit
        	}
        	else
        	{
        	PORTA &= ~(0b00000010); // clear interrupt LED bit
        	}
        }
        
        
        int main (void)
        {
         PINB = 0b00000100; // pull up resistor for PB2
         DDRA = 0b00000110; // two LEDS output
         
         GIMSK |= (1 << INT0); // enable the INT0 external interrupt on pin PB2
         sei(); //enable global interrupts
         
         while (1)
         {
        
        	 PORTA ^= 0b00000100; //toggle the blinking light
        	 PORTA &= ~0b00000010; // turn off the interrupt light
        	 _delay_ms(200);
        	
         }
        }
        

        Understanding I2C

         

        I2C (a.k.a. TWI, 2 Wire Interface) is a communication protocol that can allow your “master” microcontroller to speak to up 120+ “slave” devices. The protocol requires only two wires, one for data (SDA) and the other for the clock (SCL). Each device is connected to the same two wires but each also has a unique 7 bit address so it knows when the master is communicating with it. Data can go both ways, from the Master to the Slave but also from the Slave to the Master.

        Data is read when the SCL goes high (a.k.a. when SCL is “released”, i.e. not pulled down, and allowed to be pulled up by the 4.7k resistors) and data can be changed while SCL is low.

        After the Master sends the seven bit address of the slave it wishes to speak with, it sends Read (1) or Write (0) bit. The slave then sends an Acknowledge (ACK) – 0 / Not Acknowledge (NACK) – 1 bit. This means that each communication portion is 9 bits in total.

        If the master wants to write to the slave the next communication portion will be 8 bits of data followed by an ACK/NACK from the Slave.

        Pins 27 (Data) and 28 (Clock) for the Atmega 168 / 328 are the pertinent pins.

        Here is my breadboard set up for testing two Atmega168 with I2C. I have I2C cables which makes changing the sender and receiver code with the atmelICE less of a chore. I first wanted to use attiny but then learned that the Attiny family does not have the same level of I2C support as the Atmega family. The Attiny Universal Serial Interface (USI) can be used with I2C but the user has to do more work in software and can rely on fewer hardware peripherals to help with the job.

        While there are many examples of Atmega master codes, I found it hard to find examples of Atmega slave code. Most often we use I2C to speak with a sensor which is the slave. I was interested in microchip to microchip communication and so decided to get two atmegas talking to one another. Elliot Williams’ Make: AVR Programming was a great resource for making the code.

        For the breadboard set up I have a power LED and a yellow LED for successfully sending the slave address and a green LED for successfully sending the data. On the RX side I have a yellow LED for successfully receiving the slave address and a green LED for successfully receiving the data. I have two 4.7K pull-up resistors for the SDA and SCL lines and a 10K pullup on reset. It’s powered with a 5V adapter.

        Digiscope Fablab has a 100MHz 4 channel Agilent DSO-X 2014A Oscilloscope with a demo version of the I2C, SPI and USART decoder software. To get the oscilloscope into the right mode I selected the Serial button (on the right side), select the type of protocol to decode (I2C) and then the Lister Button to show the decoded data above the signals. To set the trigger press trigger and select Serial (I2C) from the Trigger Type menu and then select to Trigger on Start. Put the oscilloscope into Single Run Control mode and then connect the circuit to power and hope it triggers. If you are triggering on the power spike that occurs when you plug in the circuit, try adding a delay at the start of the code so you can comfortably do the plugging in, then setting into Single Run Mode and waiting for the trigger.

        I have my signals in 1V per square and am between 200ms and 20ms per time division. I use the smaller horizontal knob to move the recorded signal side to side.

        In these first examples I had difficulty getting past the first part of the I2C communication of sending the slave address (7 bits) + the Read (1) or Write (0) bit.

        Eventually I could get communication going, including reading bytes sent from the slave and multiple starts during a conversation. The issue turned out to be strange, if I set both the TWEN and TWEA bits of the TWCR in the same line, I never got past the first slave address stage of the I2C conversation. When I set then in different lines everything works smoothly. Still not sure why this is the case!

        Here is the code I developed (for Master):

        
        /*
         * I2C atmega168.c
         *
         * Created: 2/22/2019 10:57:39 AM
         * Author : FablabDigiscope
         
         4.7K pull up resistors on SDA and SCL
         10K pull up on reset
         
         2x atmega168
         
         */ 
        
        #include <avr/io.h>
        #include <util/delay.h>
        
        #define F_CPU                8000000
        #define SLAVE_ADDRESS_W	     0b10010000/* 0x90: 7 bit slave address + R(1)/W(0) bit (then slave responds with ACK(0)/NACK(1) bit) */
        #define SLAVE_ADDRESS_R		 0b10010001/* 0x91: 7 bit slave address + R(1)/W(0) bit (then slave responds with ACK(0)/NACK(1) bit) */
        
        #define SLAVE2_ADDRESS_W	 0b10110000/* 0x90: 7 bit slave address + R(1)/W(0) bit (then slave responds with ACK(0)/NACK(1) bit) */
        #define SLAVE2_ADDRESS_R	 0b10110001/* 0xB1*/
        
        #define DATA_TO_SEND		 0b00000000/* 0x00 */
        #define DATA_TO_SEND_2		 0b11111111/* 0xFF */
        #define DATA_TO_RECEIVE		 0b10101010/* OxAA */
        #define DATA_TO_RECEIVE_2	 0b01010101/* Ox55 */
        
        //function prototypes
        void i2cInit(void);
        void i2cStart(void);
        void i2cWaitForComplete1(void);
        void i2Send(uint8_t data); //data sent same way as address
        void i2cWaitForComplete2(void);
        							//data sent same way as address
        void i2cWaitForComplete3(void);
        void i2cStop(void);
        uint8_t  i2cRead(void);
        
        
        int main(void)
        {
        	DDRB = 0b11111111; /* green LED for all good */
        	DDRD = 0b11111111; /* yellow LED for all good, red for errors */
        
        	i2cInit();
        		
            while (1) 
            {
        		_delay_ms(1000);
        		
        
        		i2cStart();
        		i2cWaitForComplete1();
        		i2Send(SLAVE_ADDRESS_W);
        		i2cWaitForComplete2();
        		i2Send(DATA_TO_SEND);
        		i2cWaitForComplete3();
        		
        				i2cStart();
        				i2cWaitForComplete1();
        				i2Send(SLAVE_ADDRESS_R);
        				i2cWaitForComplete2();
        				
        				if (DATA_TO_RECEIVE == i2cRead())
        				{
        					PORTD |= (1<<PD7); // turn on green led
        				}				
        				
        								i2cStart();
        								i2cWaitForComplete1();
        								i2Send(SLAVE2_ADDRESS_W);
        								i2cWaitForComplete2();
        								i2Send(DATA_TO_SEND_2);
        								i2cWaitForComplete3();
        								i2cStop();
        								
        								i2cStart();
        								i2cWaitForComplete1();
        								i2Send(SLAVE2_ADDRESS_R);
        								i2cWaitForComplete2();
        								
        								if (DATA_TO_RECEIVE_2 == i2cRead())
        								{
        									PORTD |= (1<<PD6); // turn on green led
        								}
        								
        
        	}
        }
        
        
        //function declarations
        
        void i2cInit(void)
        {
        		TWBR = 32; /*set bit rate to 100kHz*/
        		TWCR |= (1 << TWEN); /*enable I2C*/
        }
        
        void i2cStart(void)
        		{
        			/*
        	
        		I2C start
        					   _____________
        					  |				|
        		SCL: _________|				|_____
        	
        				  __________
        				 |		    |
        		SDA: ____|		    |_____________
        	
        		*/
        
        		TWCR = (1 << TWEN) | (1 << TWINT) | (1 << TWSTA); /*set to enable the 2-wire serial interface ; clear the TWINT flag ; TWSTA written to one to transmit a START condition */
        			
        		}
        
        void i2cWaitForComplete1(void)
        	{
        	
        	/*I2C wait for complete*/
        	while (!(TWCR & (1<<TWINT)));
        	
        	if ((TWSR & 0xF8) != 0x08) // 0x08 = A START condition has been transmitted
        	{
        		PORTD |= (1 << PD0);	//ERROR
        	}
        	
        	if ((TWSR & 0xF8) == 0x00)
        	{
        		PORTD |= (1 << PD5);	//ERROR	 Bus error
        	}
        	}
        
        void i2Send(uint8_t data)
        {
        		/*
        	I2C send data
        	
        	
        			 ___     ___	 ___	 ___	 ___	 ___	 ___	 ___	 ___
        		    |	|	|	|	|	|	|	|	|	|	|	|	|	|	|	|	|	|
        	SCL: ___|	|___|	|___|	|___|	|___|	|___|	|___|	|___|	|___|	|___
        	
        
        	SDA:	 D8		 D7		  D6	 D5       D4      D3      D2     D1       D0	
        	
        	*/
        
        	TWDR = data; /*send data - do this BEFORE setting TWINT*/
        	TWCR = (1 << TWINT) | (1 << TWEN);
        	
        }
        
        void i2cWaitForComplete2(void)
        {
        	
        /*I2C wait for complete*/
        while (!(TWCR & (1<<TWINT)));
        
        if ((TWSR & 0xF8) != 0x18) // 0x18 = SLA+W has been transmitted; ACK has been received
        {
        	if ((TWSR & 0xF8) == 0x20)
        	{
        		PORTD |= (1 << PD1);	//ERROR	 Not acknowledge received after the slave address
        	}
        	if ((TWSR & 0xF8) == 0x38)
        	{
        		PORTD |= (1 << PD2);	//ERROR	 Arbitration lost in slave address or data byte
        	}
        	if((TWSR & 0xF8) == 0x68)
        	{
        		PORTD |= (1 << PD3);	//ERROR	 Arbitration lost and addressed as slave
        	}
        	if ((TWSR & 0xF8) == 0x00)
        	{
        		PORTD |= (1 << PD5);	//ERROR	 Bus error
        	}
        }
        }
        void i2cWaitForComplete3(void)
        {
        	
        	/*I2C wait for complete*/
        	
        	while (!(TWCR & (1<<TWINT)));
        
        	if ((TWSR & 0xF8) != 0x28) //0x28 = Data byte has been transmitted; ACK has been received
        	{
        		if ((TWSR & 0xF8) == 0x30)
        		{
        			PORTD |= (1 << PD4);	//ERROR	Not acknowledge received after a data byte
        		}
        		if ((TWSR & 0xF8) == 0x00)
        		{
        			PORTD |= (1 << PD5);	//ERROR	 Bus error
        		}
        	}
        }
        
        void i2cStop(void)
        {
        	/*
        	
        	I2C stop
        	
        				   _____________
        				  |				|
        	SCL: _________|				|_____
        	
        					  ________________
        					 |		    
        	SDA: ____________|		    
        	
        	*/
        
        	TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
        	//_delay_us(10);
        }
        
        uint8_t i2cRead(void)
        {
        	
        	TWCR = (1 << TWINT) | (1 << TWEN); //read with no ack
        	while (!(TWCR & (1<<TWINT)));
        	return (TWDR);
        }
        
        

        Here is the code I developed (for Slave):

        
        /*
         * atmega I2C slave.c
         *
         * Created: 2/27/2019 7:02:34 PM
         * Author : FablabDigiscope
         */ 
        
        #include <avr/io.h>
        
        
        #define F_CPU                8000000
        #define SLAVE_ADDRESS_W	     0b10010000/* 0x90: 7 bit slave address + R(1)/W(0) bit (then slave responds with ACK(0)/NACK(1) bit) */
        #define SLAVE_ADDRESS_R		 0b10010001/* 0x91: 7 bit slave address + R(1)/W(0) bit (then slave responds with ACK(0)/NACK(1) bit) */
        
        #define SLAVE2_ADDRESS_W	 0b10110000/* 0x90: 7 bit slave address + R(1)/W(0) bit (then slave responds with ACK(0)/NACK(1) bit) */
        #define SLAVE2_ADDRESS_R	 0b10110001/* 0xB1*/
        
        #define DATA_TO_SEND		 0b00000000/* 0x00 */
        #define DATA_TO_SEND_2		 0b11111111/* 0xFF */
        #define DATA_TO_RECEIVE		 0b10101010/* OxAA */
        #define DATA_TO_RECEIVE_2	 0b01010101/* Ox55 */
        
        int main(void)
        {
        	DDRB = 0b11111111;
        	DDRD = 0b11111111;
        
            while (1) 
            {
        
        
        	TWAR = SLAVE2_ADDRESS_W;    // Fill slave address in TWAR register
        	TWCR |= (1 << TWEN);  // I2c enable; MYSTERY: if I set these two bits at the same time nothing works.
        	TWCR |= (1 << TWEA);  // enable acknowledge (automatically generates ACK)
        	
        	while (!(TWCR & (1<<TWINT))); // interrupt flag which tells slave when it's address is being called
        	
        		if((TWSR & 0xF8) == 0x60) // SLA+W received, ACK'd
        		{
        			PORTB |= (1<<PB0);
        		}
        		
        			
        		if((TWSR & 0xF8) == 0x80) // Data received, ACK'd...
        		{
        			if (TWDR == DATA_TO_SEND_2) // ...and data matches what was send
        				{
        				PORTD |= (1<<PD7);
        				}
        		}
        		
        
        	while (!(TWCR & (1<<TWINT))); // interrupt flag which tells slave when it's address is being called
        	
        			if((TWSR & 0xF8) == 0xA8) // Own SLA+R has been received; ACK has been returned
        			{
        				TWDR = DATA_TO_RECEIVE_2;
        			}
        			
        		while (!(TWCR & (1<<TWINT))); // interrupt flag which tells slave when it's address is being called
        		
        		if((TWSR & 0xF8) == 0xC0) // Data byte in TWDR has been transmitted; NOT ACK has been received
        		{
        			PORTD |= (1<<PD6);
        		}
        		
        // 		if((TWSR & 0xF8) == 0xB8) // Data byte in TWDR has been transmitted; ACK has been received
        // 			PORTD |= (1<<PD6);
        // 		}	
        		
        // 		if((TWSR & 0xF8) == 0xC8) // Own SLA+R has been received; ACK has been returned
        // 		{
        // 			PORTD |= (1<<PD6);
        // 		}
        // 			
        	
        	}
        }
        
        

        In the end there were so many issues that I had added LEDs for each different error type with little notes describing the error in question. This experience also convinced me that it would help to have a dedicated I2C board for first-timers.

        EEPROM

         

        Want to write a value so that you can access it if the microchip loses power and restarts? Write it to EEPROM!

        The Attiny 24/44/84 family have 128/256/512 Bytes of In-System Programmable EEPROM, so it’s not enormous…

        The avr/eeprom.h code is from avr-libc, a Standard C library for AVR-GCC. Here’s a link to the library:(https://www.nongnu.org/avr-libc/user-manual/group__avr__eeprom.html)

        The coolest thing about this code is seeing what you have written to EEPROM in AVR Studio’s EEPROM viewer when you’re in Debug Mode.

        
        /*
         * Attiny84 EEPROM.c
         *
         * Created: 2/5/2019 2:18:28 PM
         * Author : FablabDigiscope
         */ 
        
        #include <avr/io.h>
        #include <avr/eeprom.h>
        
        int main(void)
        {
        
            while (1) 
            {	
        		uint8_t hello[] = "Wow this is really cool!"; 
        		eeprom_write_block (hello, (void*)0x00AA, sizeof (hello)); // structure: void eeprom_write_block (const void *__src, void *__dst, size_t __n)
            }
        }
        
        

        Atmel’s instructional videos on EEPROM on Youtube were helpful for this code.

        Dual Solar Tracking

         

        Following on from the single axis solar tracker I built recently I would like to make a dual axis solar tracker – but with a twist: I would like one axis to be a set of tank treads. This way one can eventually imagine an autonomous solar powered robot which tracks the sun and maybe can complete other tasks.

        After making the single axis solar tracker from logic chips I decided there was a limit to how complex a system could be created in this manner. There were a sufficient number of connections that debugging the system was discouraging. I would like to continue the sleep project, which uses an Attiny2313 and a simple nocturnal solar charging circuit to play a user-defined piezo piano tune. I would like in this project to use an Attiny2313 to control the two axis solar tracking. This will involve a series of mosfet H-bridges (I can’t find commercial H-bridges that are low power enough at the moment), and a capacitor to smooth out the spikes produced by the motors which could threaten the microchip’s power supply. It may also require the use of a more sophisticated system for running a chip and motors off a solar charged super capacitor, such as the LTC3105 solar harvesting chip.

        The first test is if a solar engine can boot up a microchip (not awaken from sleep as the power supply is zero after a solar engine firing) which can then check a sensor and activate (latch on, really) an H-bridge which will allow a motor to turn until beyond the point the microchip loses power and shuts down. However, this is currently not working and I’m not quite sure exactly why at the moment…

        Here is the breadboarded circuit with Attiny84 and h-bridge all powered by a solar engine.

        The h-bridge…

        …and the solar engine…

        My first step was to figure out how to attach a motor to a solar panel and then to a post which is attached to a tank movement system? Here are some of my attempts below. I am using a robot tank tread platform I developed a few years ago and which I describe at this instructable: https://www.instructables.com/id/Mini-Robot-Platform/ . The 3D model of the chassis is available there, it is printed on a resin printer and pairs nicely with a popular gear motor product. The robot platform has limitations: it doesn’t like slippery surfaces and the gears do fall off the rollers occassionally but it’s compact and I already have leftover chassis so it makes sense to reuse it.

        This is inelegant and takes up too much space:

        This is more compact but destroys itself if you turn the motor too much.

        This might be a decent balance but it’s still heavy.

        This little motor would have been perfect to turn a solar panel but the tiny shaft is difficult to attach to the panel.

        Here I’m using an allen key which is super glued to the flat side of a different motor shaft. Works for me!

        Moving on to using the LTC3105 board now. The goal is to get a microchip running with the LDO output by the chip, and then to have the microchip activate the motors with an H-bridge which is connected to the Vout of the LTC3105.

        I’m combining several typical application circuits which are presented in the datasheet and have made a small PCB which will serve as a demoboard to do some tests.

        Here is the demo set up which is currently not working:


        I 3D printed some nice thingiverse 2 axis pan-tilts which use mini-servos:

        https://www.thingiverse.com/thing:1401116

        https://www.thingiverse.com/thing:708819

        I’m looking in to which voltage and current rating I should chose for the solar panels.

        Part Number : KXOB25-05X3F Open Circuit Voltage [V]: 2.07 Short Circuit Current [mA]: 19.5 Typ. Voltage @ Pmpp [V]: 1.67 Typ. Current @ Pmpp [mA]: 18.4

        For the LTC3105 the highest efficiency is around 2.25V (between 2-2.5V) and drops sharply off at 2.5V. 2.07V is at 85% efficiency which I think it good enough. I have found that solar panels sometimes go above their OCV too.

        Quoting from the datasheet: http://ixapps.ixys.com/DataSheet/KXOB25-05X3F.pdf

        “Monocrystalline cells, such as the IXYS SolarMD, have a spectral sensitivity range from 300 nm (near-ultraviolet) to 1100 nm (near-infrared), which includes visible light (400 to 700 nm). Due to this wide spectral range, they can be used in both indoor and outdoor applications. Monocrystalline or single-crystalline material is the most expensive but it does not contain impurities, and as such the power conversion efficiency does not degrade over operating time. The power conversion efficiency of commercially available monocrystalline cells ranges from 15 to 25%. The surface of these cells is a homogenous dark blue or dark grey.”

        Apparently there is about 1,360 watts per square meter of sunlight. However, “Averaged over the entire planet, the amount of sunlight arriving at the top of Earth’s atmosphere is only one-fourth of the total solar irradiance, or approximately 340 watts per square meter.” (https://earthobservatory.nasa.gov/features/EnergyBalance/page2.php). Solar panels are 15-20% efficient so, on average, 50-70W ish.

        Button Debouncing

        Microchips exist at a different time scale from humans, their electronics “hearts” can beat millions of times a second – think of them as extremely hyperactive brains. Microchip pins can either “listen” (take input) or send voltage signals (output).When in input mode, a microchip pin can detect the state of a button (button pushed, or button unpushed) and in output mode it can turn on or off an LED.When a human pushes down on a mechanical button and lifts their finger away, the spring inside the button pushes the button back up to its initial position. During this process there may be micro-bounces of the metal contacts inside the button. A microchip listening to the input at this time can interpret these micro bounces as changes of button states, and if the number of these bounces is odd, the microchip can conclude that the button has settled into a state other than the one that it is from the perspective of a human being.To resolve this issue, known as bouncing, there are two approaches: software or hardware. In software, the microchip can effectively check the state of the button twice, after a delay which exeeds the period of time that a button can bounce for. This code essentially detects a change in button state, then waits a few milliseconds and checks to see that the button is in the same state. This effectively rules out the possibility of having detected a button debounce.The second possibility is to do the debouncing with hardware, that is, electronically. In this approach, we stop the erroneous button state signals from arriving at the microchip in the first place. This typically involves using a small capacitor, the hardware equivalent of a delay in our code.Top and bottom rows of the button are connected when the switch is activated (pushed on).We want the microchip to read two distinct states: HIGH or LOW. When the button is unpressed, the 10K pull-down resistor connects PB0 to GND. When the button is pressed, the pin is connected to 5V directly, a more appealing option for the electrons than the large resistor towards ground. This circuit is NOT debounced.

        Bouncing button gallery

        Debounced circuit

        The solution to this bouncing button with hardware is a combination of an RC (resistor capacitor) circuit along with an inverter. The product of R*C (known as tau) should be greater than the debounce time. In the circuit below, 100K * 0.1uF = 0.01 seconds, which is the time it will take to charge 63.2% of the capacitor.

        Debounce circuit from Learning the Art of Electronics, p.874 by Thomas C. Hayes. Ignore the stuff after the digital input.

        This article: https://hackaday.com/2015/12/09/embed-with-elliot-debounce-your-noisy-buttons-part-i/ goes in to greater depth explaining how this process works but check out the results:

        Debounced circuit oscilloscope trace

        Yellow is inverter output, green is RC output.

        To confirm our calculation of RC (time until 63% charged) = 0.01 seconds.

        Deboucing in action

        Software debouncing

        
        int main(void)
         {
             DDRD  = 0b11111111; // Port D output
             DDRB  = 0b00000000; // Port B input
         
        PORTB = 0b11111111; // pull-up resistors enabled
         
        while (1) 
        {
         
            if (PINB & (1<<0))
            {
                _delay_ms(50); //
         
                        if (PINB & (1<<0))
                        {
         
                            PORTD = 0b00000000; // turn off LED
                        }
            }        
         
         
            else 
            {
                 PORTD = 0b11111111; // turn on LED
            }
        }
        return(1);
        }
        

         

        555 signal generator + LM358 op amp + recycled 8ohm speaker

         

        This circuit is from page 19 of Forrest M. Mimm’s Engineer’s Mini-Notebook : 555 TImer IC Circuits.

        I am using only a 0.1uF capacitor and don’t have any of the buttons shown in this circuit.

        This circuit is from page 259 of Learning the Art of Electronics: A Hands on Lab course by Thomas C. Hayes.

        I am taking the output from the 555 circuit and feeding it to the + input of an LM358 operational amplifier. Unlike the LM386 which is optimized for audio amplifying, the LM358 needs a push pull to amplify the signal.

        I’m using an 2N2222 and a 3906 as transistors and a recycled speaker as output. I added a 270 ohm resistor between the push-pull an the output of the LM358 as well as a 100K resistor in the negative feedback loop, as is done elsewhere in Learning the Art of Electronics.

        The yellow is the signal produced by the 555 and the green is the amplified (and inverted – because we’re using an inverting op amp configuration) signal. But I think there is a mistake here because I don’t see any amplification here, just inversion!

        I tried again with a different opamp and circuit configuration. This is the LM386 which is optimized for audio amplification of digital signals.

        I am using a circuit from p.920 of The Art of Electronics but it is essentially the same as the base circuit suggested in the LM386 datasheet:

        I’m running out of ways to take interesting photos of breadboards but here’s another go:

        And here is a look on the scope, the green is the input signal (2.5V 1KHz demo signal from oscilloscope) and the yellow is the amplified signal. This circuit is supposed to have a gain of 20, gain being the ratio of Vout/Vin. This scope image would appear show a gain of under 2 though… In any case pins 1 and 8 allow to increase the gain to higher values and increasing the supply voltage of the LM386 up to 12V would allow for even more amplification. The only trick at that point is checking to see what the range of voltage is for the input signal and about what voltage it is centered (say ground, for instance).

        Attiny Blink

        If you have a brand spanking new Attiny84 you’ll need to change the fuses in order to get the timing right.

        In AVR Studio you can change fuses in the Device Programming Window:

        
        #define F_CPU 8000000 // I also disable the CLKDIV8 fuse //
        
        #include <avr/io.h>
        
        #include <util/delay.h> // because I'm using _delay_ms() //
         
        int main(void)
        {
         DDRA = 0b11111111; // All port A pins set to output
         
            while (1) // while value inside the brackets is non-zero,
            {
                PORTA = 0b11111111; // All port A pins HIGH
                _delay_ms(1000); //1000ms = 1 second
                 
                PORTA = 0b00000000; // All port A pins LOW
                _delay_ms(1000);        
            }
            return (0);
        }
        
        

        The only thing to remember here is to first declare the pins to be outputs before setting them to high. Most AVR registers default to 0, so the DDRx registers are by default set to all inputs. To change that you need to write 1’s to the pins in that register. You can think of a register as a series of 8 switches, just like this guy:

        These are the two registers you are changing: DDRx to set the pin as an output, PORTx to send it HIGH.

        Analog Read

        Being able to read values other than 0 or 1 can come in handy!

        Here I am using the Attiny84 along with a 10K potentiometer. The two ends of the potentiometer are attached to GND and VCC and the middle pin is going to PA1 (ADC1). I have PA0, the ADC reference voltage pin I selected, connected to 5V. I have an LED on PB1 to display the treshold being crossed. I am running the microchip at 8MHz.

        
        /*
         * Attiny84 analog read.c
         *
         * Created: 19/01/28 2:20:06 PM
         * Author : marrs
         */
        #define F_CPU 8000000
        
        #include <avr/io.h>
        #include <util/delay.h>
        
        
        int main(void)
        {
        	ADMUX |= (1 << REFS0); // PA0 ref voltage
        	ADMUX = (0b11110000 & ADMUX) | PA1; // PA1 input select
        	ADCSRA |= (1 << ADPS2) | (1 << ADPS0); // set clock to 32 divisions for 8MHz
        	ADCSRA |= (1 << ADEN); /* enable ADC */
        
        	uint16_t adcValue; //16 bit variable because the ADC on the Attiny84 is 10 bits.
        	DDRB = 0b00000001; //LED on PB0
        
            while (1)
            {
        	ADCSRA |= (1 << ADSC); /* start conversion */
        	_delay_ms(1); //instead of waiting for ready flag
        	adcValue = ADC; /* store high byte into adcValue */
        
        	if (adcValue > 200)
        	{
        		PORTB = 0b00000001;
        	}
        	else
        	{
        		PORTB = 0b00000000;
            }
        	_delay_ms(50);
        }
        return(0);
        }
        
        

        The only tricky part here for me (being a C newb!) was storing just the high byte of the ADC and understanding the size of data types relative to what is being stored inside of them. I couldn’t manage to read just ADCH or ADCL

        Elliot Williams’ Make: AVR Programming came in handy for this project.

        The result (the analog value from the potentiometer is in yellow, the green LED which is triggered is in green):